diff --git a/docs/user_guide.md b/docs/user_guide.md
index 1ba01a4..40dd61b 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -1272,11 +1272,11 @@ Select **Return to Forecast** to restore the saved Caelus theme.
The weather display uses the existing Sensorius Astral latitude, longitude,
timezone, and optional Community/Location Name. Its top row presents the selected sensor's latest current
readings, the canonical Sensorius forecast, and the sunrise/sunset daylight
-track. Hourly forecast times use the local 12-hour AM/PM clock. The sunlight tile also shows current North and South Pole daylight, the
+track. Hourly forecast times use the local 12-hour AM/PM clock. The sunlight tile also shows the
next seasonal event, and up to three solar or lunar eclipses visible from the
-configured Astral location during the next twelve months. A full-width Windy
-map opens in radar view below that row, followed by
-the current Moon and phase cycle at the bottom. The display also provides
+configured Astral location during the next twelve months. A two-column row below pairs the Windy
+map in radar view with the Lunar Calendar showing the current Moon and phase
+cycle. These tiles stack on narrow screens. The display also provides
theme-matched six-day details. It reuses the Sensorius forecast and does not
maintain a separate settings file or readings database.
diff --git a/sensorius/__init__.py b/sensorius/__init__.py
index 630118e..3d934b0 100644
--- a/sensorius/__init__.py
+++ b/sensorius/__init__.py
@@ -4,4 +4,4 @@
release notes, and supporting tooling can report a consistent build identity.
"""
-__version__ = "v0.26.271.7"
+__version__ = "v0.26.274.2"
diff --git a/testApparatus/test_integrated_biodynamic_calendar.py b/testApparatus/test_integrated_biodynamic_calendar.py
index 393e786..1021730 100644
--- a/testApparatus/test_integrated_biodynamic_calendar.py
+++ b/testApparatus/test_integrated_biodynamic_calendar.py
@@ -231,7 +231,8 @@ def test_integrated_assets_use_namespaced_routes_and_dashboard_navigation():
assert '
Planetary Aspects ' in template
assert 'class="hero-calendar-mark"' in template
assert '/ui_static/biodynamic_calendar/bd-calendar-icon-512.svg' in template
- assert "grid-template-columns: minmax(320px, 1fr) 214px minmax(320px, 1fr);" in stylesheet
+ assert "--top-middle-col: 214px;" in stylesheet
+ assert "grid-template-columns: var(--top-left-col) var(--top-middle-col) var(--top-right-col);" in stylesheet
assert "border-radius: 50%;" in stylesheet
assert "object-fit: contain;" in stylesheet
assert "transform: scale(1.24);" not in stylesheet
diff --git a/testApparatus/test_integrated_weather_forecast.py b/testApparatus/test_integrated_weather_forecast.py
index 54cf8f2..0649543 100644
--- a/testApparatus/test_integrated_weather_forecast.py
+++ b/testApparatus/test_integrated_weather_forecast.py
@@ -420,16 +420,15 @@ def test_astronomy_context_flags_an_observer_visible_eclipse_in_the_next_24_hour
assert eclipse["ends"].endswith((" AM", " PM"))
-def test_sunlight_card_places_times_horizontally_and_shows_poles():
+def test_sunlight_card_places_times_horizontally_without_poles():
template = (ROOT / "ui_templates" / "weather_forecast" / "index.html").read_text()
script = (ROOT / "ui_static" / "weather_forecast" / "app.js").read_text()
css = (ROOT / "ui_static" / "weather_forecast" / "app.css").read_text()
- assert 'id="northPoleDaylight"' in template
- assert 'id="southPoleDaylight"' in template
+ assert 'id="northPoleDaylight"' not in template
+ assert 'id="southPoleDaylight"' not in template
assert 'id="nextSeasonLabel"' in template
assert 'id="nextSeasonDate"' in template
- assert template.index('id="northPoleDaylight"') < template.index('id="nextSeasonHeading"')
assert 'id="nextEclipseHeading"' in template
assert 'id="nextEclipseList"' in template
assert "No visible eclipses for the next 12 months" in template
@@ -437,7 +436,8 @@ def test_sunlight_card_places_times_horizontally_and_shows_poles():
assert "grid-template-columns: repeat(3, minmax(0, 1fr))" in css
assert ".daylight-times dd { order: -1;" in css
assert "function formatSolarTime(value)" in script
- assert 'moon.north_pole_daylight ?? "—"' in script
+ assert 'document.getElementById("northPoleDaylight")' not in script
+ assert 'document.getElementById("southPoleDaylight")' not in script
assert 'moon.next_season_label ?? "—"' in script
assert "moon.next_eclipses" in script
@@ -451,7 +451,6 @@ def test_sunlight_card_uses_three_semantic_text_colors():
assert ".daylight-card .eyebrow," in css
assert ".daylight-detail-section h3 { color: var(--daylight-title-color); }" in css
assert ".daylight-times dd { order: -1; margin: 0; color: var(--daylight-data-color);" in css
- assert ".polar-daylight-note strong { display: block; color: var(--daylight-data-color);" in css
assert ".eclipse-list time { color: var(--daylight-data-color);" in css
assert ".daylight-times dt { color: var(--daylight-status-color);" in css
assert ".next-season-note strong { color: var(--daylight-status-color);" in css
@@ -608,7 +607,7 @@ async def _fake_forecast(*_args, **_kwargs):
assert "Station reporting" in readings_panel
assert "Station reporting" not in page.text[:page.text.index('id="conditions"')]
assert "Environmental decisions" not in page.text
- assert 'class="glass-card map-card full-width-map"' in page.text
+ assert 'class="glass-card map-card"' in page.text
assert page.text.index('id="conditions"') < page.text.index('id="map"') < page.text.index('id="moon"')
assert "overlay=radar" in page.text
assert forecast.json()["days"][0]["label"] == "Sat Aug 8"
diff --git a/ui_static/biodynamic_calendar/app.css b/ui_static/biodynamic_calendar/app.css
index dd474a0..eb447a6 100644
--- a/ui_static/biodynamic_calendar/app.css
+++ b/ui_static/biodynamic_calendar/app.css
@@ -97,7 +97,7 @@ body.biodynamic-theme-garden_tools {
.page {
--layout-gap: 16px;
--top-left-col: minmax(320px, 1fr);
- --top-middle-col: minmax(320px, 0.88fr);
+ --top-middle-col: 214px;
--top-right-col: minmax(320px, 1fr);
max-width: 1320px;
margin: 0 auto;
@@ -477,7 +477,7 @@ button:active {
.hero-grid {
display: grid;
- grid-template-columns: minmax(320px, 1fr) 214px minmax(320px, 1fr);
+ grid-template-columns: var(--top-left-col) var(--top-middle-col) var(--top-right-col);
grid-auto-rows: 214px;
gap: var(--layout-gap);
align-items: stretch;
diff --git a/ui_static/weather_forecast/app.css b/ui_static/weather_forecast/app.css
index b07e258..79315b9 100644
--- a/ui_static/weather_forecast/app.css
+++ b/ui_static/weather_forecast/app.css
@@ -461,18 +461,23 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.lunar-header {
display: grid;
- grid-template-columns: 1fr minmax(15rem, 0.72fr) 1fr;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 0.8rem;
align-items: center;
- min-height: 17rem;
- margin-top: 0.85rem;
- padding: 1.25rem 1.5rem 4.8rem;
+ min-height: 31rem;
+ min-width: 0;
+ padding: 1.25rem 1.5rem 5.5rem;
background: linear-gradient(105deg, rgba(4, 16, 40, 0.94), rgba(7, 35, 51, 0.78));
}
+.lunar-calendar-title { grid-column: 1 / -1; margin: 0; align-self: start; font: 700 1.4rem Georgia, serif; }
+.lunar-side-left { grid-column: 1; grid-row: 3; }
+.lunar-side-right { grid-column: 2; grid-row: 3; }
+.lunar-current { grid-column: 1 / -1; grid-row: 2; }
.lunar-arc {
position: absolute;
left: 5%;
right: 5%;
- top: 2.1rem;
+ top: 4.1rem;
height: 8rem;
border-top: 1px solid rgba(239, 194, 101, 0.7);
border-radius: 50% 50% 0 0;
@@ -480,16 +485,16 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
}
.lunar-side { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 0.6rem; position: relative; z-index: 1; }
.lunar-side-title { grid-column: 1 / -1; margin: 0 0 0.1rem; color: rgba(235,247,240,.52); font-size: 0.5rem; font-weight: 750; letter-spacing: 0.14em; text-align: center; text-transform: uppercase; }
-.lunar-step { text-align: center; color: var(--muted); }
-.lunar-step-disk { display: block; width: clamp(2.2rem, 3.6vw, 3.6rem); height: clamp(2.2rem, 3.6vw, 3.6rem); margin: 0 auto; filter: drop-shadow(0 0 11px rgba(237,229,198,.18)); }
-.lunar-step small { display: block; min-height: 1.3rem; margin-top: 0.3rem; font-size: 0.48rem; line-height: 1.25; letter-spacing: 0.08em; text-transform: uppercase; }
+.lunar-step { min-width: 0; text-align: center; color: var(--muted); }
+.lunar-step-disk { display: block; width: min(100%, clamp(2.2rem, 3.6vw, 3.6rem)); height: auto; aspect-ratio: 1; margin: 0 auto; filter: drop-shadow(0 0 11px rgba(237,229,198,.18)); }
+.lunar-step small { display: block; overflow-wrap: anywhere; min-height: 1.3rem; margin-top: 0.3rem; font-size: 0.48rem; line-height: 1.25; letter-spacing: 0.08em; text-transform: uppercase; }
.lunar-step time { display: block; margin-top: 0.14rem; color: var(--warm); font-size: 0.52rem; font-weight: 700; letter-spacing: 0.04em; }
.lunar-current { position: relative; z-index: 2; text-align: center; align-self: start; margin-top: -0.35rem; }
.lunar-current-disk { display: block; width: 6rem; height: 6rem; margin: 0 auto; filter: drop-shadow(0 0 24px rgba(255,235,177,.34)); }
.lunar-current h2 { margin: 0.2rem 0 0; font: 500 1.25rem Georgia, serif; }
.lunar-current p:last-child { margin: 0.35rem 0 0; color: var(--muted); font-size: 0.65rem; }
.observer-view-label { color: var(--accent-2); font-size: 0.52rem; letter-spacing: 0.08em; text-transform: uppercase; }
-.lunar-event-footer { position: absolute; left: 1.5rem; right: 1.5rem; bottom: 0.45rem; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 1rem; color: var(--muted); font-size: 0.58rem; }
+.lunar-event-footer { position: absolute; left: 1.5rem; right: 1.5rem; bottom: 0.45rem; display: grid; grid-template-columns: minmax(0, 1fr); align-items: end; gap: 0.3rem; color: var(--muted); font-size: 0.58rem; }
.lunar-event-timeline { display: grid; gap: 0.3rem; min-width: 0; }
.lunar-event-row { display: grid; grid-template-columns: 2.5rem minmax(0, 1fr); align-items: center; gap: 0.55rem; }
.lunar-event-row-title { color: rgba(235,247,240,.58); font-size: 0.48rem; font-weight: 750; letter-spacing: 0.13em; text-align: right; text-transform: uppercase; }
@@ -503,7 +508,7 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.lunar-event-marker.is-start { justify-items: start; transform: none; }
.lunar-event-marker.is-end { justify-items: end; transform: translateX(-100%); }
.lunar-event-marker.is-unavailable { display: none; }
-.lunar-event-footer > small { align-self: end; color: rgba(235,247,240,.42); white-space: nowrap; }
+.lunar-event-footer > small { justify-self: end; align-self: end; color: rgba(235,247,240,.42); white-space: nowrap; }
.conditions-row { display: grid; grid-template-columns: 0.82fr 1.48fr 0.82fr; gap: 0.85rem; align-items: stretch; }
.readings-panel, .forecast-panel, .daylight-card { min-height: 32rem; padding: 1.25rem; }
@@ -593,9 +598,9 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.forecast-empty { min-height: 22rem; display: grid; place-content: center; text-align: center; color: var(--muted); }
.forecast-empty span { color: var(--accent-2); font-size: 4rem; }
.forecast-empty p { max-width: 20rem; font-size: 0.75rem; }
-.map-row { display: grid; grid-template-columns: 0.82fr 1.48fr 0.82fr; gap: 0.85rem; padding: 0.85rem 0; }
-.full-width-map { grid-column: 1 / 4; width: 100%; min-height: 31rem; }
-.full-width-map iframe, .full-width-map .windy-map-interaction { min-height: 26rem; }
+.map-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.85rem; padding: 0.85rem 0; }
+.map-row > .map-card { grid-column: auto; min-width: 0; min-height: 31rem; }
+.map-row iframe, .map-row .windy-map-interaction { min-height: 26rem; }
.daylight-card {
--daylight-title-color: var(--accent);
--daylight-data-color: var(--ink);
@@ -623,9 +628,6 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.next-season-note { display: flex; justify-content: space-between; gap: 0.5rem; align-items: baseline; margin: 0; color: var(--daylight-status-color); font-size: 0.58rem; }
.next-season-note strong { color: var(--daylight-status-color); }
.next-season-note time { color: var(--daylight-data-color); white-space: nowrap; }
-.polar-daylight-note { display: flex; justify-content: space-between; gap: 1rem; margin: 0.9rem 0 0; color: var(--daylight-status-color); font-size: 0.6rem; line-height: 1.5; }
-.polar-daylight-note span:last-child { text-align: right; }
-.polar-daylight-note strong { display: block; color: var(--daylight-data-color); font-size: 0.78rem; }
.eclipse-list { display: grid; gap: 0.32rem; margin: 0; padding: 0; list-style: none; font-size: 0.56rem; }
.eclipse-list li { display: flex; justify-content: space-between; gap: 0.5rem; color: var(--daylight-status-color); }
.eclipse-list strong { color: var(--daylight-status-color); font-weight: 650; }
@@ -735,9 +737,9 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.moon-orbit::before { display: none; }
.conditions-row { grid-template-columns: 1fr 1fr; }
.daylight-card { min-height: auto; }
- .lunar-header { grid-template-columns: 1fr minmax(12rem, .7fr) 1fr; }
+ .lunar-side { grid-column: 1 / -1; }
+ .lunar-side-right { grid-row: 4; }
.map-row { grid-template-columns: 1fr 1fr; }
- .full-width-map { grid-column: 1 / 3; }
}
@media (max-width: 760px) {
@@ -764,7 +766,7 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.modal-footer p { display: none; }
.modal-footer { justify-content: flex-end; }
.lunar-header { grid-template-columns: 1fr; min-height: 28rem; padding: 1rem 0.65rem 5rem; }
- .lunar-side { width: 100%; max-width: 34rem; margin: 0 auto; }
+ .lunar-side { grid-column: 1; grid-row: auto; width: 100%; max-width: 34rem; margin: 0 auto; }
.lunar-current { margin-top: 0; }
.lunar-current-disk { width: 5.4rem; height: 5.4rem; }
.lunar-arc { left: 16%; right: 16%; }
@@ -780,7 +782,6 @@ body.caelus-theme-preview-mode .dashboard-return { opacity: 0; pointer-events: n
.forecast-detail-grid { grid-template-columns: 1fr; }
.reading-hero { grid-template-columns: 7rem 1fr; }
.map-row { grid-template-columns: 1fr; }
- .full-width-map { grid-column: 1; }
.daylight-card { min-height: auto; }
}
diff --git a/ui_static/weather_forecast/app.js b/ui_static/weather_forecast/app.js
index 09ce44d..ceac8c9 100644
--- a/ui_static/weather_forecast/app.js
+++ b/ui_static/weather_forecast/app.js
@@ -496,8 +496,6 @@
eclipseList.append(item);
});
}
- document.getElementById("northPoleDaylight").textContent = moon.north_pole_daylight ?? "—";
- document.getElementById("southPoleDaylight").textContent = moon.south_pole_daylight ?? "—";
document.getElementById("sunState").textContent = moon.sun_is_up ? "Sun above horizon" : "Sun below horizon";
updateDaylightTrack(moon.daylight_progress);
document.getElementById("lunarUpdated").textContent = `Updated ${moon.updated_at.slice(11, 16)} UTC`;
diff --git a/ui_templates/weather_forecast/index.html b/ui_templates/weather_forecast/index.html
index 163a091..030a0a7 100644
--- a/ui_templates/weather_forecast/index.html
+++ b/ui_templates/weather_forecast/index.html
@@ -98,7 +98,6 @@ {{ location.name or 'Your weather, alive.' }}
Sunlight today
{{ moon.get('daylight_duration', '—') }} {{ 'Sun above horizon' if moon.get('sun_is_up', false) else 'Sun below horizon' }}
☀
Sunrise {{ moon.get('sunrise_display', moon.sunrise) }}
Solar noon {{ moon.get('solar_noon_display', moon.get('solar_noon', '—')) }}
Sunset {{ moon.get('sunset_display', moon.sunset) }}
- {{ moon.get('north_pole_daylight', '—') }} North Pole{{ moon.get('south_pole_daylight', '—') }} South Pole
Next seasonal event
{{ moon.get('next_season_label', '—') }} {{ moon.get('next_season_date', '—') }}
@@ -113,7 +112,7 @@ Next eclipse for your location
-
+
Weather in motion
Regional radar × Close forecast Windy
@@ -122,40 +121,41 @@
Next eclipse for your location
-
-