From a77db16a274e6747ce7a601e921489e3d71c905a Mon Sep 17 00:00:00 2001 From: Jonas Hertner Date: Sat, 22 Aug 2026 12:40:01 +0200 Subject: [PATCH] Replace map overlays with a two-surface workspace --- build/pages/index.en.html | 15 +- build/pages/updates.json | 2 +- scripts/verify-site.mjs | 30 +++- site/app.js | 134 ++++++++---------- site/data/provenance.json | 2 +- site/de/index.html | 8 +- site/en/index.html | 15 +- site/fr/index.html | 8 +- site/index.html | 8 +- site/it/index.html | 8 +- site/rm/index.html | 8 +- site/sitemap.xml | 50 +++---- site/style.css | 286 +++++++++++++++++++++++++++++++++++++- 13 files changed, 429 insertions(+), 145 deletions(-) diff --git a/build/pages/index.en.html b/build/pages/index.en.html index 37c7497..2412430 100644 --- a/build/pages/index.en.html +++ b/build/pages/index.en.html @@ -33,7 +33,7 @@ - + @@ -383,12 +383,13 @@

Explore this layer as data

Follow the water. Protect its pathways.

-

Trace water through Switzerland—from ice and groundwater to - rivers, storage, use, return and the border—and identify the law protecting each essential - pathway. The aim is to protect wetlands and enable legal action when those protections fail. - This first release focuses on surface water. Every figure states its source, date and evidence class.

+

Riverflow follows water through Switzerland—from ice and + groundwater to rivers, storage, use and return. It shows what is measured, what is estimated and + which law protects each essential pathway. The aim: protect wetlands and enforce the law when + necessary. This first release focuses on surface water. Every figure names its source, date and + evidence class.

- + How the evidence works
@@ -441,6 +442,6 @@

Follow the water. Protect its pathways.

