From 1cd2cd0ab4a791c249881497252422896ad2597f Mon Sep 17 00:00:00 2001 From: fraxle Date: Sun, 17 May 2026 16:01:34 +0100 Subject: [PATCH] 1.44 - New Filters, profiles & Mobile fixes --- .gitignore | 3 + about.html | 100 +++++++++++--- assets/css/base.css | 3 +- assets/css/header.css | 33 ----- assets/css/table.css | 192 ++++++++++++++------------- assets/css/ui.css | 60 +++++++-- assets/js/app.js | 281 +++++++++++++++++++++++++++++++++------- assets/js/components.js | 63 +++++---- assets/js/events.js | 81 ++++++++---- assets/js/physics.js | 71 +++++----- assets/js/utils.js | 51 +++++--- 11 files changed, 640 insertions(+), 298 deletions(-) diff --git a/.gitignore b/.gitignore index aad14be..5dbc419 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Cowork session backups +backup/ + # OS / editor cruft .DS_Store Thumbs.db diff --git a/about.html b/about.html index e386c4b..746b0c1 100644 --- a/about.html +++ b/about.html @@ -4,7 +4,7 @@ About SunScope · The True Temperature - + @@ -36,7 +36,7 @@ "@type": "WebApplication", "name": "SunScope", "url": "https://sunscope.net", - "description": "A free hourly UTCI thermal comfort forecast combining air temperature, humidity, wind speed, and solar radiation. Includes vehicle interior temperature, indoor temperature, UV burn time, concrete surface temperature, and soil data.", + "description": "A free hourly UTCI thermal comfort forecast combining air temperature, humidity, wind speed, and solar radiation. Includes vehicle interior temperature, indoor temperature, UV burn time, concrete surface temperature, soil data, air quality index, pollen, and visibility.", "applicationCategory": "WeatherApplication", "operatingSystem": "Any", "offers": { @@ -187,6 +187,14 @@ "@type": "Answer", "text": "The free tier shows 3 days of hourly data. The forecast auto-refreshes every 5 minutes while the page is open, so the current hour always reflects the latest available data from Open-Meteo." } + }, + { + "@type": "Question", + "name": "Does SunScope show air quality and pollen forecasts?", + "acceptedAnswer": { + "@type": "Answer", + "text": "Yes. SunScope includes three air environment columns: Visibility (horizontal visibility in km, useful for fog, haze, and driving conditions), AQI (European Air Quality Index combining PM2.5, PM10, ozone, NO2, and SO2, sourced from Copernicus CAMS), and Pollen (concentration in grains per cubic metre with a selectable pollen type — grass, birch, alder, mugwort, olive, ragweed, or total). These columns appear after Cloud in the table and can be toggled on or off in the column bar." + } } ] } @@ -291,6 +299,17 @@ agricultural and outdoor-work planning. Soil moisture indicates whether the ground is workable or saturated — particularly useful for motorhome and caravan pitching.

+

+ Air quality, visibility, and pollen. Three atmospheric columns sit together + after cloud cover in the table. Visibility shows horizontal visibility in kilometres, + sourced from the CAMS model — values below 1 km indicate fog, below 10 km suggest mist or + pollution haze. AQI shows the European Air Quality Index (0–100+), combining PM2.5, + PM10, ozone, nitrogen dioxide, and sulphur dioxide, with colour-coded bands from Good to + Hazardous. Pollen shows concentration in grains per cubic metre for a selectable + pollen type — choose from total pollen, grass, birch, alder, mugwort, olive, or ragweed — with + Low / Moderate / High / Very High labels. All three are sourced from the Copernicus CAMS + air quality forecast. +

The sky scope. The large circular visualisation at the top of the page shows the sky conditions for the currently selected hour. The sky colour smoothly interpolates through @@ -299,6 +318,19 @@ intensity scale with the actual shortwave radiation at that moment. It refreshes automatically alongside the forecast.

+

