style(portal): live-console location page, polished access splash, light/dark toggle

- Location page rebuilt as a monitoring console: Leq hero readout (mono, level-
  colored, auto-flips with theme), instrument strip for Lp/Lmax/L1/L10, refined
  dark Chart.js (mono ticks, thin lines), panel-styled alert history, polished
  pause overlay. All live-stream/chart/alert JS hooks preserved.
- Access page → centered branded splash.
- Light/Dark toggle: CSS-variable theme system (structure + level/metric accent
  colors flip), header sun/moon button, localStorage + no-flash boot script,
  smooth body transition. On toggle, a 'portal-theme' event re-skins the Chart.js
  trace and swaps Leaflet tiles (CARTO dark <-> light) + recolors map dots.

All JS hook IDs intact (verified); both themes validated to parse + balance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 20:10:29 +00:00
parent 4839d14a22
commit f760e81309
4 changed files with 205 additions and 107 deletions
+14 -7
View File
@@ -62,16 +62,23 @@
const LOCATIONS = {{ locations|tojson }};
const liveState = {}; // loc.id -> {status, leq(num|null), leqStr}
const markersById = {}; // loc.id -> circleMarker (for live recolor)
let tiles = null; // map tile layer (re-skinned on theme toggle)
// Dot/level color. Placeholder bands until per-location alert thresholds exist.
// Dot/level color (computed hex; reads the theme CSS vars so it flips with theme).
const LEVEL_AMBER = 55, LEVEL_RED = 70;
const COLOR_IDLE = '#5a6478';
function levelColor(st) {
if (!st || st.status !== 'measuring' || st.leq == null) return COLOR_IDLE;
if (st.leq >= LEVEL_RED) return '#f87171';
if (st.leq >= LEVEL_AMBER) return '#fbbf24';
return '#34d399';
if (!st || st.status !== 'measuring' || st.leq == null) return cssVar('--text-dim');
if (st.leq >= LEVEL_RED) return cssVar('--lvl-bad');
if (st.leq >= LEVEL_AMBER) return cssVar('--lvl-warn');
return cssVar('--lvl-ok');
}
function tileUrl() {
return document.documentElement.getAttribute('data-theme') === 'light'
? 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png';
}
// Re-skin map tiles + recolor everything when the theme flips.
document.addEventListener('portal-theme', () => { if (tiles) tiles.setUrl(tileUrl()); refreshAll(); });
function num(v) { const f = parseFloat(v); return isNaN(f) ? null : f; }
function fmtAgo(iso) {
@@ -163,7 +170,7 @@ if (withCoords.length) {
const mapEl = document.getElementById('loc-map');
mapEl.classList.remove('hidden');
const map = L.map('loc-map', { scrollWheelZoom: false, attributionControl: true });
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
tiles = L.tileLayer(tileUrl(), {
maxZoom: 19, subdomains: 'abcd', attribution: '© OpenStreetMap © CARTO'
}).addTo(map);
const pts = [];