- + diff --git a/build/pages/updates.json b/build/pages/updates.json index 775a525..46da783 100644 --- a/build/pages/updates.json +++ b/build/pages/updates.json @@ -327,7 +327,7 @@ "The inventories do not age together. The amphibian inventory was revised in May 2026. The alluvial-zone and mire-landscape inventories still state November 2017. See every date.": {"de":"Die Inventare altern nicht gemeinsam. Das Amphibieninventar wurde im Mai 2026 revidiert. Das Auenschutz- und das Moorlandschaftsinventar nennen weiterhin November 2017. Alle Daten ansehen.","fr":"Les inventaires ne vieillissent pas ensemble. L’inventaire des batraciens a été révisé en mai 2026. Ceux des zones alluviales et des sites marécageux indiquent toujours novembre 2017. Voir toutes les dates.","it":"Gli inventari non invecchiano insieme. L’inventario degli anfibi è stato rivisto nel maggio 2026. Quelli delle zone golenali e delle zone palustri indicano ancora novembre 2017. Vedere tutte le date.","rm":"Ils inventaris na vegneschan betg ensemen. L’inventari dals amfibis è vegnì revedì il matg 2026. Ils inventaris da las zonas alluvialas e da las cuntradas da palì inditgeschan anc november 2017. Vesair tut las datas."}, "Three overlays arrive as images. This map cannot query or verify the groundwater bodies, protection zones or catchments. Headwaters are the exception: they are drawn from this map’s network.": {"de":"Drei Überlagerungen kommen als Bilder an. Diese Karte kann Grundwasserkörper, Schutzzonen und Einzugsgebiete weder abfragen noch prüfen. Die Oberläufe sind die Ausnahme: Sie stammen aus dem Flussnetz dieser Karte.","fr":"Trois superpositions arrivent sous forme d’images. La carte ne peut ni interroger ni vérifier les masses d’eau souterraine, zones de protection et bassins versants. Les têtes de bassin font exception : elles proviennent du réseau de cette carte.","it":"Tre sovrapposizioni arrivano come immagini. La carta non può interrogare o verificare corpi idrici sotterranei, zone di protezione e bacini. Fanno eccezione i tratti di testa, ricavati dalla rete della carta.","rm":"Trais surposiziuns arrivan sco maletgs. La charta na po ni dumandar ni verifitgar ils corps d’aua sutterrana, las zonas da protecziun ed ils intschess. Ils curs superiurs èn l’excepziun: els derivan da la rait da questa charta."}, "A headwater is not a spring. It is the top of the drawn river network, clipped at 5 km² of upstream area. At closer zoom, swissNAMES3D adds 86 named springs and 157 named waterfalls—not a complete national inventory.": {"de":"Ein Oberlauf ist keine Quelle. Er ist das obere Ende des gezeichneten Flussnetzes, begrenzt auf 5 km² Einzugsfläche. Bei näherem Zoom ergänzt swissNAMES3D 86 benannte Quellen und 157 benannte Wasserfälle – kein vollständiges Landesinventar.","fr":"Une tête de bassin n’est pas une source. C’est le sommet du réseau fluvial dessiné, limité à 5 km² de bassin amont. À plus grande échelle, swissNAMES3D ajoute 86 sources et 157 cascades nommées — ce n’est pas un inventaire national complet.","it":"Un tratto di testa non è una sorgente. È l’estremità superiore della rete fluviale disegnata, limitata a 5 km² di bacino a monte. A maggior ingrandimento swissNAMES3D aggiunge 86 sorgenti e 157 cascate nominate, non un inventario nazionale completo.","rm":"In curs superiur n’è betg ina funtauna. El è il punct il pli aut da la rait da flums dissegnada, limità a 5 km² d’intschess amunt. Cun dapli zoom agiunta swissNAMES3D 86 funtaunas e 157 cascadas numnadas – betg in inventari naziunal cumplet."}, - "Trace water through Switzerland—from ice and groundwater to rivers, storage, use, return and the border—and identify the law protecting each essential pathway. The aim is to protect wetlands and enable legal action when those protections fail. This first release focuses on surface water. Every figure states its source, date and evidence class.": {"de":"Das Projekt folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicherung, Nutzung und Rücklauf bis zur Grenze – und zeigt, welches Recht jeden wesentlichen Weg schützt. Ziel ist es, Feuchtgebiete zu schützen und rechtliche Schritte zu ermöglichen, wenn dieser Schutz nicht durchgesetzt wird. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.","fr":"Suivre l’eau à travers la Suisse — de la glace et des eaux souterraines aux cours d’eau, au stockage, aux usages, aux retours et à la frontière — et identifier le droit qui protège chaque voie essentielle. Le but est de protéger les zones humides et de permettre une action juridique lorsque ces protections échouent. Cette première version porte sur les eaux superficielles. Chaque chiffre indique sa source, sa date et sa classe de preuve.","it":"Seguire l’acqua attraverso la Svizzera — dal ghiaccio e dalle acque sotterranee ai corsi d’acqua, allo stoccaggio, all’uso, alla restituzione e al confine — e identificare il diritto che protegge ogni via essenziale. L’obiettivo è proteggere le zone umide e rendere possibile l’azione legale quando tali protezioni falliscono. Questa prima edizione si concentra sulle acque superficiali. Ogni dato indica fonte, data e classe di prova.","rm":"Suandar l’aua tras la Svizra – dal glatsch e da l’aua sutterrana als flums, a l’accumulaziun, utilisaziun, return e cunfin – ed identifitgar il dretg che protegia mintga via essenziala. La finamira è da proteger las zonas umidas e da pussibilitar pass giuridics sche quella protecziun n’effectuescha betg. Questa emprima ediziun sa concentrescha sin las auas a la surfatscha. Mintga cifra inditgescha funtauna, data e classa da cumprova."}, + "Riverflow follows water through Switzerland—from ice and groundwater to rivers, storage, use and return. It shows what is measured, what is estimated and which law protects each essential pathway. The aim: protect wetlands and enforce the law when necessary. This first release focuses on surface water. Every figure names its source, date and evidence class.": {"de":"Riverflow folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicher und Nutzung bis zum Rücklauf. Es zeigt, was gemessen ist, was geschätzt wird und welches Recht jeden wesentlichen Weg schützt. Das Ziel: Feuchtgebiete schützen und das Recht durchsetzen, wenn nötig. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.","fr":"Riverflow suit l’eau à travers la Suisse — de la glace et des eaux souterraines aux cours d’eau, au stockage, à l’usage et au retour. Il distingue ce qui est mesuré de ce qui est estimé et indique le droit qui protège chaque voie essentielle. L’objectif : protéger les zones humides et, si nécessaire, faire appliquer le droit. Cette première édition porte sur les eaux superficielles. Chaque chiffre indique sa source, sa date et sa classe de preuve.","it":"Riverflow segue l’acqua attraverso la Svizzera — dal ghiaccio e dalle acque sotterranee ai corsi d’acqua, allo stoccaggio, all’uso e alla restituzione. Mostra ciò che è misurato, ciò che è stimato e quale diritto protegge ogni via essenziale. L’obiettivo: proteggere le zone umide e far rispettare il diritto quando necessario. Questa prima edizione si concentra sulle acque superficiali. Ogni dato indica fonte, data e classe di prova.","rm":"Riverflow suonda l’aua tras la Svizra – dal glatsch e da l’aua sutterrana als flums, a l’accumulaziun, a l’utilisaziun ed al return. El mussa tge ch’è mesirà, tge ch’è stimà e tge dretg che protegia mintga via essenziala. La finamira: proteger las zonas umidas ed exequir il dretg sche necessari. Questa emprima ediziun sa concentrescha sin las auas a la surfatscha. Mintga cifra inditgescha funtauna, data e classa da cumprova."}, "Mountain precipitation becomes ice, groundwater, springs, rivers and wetlands. Water is stored, used, returned and evaporated.": {"de":"Niederschlag im Gebirge wird zu Eis, Grundwasser, Quellen, Flüssen und Feuchtgebieten. Wasser wird gespeichert, genutzt, zurückgeführt und verdunstet.","fr":"Les précipitations en montagne deviennent glace, eaux souterraines, sources, cours d’eau et zones humides. L’eau est stockée, utilisée, restituée et évaporée.","it":"Le precipitazioni in montagna diventano ghiaccio, acque sotterranee, sorgenti, corsi d’acqua e zone umide. L’acqua viene accumulata, usata, restituita ed evapora.","rm":"La precipitaziun en muntogna daventa glatsch, aua sutterrana, funtaunas, flums e zonas umidas. L’aua vegn accumulada, utilisada, restituida ed evapurescha."}, "Springs & groundwaterpartly mapped": {"de":"Quellen & Grundwasserteilweise kartiert","fr":"Sources et eaux souterrainespartiellement cartographiées","it":"Sorgenti e acque sotterraneeparzialmente cartografate","rm":"Funtaunas ed aua sutterranaper part sin la charta"}, "2011–2024Federal water-quality results": {"de":"2011–2024Bundesmessdaten zur Wasserqualität","fr":"2011–2024Résultats fédéraux sur la qualité de l’eau","it":"2011–2024Risultati federali sulla qualità dell’acqua","rm":"2011–2024Resultats federals da la qualitad da l’aua"}, diff --git a/scripts/verify-site.mjs b/scripts/verify-site.mjs index c086ce2..c3c6fe5 100644 --- a/scripts/verify-site.mjs +++ b/scripts/verify-site.mjs @@ -163,13 +163,17 @@ const responsiveCssFile = join(site, 'style.css'); const responsiveCss = readFileSync(join(site, 'tokens.css'), 'utf8') + '\n' + readFileSync(responsiveCssFile, 'utf8'); for (const [label, pattern] of [ ['safe-area top inset', /--safe-t:\s*env\(safe-area-inset-top/], - ['44 px mobile layer controls', /#modes button\s*\{[^}]*min-height:\s*44px/s], + ['44 px mobile layer controls', /#workspace #modes button\s*\{[^}]*min-height:\s*44px/s], ['44 px mobile legend controls', /\.controls label\s*\{\s*min-height:\s*44px/], - ['dynamic mobile sheet height', /max-height:\s*76dvh/], ['dynamic mobile dialog height', /max-height:\s*calc\(100dvh - 12px\)/], ['large landing language targets', /#intro \.introLangs \[lang\][^{]*\{[^}]*min-width:\s*40px;[^}]*min-height:\s*44px/s], - ['contained intermediate mode rail', /@media \(min-width:\s*701px\) and \(max-width:\s*1340px\)[\s\S]*?#modes\s*\{[^}]*overflow-x:\s*auto/], - ['collision-free wide mode rail', /@media \(min-width:\s*1900px\)[\s\S]*?#modes\s*\{[^}]*left:\s*calc\(var\(--pad\) \+ 484px\)/], + ['persistent evidence workspace', /#workspace\s*\{[^}]*display:\s*flex;[^}]*flex-direction:\s*column;[^}]*width:\s*var\(--workspace-w\)/s], + ['unobstructed desktop map surface', /#map, #flow\s*\{[^}]*left:\s*var\(--workspace-w\);[^}]*width:\s*calc\(100vw - var\(--workspace-w\)\);[^}]*height:\s*100dvh/s], + ['independently scrolling evidence', /#workspace #legend\s*\{[^}]*flex:\s*1 1 auto;[^}]*overflow-y:\s*auto/s], + ['stacked portrait workspace', /@media \(max-width:\s*900px\) and \(orientation:\s*portrait\)[\s\S]*?#workspace\s*\{[^}]*height:\s*var\(--workspace-h\)[\s\S]*?#map, #flow\s*\{[^}]*width:\s*100vw;[^}]*height:\s*calc\(100dvh - var\(--workspace-h\)\)/s], + ['compact short-landscape rail', /@media \(max-height:\s*560px\) and \(orientation:\s*landscape\)[\s\S]*?#workspace #modes\s*\{[^}]*overflow-x:\s*auto/s], + ['sidebar project brief', /#workspace #intro\s*\{[^}]*position:\s*absolute;[^}]*inset:\s*0;[^}]*height:\s*100%/s], + ['sidebar map readout', /#workspace #tooltip\s*\{[^}]*position:\s*static;[^}]*border-bottom:/s], ['high-contrast control boundary token', /--control-border:\s*#[0-9a-f]{6}/i], ]) if (!pattern.test(responsiveCss)) fail(responsiveCssFile, `missing responsive contract: ${label}`); const appSource = readFileSync(join(site, 'app.js'), 'utf8'); @@ -179,6 +183,24 @@ if (!/window\.visualViewport\?\.addEventListener\(['"]resize['"]/.test(appSource if (!/modeNav\.addEventListener\(['"]wheel['"]/.test(appSource)) { fail(join(site, 'app.js'), 'intermediate mode rail has no practical mouse-wheel navigation'); } +if (!/workspace\.append\(el\)/.test(appSource) || !/dialog\.show\(\)/.test(appSource) || /dialog\.showModal\(\)/.test(appSource)) { + fail(join(site, 'app.js'), 'project brief must occupy the evidence workspace without covering the map'); +} +const workspaceComposition = /for \(const id of \[([^\]]+)\]\)/.exec(appSource.slice(0, 3500))?.[1] ?? ''; +for (const id of ['titlebar', 'modes', 'mapControls', 'tooltip', 'legend', 'ribbon', 'panel', 'intro']) { + if (!new RegExp(`['"]${id}['"]`).test(workspaceComposition)) { + fail(join(site, 'app.js'), `${id} is not composed into the evidence workspace`); + } +} +if (/tt\.style\.(?:left|top)\s*=/.test(appSource)) { + fail(join(site, 'app.js'), 'map readout must live in the evidence workspace instead of floating over geography'); +} +if (!/function canvasPoint\(e\)/.test(appSource) || !/e\.clientX - box\.left/.test(appSource)) { + fail(join(site, 'app.js'), 'pointer coordinates are not translated into the offset canvas'); +} +if (!/function fitBox\(\)\s*\{[\s\S]*?return \{ t: 0, b: 0 \};[\s\S]*?\n\}/.test(appSource)) { + fail(join(site, 'app.js'), 'map fit still reserves space for controls that no longer overlay the canvas'); +} const initialLoad = /async function load\(\)\s*\{([\s\S]*?)\n\}\n\n\/\/ The ice/.exec(appSource)?.[1] ?? ''; for (const name of ['loadIce', 'loadUsers', 'loadReservoirs', 'loadResidual', 'loadWetlands', 'loadCantons']) { if (new RegExp('\\b' + name + '\\s*\\(').test(initialLoad)) fail(join(site, 'app.js'), name + ' is loaded eagerly instead of on layer request'); diff --git a/site/app.js b/site/app.js index 3c861a9..f702440 100644 --- a/site/app.js +++ b/site/app.js @@ -9,6 +9,21 @@ * nearest gauge downstream. A layer selects and weights; it adds no fact. */ +// The publication is one instrument with two persistent surfaces: evidence and +// map. Keep every control, legend, detail and time series in the evidence +// workspace so the geography is never used as furniture space. The source order +// remains reading-first; this small composition step gives CSS one stable layout +// container without duplicating the five translated documents. +const workspace = document.createElement('aside'); +workspace.id = 'workspace'; +document.getElementById('main').before(workspace); +for (const id of ['titlebar', 'modes', 'mapControls', 'tooltip', 'legend', 'ribbon', 'panel', 'intro']) { + const el = document.getElementById(id); + if (el) workspace.append(el); +} +const publicationCredits = document.getElementById('credits'); +if (publicationCredits) document.getElementById('legendBody')?.append(publicationCredits); + const ENDPOINT = 'https://lindas.admin.ch/query'; /* ---- the palette ----------------------------------------------------------- @@ -989,39 +1004,21 @@ function fit() { if (r.py[i] < y0) y0 = r.py[i]; if (r.py[i] > y1) y1 = r.py[i]; } mapBounds = { x0, x1, y0, y1 }; - // On a phone the header and the sheet own real estate at the top and the bottom, - // and a country fitted to the whole viewport lands as a band in the middle with - // black above and below it. Fit to the strip that is actually visible instead, - // and let the width run nearly edge to edge: Switzerland is about 2.2 to 1, so on - // a portrait screen the width is always the binding constraint. + // The map owns its entire canvas at every breakpoint. Switzerland is about 2.2 + // to 1, so width is normally the binding constraint in a portrait map surface. const box = fitBox(); const availH = Math.max(80, H - box.t - box.b); - const padX = isPhone() ? 0.99 : 0.94, padY = 0.94; + const padX = 0.95, padY = 0.94; view.k = Math.min(W / (x1 - x0) * padX, availH / (y1 - y0) * padY); K0 = view.k; view.x = W / 2 - view.k * (x0 + x1) / 2; - // Held a little above centre on a phone, so the room that a 2.2-to-1 country - // cannot fill on a portrait screen collects at the bottom, which is exactly where - // the sheet opens into. Opening the legend then costs no map. - view.y = box.t + availH * (isPhone() ? 0.4 : 0.5) - view.k * (y0 + y1) / 2; + view.y = box.t + availH * 0.5 - view.k * (y0 + y1) / 2; updateZoomControls(); } -// Everything here is looked up rather than closed over, because fit() runs during -// load and the furniture it measures is declared further down the file. function fitBox() { - // Only a portrait screen has this problem. In landscape the country and the - // window are about the same shape and the furniture sits in the corners. - if (!isPhone() && H <= W * 1.05) return { t: 0, b: 0 }; - const nav = document.getElementById('modes').getBoundingClientRect(); - const rb = document.getElementById('ribbon'); - const ribH = rb.hidden ? 0 : rb.offsetHeight + 10; - const sheet = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--sheet-h')) || 0; - const navLow = nav.top > H / 2; // narrow layouts put the switch at the foot - const title = document.getElementById('titlebar').getBoundingClientRect(); - return { - t: Math.max(0, navLow ? title.bottom : nav.bottom) + 6, - b: (navLow ? Math.max(0, H - nav.top) : 0) + sheet + ribH + 8, - }; + // Permanent controls live outside the canvas. Every pixel in this box belongs + // to the geography, at every breakpoint. + return { t: 0, b: 0 }; } function resize() { dpr = Math.min(2, window.devicePixelRatio || 1); @@ -1343,7 +1340,7 @@ function clampView() { return Math.max(minPos, Math.min(maxPos, pos)); }; view.x = axis(view.x, mapBounds.x0, mapBounds.x1, 0, W, 0.5); - view.y = axis(view.y, mapBounds.y0, mapBounds.y1, top, bottom, isPhone() ? 0.4 : 0.5); + view.y = axis(view.y, mapBounds.y0, mapBounds.y1, top, bottom, 0.5); updateZoomControls(); } @@ -1368,39 +1365,45 @@ function zoomAbout(k2, cx, cy, from) { clampView(); invalidate(); dirtyAlloc = true; } +function canvasPoint(e) { + const box = cv.getBoundingClientRect(); + return { x: e.clientX - box.left, y: e.clientY - box.top }; +} function twoFingers() { const [a, b] = [...ptrs.values()]; return { d: Math.max(1, Math.hypot(a.x - b.x, a.y - b.y)), cx: (a.x + b.x) / 2, cy: (a.y + b.y) / 2 }; } cv.addEventListener('pointerdown', e => { + const p = canvasPoint(e); cv.setPointerCapture(e.pointerId); - ptrs.set(e.pointerId, { x: e.clientX, y: e.clientY }); + ptrs.set(e.pointerId, p); if (ptrs.size === 2) { const t = twoFingers(); pinch = { d: t.d, cx: t.cx, cy: t.cy, k: view.k, vx: view.x, vy: view.y }; drag = null; cv.classList.remove('dragging'); } else if (ptrs.size === 1) { - drag = { x: e.clientX, y: e.clientY, vx: view.x, vy: view.y, moved: false, touch: e.pointerType !== 'mouse' }; + drag = { x: p.x, y: p.y, vx: view.x, vy: view.y, moved: false, touch: e.pointerType !== 'mouse' }; cv.classList.add('dragging'); } }); cv.addEventListener('pointermove', e => { - if (ptrs.has(e.pointerId)) ptrs.set(e.pointerId, { x: e.clientX, y: e.clientY }); + const p = canvasPoint(e); + if (ptrs.has(e.pointerId)) ptrs.set(e.pointerId, p); if (pinch && ptrs.size >= 2) { const t = twoFingers(); zoomAbout(pinch.k * (t.d / pinch.d), t.cx, t.cy, pinch); return; } if (drag) { - const dx = e.clientX - drag.x, dy = e.clientY - drag.y; + const dx = p.x - drag.x, dy = p.y - drag.y; if (Math.abs(dx) + Math.abs(dy) > 4) drag.moved = true; view.x = drag.vx + dx; view.y = drag.vy + dy; clampView(); invalidate(); dirtyAlloc = true; return; } - if (e.pointerType === 'mouse') pick(e.clientX, e.clientY); + if (e.pointerType === 'mouse') pick(p.x, p.y); }); function endPointer(e) { ptrs.delete(e.pointerId); @@ -1409,7 +1412,8 @@ function endPointer(e) { const was = drag; drag = null; cv.classList.remove('dragging'); if (!was || was.moved) { writeHash(); return; } - pick(e.clientX, e.clientY); + const p = canvasPoint(e); + pick(p.x, p.y); select(hovered); // A finger leaves no cursor behind, so the highlight it lit has to go out with // it, or the map keeps showing a hover that nobody is making. @@ -1419,15 +1423,17 @@ cv.addEventListener('pointerup', endPointer); cv.addEventListener('pointercancel', endPointer); cv.addEventListener('wheel', e => { e.preventDefault(); - zoomAbout(view.k * Math.exp(-e.deltaY * 0.0016), e.clientX, e.clientY, - { k: view.k, cx: e.clientX, cy: e.clientY, vx: view.x, vy: view.y }); + const p = canvasPoint(e); + zoomAbout(view.k * Math.exp(-e.deltaY * 0.0016), p.x, p.y, + { k: view.k, cx: p.x, cy: p.y, vx: view.x, vy: view.y }); clearTimeout(window.__hashT); window.__hashT = setTimeout(writeHash, 350); }, { passive: false }); cv.addEventListener('dblclick', e => { e.preventDefault(); - zoomAbout(view.k * 1.7, e.clientX, e.clientY, - { k: view.k, cx: e.clientX, cy: e.clientY, vx: view.x, vy: view.y }); + const p = canvasPoint(e); + zoomAbout(view.k * 1.7, p.x, p.y, + { k: view.k, cx: p.x, cy: p.y, vx: view.x, vy: view.y }); writeHash(); }); @@ -1588,7 +1594,7 @@ function fmtQ(q) { return nfd(q, q >= 100 ? 0 : q >= 10 ? 1 : q >= 1 ? 2 : 3); } function tip(mx, my) { - if (!hovered) { tt.hidden = true; return; } + if (!hovered || !document.getElementById('panel').hidden) { tt.hidden = true; return; } if (hovered.kind === 'dam') { const d = hovered.ref; const lv = resLevels(resWeekIndex()); @@ -1655,8 +1661,6 @@ function tip(mx, my) { T('m.no', { n: esc(alluvialByReach.get(r.id).num) }) })}` : ''); } tt.hidden = false; - tt.style.left = Math.min(mx + 14, window.innerWidth - 250) + 'px'; - tt.style.top = Math.min(my + 14, window.innerHeight - 90) + 'px'; } const panel = document.getElementById('panel'); @@ -1676,6 +1680,7 @@ function closePanel(returnFocus = true) { function select(h, moveFocus = false) { selected = h; if (!h) { closePanel(false); return; } + tt.hidden = true; if (!panel.contains(document.activeElement)) panelReturnFocus = document.activeElement; const titleEl = document.getElementById('panelTitle'); const B = document.getElementById('panelBody'); @@ -3067,10 +3072,10 @@ function renderVintage() { } /* =========================================================================== - THE SHEET - On a phone the legend stops being a floating card and becomes a bottom sheet. - It opens to the layer it is describing and closes to a handle, and the ribbon - rides above whichever of the two it is. + THE EVIDENCE WORKSPACE + Earlier releases turned the legend into a bottom sheet on phones. The map and + evidence now occupy separate boxes, so relayout only clears any transform left + by an older cached stylesheet. =========================================================================== */ function isPhone() { return window.matchMedia('(max-width: 700px)').matches; } let sheetOpen = false; @@ -3079,26 +3084,15 @@ const handleEl = document.getElementById('sheetHandle'); function layoutSheet() { const root = document.documentElement; - // The title block grows and shrinks with the screen: the subtitle appears at - // 1500 px and the evidence bar only where there is room for it. So its height is - // measured rather than assumed, and the legend below is given what is left. - const tb = document.getElementById('titlebar').getBoundingClientRect(); - root.style.setProperty('--title-h', Math.round(tb.bottom) + 'px'); - if (!isPhone()) { - legendEl.style.transform = ''; - legendEl.classList.remove('collapsed'); - root.style.setProperty('--sheet-h', '0px'); - return; - } - const hh = handleEl.offsetHeight || 46; - legendEl.classList.toggle('collapsed', !sheetOpen); - legendEl.style.transform = sheetOpen ? '' : `translateY(${Math.max(0, legendEl.offsetHeight - hh)}px)`; - root.style.setProperty('--sheet-h', (sheetOpen ? Math.min(legendEl.offsetHeight, innerHeight * 0.76) : hh) + 'px'); + legendEl.style.transform = ''; + legendEl.classList.remove('collapsed'); + root.style.setProperty('--sheet-h', '0px'); } handleEl.onclick = () => { - sheetOpen = !sheetOpen; - handleEl.setAttribute('aria-expanded', String(sheetOpen)); - layoutSheet(); + // Backward-compatible fallback for an older cached stylesheet. The current + // layout hides this handle and scrolls the evidence workspace instead. + sheetOpen = true; + handleEl.setAttribute('aria-expanded', 'true'); }; @@ -3571,18 +3565,8 @@ function drawPlaces() { boxes.push([a, b, c, d]); return true; }; - // The furniture is claimed before any name is placed, so nothing is ever drawn - // under the legend or behind the title. A label hidden by a panel is worse than a - // label not drawn: it costs the reader a name AND spends the space that the next - // name down the ranking would have used. - for (const id of ['titlebar', 'legend', 'ribbon', 'panel', 'modes']) { - // offsetParent is null for a fixed-positioned element, which every panel here - // is, so visibility is taken from the rectangle and the computed display. - const el = document.getElementById(id); - if (!el || el.hidden || getComputedStyle(el).display === 'none') continue; - const b = el.getBoundingClientRect(); - if (b.width && b.height) boxes.push([b.left - 6, b.top - 6, b.right + 6, b.bottom + 6]); - } + // Interface and evidence occupy their own surface, outside this coordinate + // system. Labels therefore compete only with other labels. ctx.save(); ctx.textAlign = 'center'; @@ -3729,7 +3713,9 @@ function setLiveOnly(v) { let seen = false; try { seen = localStorage.getItem(KEY) === '1'; } catch (e) { /* private mode */ } const show = () => { - if (!dialog.open) dialog.showModal(); + if (!dialog.open) dialog.show(); + dialog.scrollTop = 0; + document.getElementById('introClose').focus({ preventScroll: true }); }; const close = () => { try { localStorage.setItem(KEY, '1'); } catch (e) { /* private mode */ } diff --git a/site/data/provenance.json b/site/data/provenance.json index a937d95..bade62b 100644 --- a/site/data/provenance.json +++ b/site/data/provenance.json @@ -1 +1 @@ -{"schema":3,"built":"2026-08-22T10:08:26.511Z","revision":"sha256:319b28697e3beed0029f1632731890a8872e632077a2c47965ff032edb1763ed","publicationDigest":"sha256:319b28697e3beed0029f1632731890a8872e632077a2c47965ff032edb1763ed","sourceRevision":null,"scope":"The publication digest identifies the exact public artifacts and generators. sourceRevision is added by the deployment workflow. Reviewed archive hashes survive partial API refreshes; mutable API responses are identified by their published source state.","archiveEvidence":"unavailable","facts":{"stations":233,"uniqueStationIds":233,"boundStations":227,"dischargeStations":187,"unresolvedDischargeUnits":5,"dischargeReaches":173,"eligibleDischargeReaches":168,"reaches":8711,"uniqueReachIds":8711,"q347Points":1041,"reservoirWeeks":1390,"qualityRows":1543996,"qualityStations":144,"qualityParameters":724,"monitoringCantons":26,"monitoringCantonsWithNawa":25},"sources":[{"key":"q347","holder":"BAFU","datenstand":"2000-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.hydrologie-q347","links":[],"licence":"opendata.swiss, attribution"},{"key":"abstraction","holder":"BAFU and the cantons","datenstand":"2004-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.wasser-entnahme","links":[],"licence":"opendata.swiss, attribution"},{"key":"ara","holder":"BAFU and the cantons","datenstand":"2014-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.gewaesserschutz-klaeranlagen_anteilq347","links":[],"licence":"opendata.swiss, attribution"},{"key":"groundwater","holder":"BAFU","datenstand":"2017-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.grundwasserkoerper","links":[],"licence":"FSDI general terms of use, free use with source attribution"},{"key":"wet_auen","holder":"BAFU","datenstand":"2017-11-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-auen","links":[],"licence":"opendata.swiss, attribution"},{"key":"wet_moorl","holder":"BAFU","datenstand":"2017-11-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-moorlandschaften","links":[],"licence":"opendata.swiss, attribution"},{"key":"rivers","holder":"HydroSHEDS / WWF, HydroRIVERS v1.0","datenstand":"2019-01-01","live":false,"url":"https://www.hydrosheds.org/products/hydrorivers","links":[],"licence":"Free for non-commercial and commercial use with attribution"},{"key":"npp","holder":"BFE","datenstand":"2019-12-20","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bfe.kernkraftwerke","links":[],"licence":"opendata.swiss, attribution"},{"key":"context","holder":"Natural Earth 10m","datenstand":"2022-05-01","live":false,"url":"https://www.naturalearthdata.com/","links":[{"label":"10m lakes","url":"https://www.naturalearthdata.com/downloads/10m-physical-vectors/10m-lakes/"},{"label":"10m countries","url":"https://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/"}],"licence":"Public domain"},{"key":"zones","holder":"the 26 cantons via geodienste.ch","datenstand":"2023-05-16","live":false,"url":"https://geodienste.ch/services/planerischer_gewaesserschutz","links":[],"licence":"per canton; all 26 currently publish this model freely"},{"key":"ice","holder":"GLAMOS","datenstand":"2023-09-01","live":false,"url":"https://doi.glamos.ch/","links":[{"label":"SGI1850","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1850_r1992.html"},{"label":"SGI1931","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1931_r2022.html"},{"label":"SGI1973","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1973_r1976.html"},{"label":"SGI2010","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2010_r2010.html"},{"label":"SGI2016","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2016_r2020.html"},{"label":"SGI2023","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2023_r2026.html"},{"label":"length change","url":"https://doi.glamos.ch/data/lengthchange/lengthchange_2025_r2025.html"}],"licence":"CC BY 4.0 per the DOI index; the length-change file header adds \"scientific and non-commercial use\". Both statements stand."},{"key":"dams","holder":"BFE","datenstand":"2023-11-28","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bfe.stauanlagen-bundesaufsicht","links":[],"licence":"opendata.swiss, attribution"},{"key":"catchments","holder":"BAFU","datenstand":"2024-06-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.wasser-teileinzugsgebiete_2","links":[],"licence":"FSDI general terms of use, free use with source attribution"},{"key":"quality","holder":"BAFU and the cantons","datenstand":"2024-12-31","live":false,"url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/dataproduct-water-nawa-trend","links":[{"label":"NAWA programme","url":"https://www.bafu.admin.ch/de/nawa"},{"label":"dataset and field definitions","url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/dataproduct-water-nawa-trend"},{"label":"prepared annual files","url":"https://data.bafu.admin.ch/download?list-type=2&prefix=water/nawa-trend/"},{"label":"licence and source","url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/lizenz-und-quelle"}],"licence":"Open use. Must provide the source. Commercial and non-commercial use permitted with attribution."},{"key":"wet_hoch","holder":"BAFU","datenstand":"2025-12-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-hochmoore","links":[],"licence":"opendata.swiss, attribution"},{"key":"wet_flach","holder":"BAFU","datenstand":"2025-12-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-flachmoore","links":[],"licence":"opendata.swiss, attribution"},{"key":"hydro","holder":"BFE","datenstand":"2025-12-31","live":false,"url":"https://www.bfe.admin.ch/de/wasserkraft","links":[],"licence":"opendata.swiss, attribution"},{"key":"names","holder":"swisstopo, swissNAMES3D 2026","datenstand":"2026-03-09","live":false,"url":"https://data.geo.admin.ch/ch.swisstopo.swissnames3d/","links":[],"licence":"Open data, free use with source attribution"},{"key":"wet_amphi","holder":"BAFU","datenstand":"2026-05-20","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-amphibien","links":[],"licence":"opendata.swiss, attribution"},{"key":"reservoirFill","holder":"BFE","datenstand":"2026-08-17","live":false,"url":"https://www.uvek-gis.admin.ch/BFE/ogd/17/ogd17_fuellungsgrad_speicherseen.csv","links":[],"licence":"opendata.swiss, attribution"},{"key":"gauges","holder":"BAFU","datenstand":null,"live":true,"url":"https://lindas.admin.ch/query","links":[{"label":"LINDAS observations","url":"https://lindas.admin.ch/query"},{"label":"hydrodaten station plots and units","url":"https://www.hydrodaten.admin.ch/"}],"licence":"Open government data, no key"},{"key":"basemap","holder":"swisstopo","datenstand":null,"live":false,"url":"https://docs.geo.admin.ch/visualize-data/wms.html","links":[],"licence":"FSDI general terms of use, free use with source attribution"}],"upstreamArchives":[],"generators":{".github/workflows/pages.yml":{"bytes":3319,"sha256":"db641bc9404c15c8ea4305ab9700998d4cd390d5d0dffa255c75979819c431bc"},"build/00-sources.sh":{"bytes":8872,"sha256":"8ec73140ecc00c2b7cb44f17115706cb0f3a520927c4c452b9c2858a90b37f28"},"build/01-stations.mjs":{"bytes":5719,"sha256":"4d81f143d263c84c05ef541b732d6e2dcee6aa60a59971b543faf04a77bc2a1e"},"build/02-network.mjs":{"bytes":3111,"sha256":"394b73f8e36e98d8d09bea66fcaf42883bfd7bfe228000341ba72444047975d8"},"build/03-context.mjs":{"bytes":1641,"sha256":"9061bfb755f87b78e3a52d0d080c19845f3cd131c173683131610613b0188038"},"build/04-glaciers.mjs":{"bytes":8309,"sha256":"b02d1e76ddfddb9ae2c2100c0f6d9d1c388afa23268ad29a052ec99080423320"},"build/05-users.mjs":{"bytes":11468,"sha256":"7a4bdf9fd2b521e19641b34cb74b4a3b720a569a88fd2b3f36570273b2dccb5c"},"build/06-reservoirs.mjs":{"bytes":12702,"sha256":"a6188f5c0c11a1a2822f4f9f9100750123442dde6a9d6ae1b983e3971fee9635"},"build/07-residual.mjs":{"bytes":13124,"sha256":"85991e1825ff9f027c0d9147616941ef4505c9dd25fa291826884650b135ff80"},"build/08-vintage.mjs":{"bytes":21960,"sha256":"10d0191fa478023043555fcd592ef6efcfdebcedd9941b431e65e7339d342fe3"},"build/09-icehistory.mjs":{"bytes":6719,"sha256":"24bd4e2d658b2169f0864aa4475e0d8b976462c1598051f43c4e266ef0ef2e57"},"build/10-names.mjs":{"bytes":16681,"sha256":"70313e9fad336e981fd14affd094e9dec2214d1d5dbf9facb1d408c28f31749b"},"build/11-cantons.mjs":{"bytes":3034,"sha256":"9f88c2f7b8bef1bc9901e17b1075a520ce4061984ebf9a672349d2d8659045b4"},"build/12-wetlands.mjs":{"bytes":13776,"sha256":"9703b964901932b5382212f207c0570c0a1033b3d3d6dbd7aabf70019a4195e5"},"build/13-basins.mjs":{"bytes":13142,"sha256":"c89b0a6797b4b824c62119b9ddbd6f260d29fac0041757d904acef81c34158fc"},"build/14-pages.py":{"bytes":15342,"sha256":"802edb86225eb60378f489805c7303dbda6c74de9db7b2b2424667905ef86c54"},"build/15-social-card.mjs":{"bytes":6144,"sha256":"51414e2510d58ea5af4373264efcbb937984443e826e5b5a71fbd067f44bc6c1"},"build/16-provenance.mjs":{"bytes":5320,"sha256":"08e31dc626be2c9809191bc7d0fd8b20d63446c41444788b9d26f46bc3026c07"},"build/17-quality.mjs":{"bytes":12969,"sha256":"80cdb035bb96541066904c02ef7f33bf60719d4550d51d49a3cd465d13effa7a"},"build/18-monitoring.mjs":{"bytes":7703,"sha256":"8c4bb64641562a77fb6dd22940b4c607e2f1da245ac453cb09c283ecefc24550"},"build/lib-stations.mjs":{"bytes":1501,"sha256":"17d8c00b36f96fba383b430f8f22d37c83e7c5f895fa9f99823dd0ead087b3f5"},"build/pages/de.json":{"bytes":155594,"sha256":"662f1344c6991313e33d9b6a73ea166681e300d5ccf35de9f9672bd151913ce2"},"build/pages/fr.json":{"bytes":159142,"sha256":"cf4d1a4ed1cd09c18c0346b58654b192180f8932105379943a1883186f18b0c5"},"build/pages/index.en.html":{"bytes":27020,"sha256":"98bf7f7448ad7acacc05a476417a2cc37f340cd220fa7fdfb83b9b9b65a38a0b"},"build/pages/it.json":{"bytes":154686,"sha256":"6bbfbbec18f06c0536bf4cbd3d668d856b04fa3f84917567e6887acf81fc9fd2"},"build/pages/rm.json":{"bytes":157695,"sha256":"33b229bacffbee7c5c8f788bcec8023c3ebf7bda5b3187a90e974140e9c2fe7f"},"build/pages/updates.json":{"bytes":229450,"sha256":"473c10b0ad01d111adad65301a7d02776dd57317fbbe1139f7dd22352a17ef7d"},"package-lock.json":{"bytes":63777,"sha256":"c3c32ff1af7525028f4c955f9aadba58f0a252692b04f3ee54958ebcbf268e50"},"package.json":{"bytes":1070,"sha256":"53caf42b0f271b8567a224f37d229fb516257d30cfe536661cc96433f8d75bfe"},"scripts/check-external-links.mjs":{"bytes":2337,"sha256":"0d23a491568eec07b7830c5598f7ae6478e376e82ded972dade923ccf8d70352"},"scripts/check-performance-budget.mjs":{"bytes":3003,"sha256":"5217493c384f55470b32c94a15e359742a1aecb6371b87bbf9e469e553101cfd"},"scripts/verify-site.mjs":{"bytes":32807,"sha256":"9a2d7f9e96fc4365ba58e205036b0b87adf78a94e7e72d0c7ab55a9bd8e1c34b"}},"artifacts":{"about.html":{"bytes":22030,"sha256":"104ac5350e5521571b2b2f1c110919233e2ae8a59c2deea93fcefbafba88c26e"},"app.js":{"bytes":170606,"sha256":"a13b07cfd03a34c8d89d64d667b66a1659c59306f13852267387b4ea4d140238"},"data/basins.json":{"bytes":3488,"sha256":"9fc9f8a3f55a42580dafac22eedf9f1090ee6b31873110719bde121ff968c537"},"data/canton-monitoring.json":{"bytes":6601,"sha256":"5a0723e5a87ba9643591c466d653604e0418c364ed4487791c84e26afdbdbb69"},"data/cantons.json":{"bytes":3625,"sha256":"31d0e54c707c1cb511b22dfb263a3edaeeb81f098f5643dab3828e945f8e5b69"},"data/context.json":{"bytes":23094,"sha256":"274f0a40245933156cb8daa994f1e9188fbe6f99db0be2e3cea80925f2368007"},"data/glaciers.json":{"bytes":1115131,"sha256":"2ab15ef0c109995bd740902856eb6b3c6d12f82899d2af39e893e28e5b0dc35f"},"data/icehistory.json":{"bytes":625151,"sha256":"9ceb75bab89879565bfd13e2d7614f0bd25c6a63d92733cd27f841931e63e5ab"},"data/names.json":{"bytes":365854,"sha256":"97c91c4a7b74685ab81752617c2db1399408ece0d3f0e11683f621711dcf2192"},"data/network.json":{"bytes":1105368,"sha256":"4b04980eb3a2f4385d783af8da41eb15bcc28ad2e5f22f94d6c3ced7938ba12c"},"data/quality.json":{"bytes":4624338,"sha256":"47cfd9b0e50d22baa268280250401d3af2bc2e70af08455a053addde2ee94084"},"data/reservoirs.json":{"bytes":110597,"sha256":"26de75b0047008cdd94c563c78f67eb737f4f450763ef11cab3c140a48320bef"},"data/residual.json":{"bytes":235789,"sha256":"614e5f00f726148d30a23b5debe508561ae0163ce60002ef3723b48f9aece406"},"data/social-card.json":{"bytes":184,"sha256":"ce6711de7c2883ace6daf796e4044124b104c1392d553374af08a178b1782172"},"data/stations.json":{"bytes":41444,"sha256":"9f55491b1b1aa992a79e2568bcb3363e6daf02cc9f121e8a82afdc8d005f53c8"},"data/users.json":{"bytes":346478,"sha256":"1399cdf3eabed26a519f609db326cecdb99ea4a9146aa640630fbd45d2c7f4bf"},"data/vintage.json":{"bytes":13903,"sha256":"c98fcc5bf4a509aba37cc804945bbc931ad843e3913ccaf24aebd01c45a83db0"},"data/wetlands.json":{"bytes":1081150,"sha256":"eee2b59945140fea80178edae6b5bfe973f2a52475c72d0bf268d4a7809170cb"},"de/about.html":{"bytes":23133,"sha256":"053fb855d8319f5218bbc743e74aa13bf41809af8a7023911d2b9158dabfe042"},"de/index.html":{"bytes":28006,"sha256":"d98e734f34c92943c707012ef0a7620f5f357dd8a44f5b1e885939bc0d0dc4f5"},"de/law.html":{"bytes":29056,"sha256":"96f0c88182abde0323531d25adff9a6d2bfd5b79ab68f738832439ef76ac366e"},"de/method.html":{"bytes":46693,"sha256":"c3bc531bbfb673a0c445ad29ebbe4c0e6a7a80d4589c035d897705673660b23e"},"de/sources.html":{"bytes":13170,"sha256":"550c3e0e66d0c89d1a75a2473d9f246d04af3e0f4956e9c93464ebe6800f5bb3"},"en/index.html":{"bytes":27112,"sha256":"6b4b6dc9cb0ff319a0860ad5c6413087eded3470da64ca37aded1659cfae8b68"},"favicon.svg":{"bytes":598,"sha256":"ffb7394f49c0ac90daa0203d9ce45bfafbf7d3d9cac1f0fdafc561f797f9195f"},"fmt.js":{"bytes":2388,"sha256":"eb50d45ea49aa0b8ef4d434d6dce3ece9537965a44948b350ce8b99b1c03cb66"},"fonts.css":{"bytes":4631,"sha256":"a919c516ef820d854333516658188efaa463cb5535277a12a14c5afd9e2d3be8"},"fonts/OFL-Archivo.txt":{"bytes":4387,"sha256":"1778201b7bd33e8c08a2eda32a4ad2f69bc38ced9731b01cc3fc47f268c8ef3c"},"fonts/OFL-IBM-Plex-Mono.txt":{"bytes":4362,"sha256":"d741e57d5f865e294df801f96b7b5161a88b211df65887e4358d271c9fc5fb4f"},"fonts/OFL-Source-Serif-4.txt":{"bytes":4399,"sha256":"0fd8b796c1c6220a559a5682cfd00d1c8488b428369f7cb70deb671888cef85f"},"fonts/archivo-italic-latin-ext.woff2":{"bytes":35208,"sha256":"71d5c4e5da1cc99cf78fe92f2fb9880dfc03dfef7122b0d1ed1d56f25452e824"},"fonts/archivo-italic-latin.woff2":{"bytes":39132,"sha256":"e1989a5727374fcd299979407c8087669ca223f5281f8645891e5400f3e61aeb"},"fonts/archivo-normal-latin-ext.woff2":{"bytes":32672,"sha256":"a999e009fdcd0f939c138e04043b3b2ad083b26252b644e8f94c083d48b77af0"},"fonts/archivo-normal-latin.woff2":{"bytes":34940,"sha256":"7150c0ec5ad356453013d11affec1fbab95de0dd2dcecb043b4f1cb7f87c4ba4"},"fonts/ibm-plex-mono-400-latin-ext.woff2":{"bytes":8860,"sha256":"f1050dc5317b43434c0aeda599d4624c774ffc162e87a8cf204b949b6a85816d"},"fonts/ibm-plex-mono-400-latin.woff2":{"bytes":10052,"sha256":"c36f509c0a8f9f85f29cb44bc8701d8a9e0b14c499e77a884f789ead7093a7ac"},"fonts/ibm-plex-mono-500-latin-ext.woff2":{"bytes":8848,"sha256":"77f03e26f981c582bdba3a7abed4baa2d3149211c01366bb3ab3ba7622ec4ae5"},"fonts/ibm-plex-mono-500-latin.woff2":{"bytes":10060,"sha256":"a76f53ca6612e7b3828eec2311098675b7f9849ae4169a8bcef6302aec02a6c0"},"fonts/source-serif-italic-latin-ext.woff2":{"bytes":43964,"sha256":"3eff7682cb08de3fbd2f411e05dc3f68fbd3ce721ec554ffafa7b0397214e76f"},"fonts/source-serif-italic-latin.woff2":{"bytes":50584,"sha256":"419703e180143eb9033e02651331fcf3cc8558d42f1041140a0b24e5be0933bb"},"fonts/source-serif-normal-latin-ext.woff2":{"bytes":100772,"sha256":"a0cd390d017200f91327781204458ff918999584bece8902497ba6c08472178d"},"fonts/source-serif-normal-latin.woff2":{"bytes":122168,"sha256":"2a24bad466f09b88b8e9cbc488bf774117c912e66374c57bf4716855849143f8"},"fr/about.html":{"bytes":23501,"sha256":"e675f5537e9890ddf10e26b15e3e5954bf3855f3865635294486490fb23f99e2"},"fr/index.html":{"bytes":28749,"sha256":"1bb44a6827e1ba8478ca4ed9500ef5898006c3bc377f3c70be642d2638785173"},"fr/law.html":{"bytes":30444,"sha256":"b64df0be0a3aa29350c72dba84dcd33ee8fa7187b6c962a3a23f7378c3768cac"},"fr/method.html":{"bytes":48291,"sha256":"cd09ad9691472cf8947e257c184e8d3aa6faa31dbeafe30046d40bc77e10c593"},"fr/sources.html":{"bytes":13483,"sha256":"ea299ef805f62d9e7bdcfd7ee96cf3d795a8d97ec17af052944d0ee07189964a"},"gschg31.js":{"bytes":1894,"sha256":"bf4673598fd6936aec1a2eeb2e692b24ea984fb728146324dcf8586f183a9c08"},"i18n-data.js":{"bytes":59884,"sha256":"e416f44065e452e52f810fc76c3df633853c6ae8604517d79d6b61453789f129"},"i18n-map.js":{"bytes":133286,"sha256":"23cd200bd5d2a014588079853a6e948beef5176405ceb380d2ccd7d7e0ddd6bc"},"i18n.js":{"bytes":38264,"sha256":"260d45c429e32b77efffbeb1fb84f2fad55de31654e8df2a36e8b2ec6fb828c5"},"index.html":{"bytes":28000,"sha256":"0c9f931ce9116991ec3b3f1105851bf05bb32af76081b0d16962a5d9c16a2710"},"it/about.html":{"bytes":22654,"sha256":"b5b075cbab0006ed5ae5879b11a4c7eca074c5cfde1174252636b61ff08a534a"},"it/index.html":{"bytes":28183,"sha256":"a65fa733071bddb56d7e5087cd14b3cadee14f73d68c3f2355778ddf7d5538e6"},"it/law.html":{"bytes":29425,"sha256":"3403e5298ea6a1af35d57e26bc7774183bf1f29fc0d4eeb5f034ad1acb10502a"},"it/method.html":{"bytes":46086,"sha256":"ef07d64bc11d5eaff3d6137b09ed527f1f0e4573c95f4de0ad89410db078a55a"},"it/sources.html":{"bytes":13034,"sha256":"24d0e39523b86ce07b2d8954bc006dba83d4361666ba07429c14206819593d66"},"law.html":{"bytes":27787,"sha256":"acef5db2024280f2b898a3f5b954c3c9d1b85413b55b7612ad6f1b887a49bfe8"},"method.html":{"bytes":44790,"sha256":"a6abe0120a88f92ff39b8cd6867f51ff4eaa610eec0bb99a6e6d763be30811fd"},"og-image.jpg":{"bytes":200325,"sha256":"f6a6cbd88798900d3b584b1e966c764506ddd85e88328d77e17cf77cf89f5144"},"rm/about.html":{"bytes":22969,"sha256":"2277a5c47179ee744ad5f73e0fe7942d9b6d1ab08896fd8ad2462e1e3447fbf5"},"rm/index.html":{"bytes":28468,"sha256":"a4149f4376abe0adf7739b504878763577d1afe05714c41a46bfbf32a9caa922"},"rm/law.html":{"bytes":29984,"sha256":"49090f341a4f86169140bc5b2ad97c19f33a8572067d6594454de719c23d0316"},"rm/method.html":{"bytes":47388,"sha256":"0e3c3d517c8fe2ba40f018caf8ce20d81a31147f4d0a4f312eaeffbfeb268e34"},"rm/sources.html":{"bytes":13545,"sha256":"d68c85b0d96ddb0b487190e72f547163aa2892325b811204b51d1d4b7cdab02d"},"robots.txt":{"bytes":80,"sha256":"f8de6fa3cc79f4c7b56023e2618742b16dabde01c2d950dc0a48f71309ed9018"},"site.css":{"bytes":23697,"sha256":"db2e7c2177fb509ba9fda4cd39128995abbef46bef84989dd543745f8005df0c"},"site.js":{"bytes":23793,"sha256":"e72c6c292aa0b4c1dbf81a02cb4a0a5a60e8b421e0d58142bad644dc3604ff40"},"sitemap.xml":{"bytes":18506,"sha256":"c7ce280de0075811152bff4e24c596ecc2bd80b230fda8af033ad1b4cbb13153"},"sources.html":{"bytes":12702,"sha256":"5bfa6f4f48f3b6dbf9086f2375c4f692c4dcd78561b4ad4d5d2788674813c0ae"},"style.css":{"bytes":52424,"sha256":"448dd0044c6488763133014c7ee226128b1e99dcb2298404c8d8ff15eca20a03"},"theme.js":{"bytes":3354,"sha256":"a1811f92d1d08cc87cb3c8f41515a9f0868bdad44782150bd41074af1df06b3a"},"tokens.css":{"bytes":16169,"sha256":"a09dd19c74aa879f8211196eed3ed19b97c6a576985253b4c468e9b52b5b4f7a"}}} \ No newline at end of file +{"schema":3,"built":"2026-08-22T10:37:33.923Z","revision":"sha256:b73efb883d016865b78ce9d6707bb7eedc3df8864c9fde36e08b7dad867e5ecd","publicationDigest":"sha256:b73efb883d016865b78ce9d6707bb7eedc3df8864c9fde36e08b7dad867e5ecd","sourceRevision":null,"scope":"The publication digest identifies the exact public artifacts and generators. sourceRevision is added by the deployment workflow. Reviewed archive hashes survive partial API refreshes; mutable API responses are identified by their published source state.","archiveEvidence":"unavailable","facts":{"stations":233,"uniqueStationIds":233,"boundStations":227,"dischargeStations":187,"unresolvedDischargeUnits":5,"dischargeReaches":173,"eligibleDischargeReaches":168,"reaches":8711,"uniqueReachIds":8711,"q347Points":1041,"reservoirWeeks":1390,"qualityRows":1543996,"qualityStations":144,"qualityParameters":724,"monitoringCantons":26,"monitoringCantonsWithNawa":25},"sources":[{"key":"q347","holder":"BAFU","datenstand":"2000-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.hydrologie-q347","links":[],"licence":"opendata.swiss, attribution"},{"key":"abstraction","holder":"BAFU and the cantons","datenstand":"2004-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.wasser-entnahme","links":[],"licence":"opendata.swiss, attribution"},{"key":"ara","holder":"BAFU and the cantons","datenstand":"2014-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.gewaesserschutz-klaeranlagen_anteilq347","links":[],"licence":"opendata.swiss, attribution"},{"key":"groundwater","holder":"BAFU","datenstand":"2017-01-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.grundwasserkoerper","links":[],"licence":"FSDI general terms of use, free use with source attribution"},{"key":"wet_auen","holder":"BAFU","datenstand":"2017-11-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-auen","links":[],"licence":"opendata.swiss, attribution"},{"key":"wet_moorl","holder":"BAFU","datenstand":"2017-11-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-moorlandschaften","links":[],"licence":"opendata.swiss, attribution"},{"key":"rivers","holder":"HydroSHEDS / WWF, HydroRIVERS v1.0","datenstand":"2019-01-01","live":false,"url":"https://www.hydrosheds.org/products/hydrorivers","links":[],"licence":"Free for non-commercial and commercial use with attribution"},{"key":"npp","holder":"BFE","datenstand":"2019-12-20","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bfe.kernkraftwerke","links":[],"licence":"opendata.swiss, attribution"},{"key":"context","holder":"Natural Earth 10m","datenstand":"2022-05-01","live":false,"url":"https://www.naturalearthdata.com/","links":[{"label":"10m lakes","url":"https://www.naturalearthdata.com/downloads/10m-physical-vectors/10m-lakes/"},{"label":"10m countries","url":"https://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/"}],"licence":"Public domain"},{"key":"zones","holder":"the 26 cantons via geodienste.ch","datenstand":"2023-05-16","live":false,"url":"https://geodienste.ch/services/planerischer_gewaesserschutz","links":[],"licence":"per canton; all 26 currently publish this model freely"},{"key":"ice","holder":"GLAMOS","datenstand":"2023-09-01","live":false,"url":"https://doi.glamos.ch/","links":[{"label":"SGI1850","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1850_r1992.html"},{"label":"SGI1931","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1931_r2022.html"},{"label":"SGI1973","url":"https://doi.glamos.ch/data/inventory/inventory_sgi1973_r1976.html"},{"label":"SGI2010","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2010_r2010.html"},{"label":"SGI2016","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2016_r2020.html"},{"label":"SGI2023","url":"https://doi.glamos.ch/data/inventory/inventory_sgi2023_r2026.html"},{"label":"length change","url":"https://doi.glamos.ch/data/lengthchange/lengthchange_2025_r2025.html"}],"licence":"CC BY 4.0 per the DOI index; the length-change file header adds \"scientific and non-commercial use\". Both statements stand."},{"key":"dams","holder":"BFE","datenstand":"2023-11-28","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bfe.stauanlagen-bundesaufsicht","links":[],"licence":"opendata.swiss, attribution"},{"key":"catchments","holder":"BAFU","datenstand":"2024-06-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.wasser-teileinzugsgebiete_2","links":[],"licence":"FSDI general terms of use, free use with source attribution"},{"key":"quality","holder":"BAFU and the cantons","datenstand":"2024-12-31","live":false,"url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/dataproduct-water-nawa-trend","links":[{"label":"NAWA programme","url":"https://www.bafu.admin.ch/de/nawa"},{"label":"dataset and field definitions","url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/dataproduct-water-nawa-trend"},{"label":"prepared annual files","url":"https://data.bafu.admin.ch/download?list-type=2&prefix=water/nawa-trend/"},{"label":"licence and source","url":"https://api.data-platform-stg.cloud.bafu.admin.ch/en/lizenz-und-quelle"}],"licence":"Open use. Must provide the source. Commercial and non-commercial use permitted with attribution."},{"key":"wet_hoch","holder":"BAFU","datenstand":"2025-12-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-hochmoore","links":[],"licence":"opendata.swiss, attribution"},{"key":"wet_flach","holder":"BAFU","datenstand":"2025-12-01","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-flachmoore","links":[],"licence":"opendata.swiss, attribution"},{"key":"hydro","holder":"BFE","datenstand":"2025-12-31","live":false,"url":"https://www.bfe.admin.ch/de/wasserkraft","links":[],"licence":"opendata.swiss, attribution"},{"key":"names","holder":"swisstopo, swissNAMES3D 2026","datenstand":"2026-03-09","live":false,"url":"https://data.geo.admin.ch/ch.swisstopo.swissnames3d/","links":[],"licence":"Open data, free use with source attribution"},{"key":"wet_amphi","holder":"BAFU","datenstand":"2026-05-20","live":false,"url":"https://map.geo.admin.ch/?layers=ch.bafu.bundesinventare-amphibien","links":[],"licence":"opendata.swiss, attribution"},{"key":"reservoirFill","holder":"BFE","datenstand":"2026-08-17","live":false,"url":"https://www.uvek-gis.admin.ch/BFE/ogd/17/ogd17_fuellungsgrad_speicherseen.csv","links":[],"licence":"opendata.swiss, attribution"},{"key":"gauges","holder":"BAFU","datenstand":null,"live":true,"url":"https://lindas.admin.ch/query","links":[{"label":"LINDAS observations","url":"https://lindas.admin.ch/query"},{"label":"hydrodaten station plots and units","url":"https://www.hydrodaten.admin.ch/"}],"licence":"Open government data, no key"},{"key":"basemap","holder":"swisstopo","datenstand":null,"live":false,"url":"https://docs.geo.admin.ch/visualize-data/wms.html","links":[],"licence":"FSDI general terms of use, free use with source attribution"}],"upstreamArchives":[],"generators":{".github/workflows/pages.yml":{"bytes":3319,"sha256":"db641bc9404c15c8ea4305ab9700998d4cd390d5d0dffa255c75979819c431bc"},"build/00-sources.sh":{"bytes":8872,"sha256":"8ec73140ecc00c2b7cb44f17115706cb0f3a520927c4c452b9c2858a90b37f28"},"build/01-stations.mjs":{"bytes":5719,"sha256":"4d81f143d263c84c05ef541b732d6e2dcee6aa60a59971b543faf04a77bc2a1e"},"build/02-network.mjs":{"bytes":3111,"sha256":"394b73f8e36e98d8d09bea66fcaf42883bfd7bfe228000341ba72444047975d8"},"build/03-context.mjs":{"bytes":1641,"sha256":"9061bfb755f87b78e3a52d0d080c19845f3cd131c173683131610613b0188038"},"build/04-glaciers.mjs":{"bytes":8309,"sha256":"b02d1e76ddfddb9ae2c2100c0f6d9d1c388afa23268ad29a052ec99080423320"},"build/05-users.mjs":{"bytes":11468,"sha256":"7a4bdf9fd2b521e19641b34cb74b4a3b720a569a88fd2b3f36570273b2dccb5c"},"build/06-reservoirs.mjs":{"bytes":12702,"sha256":"a6188f5c0c11a1a2822f4f9f9100750123442dde6a9d6ae1b983e3971fee9635"},"build/07-residual.mjs":{"bytes":13124,"sha256":"85991e1825ff9f027c0d9147616941ef4505c9dd25fa291826884650b135ff80"},"build/08-vintage.mjs":{"bytes":21960,"sha256":"10d0191fa478023043555fcd592ef6efcfdebcedd9941b431e65e7339d342fe3"},"build/09-icehistory.mjs":{"bytes":6719,"sha256":"24bd4e2d658b2169f0864aa4475e0d8b976462c1598051f43c4e266ef0ef2e57"},"build/10-names.mjs":{"bytes":16681,"sha256":"70313e9fad336e981fd14affd094e9dec2214d1d5dbf9facb1d408c28f31749b"},"build/11-cantons.mjs":{"bytes":3034,"sha256":"9f88c2f7b8bef1bc9901e17b1075a520ce4061984ebf9a672349d2d8659045b4"},"build/12-wetlands.mjs":{"bytes":13776,"sha256":"9703b964901932b5382212f207c0570c0a1033b3d3d6dbd7aabf70019a4195e5"},"build/13-basins.mjs":{"bytes":13142,"sha256":"c89b0a6797b4b824c62119b9ddbd6f260d29fac0041757d904acef81c34158fc"},"build/14-pages.py":{"bytes":15342,"sha256":"802edb86225eb60378f489805c7303dbda6c74de9db7b2b2424667905ef86c54"},"build/15-social-card.mjs":{"bytes":6144,"sha256":"51414e2510d58ea5af4373264efcbb937984443e826e5b5a71fbd067f44bc6c1"},"build/16-provenance.mjs":{"bytes":5320,"sha256":"08e31dc626be2c9809191bc7d0fd8b20d63446c41444788b9d26f46bc3026c07"},"build/17-quality.mjs":{"bytes":12969,"sha256":"80cdb035bb96541066904c02ef7f33bf60719d4550d51d49a3cd465d13effa7a"},"build/18-monitoring.mjs":{"bytes":7703,"sha256":"8c4bb64641562a77fb6dd22940b4c607e2f1da245ac453cb09c283ecefc24550"},"build/lib-stations.mjs":{"bytes":1501,"sha256":"17d8c00b36f96fba383b430f8f22d37c83e7c5f895fa9f99823dd0ead087b3f5"},"build/pages/de.json":{"bytes":155594,"sha256":"662f1344c6991313e33d9b6a73ea166681e300d5ccf35de9f9672bd151913ce2"},"build/pages/fr.json":{"bytes":159142,"sha256":"cf4d1a4ed1cd09c18c0346b58654b192180f8932105379943a1883186f18b0c5"},"build/pages/index.en.html":{"bytes":27027,"sha256":"e37a648c18351142209dadc0e83a2346ebc37f2ef7a619ce5376de632e3ec2da"},"build/pages/it.json":{"bytes":154686,"sha256":"6bbfbbec18f06c0536bf4cbd3d668d856b04fa3f84917567e6887acf81fc9fd2"},"build/pages/rm.json":{"bytes":157695,"sha256":"33b229bacffbee7c5c8f788bcec8023c3ebf7bda5b3187a90e974140e9c2fe7f"},"build/pages/updates.json":{"bytes":229446,"sha256":"91bfb428c393e6da42cf593caefb055a7c2e7bf77e7f8a28c2c1277eadc65a93"},"package-lock.json":{"bytes":63777,"sha256":"c3c32ff1af7525028f4c955f9aadba58f0a252692b04f3ee54958ebcbf268e50"},"package.json":{"bytes":1070,"sha256":"53caf42b0f271b8567a224f37d229fb516257d30cfe536661cc96433f8d75bfe"},"scripts/check-external-links.mjs":{"bytes":2337,"sha256":"0d23a491568eec07b7830c5598f7ae6478e376e82ded972dade923ccf8d70352"},"scripts/check-performance-budget.mjs":{"bytes":3003,"sha256":"5217493c384f55470b32c94a15e359742a1aecb6371b87bbf9e469e553101cfd"},"scripts/verify-site.mjs":{"bytes":34715,"sha256":"84ec0de8b2fc2cf0f6b4151abb2754e561e55290c74883661c7155602443441f"}},"artifacts":{"about.html":{"bytes":22030,"sha256":"104ac5350e5521571b2b2f1c110919233e2ae8a59c2deea93fcefbafba88c26e"},"app.js":{"bytes":168983,"sha256":"e96f9d5b5535e66a53fd19c6ea6b10416b15cfa0f50d3450bd2ec5f9e4cbe66f"},"data/basins.json":{"bytes":3488,"sha256":"9fc9f8a3f55a42580dafac22eedf9f1090ee6b31873110719bde121ff968c537"},"data/canton-monitoring.json":{"bytes":6601,"sha256":"5a0723e5a87ba9643591c466d653604e0418c364ed4487791c84e26afdbdbb69"},"data/cantons.json":{"bytes":3625,"sha256":"31d0e54c707c1cb511b22dfb263a3edaeeb81f098f5643dab3828e945f8e5b69"},"data/context.json":{"bytes":23094,"sha256":"274f0a40245933156cb8daa994f1e9188fbe6f99db0be2e3cea80925f2368007"},"data/glaciers.json":{"bytes":1115131,"sha256":"2ab15ef0c109995bd740902856eb6b3c6d12f82899d2af39e893e28e5b0dc35f"},"data/icehistory.json":{"bytes":625151,"sha256":"9ceb75bab89879565bfd13e2d7614f0bd25c6a63d92733cd27f841931e63e5ab"},"data/names.json":{"bytes":365854,"sha256":"97c91c4a7b74685ab81752617c2db1399408ece0d3f0e11683f621711dcf2192"},"data/network.json":{"bytes":1105368,"sha256":"4b04980eb3a2f4385d783af8da41eb15bcc28ad2e5f22f94d6c3ced7938ba12c"},"data/quality.json":{"bytes":4624338,"sha256":"47cfd9b0e50d22baa268280250401d3af2bc2e70af08455a053addde2ee94084"},"data/reservoirs.json":{"bytes":110597,"sha256":"26de75b0047008cdd94c563c78f67eb737f4f450763ef11cab3c140a48320bef"},"data/residual.json":{"bytes":235789,"sha256":"614e5f00f726148d30a23b5debe508561ae0163ce60002ef3723b48f9aece406"},"data/social-card.json":{"bytes":184,"sha256":"ce6711de7c2883ace6daf796e4044124b104c1392d553374af08a178b1782172"},"data/stations.json":{"bytes":41444,"sha256":"9f55491b1b1aa992a79e2568bcb3363e6daf02cc9f121e8a82afdc8d005f53c8"},"data/users.json":{"bytes":346478,"sha256":"1399cdf3eabed26a519f609db326cecdb99ea4a9146aa640630fbd45d2c7f4bf"},"data/vintage.json":{"bytes":13903,"sha256":"c98fcc5bf4a509aba37cc804945bbc931ad843e3913ccaf24aebd01c45a83db0"},"data/wetlands.json":{"bytes":1081150,"sha256":"eee2b59945140fea80178edae6b5bfe973f2a52475c72d0bf268d4a7809170cb"},"de/about.html":{"bytes":23133,"sha256":"053fb855d8319f5218bbc743e74aa13bf41809af8a7023911d2b9158dabfe042"},"de/index.html":{"bytes":27971,"sha256":"863b2c3cbe8aa35fdfc6ba4a3874946c8acdc151fc3da39f33b498fa06e9a29a"},"de/law.html":{"bytes":29056,"sha256":"96f0c88182abde0323531d25adff9a6d2bfd5b79ab68f738832439ef76ac366e"},"de/method.html":{"bytes":46693,"sha256":"c3bc531bbfb673a0c445ad29ebbe4c0e6a7a80d4589c035d897705673660b23e"},"de/sources.html":{"bytes":13170,"sha256":"550c3e0e66d0c89d1a75a2473d9f246d04af3e0f4956e9c93464ebe6800f5bb3"},"en/index.html":{"bytes":27119,"sha256":"4cad64e6d8adb43ccbc738545d770fc8a6a62b0ddf6a956fe4f3bc7e67b17c5b"},"favicon.svg":{"bytes":598,"sha256":"ffb7394f49c0ac90daa0203d9ce45bfafbf7d3d9cac1f0fdafc561f797f9195f"},"fmt.js":{"bytes":2388,"sha256":"eb50d45ea49aa0b8ef4d434d6dce3ece9537965a44948b350ce8b99b1c03cb66"},"fonts.css":{"bytes":4631,"sha256":"a919c516ef820d854333516658188efaa463cb5535277a12a14c5afd9e2d3be8"},"fonts/OFL-Archivo.txt":{"bytes":4387,"sha256":"1778201b7bd33e8c08a2eda32a4ad2f69bc38ced9731b01cc3fc47f268c8ef3c"},"fonts/OFL-IBM-Plex-Mono.txt":{"bytes":4362,"sha256":"d741e57d5f865e294df801f96b7b5161a88b211df65887e4358d271c9fc5fb4f"},"fonts/OFL-Source-Serif-4.txt":{"bytes":4399,"sha256":"0fd8b796c1c6220a559a5682cfd00d1c8488b428369f7cb70deb671888cef85f"},"fonts/archivo-italic-latin-ext.woff2":{"bytes":35208,"sha256":"71d5c4e5da1cc99cf78fe92f2fb9880dfc03dfef7122b0d1ed1d56f25452e824"},"fonts/archivo-italic-latin.woff2":{"bytes":39132,"sha256":"e1989a5727374fcd299979407c8087669ca223f5281f8645891e5400f3e61aeb"},"fonts/archivo-normal-latin-ext.woff2":{"bytes":32672,"sha256":"a999e009fdcd0f939c138e04043b3b2ad083b26252b644e8f94c083d48b77af0"},"fonts/archivo-normal-latin.woff2":{"bytes":34940,"sha256":"7150c0ec5ad356453013d11affec1fbab95de0dd2dcecb043b4f1cb7f87c4ba4"},"fonts/ibm-plex-mono-400-latin-ext.woff2":{"bytes":8860,"sha256":"f1050dc5317b43434c0aeda599d4624c774ffc162e87a8cf204b949b6a85816d"},"fonts/ibm-plex-mono-400-latin.woff2":{"bytes":10052,"sha256":"c36f509c0a8f9f85f29cb44bc8701d8a9e0b14c499e77a884f789ead7093a7ac"},"fonts/ibm-plex-mono-500-latin-ext.woff2":{"bytes":8848,"sha256":"77f03e26f981c582bdba3a7abed4baa2d3149211c01366bb3ab3ba7622ec4ae5"},"fonts/ibm-plex-mono-500-latin.woff2":{"bytes":10060,"sha256":"a76f53ca6612e7b3828eec2311098675b7f9849ae4169a8bcef6302aec02a6c0"},"fonts/source-serif-italic-latin-ext.woff2":{"bytes":43964,"sha256":"3eff7682cb08de3fbd2f411e05dc3f68fbd3ce721ec554ffafa7b0397214e76f"},"fonts/source-serif-italic-latin.woff2":{"bytes":50584,"sha256":"419703e180143eb9033e02651331fcf3cc8558d42f1041140a0b24e5be0933bb"},"fonts/source-serif-normal-latin-ext.woff2":{"bytes":100772,"sha256":"a0cd390d017200f91327781204458ff918999584bece8902497ba6c08472178d"},"fonts/source-serif-normal-latin.woff2":{"bytes":122168,"sha256":"2a24bad466f09b88b8e9cbc488bf774117c912e66374c57bf4716855849143f8"},"fr/about.html":{"bytes":23501,"sha256":"e675f5537e9890ddf10e26b15e3e5954bf3855f3865635294486490fb23f99e2"},"fr/index.html":{"bytes":28754,"sha256":"cfcf2b6c9267674331c18466f3673fcc125bc545b6ffe167a0b105aaf9beb5a9"},"fr/law.html":{"bytes":30444,"sha256":"b64df0be0a3aa29350c72dba84dcd33ee8fa7187b6c962a3a23f7378c3768cac"},"fr/method.html":{"bytes":48291,"sha256":"cd09ad9691472cf8947e257c184e8d3aa6faa31dbeafe30046d40bc77e10c593"},"fr/sources.html":{"bytes":13483,"sha256":"ea299ef805f62d9e7bdcfd7ee96cf3d795a8d97ec17af052944d0ee07189964a"},"gschg31.js":{"bytes":1894,"sha256":"bf4673598fd6936aec1a2eeb2e692b24ea984fb728146324dcf8586f183a9c08"},"i18n-data.js":{"bytes":59884,"sha256":"e416f44065e452e52f810fc76c3df633853c6ae8604517d79d6b61453789f129"},"i18n-map.js":{"bytes":133286,"sha256":"23cd200bd5d2a014588079853a6e948beef5176405ceb380d2ccd7d7e0ddd6bc"},"i18n.js":{"bytes":38264,"sha256":"260d45c429e32b77efffbeb1fb84f2fad55de31654e8df2a36e8b2ec6fb828c5"},"index.html":{"bytes":27965,"sha256":"5558fcdddb4b0d40b17b5dc7fc34db3f343226394dea1bc815c466545526acb6"},"it/about.html":{"bytes":22654,"sha256":"b5b075cbab0006ed5ae5879b11a4c7eca074c5cfde1174252636b61ff08a534a"},"it/index.html":{"bytes":28174,"sha256":"dbaa5915cda8bf3680367ad08b5c04212a4f8b520fd46345c277df7825759bed"},"it/law.html":{"bytes":29425,"sha256":"3403e5298ea6a1af35d57e26bc7774183bf1f29fc0d4eeb5f034ad1acb10502a"},"it/method.html":{"bytes":46086,"sha256":"ef07d64bc11d5eaff3d6137b09ed527f1f0e4573c95f4de0ad89410db078a55a"},"it/sources.html":{"bytes":13034,"sha256":"24d0e39523b86ce07b2d8954bc006dba83d4361666ba07429c14206819593d66"},"law.html":{"bytes":27787,"sha256":"acef5db2024280f2b898a3f5b954c3c9d1b85413b55b7612ad6f1b887a49bfe8"},"method.html":{"bytes":44790,"sha256":"a6abe0120a88f92ff39b8cd6867f51ff4eaa610eec0bb99a6e6d763be30811fd"},"og-image.jpg":{"bytes":200325,"sha256":"f6a6cbd88798900d3b584b1e966c764506ddd85e88328d77e17cf77cf89f5144"},"rm/about.html":{"bytes":22969,"sha256":"2277a5c47179ee744ad5f73e0fe7942d9b6d1ab08896fd8ad2462e1e3447fbf5"},"rm/index.html":{"bytes":28455,"sha256":"85eda4963d6dac4762070406ee80b73f7ab1765944b52fe81eabbc68ede22ff3"},"rm/law.html":{"bytes":29984,"sha256":"49090f341a4f86169140bc5b2ad97c19f33a8572067d6594454de719c23d0316"},"rm/method.html":{"bytes":47388,"sha256":"0e3c3d517c8fe2ba40f018caf8ce20d81a31147f4d0a4f312eaeffbfeb268e34"},"rm/sources.html":{"bytes":13545,"sha256":"d68c85b0d96ddb0b487190e72f547163aa2892325b811204b51d1d4b7cdab02d"},"robots.txt":{"bytes":80,"sha256":"f8de6fa3cc79f4c7b56023e2618742b16dabde01c2d950dc0a48f71309ed9018"},"site.css":{"bytes":23697,"sha256":"db2e7c2177fb509ba9fda4cd39128995abbef46bef84989dd543745f8005df0c"},"site.js":{"bytes":23793,"sha256":"e72c6c292aa0b4c1dbf81a02cb4a0a5a60e8b421e0d58142bad644dc3604ff40"},"sitemap.xml":{"bytes":18506,"sha256":"862dd487e520d4006c55150f2e4c7c42c7a93366f3fc01b8dfb27f68fe2b94a6"},"sources.html":{"bytes":12702,"sha256":"5bfa6f4f48f3b6dbf9086f2375c4f692c4dcd78561b4ad4d5d2788674813c0ae"},"style.css":{"bytes":63695,"sha256":"d39b85f31f453b4b213403b60008b4f8bd04c73cc086448ce27d061e78766c1f"},"theme.js":{"bytes":3354,"sha256":"a1811f92d1d08cc87cb3c8f41515a9f0868bdad44782150bd41074af1df06b3a"},"tokens.css":{"bytes":16169,"sha256":"a09dd19c74aa879f8211196eed3ed19b97c6a576985253b4c468e9b52b5b4f7a"}}} \ No newline at end of file diff --git a/site/de/index.html b/site/de/index.html index 127a094..882df63 100644 --- a/site/de/index.html +++ b/site/de/index.html @@ -33,7 +33,7 @@ - + @@ -344,9 +344,9 @@

Diese Ebene als Daten erkunden

Dem Wasser folgen. Seine Wege schützen.

-

Das Projekt folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicherung, Nutzung und Rücklauf bis zur Grenze – und zeigt, welches Recht jeden wesentlichen Weg schützt. Ziel ist es, Feuchtgebiete zu schützen und rechtliche Schritte zu ermöglichen, wenn dieser Schutz nicht durchgesetzt wird. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.

+

Riverflow folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicher und Nutzung bis zum Rücklauf. Es zeigt, was gemessen ist, was geschätzt wird und welches Recht jeden wesentlichen Weg schützt. Das Ziel: Feuchtgebiete schützen und das Recht durchsetzen, wenn nötig. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.

- + Wie die Daten belegt sind
@@ -393,6 +393,6 @@

Dem Wasser folgen. Seine Wege schützen.

- + diff --git a/site/en/index.html b/site/en/index.html index 2b65b57..91b7333 100644 --- a/site/en/index.html +++ b/site/en/index.html @@ -33,7 +33,7 @@ - + @@ -383,12 +383,13 @@

Explore this layer as data

Follow the water. Protect its pathways.

-

Trace water through Switzerland—from ice and groundwater to - rivers, storage, use, return and the border—and identify the law protecting each essential - pathway. The aim is to protect wetlands and enable legal action when those protections fail. - This first release focuses on surface water. Every figure states its source, date and evidence class.

+

Riverflow follows water through Switzerland—from ice and + groundwater to rivers, storage, use and return. It shows what is measured, what is estimated and + which law protects each essential pathway. The aim: protect wetlands and enforce the law when + necessary. This first release focuses on surface water. Every figure names its source, date and + evidence class.

- + How the evidence works
@@ -441,6 +442,6 @@

Follow the water. Protect its pathways.

- + diff --git a/site/fr/index.html b/site/fr/index.html index 0b6fe35..6aeeda3 100644 --- a/site/fr/index.html +++ b/site/fr/index.html @@ -33,7 +33,7 @@ - + @@ -344,9 +344,9 @@

Explorer cette couche sous forme de données

Suivre l’eau. Protéger ses voies.

-

Suivre l’eau à travers la Suisse — de la glace et des eaux souterraines aux cours d’eau, au stockage, aux usages, aux retours et à la frontière — et identifier le droit qui protège chaque voie essentielle. Le but est de protéger les zones humides et de permettre une action juridique lorsque ces protections échouent. Cette première version porte sur les eaux superficielles. Chaque chiffre indique sa source, sa date et sa classe de preuve.

+

Riverflow suit l’eau à travers la Suisse — de la glace et des eaux souterraines aux cours d’eau, au stockage, à l’usage et au retour. Il distingue ce qui est mesuré de ce qui est estimé et indique le droit qui protège chaque voie essentielle. L’objectif : protéger les zones humides et, si nécessaire, faire appliquer le droit. Cette première édition porte sur les eaux superficielles. Chaque chiffre indique sa source, sa date et sa classe de preuve.

- + Comment fonctionne la preuve
@@ -393,6 +393,6 @@

Suivre l’eau. Protéger ses voies.

- + diff --git a/site/index.html b/site/index.html index b8e9ea1..cfa610c 100644 --- a/site/index.html +++ b/site/index.html @@ -33,7 +33,7 @@ - + @@ -344,9 +344,9 @@

Diese Ebene als Daten erkunden

Dem Wasser folgen. Seine Wege schützen.

-

Das Projekt folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicherung, Nutzung und Rücklauf bis zur Grenze – und zeigt, welches Recht jeden wesentlichen Weg schützt. Ziel ist es, Feuchtgebiete zu schützen und rechtliche Schritte zu ermöglichen, wenn dieser Schutz nicht durchgesetzt wird. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.

+

Riverflow folgt dem Wasser durch die Schweiz – von Eis und Grundwasser über Flüsse, Speicher und Nutzung bis zum Rücklauf. Es zeigt, was gemessen ist, was geschätzt wird und welches Recht jeden wesentlichen Weg schützt. Das Ziel: Feuchtgebiete schützen und das Recht durchsetzen, wenn nötig. Diese erste Ausgabe konzentriert sich auf Oberflächengewässer. Jede Zahl nennt Quelle, Datum und Beweisklasse.

- + Wie die Daten belegt sind
@@ -393,6 +393,6 @@

Dem Wasser folgen. Seine Wege schützen.

- + diff --git a/site/it/index.html b/site/it/index.html index 9d2fa5e..8008279 100644 --- a/site/it/index.html +++ b/site/it/index.html @@ -33,7 +33,7 @@ - + @@ -344,9 +344,9 @@

Esplorare questo livello come dati

Seguire l’acqua. Proteggere i suoi percorsi.

-

Seguire l’acqua attraverso la Svizzera — dal ghiaccio e dalle acque sotterranee ai corsi d’acqua, allo stoccaggio, all’uso, alla restituzione e al confine — e identificare il diritto che protegge ogni via essenziale. L’obiettivo è proteggere le zone umide e rendere possibile l’azione legale quando tali protezioni falliscono. Questa prima edizione si concentra sulle acque superficiali. Ogni dato indica fonte, data e classe di prova.

+

Riverflow segue l’acqua attraverso la Svizzera — dal ghiaccio e dalle acque sotterranee ai corsi d’acqua, allo stoccaggio, all’uso e alla restituzione. Mostra ciò che è misurato, ciò che è stimato e quale diritto protegge ogni via essenziale. L’obiettivo: proteggere le zone umide e far rispettare il diritto quando necessario. Questa prima edizione si concentra sulle acque superficiali. Ogni dato indica fonte, data e classe di prova.

- + Come funziona la prova
@@ -393,6 +393,6 @@

Seguire l’acqua. Proteggere i suoi percorsi.

- + diff --git a/site/rm/index.html b/site/rm/index.html index beccb5f..6f2ba0f 100644 --- a/site/rm/index.html +++ b/site/rm/index.html @@ -33,7 +33,7 @@ - + @@ -344,9 +344,9 @@

Explorar quest nivel sco datas

Suandar l’aua. Proteger sias vias.

-

Suandar l’aua tras la Svizra – dal glatsch e da l’aua sutterrana als flums, a l’accumulaziun, utilisaziun, return e cunfin – ed identifitgar il dretg che protegia mintga via essenziala. La finamira è da proteger las zonas umidas e da pussibilitar pass giuridics sche quella protecziun n’effectuescha betg. Questa emprima ediziun sa concentrescha sin las auas a la surfatscha. Mintga cifra inditgescha funtauna, data e classa da cumprova.

+

Riverflow suonda l’aua tras la Svizra – dal glatsch e da l’aua sutterrana als flums, a l’accumulaziun, a l’utilisaziun ed al return. El mussa tge ch’è mesirà, tge ch’è stimà e tge dretg che protegia mintga via essenziala. La finamira: proteger las zonas umidas ed exequir il dretg sche necessari. Questa emprima ediziun sa concentrescha sin las auas a la surfatscha. Mintga cifra inditgescha funtauna, data e classa da cumprova.

- + Co che las datas èn cumprovadas
@@ -393,6 +393,6 @@

Suandar l’aua. Proteger sias vias.

- + diff --git a/site/sitemap.xml b/site/sitemap.xml index 4bf4ea7..de16fd7 100644 --- a/site/sitemap.xml +++ b/site/sitemap.xml @@ -3,7 +3,7 @@ xmlns:xhtml="http://www.w3.org/1999/xhtml"> https://opengovclimate.ch/riverflow/ - 2026-08-21 + 2026-08-22 @@ -13,7 +13,7 @@ https://opengovclimate.ch/riverflow/en/ - 2026-08-21 + 2026-08-22 @@ -23,7 +23,7 @@ https://opengovclimate.ch/riverflow/fr/ - 2026-08-21 + 2026-08-22 @@ -33,7 +33,7 @@ https://opengovclimate.ch/riverflow/it/ - 2026-08-21 + 2026-08-22 @@ -43,7 +43,7 @@ https://opengovclimate.ch/riverflow/rm/ - 2026-08-21 + 2026-08-22 @@ -53,7 +53,7 @@ https://opengovclimate.ch/riverflow/about.html - 2026-08-21 + 2026-08-22 @@ -63,7 +63,7 @@ https://opengovclimate.ch/riverflow/de/about.html - 2026-08-21 + 2026-08-22 @@ -73,7 +73,7 @@ https://opengovclimate.ch/riverflow/fr/about.html - 2026-08-21 + 2026-08-22 @@ -83,7 +83,7 @@ https://opengovclimate.ch/riverflow/it/about.html - 2026-08-21 + 2026-08-22 @@ -93,7 +93,7 @@ https://opengovclimate.ch/riverflow/rm/about.html - 2026-08-21 + 2026-08-22 @@ -103,7 +103,7 @@ https://opengovclimate.ch/riverflow/method.html - 2026-08-21 + 2026-08-22 @@ -113,7 +113,7 @@ https://opengovclimate.ch/riverflow/de/method.html - 2026-08-21 + 2026-08-22 @@ -123,7 +123,7 @@ https://opengovclimate.ch/riverflow/fr/method.html - 2026-08-21 + 2026-08-22 @@ -133,7 +133,7 @@ https://opengovclimate.ch/riverflow/it/method.html - 2026-08-21 + 2026-08-22 @@ -143,7 +143,7 @@ https://opengovclimate.ch/riverflow/rm/method.html - 2026-08-21 + 2026-08-22 @@ -153,7 +153,7 @@ https://opengovclimate.ch/riverflow/law.html - 2026-08-21 + 2026-08-22 @@ -163,7 +163,7 @@ https://opengovclimate.ch/riverflow/de/law.html - 2026-08-21 + 2026-08-22 @@ -173,7 +173,7 @@ https://opengovclimate.ch/riverflow/fr/law.html - 2026-08-21 + 2026-08-22 @@ -183,7 +183,7 @@ https://opengovclimate.ch/riverflow/it/law.html - 2026-08-21 + 2026-08-22 @@ -193,7 +193,7 @@ https://opengovclimate.ch/riverflow/rm/law.html - 2026-08-21 + 2026-08-22 @@ -203,7 +203,7 @@ https://opengovclimate.ch/riverflow/sources.html - 2026-08-21 + 2026-08-22 @@ -213,7 +213,7 @@ https://opengovclimate.ch/riverflow/de/sources.html - 2026-08-21 + 2026-08-22 @@ -223,7 +223,7 @@ https://opengovclimate.ch/riverflow/fr/sources.html - 2026-08-21 + 2026-08-22 @@ -233,7 +233,7 @@ https://opengovclimate.ch/riverflow/it/sources.html - 2026-08-21 + 2026-08-22 @@ -243,7 +243,7 @@ https://opengovclimate.ch/riverflow/rm/sources.html - 2026-08-21 + 2026-08-22 diff --git a/site/style.css b/site/style.css index 82ea243..e67870a 100644 --- a/site/style.css +++ b/site/style.css @@ -1,14 +1,13 @@ /* The map. * * The two kinds of ink, the palette and the type registers are in tokens.css, - * which every page on this site loads. This file is the instrument: the canvases, - * the furniture that floats over them, and the four shapes that furniture takes. + * which every page on this site loads. This file is the instrument: the canvases + * and the evidence workspace that remains physically separate from them. * * Layout runs on four shapes, not on a grid framework: - * phone the map, a compact header, and one bottom sheet that snaps - * tablet a legend rail on the left, the panel on the right - * desktop the settled arrangement - * wide a reading column on the right, so nothing floats in a sea of black + * portrait map above, independently scrolling evidence below + * landscape evidence beside an unobstructed map + * wide a calm reading column beside a larger map */ /* The active layer keys the accent. */ @@ -1011,3 +1010,278 @@ body.hasRibbon #credits { display: none; } * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } + +/* ============================================================ + TWO-SURFACE WORKSPACE + + The map is evidence, not a backdrop. Permanent controls and explanatory text + live on a separate surface: beside the map when width permits, below it on a + portrait phone or tablet. Nothing in this workspace obscures geography. + ============================================================ */ +:root { + --workspace-w: clamp(340px, 28vw, 440px); + --workspace-h: 52dvh; +} + +#workspace { + position: fixed; inset: 0 auto 0 0; z-index: 10; + display: flex; flex-direction: column; + width: var(--workspace-w); min-width: 0; min-height: 0; + color: var(--ink); background: var(--surface-1); + border-right: 1px solid var(--control-border); + box-shadow: 10px 0 28px rgba(0,0,0,.12); + overflow: hidden; +} + +#map, #flow { + inset: auto; top: 0; left: var(--workspace-w); + width: calc(100vw - var(--workspace-w)); height: 100dvh; +} + +#workspace #titlebar { + position: static; z-index: auto; flex: none; + width: auto; max-width: none; + margin: 0; padding: calc(16px + var(--safe-t)) 18px 13px calc(18px + var(--safe-l)); + pointer-events: auto; border-radius: 0; + background: transparent; border-bottom: 1px solid var(--rule); +} +#workspace #titlebar h1 { font-size: clamp(18px, 1.55vw, 23px); } +#workspace #titlebar .sub { display: block; max-width: 35em; } +#workspace #titlebar .stamp { margin-top: 6px; } +#workspace #siteNav { margin-top: 9px; padding-top: 8px; } +#workspace #introOpen { margin-left: -7px; } +#workspace #evidence { display: none; } +@media (min-width: 1100px) and (min-height: 860px) { + #workspace #evidence { display: block; } +} +@media (max-height: 799px) { + #workspace #titlebar .sub { display: none; } +} + +#workspace #modes { + position: static; z-index: auto; flex: none; transform: none; + display: flex; flex-direction: column; + gap: 0; width: auto; max-width: none; + padding: 8px 12px; overflow: visible; + background: transparent; backdrop-filter: none; + border: 0; border-bottom: 1px solid var(--rule); border-radius: 0; +} +#workspace .modeGroup { + display: grid; grid-template-columns: 68px minmax(0, 1fr); + align-items: stretch; gap: 3px; min-width: 0; + padding: 3px 4px; +} +#workspace .modeGroup:first-child, +#workspace .modeGroup:nth-child(4) { grid-template-columns: 68px repeat(3, minmax(0, 1fr)); } +#workspace .modeGroup:nth-child(3) { grid-template-columns: 68px repeat(2, minmax(0, 1fr)); } +#workspace .modeGroup + .modeGroup { + margin: 0; border-left: 0; border-top: 1px solid var(--rule); padding-left: 4px; +} +#workspace .modeGroupLabel { + display: flex; align-items: center; grid-column: auto; padding: 0 5px; + font-size: 8.5px; line-height: 1.2; +} +#workspace #modes button { + min-width: 0; min-height: 38px; padding: 7px 6px; + white-space: normal; line-height: 1.18; text-align: left; +} + +#workspace #mapControls { + position: static; z-index: auto; flex: none; + display: flex; align-items: center; gap: 6px; + width: auto; padding: 8px 16px; + background: var(--surface-2); border-bottom: 1px solid var(--rule); +} +#workspace .zoomGroup, +#workspace #zoomFit, +#workspace #dataViewOpen { + background: transparent; backdrop-filter: none; + border: 1px solid var(--control-border); border-radius: 6px; box-shadow: none; +} +#workspace .zoomGroup { display: flex; } +#workspace .zoomGroup button, +#workspace #zoomFit, +#workspace #dataViewOpen { min-width: 44px; min-height: 44px; } +#workspace #zoomLevel { + min-width: 44px; min-height: 44px; + border-block: 0; border-inline: 1px solid var(--rule); +} +#workspace #dataViewOpen { margin-left: auto; padding-inline: 10px; } + +#workspace #tooltip { + position: static; flex: none; + width: auto; max-width: none; margin: 0; padding: 9px 16px; + background: var(--surface-2); border: 0; border-bottom: 1px solid var(--rule); + border-radius: 0; box-shadow: none; +} + +#workspace #legend { + position: static; z-index: auto; flex: 1 1 auto; + width: auto; min-width: 0; min-height: 0; max-height: none; + margin: 0; padding: 16px 18px calc(18px + var(--safe-b)); + overflow-y: auto; overscroll-behavior: contain; + background: transparent; backdrop-filter: none; + border: 0; border-radius: 0; + scrollbar-gutter: stable; +} +#workspace #sheetHandle { display: none; } +#workspace #legend h2 { display: block; font-size: 12px; } +#workspace #legendBody { padding: 0; overflow: visible; } +#workspace #legendBody .notes, +#workspace #legendBody .lgNote { line-height: 1.5; } + +#workspace #credits { + position: static; display: block; max-width: none; + margin: 20px 0 0; padding: 14px 0 0; + text-align: left; pointer-events: auto; + color: var(--ink-muted); background: transparent; + border-top: 1px solid var(--rule); border-radius: 0; +} +body.hasRibbon #workspace #credits { display: block; } + +#workspace #ribbon { + position: static; z-index: auto; flex: none; transform: none; + width: auto; min-width: 0; max-height: min(250px, 38vh); + padding: 11px 16px 10px; overflow-y: auto; + background: var(--surface-2); backdrop-filter: none; + border: 0; border-top: 1px solid var(--control-border); border-radius: 0; +} +#workspace #ribbonCv { height: 64px; } + +#workspace #panel { + position: absolute; inset: auto 0 0 0; z-index: 12; + width: auto; max-height: min(76%, 660px); + padding: 18px 18px calc(18px + var(--safe-b)); + overflow-y: auto; background: var(--surface-1); backdrop-filter: none; + border: 0; border-top: 1px solid var(--control-border); border-radius: 0; + box-shadow: 0 -16px 34px rgba(0,0,0,.22); +} + +#workspace #intro { + position: absolute; inset: 0; z-index: 15; + box-sizing: border-box; width: 100%; height: 100%; max-height: none; + margin: 0; padding: 28px 24px calc(26px + var(--safe-b)); + overflow-y: auto; overscroll-behavior: contain; scrollbar-gutter: stable; + color: var(--ink); background: var(--surface-1); + border: 0; border-radius: 0; box-shadow: none; +} +#workspace #intro::backdrop { display: none; background: transparent; backdrop-filter: none; } +#workspace #introTitle { font-size: clamp(37px, 4vw, 52px); line-height: .98; } +#workspace .introTop { align-items: flex-start; flex-direction: column; gap: 9px; margin-right: 38px; } +#workspace .introLangs { margin-left: 0; padding-left: 0; } +#workspace .introLead { margin-top: 18px; font-size: 16px; line-height: 1.46; } +#workspace .introActions { align-items: stretch; flex-direction: column; gap: 8px; margin-top: 20px; } +#workspace #introEnter, +#workspace .introActions a { width: 100%; min-height: 46px; } +#workspace .cycleViz { margin-top: 24px; } +#workspace .cycleViz svg { max-height: 150px; } +#workspace .cycleStages { grid-template-columns: repeat(2, minmax(0, 1fr)); } +#workspace .cycleStages li:last-child { grid-column: 1 / -1; } +#workspace .introFacts { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 24px; } + +/* A portrait phone or tablet uses the same two surfaces, stacked. The map keeps + the upper viewport and the workspace scrolls independently below it. */ +@media (max-width: 900px) and (orientation: portrait) { + #workspace { + inset: auto 0 0; width: auto; height: var(--workspace-h); + border-right: 0; border-top: 1px solid var(--control-border); + box-shadow: 0 -10px 28px rgba(0,0,0,.16); + } + #map, #flow { + inset: auto; top: 0; left: 0; + width: 100vw; height: calc(100dvh - var(--workspace-h)); + } + #workspace #titlebar { + padding: 9px calc(11px + var(--safe-r)) 7px calc(11px + var(--safe-l)); + } + #workspace #titlebar h1 { font-size: 16px; } + #workspace #titlebar .sub, + #workspace #titlebar .hint, + #workspace #evidence, + #workspace #siteNav > a { display: none; } + #workspace #titlebar .stamp { + margin-top: 3px; font-size: 10.5px; line-height: 1.35; + white-space: normal; + } + #workspace #siteNav { + flex-wrap: nowrap; align-items: center; margin-top: 4px; padding-top: 3px; + } + #workspace #introOpen { min-height: 40px; } + #workspace #titlebar .langs { margin-left: auto; } + #workspace #titlebar .langs [lang] { + display: grid; place-items: center; min-width: 28px; min-height: 40px; padding: 0 2px; + } + #workspace #titlebar .themeBtn { width: 40px; height: 40px; } + + #workspace #modes { + display: flex; flex-direction: row; gap: 2px; padding: 4px 8px; + overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; + touch-action: pan-x; scroll-snap-type: x proximity; + scrollbar-width: thin; scrollbar-color: var(--rule) transparent; + } + #workspace #modes::-webkit-scrollbar { display: block; height: 3px; } + #workspace .modeGroup, + #workspace .modeGroup:first-child, + #workspace .modeGroup:nth-child(4) { + display: flex; flex: none; gap: 2px; padding: 0 3px; + border: 0; border-left: 1px solid var(--rule); grid-template-columns: none; + } + #workspace .modeGroup:first-child { border-left: 0; } + #workspace .modeGroupLabel { display: none; } + #workspace #modes button { + min-height: 44px; padding: 7px 10px; white-space: nowrap; scroll-snap-align: center; + } + #workspace #mapControls { padding: 5px 10px; } + #workspace .zoomGroup button, + #workspace #zoomLevel, + #workspace #zoomFit, + #workspace #dataViewOpen { min-height: 44px; } + #workspace #legend { padding: 11px 13px calc(13px + var(--safe-b)); } + #workspace #legendBody .notes, + #workspace #legendBody .lgNote { font-size: 12.5px; } + #workspace #panel { max-height: 100%; } + #workspace #intro { padding: 20px 18px calc(20px + var(--safe-b)); } + #workspace #introTitle { font-size: clamp(31px, 9vw, 43px); } + #workspace .introLangs [lang] { min-width: 40px; min-height: 44px; } +} +@media (max-width: 480px) and (max-height: 650px) and (orientation: portrait) { + :root { --workspace-h: 58dvh; } +} + +/* Short landscape screens keep real columns but compact the index into one + horizontal rail, preserving useful height for evidence and detail. */ +@media (max-height: 560px) and (orientation: landscape) { + :root { --workspace-w: min(42vw, 340px); } + #workspace #titlebar { padding: 9px 12px 7px; } + #workspace #titlebar h1 { font-size: 15px; } + #workspace #titlebar .sub, + #workspace #titlebar .hint, + #workspace #evidence, + #workspace #siteNav > a { display: none; } + #workspace #titlebar .stamp { display: block; margin-top: 2px; font-size: 10.5px; } + #workspace #siteNav { flex-wrap: nowrap; margin-top: 4px; padding-top: 3px; } + #workspace #titlebar .langs { margin-left: auto; } + #workspace #modes { + display: flex; flex-direction: row; gap: 2px; padding: 4px 7px; + overflow-x: auto; overflow-y: hidden; touch-action: pan-x; + } + #workspace .modeGroup, + #workspace .modeGroup:first-child, + #workspace .modeGroup:nth-child(4) { + display: flex; flex: none; gap: 2px; padding: 0 3px; + border: 0; border-left: 1px solid var(--rule); grid-template-columns: none; + } + #workspace .modeGroup:first-child { border-left: 0; } + #workspace .modeGroupLabel { display: none; } + #workspace #modes button { min-height: 44px; padding: 6px 9px; white-space: nowrap; } + #workspace #mapControls { padding: 4px 9px; } + #workspace .zoomGroup button, + #workspace #zoomLevel, + #workspace #zoomFit, + #workspace #dataViewOpen { min-height: 44px; } + #workspace #legend { padding: 10px 12px 12px; } + #workspace #ribbon { max-height: 58%; } + #workspace #intro { padding: 17px 16px; } + #workspace #introTitle { font-size: 32px; } + #workspace .cycleViz svg { max-height: 110px; } +}