+ Day strip weather and temperatures. Each day tab in the date selector shows the + day's forecast at a glance: a weather icon representing the dominant daytime conditions (sun, + cloud variants, or precipitation), plus the day's high and low UTCI+P felt temperatures in + warm and cool colours. The icon uses the actual daytime solar elevation and cloud category data + so it reflects the real character of the day rather than a generic symbol. +

+

+ Weather event banners and popups. When notable weather conditions are forecast + — such as a frost, strong winds, fog, or a heat event — a banner appears above the forecast + table with a brief alert. Tapping or clicking the event icon opens a detailed description + popup explaining what the condition means and what to watch out for. +

What is the SunScope soak-factor?

@@ -328,7 +360,7 @@

The default view — clean and uncluttered, showing the essentials for any outdoor situation. Air temperature, wind speed, cloud cover, - precipitation, and UTCI+P — the adjusted felt temperature + visibility, precipitation, and UTCI+P — the adjusted felt temperature that combines sun, wind, humidity, and rain into a single honest number. The right starting point for most people most of the time.

@@ -348,8 +380,9 @@ include air temperature, relative humidity and dew point (important for condensation and mould risk in cooler months), wind speed for ventilation decisions, cloud cover, - and precipitation — so you know whether to open the windows or bring - the washing in. + air quality index and pollen (useful for allergy + sufferers deciding whether to open windows), and precipitation — so + you know whether to open the windows or bring the washing in.

@@ -383,8 +416,9 @@ wind speed and direction (essential for safe spraying windows), cloud cover and sun elevation for light levels, UTCI for heat stress on the person working outdoors, - precipitation, and the three soil columns: - surface soil temperature, soil temperature at 6 cm + precipitation, air quality index and + pollen (relevant for outdoor workers and spray-window planning), + and the three soil columns: surface soil temperature, soil temperature at 6 cm (the root zone for most seedlings), and soil moisture — so you know whether the ground is workable or waterlogged before you head out.

@@ -404,9 +438,9 @@ channel wind into gusts down narrow streets, concrete absorbs heat all day and radiates it back long after sunset, and the lack of vegetation means there's no evaporative cooling. Columns: air temperature, wind speed and direction, - cloud cover, UV-A index, burn time, - UTCI+P, precipitation, and concrete surface - temperature. + cloud cover, visibility, air quality index, + UV-A index, burn time, UTCI+P, + precipitation, and concrete surface temperature.

Beach — UV exposure at the coast is significantly higher than inland: @@ -415,7 +449,8 @@ time for your skin type. Dew point captures the humidity that makes hot beach days feel muggy, wind speed and direction matters for comfort and sea state, and sun elevation tells you when the UV load - peaks. UTCI+P and precipitation round out the picture. + peaks. Visibility is included for sea-state and horizon conditions. + UTCI+P and precipitation round out the picture.

Events — for general outdoor gatherings: markets, sports fixtures, open-air @@ -423,7 +458,8 @@ temperature, wind speed and direction (useful for exposed grandstands and open sites), cloud cover, UV-A and burn time for daytime events, UTCI+P for overall felt - comfort, precipitation, and concrete surface temperature + comfort, precipitation, pollen (important for + allergy-sensitive attendees), and concrete surface temperature — relevant for anyone standing or sitting on sun-exposed hard standing for extended periods.

@@ -432,8 +468,9 @@ cold and wet conditions will actually feel, wind speed and direction for tent and stage exposure, dew point for overnight condensation, UV-A and burn time for daytime sun protection, - precipitation for planning, and soil moisture — the mud - indicator. Values above 0.4 m³/m³ mean the ground is saturated and vehicle access, + precipitation for planning, air quality index and + pollen — particularly relevant for multi-day outdoor exposure — and + soil moisture — the mud indicator. Values above 0.4 m³/m³ mean the ground is saturated and vehicle access, camping areas, and walkways will be affected.

@@ -445,6 +482,7 @@ critical for wind chill at speed and for avalanche and exposure risk on open slopes. Sun elevation indicates glare and visibility conditions on snow. UTCI+P, air temperature, cloud cover, + visibility (critical for whiteout and low-cloud conditions on the mountain), and precipitation (snow vs rain) complete the picture.

