v14.1: Trend Chart Overhaul — Six Views, Eight Signals
The 30-day trend panel gets a complete visual overhaul: six switchable chart views (Line, Area, Stacked, Step, Candle, Heatmap), all eight signal groups exposed in every view, and a bug fix that was silently dropping the AI score from all Firestore writes.
Since launch, the 30-day trend panel has shown a single line: the blended meter score over time. That was useful for spotting long-run escalation, but it told you nothing about which signals were driving a spike, how individual categories compared day-to-day, or whether a new high was broad-based or concentrated in one dimension. v14.1 replaces the single line with six switchable chart views — each designed to answer a different question about what the data is doing.
Six views, one panel
| View | Best for | Data range |
|---|---|---|
| Line | Long-run meter trajectory, version marker overlays | 30 / 60 / 90 days |
| Area | Same as Line with gradient fill emphasising magnitude | 30 / 60 / 90 days |
| Stacked | % share of total stress per signal — who is dominating | 30 / 60 / 90 days |
| Step | All 8 individual signal lines on the same 0–10 axis — 7-day breakdown | Last 7 days |
| Candle | Daily open/high/low/close per signal — volatility and direction | 30 / 60 / 90 days |
| Heatmap | Signal × day grid — spot which category spiked on which day at a glance | 30 / 60 / 90 days |
Stacked: 100% normalised share
The Stacked view started as a traditional cumulative area chart. The y-axis climbed to 36+ (the sum of all eight scores), making the scale meaningless and the lines visually correlated because every band rode on top of everything below it. The fix was simple but transformative: switch to stackOffset="expand" so the chart always fills 100% of the height. The y-axis now shows 0–100%, and the thickness of each band is that signal's proportional share of total stress at any given hour. When Geo dominates, it takes the majority of the vertical space. When a cyber incident hits, you see the Cyber band visibly widen. The chart answers a question the raw score cannot: not "how high is the meter?" but "what is driving it?"
Step: 7-day multi-signal breakdown
The Step view was originally a 24-hour single-line chart, which produced seven nearly flat data points — visually useless. The redesign draws all eight signal categories as independent step-after lines on a shared 0–10 axis, with the blended total as a bold accent line on top. The window is the last 7 days: dense enough to show intraday jumps without collapsing into noise. The step interpolation is the right choice here because signal scores do not drift continuously — they jump when new articles arrive and hold until the next scoring cycle.
Candle: daily OHLC per signal
The Candle view buckets hourly data into calendar days and computes open, high, low, and close for each signal independently. Each day renders a cluster of eight mini candlesticks side-by-side — one per signal in its category colour. A filled body means the signal closed higher than it opened (escalating day); a hollow outline means it closed lower (de-escalating). The wick shows the full intraday range. This makes volatility — not just direction — visible: a tall wick with a small body means the signal spiked and recovered within the same day.
Heatmap: signal × day grid
The Heatmap went from a single row of day cells coloured by total meter score to a full 9-row grid: Total at the top, then one row per signal. Each cell is the daily average for that signal, coloured by severity band (Calm → Guarded → Elevated → High → Critical) with opacity scaled by magnitude. Hovering a cell shows the signal name, date, and exact value. This view is the fastest way to answer "what happened on May 23rd?" — scan the column and see which rows light up.
Bug fix: AI score was never written to Firestore
v14 added the AI signal to the scoring engine and the blend formula, but a silent omission in the Cloud Function meant aiScore was computed by calculateMeter() and then dropped — never written to the meters/live document. All four scoring paths were affected: the full poll cycle (pollAllFeeds), the article-triggered rescore (onArticleCreated), and the two market and space weather propagation writes (pollMarketSignals, pollSpaceWeather). Because hourlySnapshots copies meters/live verbatim, the AI row was blank in every chart view. The fix adds aiScore to all four set() calls. Snapshots written before May 29th will not have historical AI data, but all future snapshots will.
View selection persists across sessions
Both the meter card (Arc / Gauge / Bars / Thermo) and the trend panel (Line / Area / Stacked / Step / Candle / Heatmap) store the active view in localStorage. Refreshing the page, navigating away, or reopening the app on the same device returns you to the last view you had open.