Themes - ram290476/investor-dashboard GitHub Wiki

Themes

Application color themes for the Investor Dashboard. Implementation status has been refreshed for #64 / PR #65 and #71. The earlier design proposals and source-issue notes below are retained as historical context; the current implementation and Done vs open sections supersede them.

Screenshots are from docs/design-roadmap/ on main. The theme renders show design sample data recolored at render time; they are not production theme support.

Related: Dashboard features, Continuous deployment, Deployment issues, Dashboard roadmap.

Overview

Goal (#64): one consistent, accessible visual theme system across the whole dashboard, selectable per user in Account settings → Theme & display, with matching branding on the Cognito-hosted login.

  • Six themes, reusing the existing designs in docs/design-roadmap/themes/themes.json rather than inventing new palettes: Industrial Dark (default), Terminal Amber, Charting Navy, Clean Light, Colorblind-safe High Contrast, Midnight Slate.
  • Industrial Dark stays the application default. Each theme is "inspired by" a familiar financial-dashboard look; none copies a brand palette.
  • The up/down palette stays a separate preference (green/red, red/green, blue/orange) with defined precedence over the theme's positive/negative colors.
  • Theming changes colors only. It does not change financial calculations, data collection, chart behavior, typography, density, or layout.
  • Cognito login gets a fixed dark brand that matches Industrial Dark. A signed-in user's chosen theme is not carried into Cognito.

Today: PR #65 implemented all six persistent application themes, the selector, semantic panel/chart colors and classic Cognito branding. Managed Login v2 remains opt-in. Issue #71 extends categorical colors to every overlay and tokenizes light/dark shadows and the backdrop. The original design screenshots below are still design references, not production sessions. Issue #76 / PR #77 reorganizes chart controls consistently across all six themes and renames the header to Signed In (the Account menu/settings names do not change). Its four Industrial Dark/Clean Light desktop/mobile captures use captured live market data with mock authentication/preferences. Older Account labels and always-visible unavailable Fundamentals chips below are historical, superseded references.

Available themes

All values come from themes.json, the token source of truth. render-themes.js generates the per-theme <id>.css (:root[data-theme="<id>"] { --token: … }, plus derived *-subtle tints) and contrast.md.

Token set (23 + 4 derived): bg, surface, surface-2, border, border-strong, grid, text, text-2, text-muted, text-faint, accent, accent-text, up, down, neutral, warning, series-1…6, heat, plus derived accent-subtle, up-subtle, down-subtle, warning-subtle.

Theme Id Inspired by bg · surface · text · accent · up / down
Industrial Dark (default) industrial-dark Current dashboard design #0B0E13 · #12161D · #E6EAF0 · #6CA6FF · #3FB950 / #F85149
Terminal Amber terminal-amber Bloomberg-style market terminals #000000 · #080706 · #FFA028 · #FFD23F · #22D65F / #FF4136
Charting Navy charting-navy Dark mode of popular charting platforms #0A1120 · #111A2E · #D9E1EE · #4C8DFF · #26A69A / #EF5350
Clean Light clean-light Mainstream finance portals (light) #F3F5F8 · #FFFFFF · #111827 · #1A62D6 · #0B7A3B / #C62828
Colorblind-safe High Contrast colorblind-hc Accessibility-first dashboards (Okabe-Ito) #000000 · #0B0D10 · #FFFFFF · #FFFFFF · #56B4E9 / #E69F00
Midnight Slate midnight-slate Modern fintech / crypto dashboards #0F172A · #162036 · #F1F5F9 · #818CF8 · #34D399 / #FB7185

Every body-text pair is at least 4.5:1 (WCAG AA) on its surface, and chart series are at least 3:1. The full tables are in contrast.md.

Historical pre-#64 palette (for comparison)

apps/web/styles.css defines one dark palette on :root. Its names and several values differ from the Industrial Dark design tokens:

Production variable Value Closest design token Design value
--page #0b0e13 bg #0B0E13
--surface #12161d surface #12161D
--surface-raised #171d26 surface-2 #1A2029
--line #27303c border #2A323E
--text #e6eaf0 text #E6EAF0
--muted #9ba6b5 text-muted #9BA6B5
--subtle #728094 text-faint #8B96A6
--blue #7db4ff accent #6CA6FF
--green #48c79b up #3FB950
--red #f07a7a down #F85149
--amber #f2b24e warning #E3B341

Up/down overrides today: [data-palette="blue-orange"] sets --green: #7db4ff; --red: #f2a33a, and [data-palette="red-green"] swaps green and red.

Theme palettes and screenshots

Token values from themes.json; derived tints from the generated <id>.css. Screenshots are 1440px renders of design/ai-generated/Main.dc.html with sample data.

Industrial Dark (default) (industrial-dark)

Inspired by: The current Investor Dashboard design: cool charcoal panels, blue accent, GitHub-style green/red. · color-scheme: dark

Written-as-tokens version of the current design. One deliberate change from the mockup: the driver heat-chip color moves from #8B9CFF to #5B6BD6 so white text on the strongest chip goes from 2.9:1 to 5.0:1.

Contrast (from contrast.md / #31): text/bg 16.0:1 · muted/surface 7.4:1 · faint/surface 6.1:1 · up/surface 7.1:1 · down/surface 5.4:1 · text on strongest heat chip 5.0:1

Group Tokens
Surfaces bg #0B0E13 · surface #12161D · surface-2 #1A2029 · border #2A323E · border-strong #4A5361 · grid #222A35
Text text #E6EAF0 · text-2 #C5CDD8 · text-muted #9BA6B5 · text-faint #8B96A6
Accent & semantic accent #6CA6FF · accent-text #8EBBFF · up #3FB950 · down #F85149 · neutral #9BA6B5 · warning #E3B341 · heat #5B6BD6
Chart series series-1 #6CA6FF · series-2 #F2A33A · series-3 #EC7BA6 · series-4 #B392F0 · series-5 #39C5B0 · series-6 #E3C05C
Derived tints (<id>.css) accent-subtle #24334A · up-subtle #192E25 · down-subtle #341F24 · warning-subtle #3C3524

Industrial Dark (default): full desktop (design sample data)

Terminal Amber (terminal-amber)

Inspired by: Classic professional market terminals (Bloomberg-style): pure black, amber/orange text, bright green/red ticks, dense high-signal look. · color-scheme: dark

Dense and high-signal: amber body text, yellow accent and price line. Heat chips use dark amber so amber text stays readable.

Contrast (from contrast.md / #31): text/bg 10.3:1 · muted/surface 6.9:1 · faint/surface 5.4:1 · up/surface 10.4:1 · down/surface 5.8:1 · text on strongest heat chip 4.7:1

Group Tokens
Surfaces bg #000000 · surface #080706 · surface-2 #15110A · border #3D2E14 · border-strong #6E5222 · grid #261D0E
Text text #FFA028 · text-2 #F2C46D · text-muted #CC8A2E · text-faint #B07A30
Accent & semantic accent #FFD23F · accent-text #FFE08A · up #22D65F · down #FF4136 · neutral #CC8A2E · warning #FFE600 · heat #7A4A00
Chart series series-1 #FFD23F · series-2 #FF6F00 · series-3 #FF5FA2 · series-4 #B48CFF · series-5 #00D8FF · series-6 #F5F5F5
Derived tints (<id>.css) accent-subtle #393011 · up-subtle #0C2613 · down-subtle #2D100D · warning-subtle #393405

Terminal Amber: full desktop (design sample data)

Charting Navy (charting-navy)

Inspired by: Dark mode of popular web charting platforms: deep navy canvas, teal-green up / coral-red down candles, vivid blue accent. · color-scheme: dark

Deep navy canvas, teal-green up / coral-red down, blue accent.

Contrast (from contrast.md / #31): text/bg 14.3:1 · muted/surface 6.6:1 · faint/surface 5.5:1 · up/surface 5.8:1 · down/surface 5.0:1 · text on strongest heat chip 4.6:1

Group Tokens
Surfaces bg #0A1120 · surface #111A2E · surface-2 #18233A · border #27344F · border-strong #3E4E70 · grid #1C2842
Text text #D9E1EE · text-2 #B6C1D4 · text-muted #93A0B8 · text-faint #8592AB
Accent & semantic accent #4C8DFF · accent-text #8DB6FF · up #26A69A · down #EF5350 · neutral #93A0B8 · warning #F7B32B · heat #3D6FD6
Chart series series-1 #4C8DFF · series-2 #FF9800 · series-3 #F06292 · series-4 #A98BF5 · series-5 #26C6DA · series-6 #FFD54F
Derived tints (<id>.css) accent-subtle #1D3158 · up-subtle #142F3E · down-subtle #322333 · warning-subtle #3F392D

Charting Navy: full desktop (design sample data)

Clean Light (clean-light)

Inspired by: Mainstream finance portals and brokerage web apps in light mode: white cards on soft gray, dark ink, restrained green/red. · color-scheme: light

The only light theme. Darker green/red so up/down text passes AA on white. Per #31 it is what Auto would pick for prefers-color-scheme: light.

Contrast (from contrast.md / #31): text/bg 16.2:1 · muted/surface 7.6:1 · faint/surface 5.7:1 · up/surface 5.4:1 · down/surface 5.6:1 · text on strongest heat chip 8.8:1

Group Tokens
Surfaces bg #F3F5F8 · surface #FFFFFF · surface-2 #F1F3F6 · border #D9DEE5 · border-strong #AAB3C0 · grid #E7EBF0
Text text #111827 · text-2 #2F3A4A · text-muted #4B5563 · text-faint #5D6877
Accent & semantic accent #1A62D6 · accent-text #1554BD · up #0B7A3B · down #C62828 · neutral #5D6877 · warning #A15C00 · heat #7FA6F0
Chart series series-1 #1A62D6 · series-2 #E06C00 · series-3 #C2185B · series-4 #7047D1 · series-5 #00897B · series-6 #A77B00
Derived tints (<id>.css) accent-subtle #D1E0F7 · up-subtle #DAEBE2 · down-subtle #F6DFDF · warning-subtle #ECDECC

Clean Light: full desktop (design sample data)

Colorblind-safe High Contrast (colorblind-hc)

Inspired by: Accessibility-first dashboards: near-black canvas, white text, stronger borders, Okabe-Ito blue/orange up/down so direction never relies on red vs green. · color-scheme: dark

Okabe-Ito sky-blue up / orange down so direction never depends on red vs green; white price line and accent; every text pair is AAA. Per #31 it defaults to the blue/orange up/down pair (picking green/red is allowed but should show a hint).

Contrast (from contrast.md / #31): text/bg 21.0:1 · muted/surface 13.3:1 · faint/surface 10.8:1 · up/surface 8.4:1 · down/surface 8.6:1 · text on strongest heat chip 7.0:1

Group Tokens
Surfaces bg #000000 · surface #0B0D10 · surface-2 #171B21 · border #5A6370 · border-strong #9AA4B1 · grid #2C333D
Text text #FFFFFF · text-2 #EEF1F5 · text-muted #D0D6DE · text-faint #BAC2CD
Accent & semantic accent #FFFFFF · accent-text #FFFFFF · up #56B4E9 · down #E69F00 · neutral #BAC2CD · warning #F0E442 · heat #2F6FA8
Chart series series-1 #FFFFFF · series-2 #F0E442 · series-3 #CC79A7 · series-4 #B9A3FF · series-5 #00B98A · series-6 #D55E00
Derived tints (<id>.css) accent-subtle #3C3D40 · up-subtle #162631 · down-subtle #2C230E · warning-subtle #39381A

Colorblind-safe High Contrast: full desktop (design sample data)

Midnight Slate (midnight-slate)

Inspired by: Modern fintech / crypto dashboards: blue-slate surfaces a step lighter than the default, indigo accent, mint up / rose down. · color-scheme: dark

Blue-slate surfaces a step lighter than the default, indigo accent, mint up / rose down — a softer dark option.

Contrast (from contrast.md / #31): text/bg 16.3:1 · muted/surface 7.6:1 · faint/surface 6.3:1 · up/surface 8.4:1 · down/surface 6.0:1 · text on strongest heat chip 5.2:1

Group Tokens
Surfaces bg #0F172A · surface #162036 · surface-2 #1E293B · border #334155 · border-strong #52617A · grid #243049
Text text #F1F5F9 · text-2 #CBD5E1 · text-muted #A7B3C5 · text-faint #94A3B8
Accent & semantic accent #818CF8 · accent-text #A5B4FC · up #34D399 · down #FB7185 · neutral #A7B3C5 · warning #FBBF24 · heat #6366F1
Chart series series-1 #818CF8 · series-2 #F59E0B · series-3 #F472B6 · series-4 #C084FC · series-5 #22D3EE · series-6 #FACC15
Derived tints (<id>.css) accent-subtle #2B365D · up-subtle #1B3B45 · down-subtle #382C42 · warning-subtle #444032

Midnight Slate: full desktop (design sample data)

Where the theme picker lives

Desktop: Account pop-up → Theme & display

The header Account button opens the account menu. Its Theme & display item opens the tabbed Account settings dialog on the #settings/theme tab. The dialog was built in #36 / PR #49. Its tabs are My tickers · Theme & display · Profile & time zone · Data refresh.

Account menu, 1440px (current apps/web)

Account menu today. The Theme & display hint reads "colors · chart period".

Shipped today (current apps/web): Up / down colors (Green / Red · Red / Green · Blue / Orange) with a ▲/▼ preview chip pair, and Default chart period (1D … 5Y). There is no theme selector yet.

Theme & display tab, 1440px (current apps/web)

Planned (#64, with detail from #31 and #36):

  • Picker: a full theme selector with previews in this tab. #31 and #36 describe a radio group of cards. Each card has a live swatch: a bg/surface split, a text line, the accent, an up/down pair, and a 6-dot series strip.
  • Applying a theme: the change applies immediately to the whole page behind the dialog, via data-theme on the document root. #36 also wants a small in-tab preview (mini price line, up/down chips, status dot).
  • Up/down colors: stay a separate control in the same tab.
  • Account menu label: #36 planned the hint "theme · colors" once the picker ships.

Mobile

  • Dialog today: the Account settings dialog already fits at 390px (see below). There is no 390px capture of the Theme & display tab yet.
  • Mobile picker: #31 and #36 made a mobile theme picker and a bottom-sheet dialog a follow-up tied to the four-tab glance view, #25. Tokens are shared, so the mobile view consumes the same themes.
  • #25 and the account button: #25 now routes Data refresh through the account button rather than a separate card. It says nothing about themes.
  • Verification: #64's acceptance criteria require desktop and mobile layouts, keyboard focus, and loading/empty/error states to be verified for themes.

My tickers tab at 390px, showing the dialog's mobile layout (current apps/web)

Behavior

Topic #64 (primary) Earlier spec (#31 / #36)
Default Industrial Dark is the application default. Accounts with no theme field use Industrial Dark. Auto (match system) is the default for new users.
System preference Not mentioned. auto follows prefers-color-scheme (light → Clean Light, dark → Industrial Dark) and reacts to OS changes live. Under prefers-contrast: more, suggest Colorblind-safe HC without switching automatically.
Persistence Save a validated theme ID through the existing /prefs path, with backend schema, defaults, and tests. display.theme in /prefs, allow-listed: auto, industrial-dark, terminal-amber, charting-navy, clean-light, colorblind-hc, midnight-slate.
Invalid IDs Reject them through the existing preference validation and error handling. Reject them on write; unknown stored values fall back to auto on read.
Apply / failure Apply immediately and keep the existing save-failure notice and recovery. Apply instantly, save in the background, and roll back with an inline error if the save fails.
Up/down palette Separate preference with clearly defined precedence over the theme's positive/negative colors. Don't confuse it with categorical overlay colors. updown_palette overrides the theme's pair. Add a theme value (use the theme's own pair) as the default for new users. Per-theme AA shades, e.g. darker blue/orange on Clean Light.
Load flash Optional pre-login cache that stores only a validated, non-sensitive theme ID, never auth material. Inline <head> script sets data-theme / data-palette from a localStorage cache (or the system preference) before first paint. /prefs wins after sign-in.
Survives Redraws, ticker/period changes, refreshes, and later authenticated visits. Reloads and sign-in on another browser (server-side pref).
Non-color cues Meaning is also conveyed by text and signs. Keep ▲/▼ glyphs and +/− signs. The picker is a keyboard-navigable radio group with text labels.

Implementation notes

How colors are applied in code today

File What it does today Remaining limitation
apps/web/styles.css, theme.js Semantic properties are applied on <html>; panel surfaces, text, focus, shadows, backdrop and SVG styling follow the theme. Direction palettes remain separate overrides. Static fallback before JavaScript is Industrial Dark.
apps/web/index.html Metadata is updated by applyTheme() after configuration/preferences load. No optional pre-paint theme cache yet.
apps/web/app.js renderDashboard() applies the document theme. Paths, chips and legend swatches all use overlayColor(); fundamentals are release-date step lines. Financial missing values remain missing regardless of theme.
apps/web/chart-overlays.js, theme.js 33 stable overlay slots (including news sentiment from #74) use the six theme series tokens; later cycles blend adjacent tokens in 13% increments. Tests require distinct colors and at least 3:1 chart contrast in every theme. Five simultaneous overlays remain the limit.
apps/web/account-settings.js, settings-model.js Six-theme selector with previews, immediate apply, persisted display.theme; desktop and mobile supported. None for theme selection.
services/data-jobs/src/functions/prefs_api/prefs_api.py Allowlists theme IDs, persists them and defaults absent legacy values to Industrial Dark. Invalid values are rejected explicitly.
infra/terraform/modules/site_auth/main.tf Classic Hosted UI logo/CSS branding; optional Managed Login v2. Real login/MFA/reset flows need user verification; v2 is not enabled automatically.
docs/design-roadmap/themes/ themes.json, <id>.css, mockup-color-roles.json, render-themes.js, contrast.md, six *-desktop-full.png. Design references only. render-themes.js recolors the mockup DOM at render time; per its README, this is not how apps/web should implement themes.

The wt-themes worktree (branch design/color-themes) holds only the design commit e94b8e9 from PR #30. Its docs/design-roadmap/themes/ content is identical to main, and it contains no implementation work.

Planned approach (#64)

Order of work (suggested sequence in #64):

  1. Normalize the production semantic tokens and establish Industrial Dark as the unchanged baseline.
  2. Add the theme definitions, selector, persistence, and validation.
  3. Wire all panels and SVG/chart-series styling, keeping the accessibility palette overrides.
  4. Configure and verify Cognito branding with safe deployment and rollback sequencing.
  5. Add regression coverage and desktop/mobile visual checks, and update directly related docs.

Token roles to map (#64): page/surface backgrounds, raised surfaces, borders, primary/secondary/muted text, accents, positive/negative/neutral, warning/error, chart grids/axes, and categorical series.

Detail from #31 (earlier spec):

  • Token names: use the themes.json names as CSS variables, with one :root[data-theme="<id>"] block per theme.
  • No hard-coded hex in components. Chart code reads its colors via getComputedStyle(document.documentElement) and re-reads them when the theme changes.
  • Tints come from color-mix(in srgb, var(--accent) 20%, var(--surface)).
  • Catalyst marker categories (Rates, Inflation, Tariffs, Geopolitics, Robotaxi, Filings) map to --series-1…6.
  • Driver heat chips use --heat with alpha.
  • Status dots: ok → --up, retry → --warning.
  • CI contrast check: a script over themes.json that fails when contrast drops below the thresholds.

Surfaces to cover (#64): price chart and overlays; volume and under-chart lanes; macro trends and drivers; news and sentiment; filings and events; company fundamentals; government contracts, watchlist, header/footer, buttons, inputs, account settings, notifications, data freshness, and sign-in/loading gates.

Cognito login branding (#64 Scope B):

  • Approach: Managed Login v2 branding through its editor or API, keeping the current public-client authorization-code + PKCE flow. It is a fixed dark brand, not synced with the user's chosen theme.
  • Starting colors (from the current app): page #0B0E13, surface #12161D, raised/input #171D26, border #27303C, text #E6EAF0, secondary text #9BA6B5, accent #7DB4FF. #64 says to align these with the final Industrial Dark tokens.
  • Screens to cover: login, password reset, first-login password change, error, and code/MFA prompts.
  • Must not change: callbacks, scopes, client secret, tokens, invite-only behavior, or the user pool and app client (no recreation).
  • Management: use Terraform where the pinned provider supports it. Otherwise use a documented, repeatable step with failure handling and rollback.
  • Fallback: if v2 can't be used, use the classic Hosted UI (logo plus restricted CSS only) and document the limitation.

Done vs open

Item Status Source
Six theme designs, tokens, generated CSS, contrast report, screenshots Done (design only) PR #30, merged Oct 6, 2026
Account settings dialog with a Theme & display tab (up/down colors, default chart period) Done #36 / PR #49, merged Oct 7, 2026
Up/down palette preference (updown_palette) Done (pre-existing) prefs_api.py, styles.css
Production semantic tokens / data-theme on <html> Done PR #65 / #64
Theme selector with previews, display.theme persistence, validation, tests Done PR #65 / #64
Token-based chart, overlay, lane, and panel colors; light-theme-safe backgrounds and meta tags Done, overlay palette and shadow extension in #71 #64 and #71
No-flash pre-paint theme cache Open (optional in #64) #64, #31
Cognito login branding Classic branding done; v2 opt-in; real authentication flows unverified PR #65 / #64
Mobile theme picker parity Done PR #65 / #64
WCAG application validation and screenshots Unit contrast checks and local app browser checks done; real branded-login flows still unverified #64 and #71

Source issues

Issue State How it relates
#64 Unify dashboard themes and Cognito login branding Open, no linked PRs (opened Oct 8, 2026) Primary spec. Full app theme system (six themes, Industrial Dark default, selector in Account settings → Theme & display, persistence, token-based charts and panels) plus Cognito Managed Login branding. All acceptance criteria are unchecked.
#19 Catalyst markers on chart and catalyst calendar drawer Open, no linked PRs Not a theming issue. #31 maps its catalyst marker categories to --series-1…6 so markers, chips, and the calendar drawer recolor consistently. Nothing is built yet.
#21 Company theme panels (TSLA robotaxi / fundamentals deep-dive, SPCX ops & contracts) Open, no linked PRs "Theme" here means investment/company themes (content panels), not color themes. It is related only because #64 requires company fundamentals and panels to be theme-aware.
#23 Drawers: all driver trends, correlation drift, about this data, schedule/data refresh UX Open, no closing PRs (cross-referenced by #36 / PR #49 and #51–#52) Lists "Display prefs (palette / timezone)" as already shipped. Its schedule/data-refresh part became the Account settings Data refresh tab (#52 / PR #54). Future drawers must use theme tokens (#31 lists drawers in its apply-everywhere criterion).
#25 Mobile four-tab glance view Open, no closing PRs (related #51–#53 closed) Mobile token consumer. #31 and #36 defer the mobile theme picker and the bottom-sheet dialog to this issue. Its Oct 7 addition routes Data refresh through the account button / Account pop-up on mobile; a comment points to #53. It does not mention themes directly.

Additional context (not in the requested set, but directly theme-related):

  • #31 Color theme switcher — closed. The original detailed theme spec: token names, auto default, updown_palette: theme, no-flash script, CI contrast check. It was closed on Oct 7, 2026 6:29 PM PT by project-board automation, not by a PR. None of its acceptance criteria are checked, and only the design PR #30 landed.
  • #36 Account settings pop-up — closed via PR #49. Defines the Theme & display tab, the #settings/theme deep link, live preview, and the theme cache. Its theme-picker criterion was not delivered; the shipped tab has up/down colors and chart period only.

Open questions and next steps

  1. Default: should it be Auto/system or Industrial Dark? #31 and #36 specify auto (follow prefers-color-scheme) as the default for new users. #64 makes Industrial Dark the default and doesn't mention system preference.
  2. Invalid stored values: #31 says reads fall back to auto. #64 says to reject invalid IDs through validation, but doesn't define read-time behavior for already-stored bad values.
  3. Up/down precedence: does updown_palette gain a theme value (the theme's own pair) as the new default, as in #31? Or does the current green-red default keep overriding every theme? #64 only asks for "clearly defined precedence".
  4. Industrial Dark baseline: production and the design tokens differ (--green #48c79b vs up #3FB950, --red #f07a7a vs down #F85149, --blue #7db4ff vs accent #6CA6FF, --line #27303c vs border #2A323E, --subtle #728094 vs text-faint #8B96A6). #64 says to keep Industrial Dark "unchanged" and also to "align with the production Industrial Dark token set". Decide which values win; the Cognito colors depend on this.
  5. Token naming: keep production names (--page, --line, --green…) or adopt the themes.json names (--bg, --border, --up…)? #31 says adopt; #64 says map to "a consistent production naming scheme".
  6. Overlay colors per theme: overlays have 30 fixed colors but themes define only 6 series-* tokens. Decide how overlay identities stay distinct in every theme, and fix the current duplicate colors (SOFR/VIX, 10Y real yield/dollar index).
  7. Mobile: ship the theme picker on mobile now or after #25? Capture Theme & display at 390px for verification.
  8. Cognito: inspect the deployed domain's managed-login version and existing branding, and check whether the pinned AWS provider supports managed-login branding resources. Then document deploy and rollback before changing the live domain.
  9. Contrast in production: run the WCAG AA check (4.5:1 text, 3:1 non-text) against production rendering, not just the design renders, and add the scripted CI check from #31.
⚠️ **GitHub.com Fallback** ⚠️