@@ -465,8 +503,9 @@ cloud cover, and sun elevation for visibility and glare. UV exposure is significantly higher on open water due to reflection off the surface, so UV-A index and burn time are included. UTCI+P - captures the combined effect of wet, windy conditions, and precipitation - rounds out the picture for passage planning. + captures the combined effect of wet, windy conditions, visibility + (essential for safe passage — fog and haze at sea are serious hazards), and + precipitation rounds out the picture for passage planning.

@@ -486,10 +525,11 @@

⚙️ Custom · Extra

Full control over which columns appear in the table. Toggle any combination of air - temperature, dew point, relative humidity, wind, direction, cloud, sun elevation, direct - and diffuse radiation, mean radiant temperature, UTCI delta, UTCI, UV-A, UV-B, burn time, - vehicle interior, indoor, managed indoor, soil temperatures, soil moisture, concrete - surface, precipitation, and UTCI+P. Build exactly the view that suits your situation. + temperature, dew point, relative humidity, wind, direction, cloud, visibility, air quality + index, pollen, sun elevation, direct and diffuse radiation, mean radiant temperature, + UTCI delta, UTCI, UV-A, UV-B, burn time, vehicle interior, indoor, managed indoor, + soil temperatures, soil moisture, concrete surface, precipitation, and UTCI+P. Build + exactly the view that suits your situation.

@@ -507,6 +547,9 @@ WindMean wind speed at 10 m, with peak gust in brackets where significantly higher. Wind is the primary driver of wind chill — a key ingredient in the UTCI calculation. DirThe compass direction the wind is blowing from, shown as an arrow and abbreviated label. Useful for route planning and spray timing. CloudTotal cloud cover as a percentage of sky. High cloud blocks solar radiation, reducing daytime heating and UV exposure. + Visibility kmHorizontal visibility in kilometres, sourced from the Copernicus CAMS air quality model. Below 1 km indicates fog or very thick haze; below 10 km suggests mist, smoke, or significant pollution. Relevant for driving, flying, cycling, and photography. + AQIEuropean Air Quality Index (0–100+), combining PM2.5, PM10, ozone, nitrogen dioxide, and sulphur dioxide. 0–20 = Good; 20–40 = Fair; 40–60 = Moderate; 60–80 = Poor; 80–100 = Very Poor; 100+ = Hazardous. Sourced from Copernicus CAMS. + PollenPollen concentration in grains per cubic metre for the selected pollen type — choose from total, grass, birch, alder, mugwort, olive, or ragweed via the dropdown. Sourced from the Copernicus CAMS pollen forecast. Low = <10; Moderate = 10–50; High = 50–200; Very High = 200+. Sun °The angle of the sun above the horizon in degrees. Below 0° the sun has set. The higher the elevation, the more intense the radiation reaching the ground. Direct W/m²Shortwave solar radiation arriving as a direct beam from the sun. The primary driver of skin heating on sunny days. Diffuse W/m²Solar radiation scattered from all directions across the sky. Present even under cloud cover; contributes to the overall solar heat load. @@ -672,6 +715,21 @@

SunScope's soil moisture column shows the volumetric water content of the top centimetre of soil in m³/m³. A reading above 0.4 indicates saturated ground where a heavy vehicle is likely to sink or churn the surface. Below 0.2 is dry and firm. The Vehicle profile includes soil moisture by default precisely for motorhome and caravan users who need to assess pitch suitability before arriving on site.

+
+ Does SunScope show air quality or pollen forecasts? +

Yes. Three air environment columns sit together after Cloud cover in the table: Visibility (horizontal visibility in km — useful for fog, haze, and driving conditions), AQI (the European Air Quality Index combining PM2.5, PM10, ozone, NO₂, and SO₂ in a 0–100+ scale), and Pollen (concentration in grains per cubic metre with a type picker — grass, birch, alder, mugwort, olive, ragweed, or total). All three are sourced from the Copernicus CAMS forecast. Toggle them on or off using the column buttons above the table.

+
+ +
+ What does the weather icon and temperature on each day tab mean? +

