Server no longer holds portfolios. Holdings live in the browser (localStorage); the server publishes an anonymous ticker_universe and a gzipped /api/universe payload identical for every authenticated user, so access patterns can't betray which tickers a user holds. AI commentary is generated ephemerally from the browser-supplied pie and the cost ledger row records no positions. Migrations 0009-0011 added the universe table and dropped positions / portfolio_snapshots / portfolios. Authentication is now e-mail OTP only. Migration 0010 dropped password_hash and email_verified (every active session is by construction proof of email control). The /signup endpoint is gone; signup and login share a single email-entry page. Email rendering is HTML+plain-text multipart with a shared brand palette (app/branding.py) asserted in sync with the CSS by a drift-detection test. LLM provider defaults to DeepSeek-direct (cheaper, api.deepseek.com) with OpenRouter as automatic fallback if DeepSeek fails. ai_log_job and indicator_summary_job now iterate the two tones (NOVICE, INTERMEDIATE) per cycle so the dashboard's tone toggle is instant; PROMPT_VERSION bumped to 6 with an educational anti-TA / anti-gambling stance baked into _CORE. NOVICE mode renders a curated glossary inline (CBOE VIX, yield curve, HY OAS, etc.) with JS-positioned tooltips that survive viewport edges and sticky bars. Model name and tokens hidden from the user UI; still recorded in StrategicLog.model and AICall for admin. Layout adds a sticky top nav, a sticky bottom markets bar (one chip per exchange with status LED + headline index + 1d change), and Phase H feedback reporting is queued in tasks/todo.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
1.7 KiB
Python
55 lines
1.7 KiB
Python
"""Cassandra brand palette — single source of truth.
|
|
|
|
Both the website's CSS (`app/static/css/cassandra.css`) and the email
|
|
templates (`app/services/email_service.py`) draw from these dicts. CSS
|
|
hand-authors the values in its `:root` / `[data-theme="light"]` blocks;
|
|
a drift-detection test (`tests/test_branding_consistency.py`) asserts
|
|
that what's in this module matches what's in the CSS, so updating the
|
|
brand in one place without the other fails CI.
|
|
|
|
The light theme is the *default* in emails — mail clients can't read
|
|
`localStorage`, so we can't replicate the dashboard's user-toggled
|
|
theme. Clients that honour `prefers-color-scheme` get the dark palette
|
|
via media query.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
|
|
DARK: dict[str, str] = {
|
|
"bg": "#0a0e14",
|
|
"surface": "#11151c",
|
|
"surface-2": "#161b25",
|
|
"border": "#2a3142",
|
|
"text": "#d4dae8",
|
|
"muted": "#8189a1",
|
|
"dim": "#565f89",
|
|
"accent": "#00d9ff",
|
|
"positive": "#50fa7b",
|
|
"negative": "#ff5b5b",
|
|
"alert": "#ff8a4a",
|
|
"warning": "#f1fa8c",
|
|
}
|
|
|
|
LIGHT: dict[str, str] = {
|
|
"bg": "#f5f3ec",
|
|
"surface": "#ffffff",
|
|
"surface-2": "#efece3",
|
|
"border": "#d6d3cb",
|
|
"text": "#1c1f25",
|
|
"muted": "#545b69",
|
|
"dim": "#8a8f9a",
|
|
"accent": "#0e7490",
|
|
"positive": "#166534",
|
|
"negative": "#b91c1c",
|
|
"alert": "#c2410c",
|
|
"warning": "#a16207",
|
|
}
|
|
|
|
FONT_MONO = (
|
|
"'JetBrains Mono', 'IBM Plex Mono', 'Fira Code', "
|
|
"ui-monospace, Menlo, Consolas, monospace"
|
|
)
|
|
FONT_SANS = (
|
|
"-apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto, "
|
|
"'Helvetica Neue', system-ui, sans-serif"
|
|
)
|