diff --git a/CURRENT_STATE.md b/CURRENT_STATE.md index 0400255..45fc0fc 100644 --- a/CURRENT_STATE.md +++ b/CURRENT_STATE.md @@ -13,6 +13,8 @@ This repo is a manifest-driven static atlas of plot pages, generated homepage co - The test suite includes bootstrap smoke checks for the build and validator entrypoints. - The shared accessibility and link-check scripts are present. - The browser smoke harness exists at `scripts/browser_smoke.py`. +- The unified dashboard loads only local manifest/CSV files and no longer depends on a remote runtime CDN. + ## Commands @@ -62,7 +64,7 @@ Status below reflects the current local verification pass. ## Known Risks -- The dashboard loads D3 from a CDN, so the browser smoke harness is a preflight rather than an offline-safe guarantee. +- The dashboard is offline-safe and loads local manifest/CSV assets instead of a remote D3 CDN. - Some plot rows remain speculative or projection-based and should not be reworded into facts without source review. - Generated outputs need to be rebuilt after data or source edits to stay fresh. - The repo depends on the Python packages listed in `requirements.txt`. @@ -74,3 +76,10 @@ Status below reflects the current local verification pass. 3. Run `python build_all.py` when changing data or plot generators. 4. Run `python scripts/validate_repo.py --check` after any substantive change. 5. Keep `CURRENT_STATE.md` and `docs/agentic-overhaul/2026-05-audit.md` up to date when the repo shape changes. + +## Handoff Note + +- Changed: the unified dashboard now loads only local assets and renders with inline SVG instead of a remote D3 CDN; the year parser now preserves zero-valued years and full-width year strings. +- Verified by: `python -m pytest tests/test_dashboard.py tests/test_dashboard_year_parsing.py tests/test_browser_smoke.py -q`, `python scripts/browser_smoke.py`, and browser QA screenshots on the live Pages site. +- Evidence: `.gstack/qa-reports/screenshots/dashboard-desktop.png`, `.gstack/qa-reports/screenshots/dashboard-mobile.png`, `.gstack/qa-reports/screenshots/homepage-desktop.png`. +- Next feature: provenance coverage for speculative rows. diff --git a/adoption-timeline/output/adoption_timeline.png b/adoption-timeline/output/adoption_timeline.png index c23158c..f7e7ef2 100644 Binary files a/adoption-timeline/output/adoption_timeline.png and b/adoption-timeline/output/adoption_timeline.png differ diff --git a/adoption-timeline/output/adoption_timeline.svg b/adoption-timeline/output/adoption_timeline.svg index 4e1fcfa..7ce038d 100644 --- a/adoption-timeline/output/adoption_timeline.svg +++ b/adoption-timeline/output/adoption_timeline.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:05:55.826944 + 2026-05-20T17:31:25.935451 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -43,7 +43,7 @@ L 367.384947 386.037812 L 367.384947 32.837812 L 71.757675 32.837812 z -" clip-path="url(#pd06a61067d)" style="fill: #f0f0f0; opacity: 0.4; stroke: #f0f0f0; stroke-linejoin: miter"/> +" clip-path="url(#p775cb34a2e)" style="fill: #f0f0f0; opacity: 0.4; stroke: #f0f0f0; stroke-linejoin: miter"/> +" clip-path="url(#p775cb34a2e)" style="fill: #e8f4fd; opacity: 0.4; stroke: #e8f4fd; stroke-linejoin: miter"/> +" clip-path="url(#p775cb34a2e)" style="fill: #f3e8fd; opacity: 0.4; stroke: #f3e8fd; stroke-linejoin: miter"/> +" clip-path="url(#p775cb34a2e)" style="fill: #fde8e8; opacity: 0.4; stroke: #fde8e8; stroke-linejoin: miter"/> +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #cccccc; stroke-linecap: square"/> +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #e67e22; stroke-opacity: 0.4; stroke-width: 1.5"/> +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -327,11 +327,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -360,11 +360,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -422,11 +422,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -443,11 +443,11 @@ L 367.384947 32.837812 +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -490,11 +490,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -511,11 +511,11 @@ L 536.314817 32.837812 +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -632,16 +632,16 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -691,11 +691,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -765,11 +765,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -820,11 +820,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -877,11 +877,11 @@ z +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -898,11 +898,11 @@ L 705.244688 119.15042 +" clip-path="url(#p775cb34a2e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -919,131 +919,131 @@ L 705.244688 50.72403 - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1314,55 +1314,55 @@ L 705.244688 32.837812 - - - + + - - - + + - - - + + - - - + + - - - + + - - + + - - - + + - - - + + - - + + - - - + + - - - + + - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + - - + + - - - + + @@ -2838,7 +2838,7 @@ z - + diff --git a/adoption-timeline/output/adoption_timeline_highres.png b/adoption-timeline/output/adoption_timeline_highres.png index 13c4ef7..1be8626 100644 Binary files a/adoption-timeline/output/adoption_timeline_highres.png and b/adoption-timeline/output/adoption_timeline_highres.png differ diff --git a/adoption-timeline/output/adoption_timeline_interactive.html b/adoption-timeline/output/adoption_timeline_interactive.html index 8a0a64a..d71e20e 100644 --- a/adoption-timeline/output/adoption_timeline_interactive.html +++ b/adoption-timeline/output/adoption_timeline_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/ai-benchmark-progress/output/benchmark_progress.svg b/ai-benchmark-progress/output/benchmark_progress.svg index 024db96..981513a 100644 --- a/ai-benchmark-progress/output/benchmark_progress.svg +++ b/ai-benchmark-progress/output/benchmark_progress.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:32.137746 + 2026-05-20T17:34:33.205381 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -42,16 +42,16 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -159,11 +159,11 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -180,11 +180,11 @@ L 102.065898 71.92 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -201,11 +201,11 @@ L 144.43005 71.92 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -222,11 +222,11 @@ L 186.794202 71.92 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -277,11 +277,11 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -319,11 +319,11 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -467,16 +467,16 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -490,11 +490,11 @@ L -3.5 0 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -509,11 +509,11 @@ L 326.595904 342.851429 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -528,11 +528,11 @@ L 326.595904 279.102857 +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -579,11 +579,11 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -639,11 +639,11 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -822,7 +822,7 @@ z +" clip-path="url(#p6954e152d3)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #ef4444; stroke-opacity: 0.7; stroke-width: 1.5"/> +" clip-path="url(#p6954e152d3)" style="fill: none; stroke: #60a5fa; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - - - - + + + + + + + + @@ -1259,11 +1259,11 @@ z +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1291,11 +1291,11 @@ z +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1314,11 +1314,11 @@ L 408.870359 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1337,11 +1337,11 @@ L 440.643473 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1360,11 +1360,11 @@ L 472.416587 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1383,11 +1383,11 @@ L 504.189701 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1406,11 +1406,11 @@ L 535.962815 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1429,11 +1429,11 @@ L 567.735929 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1452,11 +1452,11 @@ L 599.509043 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1486,11 +1486,11 @@ L 631.282158 71.92 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1498,11 +1498,11 @@ L 643.991403 406.6 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1510,11 +1510,11 @@ L 643.991403 342.851429 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1522,11 +1522,11 @@ L 643.991403 279.102857 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1534,11 +1534,11 @@ L 643.991403 215.354286 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1546,11 +1546,11 @@ L 643.991403 151.605714 +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1558,7 +1558,7 @@ L 643.991403 87.857143 +" clip-path="url(#p13715d2445)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #ef4444; stroke-opacity: 0.7; stroke-width: 1.5"/> +" clip-path="url(#p13715d2445)" style="fill: none; stroke: #34d399; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - - + + + + + + @@ -1768,11 +1768,11 @@ z +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1791,11 +1791,11 @@ L 694.492744 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1814,11 +1814,11 @@ L 745.329726 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1837,11 +1837,11 @@ L 796.166709 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1860,11 +1860,11 @@ L 847.003691 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1883,11 +1883,11 @@ L 897.840674 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1917,11 +1917,11 @@ L 948.677657 71.92 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1929,11 +1929,11 @@ L 961.386902 406.6 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1941,11 +1941,11 @@ L 961.386902 342.851429 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1953,11 +1953,11 @@ L 961.386902 279.102857 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1965,11 +1965,11 @@ L 961.386902 215.354286 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1977,11 +1977,11 @@ L 961.386902 151.605714 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1989,7 +1989,7 @@ L 961.386902 87.857143 +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #ef4444; stroke-opacity: 0.7; stroke-width: 1.5"/> +" clip-path="url(#pbb7d6dbe01)" style="fill: none; stroke: #9ca3af; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - + + + + + @@ -2191,11 +2191,11 @@ z +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2212,11 +2212,11 @@ L 1011.888243 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2233,11 +2233,11 @@ L 1054.252395 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2254,11 +2254,11 @@ L 1096.616547 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2275,11 +2275,11 @@ L 1138.980699 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2296,11 +2296,11 @@ L 1181.344851 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2317,11 +2317,11 @@ L 1223.709003 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2349,11 +2349,11 @@ L 1266.073156 71.92 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2361,11 +2361,11 @@ L 1278.782401 406.6 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2373,11 +2373,11 @@ L 1278.782401 342.851429 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2385,11 +2385,11 @@ L 1278.782401 279.102857 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2397,11 +2397,11 @@ L 1278.782401 215.354286 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2409,11 +2409,11 @@ L 1278.782401 151.605714 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2421,7 +2421,7 @@ L 1278.782401 87.857143 +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke-dasharray: 5.55,2.4; stroke-dashoffset: 0; stroke: #ef4444; stroke-opacity: 0.7; stroke-width: 1.5"/> +" clip-path="url(#p7e4e08c43c)" style="fill: none; stroke: #a78bfa; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - - - - + + + + + + + + @@ -3275,16 +3275,16 @@ z - + - + - + - + diff --git a/ai-benchmark-progress/output/benchmark_progress_highres.png b/ai-benchmark-progress/output/benchmark_progress_highres.png index a9d7e1a..19a1ba2 100644 Binary files a/ai-benchmark-progress/output/benchmark_progress_highres.png and b/ai-benchmark-progress/output/benchmark_progress_highres.png differ diff --git a/ai-benchmark-progress/output/benchmark_progress_interactive.html b/ai-benchmark-progress/output/benchmark_progress_interactive.html index 02bd62b..c48f661 100644 --- a/ai-benchmark-progress/output/benchmark_progress_interactive.html +++ b/ai-benchmark-progress/output/benchmark_progress_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/ai-compute-timeline/output/ai_compute_timeline.png b/ai-compute-timeline/output/ai_compute_timeline.png index 4a03edd..93191f4 100644 Binary files a/ai-compute-timeline/output/ai_compute_timeline.png and b/ai-compute-timeline/output/ai_compute_timeline.png differ diff --git a/ai-compute-timeline/output/ai_compute_timeline.svg b/ai-compute-timeline/output/ai_compute_timeline.svg index caa9469..0f0fd81 100644 --- a/ai-compute-timeline/output/ai_compute_timeline.svg +++ b/ai-compute-timeline/output/ai_compute_timeline.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:05:52.918795 + 2026-05-20T17:31:12.237590 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -43,23 +43,23 @@ L 539.646833 470.2 L 539.646833 117.12 L 50.05625 117.12 z -" clip-path="url(#pa80ef44ba8)" style="fill: #f0f0f0; opacity: 0.35; stroke: #f0f0f0; stroke-linejoin: miter"/> +" clip-path="url(#paeedef4b1f)" style="fill: #f0f0f0; opacity: 0.35; stroke: #f0f0f0; stroke-linejoin: miter"/>
+" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -143,11 +143,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -190,11 +190,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -232,11 +232,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -285,11 +285,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -347,11 +347,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -468,16 +468,16 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -493,11 +493,11 @@ L -3.5 0 +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -540,11 +540,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -560,11 +560,11 @@ L 539.646833 369.32 +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -615,11 +615,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -648,11 +648,11 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -669,11 +669,11 @@ L 539.646833 218 +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -690,11 +690,11 @@ L 539.646833 167.56 +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1102,19 +1102,19 @@ z +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#paeedef4b1f)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -1217,31 +1217,31 @@ L 253.412316 453.649385 L 253.412316 443.902591 L 243.665522 443.902591 z -" clip-path="url(#pa80ef44ba8)" style="fill: #e67e22; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#paeedef4b1f)" style="fill: #e67e22; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#paeedef4b1f)" style="fill: #8e44ad; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#paeedef4b1f)" style="fill: #16a085; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#paeedef4b1f)" style="fill: #8b4513; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#paeedef4b1f)" style="fill: #16a085; fill-opacity: 0.55; stroke: #ffffff; stroke-opacity: 0.55; stroke-width: 1.2"/> +" clip-path="url(#pae3b69401e)" style="fill: #f0f0f0; opacity: 0.35; stroke: #f0f0f0; stroke-linejoin: miter"/> +" clip-path="url(#pae3b69401e)" style="fill: #f0e8ff; opacity: 0.35; stroke: #f0e8ff; stroke-linejoin: miter"/> +" clip-path="url(#pae3b69401e)" style="fill: #ffe8e8; opacity: 0.45; stroke: #ffe8e8; stroke-linejoin: miter"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2396,11 +2396,11 @@ L 612.166833 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2417,11 +2417,11 @@ L 677.445578 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2438,11 +2438,11 @@ L 742.724322 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2459,11 +2459,11 @@ L 808.003066 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2480,11 +2480,11 @@ L 873.281811 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2501,11 +2501,11 @@ L 938.560555 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2522,11 +2522,11 @@ L 1003.8393 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2554,11 +2554,11 @@ L 1069.118044 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2566,11 +2566,11 @@ L 1101.757416 470.2 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2578,11 +2578,11 @@ L 1101.757416 419.76 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2590,11 +2590,11 @@ L 1101.757416 369.32 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2602,11 +2602,11 @@ L 1101.757416 318.88 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2614,11 +2614,11 @@ L 1101.757416 268.44 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2626,11 +2626,11 @@ L 1101.757416 218 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2638,11 +2638,11 @@ L 1101.757416 167.56 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2650,95 +2650,95 @@ L 1101.757416 117.12 +" clip-path="url(#pae3b69401e)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #666666"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #b91c1c"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> +" clip-path="url(#pae3b69401e)" style="fill: none; stroke: #444444; stroke-opacity: 0.25; stroke-width: 1.5"/> - - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -2927,48 +2927,48 @@ z L 1031.605275 159.823397 L 1041.352069 159.823397 z -" clip-path="url(#pace7453f7b)" style="fill: #8e44ad; fill-opacity: 0.5; stroke: #ffffff; stroke-opacity: 0.5; stroke-width: 1.2"/> +" clip-path="url(#pae3b69401e)" style="fill: #8e44ad; fill-opacity: 0.5; stroke: #ffffff; stroke-opacity: 0.5; stroke-width: 1.2"/> +" clip-path="url(#pae3b69401e)" style="fill: #1d8348; fill-opacity: 0.5; stroke: #ffffff; stroke-opacity: 0.5; stroke-width: 1.2"/> - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -3750,7 +3750,7 @@ L 355.571933 50.538437 L 365.571933 50.538437 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3785,7 +3785,7 @@ L 451.685996 50.538437 L 461.685996 50.538437 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - + @@ -3825,7 +3825,7 @@ L 550.203183 50.538437 L 560.203183 50.538437 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3852,14 +3852,14 @@ L 625.587558 50.538437 L 635.587558 50.538437 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3883,15 +3883,15 @@ L 723.031308 50.538437 L 733.031308 50.538437 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3913,10 +3913,10 @@ z - + - + diff --git a/ai-compute-timeline/output/ai_compute_timeline_highres.png b/ai-compute-timeline/output/ai_compute_timeline_highres.png index bc4c992..9f0f39f 100644 Binary files a/ai-compute-timeline/output/ai_compute_timeline_highres.png and b/ai-compute-timeline/output/ai_compute_timeline_highres.png differ diff --git a/ai-compute-timeline/output/ai_compute_timeline_interactive.html b/ai-compute-timeline/output/ai_compute_timeline_interactive.html index d25758e..957e9f5 100644 --- a/ai-compute-timeline/output/ai_compute_timeline_interactive.html +++ b/ai-compute-timeline/output/ai_compute_timeline_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/civilization-scaling/output/civilization_scaling.png b/civilization-scaling/output/civilization_scaling.png index 837edba..cb1c0c9 100644 Binary files a/civilization-scaling/output/civilization_scaling.png and b/civilization-scaling/output/civilization_scaling.png differ diff --git a/civilization-scaling/output/civilization_scaling.svg b/civilization-scaling/output/civilization_scaling.svg index e0cd999..2fb484c 100644 --- a/civilization-scaling/output/civilization_scaling.svg +++ b/civilization-scaling/output/civilization_scaling.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:09.134284 + 2026-05-20T17:32:37.410558 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -43,7 +43,7 @@ L 461.910794 321.15311 L 461.910794 138.570814 L 73.947233 138.570814 z -" clip-path="url(#p1cbadcd90d)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/>
+" clip-path="url(#p0857372038)" style="fill: #aed6f1; opacity: 0.3; stroke: #aed6f1; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #f9e79f; opacity: 0.3; stroke: #f9e79f; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #fadbd8; opacity: 0.3; stroke: #fadbd8; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #fad7a0; opacity: 0.3; stroke: #fad7a0; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #d5f5e3; opacity: 0.3; stroke: #d5f5e3; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: #e8daef; opacity: 0.3; stroke: #e8daef; stroke-linejoin: miter"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke: #e67e22; stroke-opacity: 0.4; stroke-width: 1.5; stroke-linecap: square"/> - - + - + - + - + - + - + - + @@ -173,16 +173,16 @@ L 0 3.5 +" clip-path="url(#p0857372038)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -267,11 +267,11 @@ z +" clip-path="url(#p0857372038)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -308,11 +308,11 @@ z +" clip-path="url(#p0857372038)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -355,11 +355,11 @@ z +" clip-path="url(#p0857372038)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -407,11 +407,11 @@ z +" clip-path="url(#p0857372038)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -438,271 +438,271 @@ z - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1087,42 +1087,42 @@ z +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p0857372038)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> - - - + + - - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + @@ -2353,7 +2353,7 @@ L 461.910794 518.035407 L 461.910794 335.45311 L 73.947233 335.45311 z -" clip-path="url(#p97ca150965)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #aed6f1; opacity: 0.3; stroke: #aed6f1; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #f9e79f; opacity: 0.3; stroke: #f9e79f; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #fadbd8; opacity: 0.3; stroke: #fadbd8; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #fad7a0; opacity: 0.3; stroke: #fad7a0; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #d5f5e3; opacity: 0.3; stroke: #d5f5e3; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: #e8daef; opacity: 0.3; stroke: #e8daef; stroke-linejoin: miter"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #27ae60; stroke-opacity: 0.4; stroke-width: 1.5; stroke-linecap: square"/> - + - + - + - + - + - + - + @@ -2476,11 +2476,11 @@ L 1285.839224 343.752306 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2522,11 +2522,11 @@ z +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2542,11 +2542,11 @@ L 1281.559367 479.736888 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2562,11 +2562,11 @@ L 1281.559367 460.037799 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2582,11 +2582,11 @@ L 1281.559367 440.33871 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2602,11 +2602,11 @@ L 1281.559367 420.639622 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2622,11 +2622,11 @@ L 1281.559367 400.940533 +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2683,11 +2683,11 @@ z +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2735,11 +2735,11 @@ z +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -2755,511 +2755,511 @@ L 1281.559367 341.843267 - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -3519,42 +3519,42 @@ z +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p103eec7ae4)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> - - - + + - - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + @@ -3918,7 +3918,7 @@ L 461.910794 714.917703 L 461.910794 532.335407 L 73.947233 532.335407 z -" clip-path="url(#pde66232c84)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #aed6f1; opacity: 0.3; stroke: #aed6f1; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #f9e79f; opacity: 0.3; stroke: #f9e79f; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #fadbd8; opacity: 0.3; stroke: #fadbd8; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #fad7a0; opacity: 0.3; stroke: #fad7a0; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #d5f5e3; opacity: 0.3; stroke: #d5f5e3; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: #e8daef; opacity: 0.3; stroke: #e8daef; stroke-linejoin: miter"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #3498db; stroke-opacity: 0.4; stroke-width: 1.5; stroke-linecap: square"/> - + - + - + - + - + - + - + @@ -4041,11 +4041,11 @@ L 1285.839224 540.634602 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4061,11 +4061,11 @@ L 1281.559367 696.244514 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4081,11 +4081,11 @@ L 1281.559367 675.496526 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4101,11 +4101,11 @@ L 1281.559367 654.748537 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4121,11 +4121,11 @@ L 1281.559367 634.000549 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4142,11 +4142,11 @@ L 1281.559367 613.252561 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4163,11 +4163,11 @@ L 1281.559367 592.504573 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4184,11 +4184,11 @@ L 1281.559367 571.756584 +" clip-path="url(#p533346ab03)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4279,42 +4279,42 @@ z +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p533346ab03)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> - - - + + - - - + + - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + @@ -4722,7 +4722,7 @@ L 461.910794 911.8 L 461.910794 729.217703 L 73.947233 729.217703 z -" clip-path="url(#p5d9edf16a3)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #d5d8dc; opacity: 0.3; stroke: #d5d8dc; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #aed6f1; opacity: 0.3; stroke: #aed6f1; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #f9e79f; opacity: 0.3; stroke: #f9e79f; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #fadbd8; opacity: 0.3; stroke: #fadbd8; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #fad7a0; opacity: 0.3; stroke: #fad7a0; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #d5f5e3; opacity: 0.3; stroke: #d5f5e3; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: #e8daef; opacity: 0.3; stroke: #e8daef; stroke-linejoin: miter"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #9b59b6; stroke-opacity: 0.4; stroke-width: 1.5; stroke-linecap: square"/> - + @@ -4805,7 +4805,7 @@ L 1285.839224 903.500805 - + @@ -4837,7 +4837,7 @@ z - + @@ -4852,7 +4852,7 @@ z - + @@ -4866,7 +4866,7 @@ z - + @@ -4881,7 +4881,7 @@ z - + @@ -4895,7 +4895,7 @@ z - + @@ -5109,11 +5109,11 @@ z +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5139,11 +5139,11 @@ z +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5160,11 +5160,11 @@ L 1281.559367 865.196826 +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5181,11 +5181,11 @@ L 1281.559367 839.660841 +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5202,11 +5202,11 @@ L 1281.559367 814.124855 +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5223,11 +5223,11 @@ L 1281.559367 788.58887 +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5243,11 +5243,11 @@ L 1281.559367 763.052884 +" clip-path="url(#p173ea216b7)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5331,42 +5331,42 @@ z +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> +" clip-path="url(#p173ea216b7)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #888888; stroke-opacity: 0.5"/> - - - + + - - - + + - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + @@ -7709,16 +7709,16 @@ L 1151.646867 94.88875 - + - + - + - + diff --git a/civilization-scaling/output/civilization_scaling_highres.png b/civilization-scaling/output/civilization_scaling_highres.png index a9b4767..11302fa 100644 Binary files a/civilization-scaling/output/civilization_scaling_highres.png and b/civilization-scaling/output/civilization_scaling_highres.png differ diff --git a/civilization-scaling/output/civilization_scaling_interactive.html b/civilization-scaling/output/civilization_scaling_interactive.html index 52d1eac..a109b6f 100644 --- a/civilization-scaling/output/civilization_scaling_interactive.html +++ b/civilization-scaling/output/civilization_scaling_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/cost-to-train/output/cost_to_train.svg b/cost-to-train/output/cost_to_train.svg index afef14e..7d454f0 100644 --- a/cost-to-train/output/cost_to_train.svg +++ b/cost-to-train/output/cost_to_train.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:34.353180 + 2026-05-20T17:34:38.321259 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -42,16 +42,16 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -129,11 +129,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -171,11 +171,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -224,11 +224,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -286,11 +286,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -307,11 +307,11 @@ L 558.661578 42.32 +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -328,11 +328,11 @@ L 673.889103 42.32 +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -349,11 +349,11 @@ L 789.116628 42.32 +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -470,16 +470,16 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -528,11 +528,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -549,11 +549,11 @@ L 944.673786 353.67942 +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -604,11 +604,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -652,11 +652,11 @@ z +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #374151; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -967,9 +967,9 @@ L 789.116628 160.919807 L 846.73039 112.729903 L 846.73039 98.223297 L 904.344153 64.54 -" clip-path="url(#p5d98b9b3fd)" style="fill: none; stroke: #60a5fa; stroke-width: 3; stroke-linecap: square"/> +" clip-path="url(#p988dd999e0)" style="fill: none; stroke: #60a5fa; stroke-width: 3; stroke-linecap: square"/> - - - - - - - - - - - - + + + + + + + + + + + @@ -1786,7 +1786,7 @@ L 76.421844 55.418437 L 86.421844 55.418437 " style="fill: none; stroke: #60a5fa; stroke-width: 3; stroke-linecap: square"/> - + @@ -1814,7 +1814,7 @@ L 76.421844 70.096563 L 86.421844 70.096563 " style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #34d399; stroke-width: 2"/> - - + @@ -1929,12 +1929,12 @@ z - - + @@ -1960,7 +1960,7 @@ z - + @@ -1977,7 +1977,7 @@ z - + @@ -1994,7 +1994,7 @@ z - + @@ -2011,236 +2011,236 @@ z - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2316,24 +2316,24 @@ L 789.116628 451.617912 L 846.73039 472.773789 L 846.73039 461.130869 L 904.344153 508.94 -" clip-path="url(#p5d98b9b3fd)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #34d399; stroke-width: 2"/> - - - - - - - - - - - +" clip-path="url(#p988dd999e0)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #34d399; stroke-width: 2"/> + + + + + + + + + + + - + diff --git a/cost-to-train/output/cost_to_train_highres.png b/cost-to-train/output/cost_to_train_highres.png index 658a3e5..51b84ff 100644 Binary files a/cost-to-train/output/cost_to_train_highres.png and b/cost-to-train/output/cost_to_train_highres.png differ diff --git a/cost-to-train/output/cost_to_train_interactive.html b/cost-to-train/output/cost_to_train_interactive.html index 2367980..bd6d6e8 100644 --- a/cost-to-train/output/cost_to_train_interactive.html +++ b/cost-to-train/output/cost_to_train_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/dashboard/dashboard.css b/dashboard/dashboard.css index 1a8f8f7..69fb060 100644 --- a/dashboard/dashboard.css +++ b/dashboard/dashboard.css @@ -15,11 +15,22 @@ h1 { -webkit-background-clip: text; -webkit-text-fill-color: transparent; } -.subtitle { color: #6b7280; font-size: 0.9rem; margin-bottom: 28px; } +.subtitle { color: #6b7280; font-size: 0.9rem; margin-bottom: 18px; } +.dashboard-notice { + max-width: 1400px; + margin: 0 auto 18px; + padding: 12px 14px; + border: 1px solid #2d3148; + border-radius: 10px; + background: #161922; + color: #cbd5e1; + font-size: 0.93rem; +} #dashboard-container { width: 100%; max-width: 1400px; margin: 0 auto; + overflow-x: auto; } .lane { margin-bottom: 20px; diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js index 4af101c..4dfdf03 100644 --- a/dashboard/dashboard.js +++ b/dashboard/dashboard.js @@ -1,154 +1,380 @@ +"use strict"; + /** * Unified Dashboard — manifest-driven synchronized atlas view. + * + * Offline-safe: the page only loads local manifest/CSV assets and renders the + * dashboard with inline SVG primitives so it works without a remote runtime. */ const COLORS = [ '#60a5fa', '#f472b6', '#34d399', '#e8eaf6', '#f59e0b', - '#a78bfa', '#22c55e', '#fb7185' + '#a78bfa', '#22c55e', '#fb7185', ]; -const MARGIN = { top: 20, right: 40, bottom: 40, left: 150 }; +const SVG_NS = 'http://www.w3.org/2000/svg'; +const MARGIN = { top: 24, right: 36, bottom: 56, left: 150 }; const LANE_HEIGHT = 80; +const LANE_GAP = 20; const WIDTH = 1400 - MARGIN.left - MARGIN.right; const TIME_DOMAIN = [-1000000, 2030]; +const TIME_CONSTANT = 1000; +const AXIS_TICKS = [-1000000, -100000, -10000, -1000, -100, -10, 0, 10, 100, 1000, 2030]; + +function loadText(url) { + return fetch(url).then((response) => { + if (!response.ok) { + throw new Error(`${url}: ${response.status}`); + } + return response.text(); + }); +} + +function parseCSV(text) { + const rows = []; + let row = []; + let cell = ''; + let inQuotes = false; + let i = 0; + const input = text.replace(/^\uFEFF/, ''); -async function loadText(url) { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`${url}: ${response.status}`); + function pushCell() { + row.push(cell); + cell = ''; } - return response.text(); + + function pushRow() { + if (row.length) { + rows.push(row); + } + row = []; + } + + while (i < input.length) { + const char = input[i]; + const next = input[i + 1]; + + if (inQuotes) { + if (char === '"' && next === '"') { + cell += '"'; + i += 2; + continue; + } + if (char === '"') { + inQuotes = false; + i += 1; + continue; + } + cell += char; + i += 1; + continue; + } + + if (char === '"') { + inQuotes = true; + i += 1; + continue; + } + + if (char === ',') { + pushCell(); + i += 1; + continue; + } + + if (char === '\n') { + pushCell(); + pushRow(); + i += 1; + continue; + } + + if (char === '\r') { + i += 1; + continue; + } + + cell += char; + i += 1; + } + + pushCell(); + pushRow(); + + if (!rows.length) { + return []; + } + + const headers = rows.shift().map((header) => header.trim()); + return rows + .filter((values) => values.some((value) => value.trim() !== '')) + .map((values) => { + const record = {}; + headers.forEach((header, index) => { + record[header] = (values[index] ?? '').trim(); + }); + return record; + }); +} + +function formatYearLabel(year) { + if (!Number.isFinite(year)) { + return 'Unknown year'; + } + const rounded = Math.round(year); + if (rounded < 0) { + return `${Math.abs(rounded).toLocaleString()} BCE`; + } + if (rounded === 0) { + return '0'; + } + return rounded.toLocaleString(); +} + +function symlog(value, constant = TIME_CONSTANT) { + return Math.sign(value) * Math.log1p(Math.abs(value) / constant); +} + +function createScale(domainMin, domainMax, rangeMin, rangeMax, constant = TIME_CONSTANT) { + const transformedMin = symlog(domainMin, constant); + const transformedMax = symlog(domainMax, constant); + const span = transformedMax - transformedMin || 1; + + return (value) => { + const transformedValue = symlog(value, constant); + const normalized = (transformedValue - transformedMin) / span; + return rangeMin + normalized * (rangeMax - rangeMin); + }; +} + +function createSvgElement(tagName, attrs = {}) { + const element = document.createElementNS(SVG_NS, tagName); + Object.entries(attrs).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + element.setAttribute(key, String(value)); + } + }); + return element; +} + +function appendText(parent, text, attrs = {}) { + const node = createSvgElement('text', attrs); + node.textContent = text; + parent.appendChild(node); + return node; } -async function loadManifest() { - const text = await loadText('../plots_manifest.json'); - return JSON.parse(text) - .filter(entry => entry.status === 'published' && entry.kind !== 'dashboard') - .sort((a, b) => a.order - b.order); +function loadManifest() { + return loadText('../plots_manifest.json').then((text) => JSON.parse(text) + .filter((entry) => entry.status === 'published' && entry.kind !== 'dashboard') + .sort((a, b) => a.order - b.order)); } -async function loadCSV(url) { - return d3.csvParse(await loadText(url)); +function loadCSV(url) { + return loadText(url).then(parseCSV); } function parseYear(row) { - const raw = row.year || row.Year || row.Years_Ago || row.date; - if (!raw) return null; - if (row.Years_Ago) return 2026 - parseFloat(raw); - const value = parseFloat(String(raw).slice(0, 4)); - return Number.isFinite(value) ? value : null; + const raw = row.year ?? row.Year ?? row.Years_Ago ?? row.date; + if (raw === null || raw === undefined || raw === '') { + return null; + } + if (row.Years_Ago !== undefined && row.Years_Ago !== null && row.Years_Ago !== '') { + const yearsAgo = Number.parseFloat(String(raw).trim()); + return Number.isFinite(yearsAgo) ? 2026 - yearsAgo : null; + } + const parsed = Number.parseFloat(String(raw).trim()); + return Number.isFinite(parsed) ? parsed : null; } function eventName(row) { return row.event || row.Event || row.Model || row.name || row.label || row.Benchmark || 'Event'; } -function initDashboard() { - const container = d3.select('#dashboard-container'); - const tooltip = d3.select('body').append('div').attr('class', 'tooltip'); +function laneTitle(entry) { + return entry.short_title || entry.title; +} - const totalHeight = 8 * (LANE_HEIGHT + 20) + MARGIN.top + MARGIN.bottom; - const svg = container.append('svg') - .attr('viewBox', `0 0 ${WIDTH + MARGIN.left + MARGIN.right} ${totalHeight}`) - .attr('role', 'img') - .attr('aria-label', 'Unified dashboard of published Exponential Progress Atlas timelines'); +function laneLabel(entry) { + return `${entry.title} (${entry.short_title || entry.title})`; +} - const g = svg.append('g') - .attr('transform', `translate(${MARGIN.left},${MARGIN.top})`); +function renderError(container, message) { + const error = document.createElement('p'); + error.className = 'error'; + error.textContent = message; + container.innerHTML = ''; + container.appendChild(error); +} - const xScale = d3.scaleSymlog() - .domain(TIME_DOMAIN) - .range([0, WIDTH]) - .constant(1000); +function renderDashboard(entriesWithData) { + const container = document.getElementById('dashboard-container'); + container.innerHTML = ''; - loadManifest() - .then(entries => Promise.all(entries.map((entry, index) => ( - loadCSV(`../${entry.data}`).then(data => ({ - id: entry.id, - name: entry.short_title, - title: entry.title, - color: COLORS[index % COLORS.length], - data, - })) - )))) - .then(lanesWithData => { - renderLanes(g, lanesWithData, xScale, tooltip); - }) - .catch(err => { - console.error('Failed to load dashboard data:', err); - container.append('p').attr('class', 'error') - .text('Error loading dashboard data. Ensure manifest and CSV files are accessible.'); + const intro = document.createElement('section'); + intro.className = 'dashboard-notice'; + intro.innerHTML = 'Offline-safe dashboard — local manifest and CSV files only. No remote runtime assets are required.'; + container.appendChild(intro); + + const totalHeight = entriesWithData.length * (LANE_HEIGHT + LANE_GAP) + MARGIN.top + MARGIN.bottom; + const svgWidth = WIDTH + MARGIN.left + MARGIN.right; + const svg = createSvgElement('svg', { + viewBox: `0 0 ${svgWidth} ${totalHeight}`, + width: svgWidth, + height: totalHeight, + role: 'img', + 'aria-label': 'Unified dashboard of published Exponential Progress Atlas timelines', + }); + svg.style.display = 'block'; + + const xScale = createScale(TIME_DOMAIN[0], TIME_DOMAIN[1], 0, WIDTH); + const tooltip = document.createElement('div'); + tooltip.className = 'tooltip'; + tooltip.setAttribute('role', 'status'); + tooltip.setAttribute('aria-live', 'polite'); + document.body.appendChild(tooltip); + + entriesWithData.forEach((entry, index) => { + const laneY = MARGIN.top + index * (LANE_HEIGHT + LANE_GAP); + const lane = createSvgElement('g', { + transform: `translate(${MARGIN.left}, ${laneY})`, + role: 'group', + 'aria-label': laneLabel(entry), }); -} -function renderLanes(g, lanes, xScale, tooltip) { - lanes.forEach((lane, index) => { - const laneG = g.append('g') - .attr('transform', `translate(0, ${index * (LANE_HEIGHT + 20)})`); - - laneG.append('rect') - .attr('width', WIDTH) - .attr('height', LANE_HEIGHT) - .attr('fill', '#161922') - .attr('rx', 8); - - laneG.append('text') - .attr('class', 'lane-title') - .attr('x', -10) - .attr('y', LANE_HEIGHT / 2) - .attr('text-anchor', 'end') - .attr('dominant-baseline', 'middle') - .text(lane.name) - .attr('fill', lane.color); - - const events = lane.data - .map(d => { - const year = parseYear(d); - return year === null ? null : { ...d, year }; + lane.appendChild(createSvgElement('rect', { + width: WIDTH, + height: LANE_HEIGHT, + rx: 10, + fill: '#161922', + stroke: '#2d3148', + })); + + appendText(lane, laneTitle(entry), { + class: 'lane-title', + x: -12, + y: LANE_HEIGHT / 2, + 'text-anchor': 'end', + 'dominant-baseline': 'middle', + fill: COLORS[index % COLORS.length], + }); + + const events = entry.data + .map((datum) => { + const year = parseYear(datum); + return year === null ? null : { ...datum, year }; }) .filter(Boolean) .sort((a, b) => a.year - b.year); - laneG.selectAll('.event-dot') - .data(events) - .enter() - .append('circle') - .attr('class', 'event-dot') - .attr('cx', d => xScale(d.year)) - .attr('cy', LANE_HEIGHT / 2) - .attr('r', d => (String(d.estimate_status || d.Impact || '').toLowerCase().includes('speculative') ? 5 : 4)) - .attr('fill', lane.color) - .attr('opacity', d => (String(d.estimate_status || d.Impact || '').toLowerCase().includes('speculative') ? 0.55 : 0.85)) - .on('mouseover', function(event, d) { - d3.select(this).attr('r', 7).attr('opacity', 1); - tooltip.style('opacity', 1) - .style('left', (event.pageX + 10) + 'px') - .style('top', (event.pageY - 10) + 'px') - .html(`${eventName(d)}
${lane.title}
Year: ${d.year}`); - }) - .on('mouseout', function() { - d3.select(this).attr('r', 4).attr('opacity', 0.8); - tooltip.style('opacity', 0); + events.forEach((event) => { + const speculative = String(event.estimate_status || event.Impact || '').toLowerCase().includes('speculative'); + const cx = xScale(event.year); + const cy = LANE_HEIGHT / 2; + const circle = createSvgElement('circle', { + class: 'event-dot', + cx, + cy, + r: speculative ? 5 : 4, + fill: COLORS[index % COLORS.length], + opacity: speculative ? 0.55 : 0.85, + tabindex: 0, + 'aria-label': `${eventName(event)} — ${entry.title} — ${formatYearLabel(event.year)}`, }); - if (index === lanes.length - 1) { - const axis = d3.axisBottom(xScale) - .ticks(10) - .tickFormat(d => d < 0 ? `${Math.abs(d)} BCE` : d.toString()); - laneG.append('g') - .attr('class', 'axis') - .attr('transform', `translate(0, ${LANE_HEIGHT})`) - .call(axis); + const showTooltip = (clientX, clientY) => { + tooltip.innerHTML = `${eventName(event)}
${entry.title}
Year: ${formatYearLabel(event.year)}`; + tooltip.style.left = `${clientX + 12}px`; + tooltip.style.top = `${clientY - 8}px`; + tooltip.style.opacity = '1'; + }; + + const hideTooltip = () => { + tooltip.style.opacity = '0'; + }; + + circle.addEventListener('mouseenter', (eventLike) => { + circle.setAttribute('r', speculative ? '7' : '6'); + circle.setAttribute('opacity', '1'); + showTooltip(eventLike.pageX, eventLike.pageY); + }); + circle.addEventListener('mousemove', (eventLike) => { + showTooltip(eventLike.pageX, eventLike.pageY); + }); + circle.addEventListener('mouseleave', () => { + circle.setAttribute('r', speculative ? '5' : '4'); + circle.setAttribute('opacity', speculative ? '0.55' : '0.85'); + hideTooltip(); + }); + circle.addEventListener('focus', () => { + showTooltip(window.innerWidth / 2, window.scrollY + laneY + 20); + }); + circle.addEventListener('blur', hideTooltip); + + lane.appendChild(circle); + }); + + if (index === entriesWithData.length - 1) { + const axisY = LANE_HEIGHT; + lane.appendChild(createSvgElement('line', { + x1: 0, + x2: WIDTH, + y1: axisY, + y2: axisY, + stroke: '#2d3148', + })); + + AXIS_TICKS.forEach((tickValue) => { + if (tickValue < TIME_DOMAIN[0] || tickValue > TIME_DOMAIN[1]) { + return; + } + const x = xScale(tickValue); + lane.appendChild(createSvgElement('line', { + x1: x, + x2: x, + y1: axisY, + y2: axisY + 6, + stroke: '#2d3148', + })); + appendText(lane, formatYearLabel(tickValue), { + x, + y: axisY + 22, + 'text-anchor': 'middle', + fill: '#94a3b8', + 'font-size': 11, + }); + }); + + appendText(lane, 'Time (symlog years)', { + x: WIDTH / 2, + y: axisY + 42, + 'text-anchor': 'middle', + fill: '#9ca3af', + 'font-size': 13, + }); } + + svg.appendChild(lane); }); - g.append('text') - .attr('x', WIDTH / 2) - .attr('y', lanes.length * (LANE_HEIGHT + 20) + 20) - .attr('text-anchor', 'middle') - .attr('fill', '#9ca3af') - .attr('font-size', '13px') - .text('Time (years, symlog scale) ->'); + container.appendChild(svg); +} + +function initDashboard() { + const container = document.getElementById('dashboard-container'); + + loadManifest() + .then((entries) => Promise.all( + entries.map((entry) => loadCSV(`../${entry.data}`).then((data) => ({ + ...entry, + data, + }))), + )) + .then(renderDashboard) + .catch((error) => { + console.error('Failed to load dashboard data:', error); + renderError(container, 'Error loading dashboard data. Ensure the manifest and CSV files are accessible locally.'); + }); } if (document.readyState === 'loading') { diff --git a/dashboard/index.html b/dashboard/index.html index cd94c92..8a3cfa4 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -4,14 +4,13 @@ Unified Dashboard — Exponential Progress - + - +

Unified Dashboard

-

Published Exponential Progress Atlas entries on one synchronized manifest-driven view.

+

Published Exponential Progress Atlas entries on one synchronized manifest-driven view. The dashboard is offline-safe and reads only local manifest and CSV files.

- diff --git a/docs/agentic-overhaul/2026-05-audit.md b/docs/agentic-overhaul/2026-05-audit.md index d26d266..636785e 100644 --- a/docs/agentic-overhaul/2026-05-audit.md +++ b/docs/agentic-overhaul/2026-05-audit.md @@ -23,7 +23,7 @@ Scores are subjective and reflect this audit pass. | Tests | 3/5 | Pytest coverage exists and now includes bootstrap smoke checks. Browser-level checks are still lightweight. | | Data/provenance | 3/5 | CSV and `meta.json` contracts are explicit, but some rows remain speculative and need careful wording. | | CI | 3/5 | CI runs build, validation, links, accessibility, and tests after this overhaul. | -| Accessibility/performance | 2/5 | Static checks exist, but the dashboard still relies on a CDN and there is no full browser audit in CI. | +| Accessibility/performance | 3/5 | Static checks exist, and the dashboard now renders without a remote runtime dependency; browser audit coverage is still lightweight. | | Deployment | 3/5 | The repo is structured for GitHub Pages style publishing, with validation gates before publish. | | Maintainability | 3/5 | Manifest-driven inventory keeps drift low, but archival docs and hand-authored exceptions still require discipline. | @@ -43,7 +43,7 @@ Scores are subjective and reflect this audit pass. - Improve dashboard and plot-page mobile polish where layout density is still uneven. - Add richer provenance summaries only where source material already exists. -- Revisit any CDN dependency if offline or pinned delivery becomes a concern. +- Keep browser QA coverage lightweight but aligned with the manifest and dashboard changes. ## Do Not Do Yet @@ -53,4 +53,3 @@ Scores are subjective and reflect this audit pass. - Do not normalize speculative claims into factual ones without source review. - Do not rename plot directories or manifest IDs without a coordinated migration plan. - Do not delete archival docs until a retention decision is made. - diff --git a/docs/agentic-overhaul/two-prompt-buildout-plan.md b/docs/agentic-overhaul/two-prompt-buildout-plan.md index 34a4f7b..98577f9 100644 --- a/docs/agentic-overhaul/two-prompt-buildout-plan.md +++ b/docs/agentic-overhaul/two-prompt-buildout-plan.md @@ -2,7 +2,7 @@ This repo is already in the self-orienting, self-validating state. The remaining work should be done in small, independently mergeable features that fit a two-prompt loop: -Completed in this branch: item 1, the browser smoke harness for homepage + dashboard + one representative plot. The next unfinished item is order 2, offline-safe dashboard loading. +Completed in this branch: item 2, offline-safe dashboard loading. Item 3 is now the next unfinished item. 1. Build prompt: create a feature branch, implement one feature, add or update tests, update docs, commit, and push. 2. QA prompt: open the branch in the browser, take screenshots, verify behavior, update docs if needed, commit, push, and merge. diff --git a/energetic-scaling/output/energetic_scaling.png b/energetic-scaling/output/energetic_scaling.png index 3725751..787753e 100644 Binary files a/energetic-scaling/output/energetic_scaling.png and b/energetic-scaling/output/energetic_scaling.png differ diff --git a/energetic-scaling/output/energetic_scaling.svg b/energetic-scaling/output/energetic_scaling.svg index 24a216c..005a5cb 100644 --- a/energetic-scaling/output/energetic_scaling.svg +++ b/energetic-scaling/output/energetic_scaling.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:03.061061 + 2026-05-20T17:31:54.971699 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -43,23 +43,23 @@ L 592.355042 256.749333 L 592.355042 106.632 L 52.983125 106.632 z -" clip-path="url(#pc3a5f0e1b2)" style="fill: #e74c3c; opacity: 0.1; stroke: #e74c3c; stroke-linejoin: miter"/> +" clip-path="url(#p48516b8ece)" style="fill: #e74c3c; opacity: 0.1; stroke: #e74c3c; stroke-linejoin: miter"/>
+" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -152,11 +152,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -199,11 +199,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -220,11 +220,11 @@ L 207.089387 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -240,11 +240,11 @@ L 284.142518 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -260,11 +260,11 @@ L 361.195649 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -280,11 +280,11 @@ L 438.24878 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -300,11 +300,11 @@ L 515.301911 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -341,16 +341,16 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - - + @@ -358,11 +358,11 @@ L 0 2 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -370,11 +370,11 @@ L 89.746812 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -382,11 +382,11 @@ L 99.373732 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -394,11 +394,11 @@ L 106.840952 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -406,11 +406,11 @@ L 112.942115 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -418,11 +418,11 @@ L 118.100575 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -430,11 +430,11 @@ L 122.569036 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -442,11 +442,11 @@ L 126.510498 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -454,11 +454,11 @@ L 153.23156 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -466,11 +466,11 @@ L 166.799943 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -478,11 +478,11 @@ L 176.426863 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -490,11 +490,11 @@ L 183.894083 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -502,11 +502,11 @@ L 189.995246 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -514,11 +514,11 @@ L 195.153706 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -526,11 +526,11 @@ L 199.622167 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -538,11 +538,11 @@ L 203.563629 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -550,11 +550,11 @@ L 230.284691 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -562,11 +562,11 @@ L 243.853074 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -574,11 +574,11 @@ L 253.479995 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -586,11 +586,11 @@ L 260.947214 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -598,11 +598,11 @@ L 267.048377 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -610,11 +610,11 @@ L 272.206837 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -622,11 +622,11 @@ L 276.675298 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -634,11 +634,11 @@ L 280.61676 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -646,11 +646,11 @@ L 307.337822 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -658,11 +658,11 @@ L 320.906205 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -670,11 +670,11 @@ L 330.533126 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -682,11 +682,11 @@ L 338.000345 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -694,11 +694,11 @@ L 344.101508 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -706,11 +706,11 @@ L 349.259968 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -718,11 +718,11 @@ L 353.728429 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -730,11 +730,11 @@ L 357.669891 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -742,11 +742,11 @@ L 384.390953 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -754,11 +754,11 @@ L 397.959336 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -766,11 +766,11 @@ L 407.586257 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -778,11 +778,11 @@ L 415.053477 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -790,11 +790,11 @@ L 421.154639 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -802,11 +802,11 @@ L 426.313099 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -814,11 +814,11 @@ L 430.78156 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -826,11 +826,11 @@ L 434.723022 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -838,11 +838,11 @@ L 461.444084 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -850,11 +850,11 @@ L 475.012467 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -862,11 +862,11 @@ L 484.639388 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -874,11 +874,11 @@ L 492.106608 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -886,11 +886,11 @@ L 498.207771 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -898,11 +898,11 @@ L 503.36623 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -910,11 +910,11 @@ L 507.834691 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -922,11 +922,11 @@ L 511.776153 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -934,11 +934,11 @@ L 538.497215 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -946,11 +946,11 @@ L 552.065598 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -958,11 +958,11 @@ L 561.692519 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -970,11 +970,11 @@ L 569.159739 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -982,11 +982,11 @@ L 575.260902 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -994,11 +994,11 @@ L 580.419361 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1006,11 +1006,11 @@ L 584.887822 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1292,16 +1292,16 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -1344,11 +1344,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1396,11 +1396,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1428,11 +1428,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1489,11 +1489,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1541,11 +1541,11 @@ z +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1562,11 +1562,11 @@ L 592.355042 181.690667 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -1583,16 +1583,16 @@ L 592.355042 106.632 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - - + @@ -1600,11 +1600,11 @@ L -2 0 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1612,11 +1612,11 @@ L 592.355042 521.171915 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1624,11 +1624,11 @@ L 592.355042 511.79418 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1636,11 +1636,11 @@ L 592.355042 504.520243 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1648,11 +1648,11 @@ L 592.355042 498.577005 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1660,11 +1660,11 @@ L 592.355042 493.552068 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - + @@ -1672,11 +1672,11 @@ L 592.355042 489.19927 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1684,11 +1684,11 @@ L 592.355042 485.35983 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1696,11 +1696,11 @@ L 592.355042 459.330423 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1708,11 +1708,11 @@ L 592.355042 446.113248 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1720,11 +1720,11 @@ L 592.355042 436.735513 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1732,11 +1732,11 @@ L 592.355042 429.461577 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1744,11 +1744,11 @@ L 592.355042 423.518338 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1756,11 +1756,11 @@ L 592.355042 418.493401 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1768,11 +1768,11 @@ L 592.355042 414.140603 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1780,11 +1780,11 @@ L 592.355042 410.301163 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1792,11 +1792,11 @@ L 592.355042 384.271757 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1804,11 +1804,11 @@ L 592.355042 371.054581 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1816,11 +1816,11 @@ L 592.355042 361.676846 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1828,11 +1828,11 @@ L 592.355042 354.40291 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1840,11 +1840,11 @@ L 592.355042 348.459671 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1852,11 +1852,11 @@ L 592.355042 343.434735 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1864,11 +1864,11 @@ L 592.355042 339.081936 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1876,11 +1876,11 @@ L 592.355042 335.242496 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1888,11 +1888,11 @@ L 592.355042 309.21309 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1900,11 +1900,11 @@ L 592.355042 295.995915 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1912,11 +1912,11 @@ L 592.355042 286.61818 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1924,11 +1924,11 @@ L 592.355042 279.344243 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1936,11 +1936,11 @@ L 592.355042 273.401005 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1948,11 +1948,11 @@ L 592.355042 268.376068 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1960,11 +1960,11 @@ L 592.355042 264.02327 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1972,11 +1972,11 @@ L 592.355042 260.18383 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1984,11 +1984,11 @@ L 592.355042 234.154423 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -1996,11 +1996,11 @@ L 592.355042 220.937248 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2008,11 +2008,11 @@ L 592.355042 211.559513 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2020,11 +2020,11 @@ L 592.355042 204.285577 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2032,11 +2032,11 @@ L 592.355042 198.342338 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2044,11 +2044,11 @@ L 592.355042 193.317401 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2056,11 +2056,11 @@ L 592.355042 188.964603 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2068,11 +2068,11 @@ L 592.355042 185.125163 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2080,11 +2080,11 @@ L 592.355042 159.095757 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2092,11 +2092,11 @@ L 592.355042 145.878581 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2104,11 +2104,11 @@ L 592.355042 136.500846 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2116,11 +2116,11 @@ L 592.355042 129.22691 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2128,11 +2128,11 @@ L 592.355042 123.283671 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2140,11 +2140,11 @@ L 592.355042 118.258735 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2152,11 +2152,11 @@ L 592.355042 113.905936 +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 0.8,1.32; stroke-dashoffset: 0; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8"/> - +
@@ -2419,7 +2419,7 @@ L 578.345382 382.867588 L 583.015269 384.411291 L 587.685156 385.954995 L 592.355042 387.498698 -" clip-path="url(#pc3a5f0e1b2)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #3498db; stroke-opacity: 0.5; stroke-width: 2"/> +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #3498db; stroke-opacity: 0.5; stroke-width: 2"/>
+" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #9b59b6; stroke-opacity: 0.5; stroke-width: 2"/> +" clip-path="url(#p48516b8ece)" style="fill: none; stroke-dasharray: 1.5,2.475; stroke-dashoffset: 0; stroke: #808080; stroke-opacity: 0.4; stroke-width: 1.5"/> - - - + + - - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - - + + - - - + + - - - + + - - + + - - + + - - - + + - - + + @@ -4427,7 +4427,7 @@ L 68.183125 545.32025 L 76.183125 545.32025 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -4498,18 +4498,18 @@ L 1198.652712 556.984 L 1198.652712 106.632 L 1096.455928 106.632 z -" clip-path="url(#p482e4e6fde)" style="fill: #e74c3c; opacity: 0.1; stroke: #e74c3c; stroke-linejoin: miter"/> +" clip-path="url(#p9e5cb6d527)" style="fill: #e74c3c; opacity: 0.1; stroke: #e74c3c; stroke-linejoin: miter"/> +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4526,11 +4526,11 @@ L 687.668791 106.632 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4547,11 +4547,11 @@ L 801.220773 106.632 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4568,11 +4568,11 @@ L 914.772756 106.632 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4589,11 +4589,11 @@ L 1028.324739 106.632 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4635,11 +4635,11 @@ z +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4656,11 +4656,11 @@ L 1198.652712 556.984 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4677,11 +4677,11 @@ L 1198.652712 489.4312 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4698,11 +4698,11 @@ L 1198.652712 421.8784 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4718,11 +4718,11 @@ L 1198.652712 354.3256 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4738,11 +4738,11 @@ L 1198.652712 286.7728 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -4758,11 +4758,11 @@ L 1198.652712 219.22 +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -5006,7 +5006,7 @@ L 1182.308109 118.212615 L 1187.75631 113.870025 L 1193.204511 109.527435 L 1198.652712 105.184845 -" clip-path="url(#p482e4e6fde)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #e67e22; stroke-opacity: 0.6; stroke-width: 2"/> +" clip-path="url(#p9e5cb6d527)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #e67e22; stroke-opacity: 0.6; stroke-width: 2"/> - - - + + - - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + @@ -5939,15 +5939,15 @@ L 674.480795 128.85325 L 682.480795 128.85325 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -6078,12 +6078,12 @@ z - - + @@ -6099,7 +6099,7 @@ L 3.5 0 - + @@ -6115,7 +6115,7 @@ L 3.5 0 - + @@ -6131,7 +6131,7 @@ L 3.5 0 - + @@ -6147,7 +6147,7 @@ L 3.5 0 - + @@ -6163,7 +6163,7 @@ L 3.5 0 - + @@ -6179,7 +6179,7 @@ L 3.5 0 - + @@ -6273,81 +6273,81 @@ z - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + @@ -7616,10 +7616,10 @@ z - + - + diff --git a/energetic-scaling/output/energetic_scaling_highres.png b/energetic-scaling/output/energetic_scaling_highres.png index 7997b3a..09641a1 100644 Binary files a/energetic-scaling/output/energetic_scaling_highres.png and b/energetic-scaling/output/energetic_scaling_highres.png differ diff --git a/energetic-scaling/output/energetic_scaling_interactive.html b/energetic-scaling/output/energetic_scaling_interactive.html index c583778..eaba3f1 100644 --- a/energetic-scaling/output/energetic_scaling_interactive.html +++ b/energetic-scaling/output/energetic_scaling_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/energy-leverage-per-person/output/energy_leverage.png b/energy-leverage-per-person/output/energy_leverage.png index 1a5d3b1..1df7434 100644 Binary files a/energy-leverage-per-person/output/energy_leverage.png and b/energy-leverage-per-person/output/energy_leverage.png differ diff --git a/energy-leverage-per-person/output/energy_leverage.svg b/energy-leverage-per-person/output/energy_leverage.svg index 5e8293a..7962c5e 100644 --- a/energy-leverage-per-person/output/energy_leverage.svg +++ b/energy-leverage-per-person/output/energy_leverage.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:13.631641 + 2026-05-20T17:32:58.391047 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -40,33 +40,33 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke-dasharray: 1.5,2.475; stroke-dashoffset: 0; stroke: #888888; stroke-width: 1.5"/> +" clip-path="url(#pab63b7284c)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #cccccc"/> +" clip-path="url(#pab63b7284c)" style="fill: none; stroke-dasharray: 3.7,1.6; stroke-dashoffset: 0; stroke: #cccccc"/> +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -144,11 +144,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -193,11 +193,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -249,11 +249,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -267,11 +267,11 @@ L 585.723294 48.51475 +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -304,11 +304,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -337,11 +337,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -358,11 +358,11 @@ L 686.090648 48.51475 +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.2; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -505,16 +505,16 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -530,11 +530,11 @@ L -3.5 0 +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -550,11 +550,11 @@ L 696.655633 271.839319 +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -570,11 +570,11 @@ L 696.655633 196.275641 +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -603,11 +603,11 @@ z +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -624,89 +624,89 @@ L 696.655633 81.95221 - - + - + - + - + - + - + - + - + - + - + - + - + @@ -920,9 +920,9 @@ L 638.548217 249.253371 L 678.16691 183.601846 L 686.090648 151.368301 L 691.37314 84.763321 -" clip-path="url(#pff0f905739)" style="fill: none; stroke: #e67e22; stroke-width: 2.5; stroke-linecap: square"/> +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #e67e22; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - - - + + + + + + + @@ -950,9 +950,9 @@ L 638.548217 296.151732 L 678.16691 201.55683 L 686.090648 163.07615 L 691.37314 89.779752 -" clip-path="url(#pff0f905739)" style="fill: none; stroke: #3498db; stroke-width: 2.5; stroke-linecap: square"/> +" clip-path="url(#pab63b7284c)" style="fill: none; stroke: #3498db; stroke-width: 2.5; stroke-linecap: square"/> - - - - - - - - + + + + + + + @@ -1471,7 +1471,7 @@ L 74.574062 60.303344 L 83.574062 60.303344 " style="fill: none; stroke: #e67e22; stroke-width: 2.5; stroke-linecap: square"/> - + @@ -1720,7 +1720,7 @@ L 74.574062 73.513656 L 83.574062 73.513656 " style="fill: none; stroke: #3498db; stroke-width: 2.5; stroke-linecap: square"/> - + @@ -1953,7 +1953,7 @@ z - + diff --git a/energy-leverage-per-person/output/energy_leverage_highres.png b/energy-leverage-per-person/output/energy_leverage_highres.png index 9c14fc0..d0e9b8d 100644 Binary files a/energy-leverage-per-person/output/energy_leverage_highres.png and b/energy-leverage-per-person/output/energy_leverage_highres.png differ diff --git a/energy-leverage-per-person/output/energy_leverage_interactive.html b/energy-leverage-per-person/output/energy_leverage_interactive.html index a33b9f3..eb5492b 100644 --- a/energy-leverage-per-person/output/energy_leverage_interactive.html +++ b/energy-leverage-per-person/output/energy_leverage_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/model-sizes/output/model_sizes.png b/model-sizes/output/model_sizes.png index 4199fd7..2d94ff0 100644 Binary files a/model-sizes/output/model_sizes.png and b/model-sizes/output/model_sizes.png differ diff --git a/model-sizes/output/model_sizes.svg b/model-sizes/output/model_sizes.svg index 258f9f0..c675d4e 100644 --- a/model-sizes/output/model_sizes.svg +++ b/model-sizes/output/model_sizes.svg @@ -6,11 +6,11 @@ - 2026-05-19T11:06:16.618707 + 2026-05-20T17:33:10.336767 image/svg+xml - Matplotlib v3.10.8, https://matplotlib.org/ + Matplotlib v3.10.9, https://matplotlib.org/ @@ -43,7 +43,7 @@ L 406.877968 423.4915 L 406.877968 43.7555 L 63.1817 43.7555 z -" clip-path="url(#pbe15e768e2)" style="fill: #e8f4fd; opacity: 0.45; stroke: #e8f4fd; stroke-linejoin: miter"/> +" clip-path="url(#pce84382173)" style="fill: #e8f4fd; opacity: 0.45; stroke: #e8f4fd; stroke-linejoin: miter"/>
+" clip-path="url(#pce84382173)" style="fill: #f0e8ff; opacity: 0.45; stroke: #f0e8ff; stroke-linejoin: miter"/> +" clip-path="url(#pce84382173)" style="fill: #e8f8e8; opacity: 0.45; stroke: #e8f8e8; stroke-linejoin: miter"/> +" clip-path="url(#pce84382173)" style="fill: #ffe8e8; opacity: 0.45; stroke: #ffe8e8; stroke-linejoin: miter"/> +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -191,11 +191,11 @@ z +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -212,11 +212,11 @@ L 151.308948 43.7555 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -233,11 +233,11 @@ L 239.436196 43.7555 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -254,11 +254,11 @@ L 327.563445 43.7555 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -309,11 +309,11 @@ z +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -351,11 +351,11 @@ z +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -399,11 +399,11 @@ z +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.15; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -552,16 +552,16 @@ z +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -577,11 +577,11 @@ L -3.5 0 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -597,11 +597,11 @@ L 728.542424 328.5575 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -617,11 +617,11 @@ L 728.542424 233.6235 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -637,11 +637,11 @@ L 728.542424 138.6895 +" clip-path="url(#pce84382173)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.25; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -656,229 +656,229 @@ L 728.542424 43.7555 - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1863,7 +1863,7 @@ z - - - + + - - + + - - - + + - - - + + - - + + - - + + - - + + - - + + - - - + + - - - + + - - - + + - - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - + + - - + + - - - + + @@ -3078,7 +3078,7 @@ L 645.076799 379.555562 L 655.076799 379.555562 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3114,15 +3114,15 @@ L 645.076799 394.233687 L 655.076799 394.233687 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3162,15 +3162,15 @@ L 645.076799 408.911812 L 655.076799 408.911812 " style="fill: none; stroke: #ffffff; stroke-width: 1.5; stroke-linecap: square"/> - - + @@ -3211,7 +3211,7 @@ z - + diff --git a/model-sizes/output/model_sizes_highres.png b/model-sizes/output/model_sizes_highres.png index f9bbf85..8887135 100644 Binary files a/model-sizes/output/model_sizes_highres.png and b/model-sizes/output/model_sizes_highres.png differ diff --git a/model-sizes/output/model_sizes_interactive.html b/model-sizes/output/model_sizes_interactive.html index beb5ef3..5fc4284 100644 --- a/model-sizes/output/model_sizes_interactive.html +++ b/model-sizes/output/model_sizes_interactive.html @@ -1,7 +1,7 @@ -
-
+
+
\ No newline at end of file diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 106c7ac..53efc5b 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -1,40 +1,35 @@ """Tests for unified dashboard structure.""" -import os +from pathlib import Path -DASHBOARD_DIR = "dashboard" +DASHBOARD_DIR = Path("dashboard") class TestDashboardStructure: def test_dashboard_directory_exists(self): - assert os.path.isdir(DASHBOARD_DIR) + assert DASHBOARD_DIR.is_dir() def test_index_html_exists(self): - assert os.path.isfile(os.path.join(DASHBOARD_DIR, "index.html")) + assert (DASHBOARD_DIR / "index.html").is_file() def test_dashboard_js_exists(self): - assert os.path.isfile(os.path.join(DASHBOARD_DIR, "dashboard.js")) + assert (DASHBOARD_DIR / "dashboard.js").is_file() def test_dashboard_css_exists(self): - assert os.path.isfile(os.path.join(DASHBOARD_DIR, "dashboard.css")) + assert (DASHBOARD_DIR / "dashboard.css").is_file() - def test_index_html_links_d3(self): - with open(os.path.join(DASHBOARD_DIR, "index.html"), "r") as f: - content = f.read() - assert "d3" in content.lower() - - def test_index_html_links_dashboard_js(self): - with open(os.path.join(DASHBOARD_DIR, "index.html"), "r") as f: - content = f.read() + def test_index_html_uses_only_local_assets(self): + content = (DASHBOARD_DIR / "index.html").read_text() + lowered = content.lower() + assert "https://" not in lowered + assert "http://" not in lowered assert "dashboard.js" in content - - def test_index_html_links_dashboard_css(self): - with open(os.path.join(DASHBOARD_DIR, "index.html"), "r") as f: - content = f.read() assert "dashboard.css" in content + assert "offline-safe" in lowered def test_dashboard_js_references_csv_data(self): - with open(os.path.join(DASHBOARD_DIR, "dashboard.js"), "r") as f: - content = f.read() - # Should reference at least one plot CSV - assert ".csv" in content + content = (DASHBOARD_DIR / "dashboard.js").read_text() + # Should reference the manifest and local CSV loading path. + assert "plots_manifest.json" in content + assert "loadCSV" in content + assert "../" in content diff --git a/tests/test_dashboard_year_parsing.py b/tests/test_dashboard_year_parsing.py new file mode 100644 index 0000000..c24d0f5 --- /dev/null +++ b/tests/test_dashboard_year_parsing.py @@ -0,0 +1,65 @@ +"""Regression tests for dashboard year parsing.""" + +from __future__ import annotations + +import json +import subprocess +from pathlib import Path + +DASHBOARD_JS = Path("dashboard/dashboard.js") + + +def _extract_function(source: str, signature: str) -> str: + start = source.index(signature) + brace_depth = 0 + end = None + + for index in range(start, len(source)): + char = source[index] + if char == "{": + brace_depth += 1 + elif char == "}": + brace_depth -= 1 + if brace_depth == 0: + end = index + 1 + break + + assert end is not None, f"Could not extract function for {signature}" + return source[start:end] + + +def test_parse_year_preserves_zero_and_full_year_values(): + """Regression: ISSUE-PR3 — parseYear treated 0 as falsy and truncated long years. + + Found by /qa on 2026-05-20 + Report: .gstack/qa-reports/qa-report-plots-2026-05-20.md + """ + + source = DASHBOARD_JS.read_text() + parse_year = _extract_function(source, "function parseYear(row) {") + + cases = [ + {"row": {"year": 0}, "expected": 0}, + {"row": {"Year": 12345}, "expected": 12345}, + {"row": {"date": "2024-01-01"}, "expected": 2024}, + {"row": {"Years_Ago": 2}, "expected": 2024}, + {"row": {"year": ""}, "expected": None}, + ] + + script = "\n".join( + [ + parse_year, + f"const cases = {json.dumps([case['row'] for case in cases])};", + "const results = cases.map((row) => parseYear(row));", + "process.stdout.write(JSON.stringify(results));", + ] + ) + + completed = subprocess.run( + ["node", "-e", script], + check=True, + capture_output=True, + text=True, + ) + + assert json.loads(completed.stdout) == [case["expected"] for case in cases]