Each day tab in the date strip shows a small weather icon representing the dominant daytime sky conditions — a sun disc for clear days, cloud variants (wispy cirrus, scattered cumulus, overcast) for cloudy days, or a precipitation icon for rainy or snowy days. Below the icon are the day's high and low UTCI+P felt temperatures in warm and cool colours. The icon is derived from actual daytime solar elevation and cloud data, so it reflects the real character of the day rather than a generic symbol.

+
+ +
+ What are the weather event banners above the forecast table? +

When notable weather conditions are forecast — such as a ground frost, strong wind warning, fog, or a heat event — a banner with a brief alert appears above the table. Tapping or clicking the event icon opens a description popup with more detail about what the condition means and what to watch out for. Multiple events can appear at once if conditions warrant.

+
+
How far ahead does the forecast go?

The free tier shows 3 days of hourly data. SunScope Extra unlocks all 14 days. The forecast auto-refreshes every 5 minutes while the page is open, so the current hour always reflects the latest available data from Open-Meteo.

@@ -689,7 +747,7 @@
What data sources does SunScope use? -

Forecast data comes from Open-Meteo, which aggregates ECMWF, GFS, and national meteorological service models. The UTCI calculation follows the Bröde et al. 2012 polynomial. Geolocation search uses the Open-Meteo geocoding API.

+

Forecast data comes from Open-Meteo, which aggregates ECMWF, GFS, and national meteorological service models. The UTCI calculation follows the Bröde et al. 2012 polynomial. Air quality, visibility, and pollen data are sourced from the Open-Meteo Air Quality API, which draws on the Copernicus CAMS (Copernicus Atmosphere Monitoring Service) forecast. Geolocation search uses the Open-Meteo geocoding API.

