Visual + data-encoding overhaul (Lakes 0-8)#4
Draft
aurph wants to merge 26 commits into
Draft
Conversation
…quality colors, type scale, chart theme Categorical palette CVD-validated (min adjacent dE 13.9 both surfaces). Tabular figures on all mono/table numerals.
…ier to tokens Stack layers: 10 thesis layers own the 10 categorical slots, periphery layers (mining, crypto operators, benchmarks) take neutral ink tiers. Catalyst categories and supply-chain stages on distinct series slots instead of four near-identical oranges.
Migrate every remaining page and component (PowerMap, TiltOverview, neocloud-intel, TheTrade, Queue, power-deals, gpu-economics, Portfolio Overlay, stock/sector/region/operator pages, blog, admin, EmailCapture, NewsTicker, sidebar, App, not-found) off hardcoded hex/px/font values onto the token layer. Fix: Tailwind 3 silently drops slash-opacity on plain var() colors - every bg-brand/10 style tint compiled to no CSS. Token colors now wrap in color-mix with <alpha-value> so tints work; verified by compiling probes through the real config. Remaining literals are documented exceptions: external company brand colors (PowerMap operator map), data JSON. Categorical unifications: nuclear=violet everywhere (was orange/purple/ amber by page), vendor pair NVIDIA=brand/AMD=cyan, status colors from STATUS_COLORS, semantic green/red only for gain-loss state.
True UTC time scale (pixels proportional to elapsed time), log y with 1-2-5 ticks and a corner 'log scale' label, month/quarter boundary ticks that densify as the range shortens, 3M/6M/1Y/ALL selector. Data honesty: sourced month anchors = solid dots, recorded days = small dots, spans between points render dashed at reduced opacity when interpolated (linear only, no splines), launch ring on each series' first point, 'daily recorder starts <date>' note while the recorder is empty, interp/est flags in the tooltip. Legibility at 10 series: collision-avoided right-edge direct labels, hover isolates a series (others drop to 22%), chip filters (click = solo, shift-click = toggle, All resets) persisted in URL params so views are shareable, unified crosshair tooltip sorted by price. Range bands: current observed low-high marketplace range behind each line when <= 3 series visible, explained in the footnote. Overlay / grid view toggle: grid = small multiples, shared time domain, independent log y per panel. Table: honest sparkline column (same anchor/fill treatment, per-series domain), row hover highlights the series in the chart, replaced the memory column (still on cards + row tooltip); fixes the 13-spans-in- 12-columns row bug. Removed the hasRealSeries gate - the history chart now always renders, honestly. New pure transform module client/src/lib/gpu-series.ts (anchor/fill classification, clipping, log ticks, label solver, sparkline domains) sized for Lake 8 unit coverage. Adds @visx scale/shape/group/axis/event.
…ck at Lake 2 review
…ation, edge cases
Sparklines (raw SVG, no per-card Recharts): y-domain is the window's own
[low, high] with 10% padding, color = the window's net direction (was
today's quote change painted onto any window), faint dashed reference
line at prior close (1D) / window open (5D, 1M) so shape reads as
above/below the baseline, defined 'no intraday data' empty state.
Views (persisted per user in localStorage):
- cards: existing layout on the new sparklines
- table: dense rows, all 13 layers as collapsible group headers,
sortable ticker/price/1D/5D/1M/mktcap/PE/revGrowth with nulls always
last; 5D and 1M columns compute from each window's own price series,
fetched lazily via the same endpoint
- heatmap: squarified treemap, tile = market cap, color = selected
window's change on a diverging semantic ramp saturating at +-4%,
grouped by layer, ETFs excluded (AUM is not corporate market cap),
unsized tickers listed honestly, stale tiles gray with dashed border
Perf (measured via React Profiler + CDP, 3 runs each, warm cache):
before 1272/1251/1247 ms mount render with 97 Recharts instances;
after 181/185/181 ms with 1 (correlation scatter) - 6.9x faster.
Off-screen layer sections skip render via content-visibility: auto.
Edge cases: P/E null and negative are explicit states (INTC shows an
em dash with an explanation, not a blank), missing rev growth and
market cap render dashes, stale rows show delayed badge + all-dash
changes, market state hoists to one page chip ('market closed') with
per-row badges only when a ticker differs from the majority.
Fixes the approved parseM bug (M sorted equal to $50B); market cap
now prefers the live numeric field (static display strings were months
stale: cards said $3.8T for AAPL while live is $4.5T).
Server: additive passthrough of marketCap/marketState/previousClose
from the existing quote call - no new fetches, no route changes.
…me axis + ranges, honest gauge sparklines A: hero replaced by a data strip - page title, live NPI with change labeled by its TRUE span (the recorder has a 22-day gap; calling that move 1D would lie), last-updated, live dot. Data starts above the fold. C: sector chips are an aligned grid with uniform anatomy, colored purely semantically - the same diverging magnitude ramp as the Stack heatmap. Sector identity lives in the label, not a hue. D: NPI gauge chart on a true time axis (recorder gaps now render as gaps in time, not one category step), clean month-boundary ticks, 3M/6M/1Y/ALL range selector matching Neocloud, linear interpolation instead of monotone splines, baseline stated in the subtitle instead of a clipped in-chart label. Sentiment sparklines get honest domains: window values UNION the formula baseline (AI 72 / stress 68, mirroring server/indices.ts) with a dashed baseline reference - a one-point wiggle can no longer fill the full height. E: mover tags already on stable categorical tokens; filled the five missing sector short-labels (renewableGeneration rendered raw). Also: Grid Stress KpiCard red variant now renders red (was orange), demand chart no longer double-encodes 2024 as actual and projection, merged the duplicate /api/kpis query.
…ner pick) Same five catalysts were rendered three times in one column: calendar dots, Next 5 Upcoming inside the calendar card, and a standalone Next 5 Catalysts card. The standalone card is deleted; calendar dot days gain a hover popover listing that day's events (dots are now functional, not decorative); the single Next-5 list stays. Click-to- pin day detail unchanged.
Markers: size = MW (sqrt scale) with an in-map legend showing reference sizes, color = facility status from STATUS_COLORS. The dashed rotating ring encoded construction status - redundant with the status color, so it is deleted. Overlapping Southeast markers cluster with count badges (leaflet.markercluster) and spiderfy/zoom on click. Color reconciliation: the page had three disagreeing systems (legend vs stressColorForRTO vs RTO_STRESS_COLORS). Now one truth: status -> STATUS_COLORS, RTO stress -> one SEMANTIC ramp used identically by region fills, legend, and table badges. Impossible filter options removed (dataset floor is 400 MW; '<100 MW' could never match) - buckets are now 400-600 / 600-1000 / 1GW+. '>=400 MW ONLY' banner collapsed to an info popover; GW/TWh strip kept. Upcoming projects: scroll-snap cards, hidden scrollbar, edge fade, 'show all' expansion. Operator table: inline reserve-margin bar next to the number, stress badges from the same semantic ramp, duplicate aiSignal column dropped, row click filters the map to that RTO's facilities (linked views) with clear chips and active-row highlight. Perf: hover no longer rebuilds every marker layer (hoveredId out of the effect deps, imperative restyle - DOM nodes verified stable). Pulse animation honors prefers-reduced-motion; rotate keyframe gone.
The old-shape fallback injects literals (Dominion 47.1 GW, 77% withdrawal, Meta Hyperion, Stargate) that rendered indistinguishable from sourced data. They now carry est. flags whenever the fallback produced them (verified by intercepting the API with the old shape). Rows without notes/sources no longer render a cursor-help trigger that opens an empty tooltip.
…amp, invalid-mix state Approved calc fix: buildoutChart dropped each preset's last ramp entry (ramp[i-1] indexing); bars now sum exactly to the preset total (pixel-verified). Custom mode replaces flat GW/5 bars with a labeled assumed S-curve ramp scaled to the input total, so the timeline has time-shape without pretending precision. A mix that does not sum to 100% now shows 'Mix sums to N% - adjust to 100%' in place of the chart and blanks mix-dependent outputs instead of charting garbage silently. Tooltip/cursor/axis fully on chart theme.
…, flat dots 'Upcoming Earnings' renamed to Upcoming Catalysts (the list includes non-earnings items). Fetch failure now renders an in-place error card instead of a silently blank calendar, and header counts hide instead of showing misleading zeros. Timeline dot rings were a cosmetic halo with no data meaning - flattened.
…, keyboard nodes Dragging re-rendered all 21 nodes + 44 links per pointer event; now at most one render per animation frame (rAF-throttled, positions still tracked per event). First render shows a full-height skeleton instead of a null flash. Entrance animations wrapped in prefers-reduced-motion: no-preference including the dash setup (links stay visible under reduced motion). Nodes are keyboard-accessible: role=button, tabIndex, aria-label, Enter/Space select, focus-visible brand outline.
Subtitle now names the real five radar segments. Approved fix: results.sort() no longer mutates state during render (sorted copy in useMemo). Linked with The Stack: holding tickers link to their stock pages; empty state offers real example tickers, one per segment.
…inimum Rows now sort by $/PFLOP-hr ascending (nulls last) client-side and the badge attaches to the computed minimum, not to whatever row the server sent first - the header's 'sorted by cheapest compute' claim is now true by construction.
…a bucket last The year buckets sorted by localeCompare, letting the n/a bucket land between years. Numeric ascending now, n/a forced last with a muted ink fill and an explaining footnote so it cannot read as a year.
…imitives, AA token bumps Global :focus-visible ring (brand-2, offset) on every interactive element; mouse clicks do not ring. prefers-reduced-motion kills the ticker marquee, count-up, pulse/ping loops, and long transitions. News ticker: pause is real state - visible pause/play button for touch and keyboard, focus-within pauses, hover still pauses; headlines were already real click-through links. New trust primitives (components/Freshness.tsx): AsOf chip (hover = exact timestamp; flips to an amber 'stale' badge when a source misses its refresh schedule - distinct from broken), ErrorState (designed in-place failure with retry), SrChartTable (sr-only data table so chart content is not locked inside pixels). Contrast audit of every token text pair on all three surfaces (WCAG math, not eyeballs): ink-muted 3.5:1 -> #8E8B84 (4.6:1 worst case), ink-faint 2.0:1 -> #716E67 (3.0:1+, micro de-emphasis only), chart tick follows muted. All other pairs already passed AA.
…oss all tools Every data page now shows an AsOf freshness chip (hover = exact timestamp, flips to an amber stale badge when a source misses ~2x its refresh interval). Every fetch-failure branch renders the designed ErrorState with a working retry (react-query refetch) - no blank panels. Loading states are layout-matched skeletons. Screen-reader chart tables (sr-only) mirror exactly what each chart draws: NPI gauge history, US electricity demand, the Neocloud price history (visible series' real points, synthetic edge points excluded), compute-frontier bars, TheTrade buildout, power-deals offtakers. Responsive: no page scrolls horizontally at 390px (probe-verified); Power Map renders an honest 'needs a desktop screen' card on mobile with the summary strip, upcoming projects, and operator table still fully usable; Neocloud overlay collapses its label gutter under 640px. Scenario Calculator and Portfolio Overlay intentionally have no AsOf: one is a client-side model, the other is POST-driven.
…n sync client/src/lib/__tests__ joins the existing node:test harness (glob extended, JSX via tsconfig.test.json so the app build is untouched). gpu-series (100% line/func coverage): anchor vs recorded date parsing, point ordering/dedup/garbage handling, non-positive price rejection (log-scale safety), observed-vs-interpolated span classification with gap boundaries, window clipping with synthetic edge interpolation, 1-2-5 log ticks incl. degenerate domains, log domain padding, label collision solver (spacing, order, bottom overflow, unfittable stacks), crosshair valueAt (exact/interp/out-of-range), nearest-point snapping, sparkline domains (padding, single-point, flat-day, flat-at-zero, missing data), date formatting. stack-transforms (100% line/func): market-cap parsing with a regression test pinning the old parseM bug (M != B), numeric-cap preference, window % change and direction, table sorting with nulls-always-last both directions and no input mutation, heatmap grouping (ETF exclusion, unsized listing, empty groups), treemap layout (bounds, area proportionality, group containment, degenerate canvas), hex blending, diverging heat colors (null = neutral not flat-day, saturation cap, monotone magnitude). Snapshot tests (react-dom/server, no DOM needed) for the shared loading/empty/error primitives: ErrorState with/without retry, AsOf fresh/stale/hidden under mocked clocks, SrChartTable. tokens-sync test enforces the promised tokens.ts <-> index.css mirror, including all ten series slots in order. Suite: 160 tests, 0 failures.
One subject, one screen: the cost-of-compute table and training calculator are a derived view of the same per-model price data, so GPU Economics is now the Economics tab of GPU Prices (URL-persisted tabs, ?tab=economics shareable). /gpu-economics redirects into the tab; sidebar drops from 14 to 13 entries. New ToolTabs primitive carries the pattern for the remaining folds.
Three lenses on one question - where AI power comes from: physical siting, procurement contracts, and the grid queue - are now one Power tool with Map/Deals/Queue tabs. Deals and Queue render embedded (slim intro, same body, same testids); the Leaflet map survives tab round-trips (probe-verified markers after map->deals->map).
…tabs Both are 'your inputs -> analysis' tools; they now share one Analyze screen with Portfolio/Scenario tabs. Embedded modes keep the ticker input, preset row, and sourced badges; share/URL writes are path-aware so ?tickers= deep links work under /analyze too.
The daily digest renders as the first card on the Analysis page (brand-bordered, collapse to ~6 lines with read-more, own error state so a failed brief can never blank the post list). Standalone body unchanged behind the redirect.
…tool layout Client: /analyze registered; /gpu-economics, /power-deals, /queue, /brief, /portfolio, /trade redirect into their host tabs. Sidebar is 9 entries (8 after the Supply Chain fold): Overview, The Stack, Power, Compute Frontier, GPU Prices, Supply Chain, Analyze, Catalyst Tracker, Analysis. G+1..8 remap to the new order (G+S Supply Chain interim). Server: 301s for all six folded routes next to the existing legacy redirects; seo.ts drops dead entries, gains /analyze (carries the scenario FAQ structured data). Overview module grid tells the same 7-tool story. Tests still 160/160.
The force graph SURVIVES (owner call) - and it was more honest than the audit gave it credit for: the sim already pins each node's x to its supply-chain stage (forceX strength 0.85). The workaround makes that encoding legible instead of replacing it: faint stage column guides behind the nodes, and the legend now states x = supply chain stage, node size = connection count. Organic look kept, position carries data. The Stack gains 'flow' as its fourth view (lazy-loaded so the d3 sim costs nothing until opened); URL ?view=flow overrides the stored view so /supply-chain 301s land correctly. Timeframe toggle and the uranium/CCJ correlation card hide in flow view (price-view concerns). Sidebar lands at the final 8 entries: Overview, The Stack, Power, Compute Frontier, GPU Prices, Analyze, Catalyst Tracker, Analysis. Tests 160/160, build green, all 8 routes + all 7 fold redirects probe-verified.
This was referenced Jul 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Complete visual and data-encoding overhaul across all tools, built lake by lake with owner checkpoints.
Screenshots and the Lake 0 audit were delivered at checkpoints during the build.
🤖 Generated with Claude Code
https://claude.ai/code/session_011dDxxKQpH3o4CA7qnDFGdN