diff --git a/assets/css/base.css b/assets/css/base.css index 8b6add8..14e2ec1 100644 --- a/assets/css/base.css +++ b/assets/css/base.css @@ -31,7 +31,6 @@ JetBrains Mono numeric monospace table data ════════════════════════════════════════════════════════════════════════ */ - /* ── 1. FONTS + RESET ───────────────────────────────────────────────── */ /* Font loading is handled in index.html via a non-blocking @@ -177,7 +176,7 @@ body { /* ── Mobile nav drawer ──────────────────────────────────────────────── */ @media (max-width: 640px) { .utci-app { - padding: 88px 16px 24px; /* 88px = 72px nav height + 16px breathing room */ + padding: 96px 16px 24px; /* 96px = 72px nav height + 24px breathing room */ } /* Show wordmark in nav bar, hide it from main content */ diff --git a/assets/css/header.css b/assets/css/header.css index f122bf9..e935f68 100644 --- a/assets/css/header.css +++ b/assets/css/header.css @@ -2,7 +2,6 @@ header.css — Page header: title wordmark, UTCI dial, and search box. ════════════════════════════════════════════════════════════════════════ */ - /* ── 4. HEADER (title + dial + search, three-column grid) ──────────── */ .utci-header { @@ -45,15 +44,6 @@ color: #1e1208; } -.utci-title .title-beta { /* the small "beta" subscript */ - font-size: 0.38em; - text-transform: none; - font-weight: 400; - letter-spacing: 0.05em; - vertical-align: super; - color: #b09870; -} - /* "See the sun the way..." */ .utci-tagline { font-family: Fraunces, serif; @@ -63,29 +53,6 @@ margin-top: 14px; } -/* The credits line under the tagline */ -.utci-subtitle { - font-family: Manrope, sans-serif; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.08em; - margin-top: 10px; - color: #b09870; -} - -.utci-subtitle-link { /* The "UTCI" link in the credits */ - color: #c8922a; - text-decoration: none; - border-bottom: 1px solid rgba(200, 146, 42, 0.4); - transition: border-color 0.2s, color 0.2s; -} - -.utci-subtitle-link:hover { - color: #a06e18; - border-bottom-color: #a06e18; -} - /* "↳ Pangbourne, Berkshire" */ .utci-current-loc { font-family: Fraunces, serif; diff --git a/assets/css/table.css b/assets/css/table.css index 7c82275..c868abf 100644 --- a/assets/css/table.css +++ b/assets/css/table.css @@ -3,7 +3,6 @@ now-row and night-row decorations. ════════════════════════════════════════════════════════════════════════ */ - /* ── 5. DAY TABS (today / tomorrow / etc.) ─────────────────────────── */ /* Note: each tab's background colour comes from inline styles in app.js (the confidence bands). This file only handles spacing, @@ -241,31 +240,6 @@ color: #fff; } -/* Burn select — unlike regular col-toggle buttons, the select never - goes full-brass background so the native dropdown list stays readable. - Active state = brass border + brass text on parchment. - Inactive state = same as a regular col-toggle pill. */ -.burn-select { - cursor: pointer; - outline: none; -} -/* Override the inherited .col-toggle.on brass background */ -.col-toggle.burn-select.on { - background: #f5edd6; - color: #c8922a; - border-color: #c8922a; - font-weight: 600; -} -.col-toggle.burn-select.on:hover { - background: #fef3dc; - color: #c8922a; - border-color: #c8922a; -} -.burn-select:focus { - border-color: #c8922a; - outline: none; -} - /* ── Vehicle group: select + windows-open checkbox fused together ── */ .col-toggle-group { display: inline-flex; @@ -287,69 +261,12 @@ } .col-toggle-group .grouped-left { border-right: none !important; - border-radius: 20px 0 0 20px !important; + border-radius: 0 !important; } .col-toggle-group .cs-wrap:only-child .grouped-left, .col-toggle-group .cs-wrap:last-child .grouped-left { border-right: 1.5px solid #c9b08a !important; - border-radius: 20px !important; -} - -/* The checkbox pill — same height/padding as a col-toggle but styled as a label */ -.col-toggle-vent { - display: inline-flex; - align-items: center; - gap: 5px; - padding: 5px 12px 5px 10px; - font-size: 12px; - font-family: Manrope, sans-serif; - font-weight: 700; - letter-spacing: 0.05em; - text-transform: uppercase; - cursor: pointer; - border: 1.5px solid #c9b08a; - border-left: 1px solid #d4c0a0; /* softer join line */ - border-radius: 0 20px 20px 0; - background: #fef8ee; - color: #6b4228; - user-select: none; - transition: background 0.15s, color 0.15s, border-color 0.15s; - white-space: nowrap; -} -.col-toggle-vent:hover { - border-color: #c8922a; - color: #6b4f2a; - background: #fef3dc; -} -.col-toggle-vent.on { - background: #c8922a; - border-color: #c8922a; - color: #fff; -} - -/* Custom checkbox square inside the vent label */ -.vent-checkbox { - display: inline-block; - width: 12px; - height: 12px; - border: 1.5px solid currentColor; - border-radius: 3px; - background: transparent; - flex-shrink: 0; - position: relative; - transition: background 0.12s; -} -.vent-checkbox.checked::after { - content: ''; - position: absolute; - left: 2px; - top: -1px; - width: 5px; - height: 8px; - border: 2px solid currentColor; - border-top: none; - border-left: none; - transform: rotate(45deg); + border-radius: 0 !important; } @@ -636,10 +553,6 @@ .col-info-close:hover { color: #4a3420; } - then sit with balanced visual padding on either side. */ -.utci-table th.utci-tight-head { - letter-spacing: 0.04em; -} /* ── 8. NOW-ROW & NIGHT-ROW DECORATIONS ─────────────────────────────── */ @@ -715,3 +628,104 @@ text-transform: uppercase; letter-spacing: 0.06em; } + + +/* ── FILTER PROFILES + COLUMN TOGGLES — mobile fixes ───────────────── */ +@media (max-width: 640px) { + + /* ── Profile bar ──────────────────────────────────────────────────── + Keep flex-wrap but make every child a consistent ~31% width so + they land in tidy rows of 3. The label gets its own full row. */ + .filter-profiles { + flex-wrap: wrap; + gap: 6px; + padding: 10px 10px; + align-items: stretch; + } + + .filter-profiles-label { + flex: 0 0 100%; /* own row */ + margin-right: 0; + margin-bottom: 0; + } + + /* Hide vertical pipe dividers — not needed when wrapping */ + .profile-divider { + display: none; + } + + /* Plain profile buttons: fixed ~31% so 3 fit per row */ + .profile-btn { + flex: 0 0 calc(33.33% - 4px); + text-align: center; + padding: 7px 2px; + font-size: 11px; + letter-spacing: 0.03em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + /* CustomSelect (Activities / Places dropdowns) — same width as buttons */ + .filter-profiles .cs-wrap { + flex: 0 0 calc(33.33% - 4px); + } + .filter-profiles .cs-btn { + width: 100%; + padding: 7px 6px; + font-size: 11px; + letter-spacing: 0.03em; + justify-content: center; + } + + /* ── Column toggles bar ───────────────────────────────────────────── + Same approach — label full row, toggles at ~31% each. */ + .col-toggles { + flex-wrap: wrap; + gap: 6px; + padding: 10px 10px; + align-items: stretch; + } + + .col-toggles-label { + flex: 0 0 100%; + margin-right: 0; + margin-bottom: 0; + } + + .col-toggle { + flex: 0 0 calc(33.33% - 4px); + text-align: center; + padding: 7px 2px; + font-size: 11px; + letter-spacing: 0.03em; + } + + /* Pollen CustomSelect in col-toggles — match toggle width */ + .col-toggles .cs-wrap { + flex: 0 0 calc(33.33% - 4px); + } + .col-toggles .cs-btn { + width: 100%; + padding: 7px 6px; + font-size: 11px; + letter-spacing: 0.03em; + justify-content: center; + } + + /* Fused vehicle group (select + vent pill): spans 2 slots = ~65% */ + .col-toggle-group { + flex: 0 0 calc(66.66% - 1px); + display: inline-flex; + align-items: stretch; + } + .col-toggle-group .cs-wrap { + flex: 1; + } + .col-toggle-group .cs-btn { + width: 100%; + font-size: 11px; + padding: 7px 6px; + justify-content: center; + } +} diff --git a/assets/css/ui.css b/assets/css/ui.css index 2a08ef3..c479062 100644 --- a/assets/css/ui.css +++ b/assets/css/ui.css @@ -2,7 +2,6 @@ ui.css — Status banners, legend, about block, footer, and responsive. ════════════════════════════════════════════════════════════════════════ */ - /* ── EVENT BANNER ───────────────────────────────────────────────────── */ /* Animated slide-down banner for cosmic/weather/promo events. */ @@ -196,9 +195,8 @@ .almanac-entry-desc { font-family: Manrope, sans-serif; font-size: 12px; - color: '#4a3420'; - line-height: 1.55; color: #4a3420; + line-height: 1.55; } .almanac-entry-visibility { display: inline-block; @@ -236,6 +234,51 @@ } } +/* ── EVENT BANNER — mobile fixes ────────────────────────────────────── */ +@media (max-width: 640px) { + .event-banner-wrap.visible { + max-height: 260px; /* generous — banner can be tall with dates line */ + } + + .event-banner-wrap { + margin-top: 8px; /* push banner clear of the fixed nav bar */ + margin-bottom: 18px; /* breathing room between banner and header */ + } + + .event-banner { + align-items: flex-start; /* stop dismiss ✕ drifting downward */ + gap: 10px; + padding: 18px 12px 14px 14px; + margin-bottom: 0; + border-radius: 4px; + } + + .event-banner-emoji { + font-size: 20px; + margin-top: 3px; /* visually balance the dates-line margin-top below */ + } + + .event-banner-title { + font-size: 15px; + line-height: 1.25; + margin-bottom: 3px; + } + + .event-banner-msg { + font-size: 12px; + line-height: 1.5; + } + + .event-banner-dismiss { + margin-top: 0; /* keep ✕ pinned flush to the top-right */ + padding: 0 2px; + font-size: 16px; + line-height: 1.4; + flex-shrink: 0; + align-self: flex-start; + } +} + /* ── CUSTOM SELECT DROPDOWN ─────────────────────────────────────────── */ /* Cross-platform replacement for native