aboutsummaryrefslogtreecommitdiff
path: root/app.py
diff options
context:
space:
mode:
authorTyler <tyler@tylerhoang.xyz>2026-05-13 22:39:50 -0700
committerTyler <tyler@tylerhoang.xyz>2026-05-13 22:39:50 -0700
commita457bea95358825e55dbc7f48d57183004121109 (patch)
treeeafafbfa017228bc1efd14a0c63f150576ace9f2 /app.py
parent6f1c3a6b73572b3ccc5281dba45de3bba5528f5f (diff)
Apply Prism design system — brass/ink palette, EB Garamond + IBM Plex typography
Implements the design kit: champagne brass accent (#C2AA7A), deep midnight backgrounds, EB Garamond italic headings, IBM Plex Sans/Mono body and numbers, terminal-density KPI cards, restyled sidebar brand mark, flat pill tabs, and a global Plotly theme so all charts inherit the dark palette automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'app.py')
-rw-r--r--app.py658
1 files changed, 566 insertions, 92 deletions
diff --git a/app.py b/app.py
index d86475b..d5e082a 100644
--- a/app.py
+++ b/app.py
@@ -11,50 +11,414 @@ st.set_page_config(
initial_sidebar_state="expanded",
)
-# ── Global CSS ────────────────────────────────────────────────────────────────
+# ── Design system CSS ─────────────────────────────────────────────────────────
st.markdown("""
<style>
- /* Tighten metric cards */
- [data-testid="metric-container"] {
- padding: 0.4rem 0.6rem !important;
- }
- [data-testid="stMetricLabel"] {
- font-size: 0.72rem !important;
- font-weight: 500;
- color: #9aa0b0 !important;
- letter-spacing: 0.03em;
- text-transform: uppercase;
- }
- [data-testid="stMetricValue"] {
- font-size: 1.1rem !important;
- font-weight: 600;
- letter-spacing: -0.01em;
- }
- [data-testid="stMetricDelta"] svg { width: 0.75rem; height: 0.75rem; }
- [data-testid="stMetricDelta"] > div { font-size: 0.78rem !important; }
+@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500;1,600&family=IBM+Plex+Mono:wght@300;400;500;600&family=IBM+Plex+Sans:wght@300;400;500;600;700&display=swap');
- /* Tighter headings */
- h1 { font-size: 1.5rem !important; font-weight: 700 !important; }
- h2 { font-size: 1.25rem !important; font-weight: 600 !important; }
- h3 { font-size: 1.05rem !important; font-weight: 600 !important; }
+/* ── Tokens ─────────────────────────────────────────────────────────────── */
+:root {
+ --ink-0: #0B0E13;
+ --ink-1: #11151C;
+ --ink-2: #181D26;
+ --ink-3: #222934;
+ --ink-4: #2C3340;
+ --line-1: #232934;
+ --line-2: #2E3645;
+ --line-3: #3D4658;
+ --fg-1: #F2ECDC;
+ --fg-2: #C7C0AE;
+ --fg-3: #8E8676;
+ --fg-4: #5E5849;
+ --brass: #C2AA7A;
+ --brass-bright: #DCC79E;
+ --brass-deep: #8F7A50;
+ --brass-ink: #17120A;
+ --positive: #4F8C5E;
+ --positive-bg: #15241A;
+ --negative: #B5494B;
+ --negative-bg: #2A1517;
+ --warning: #C49545;
+ --font-display: 'EB Garamond', Georgia, serif;
+ --font-sans: 'IBM Plex Sans', 'Helvetica Neue', system-ui, sans-serif;
+ --font-mono: 'IBM Plex Mono', 'SF Mono', Menlo, monospace;
+}
- /* Tighter sidebar text */
- [data-testid="stSidebar"] .stCaption p {
- font-size: 0.78rem !important;
- line-height: 1.5 !important;
- }
+/* ── Base ───────────────────────────────────────────────────────────────── */
+html, body, [class*="css"] {
+ font-family: var(--font-sans) !important;
+ -webkit-font-smoothing: antialiased;
+}
- /* Slim dividers */
- hr { margin: 0.5rem 0 !important; }
+.stApp { background: var(--ink-0) !important; }
- /* Tab labels */
- [data-testid="stTab"] p { font-size: 0.85rem !important; font-weight: 500; }
+/* ── Sidebar shell ──────────────────────────────────────────────────────── */
+[data-testid="stSidebar"] {
+ background: var(--ink-1) !important;
+ border-right: 1px solid var(--line-1) !important;
+}
- /* Top padding — enough to clear Streamlit's sticky toolbar */
- .block-container { padding-top: 3.5rem !important; }
+[data-testid="stSidebar"] > div:first-child {
+ padding-top: 0 !important;
+}
+
+[data-testid="stSidebarCollapseButton"] { opacity: 0.4; }
+
+/* ── Sidebar text ───────────────────────────────────────────────────────── */
+[data-testid="stSidebar"] p {
+ font-family: var(--font-sans) !important;
+ font-size: 0.8125rem !important;
+ color: var(--fg-3) !important;
+ line-height: 1.45 !important;
+}
+
+[data-testid="stSidebar"] .stCaption p {
+ font-size: 0.75rem !important;
+ color: var(--fg-3) !important;
+}
+
+[data-testid="stSidebar"] strong { color: var(--fg-1) !important; }
+
+[data-testid="stSidebar"] hr {
+ border: none !important;
+ border-top: 1px solid var(--line-1) !important;
+ margin: 0.375rem 0 !important;
+}
+
+[data-testid="stSidebar"] h3 {
+ font-family: var(--font-display) !important;
+ font-size: 1.125rem !important;
+ color: var(--fg-1) !important;
+ font-weight: 500 !important;
+ letter-spacing: -0.01em !important;
+ margin: 0 !important;
+}
+
+/* ── Sidebar inputs ─────────────────────────────────────────────────────── */
+[data-testid="stSidebar"] .stTextInput input {
+ background: var(--ink-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+ font-family: var(--font-mono) !important;
+ font-size: 0.8125rem !important;
+ color: var(--fg-1) !important;
+}
+
+[data-testid="stSidebar"] .stTextInput input::placeholder { color: var(--fg-4) !important; }
+
+[data-testid="stSidebar"] .stTextInput input:focus {
+ border-color: var(--brass-deep) !important;
+ box-shadow: none !important;
+}
+
+[data-testid="stSidebar"] .stTextInput label {
+ font-family: var(--font-sans) !important;
+ font-size: 10px !important;
+ font-weight: 600 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.14em !important;
+ color: var(--fg-4) !important;
+}
+
+/* ── Selectbox ──────────────────────────────────────────────────────────── */
+[data-baseweb="select"] > div {
+ background: var(--ink-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+ font-family: var(--font-mono) !important;
+ font-size: 0.8125rem !important;
+ color: var(--fg-1) !important;
+}
+
+[data-baseweb="select"] [data-baseweb="menu"] {
+ background: var(--ink-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+}
+
+[data-baseweb="select"] li {
+ font-family: var(--font-mono) !important;
+ font-size: 0.8125rem !important;
+ color: var(--fg-2) !important;
+}
+
+/* ── Buttons ────────────────────────────────────────────────────────────── */
+button[kind="primary"],
+[data-testid="stFormSubmitButton"] button {
+ background: var(--brass) !important;
+ color: var(--brass-ink) !important;
+ border: none !important;
+ border-radius: 2px !important;
+ font-family: var(--font-sans) !important;
+ font-size: 0.75rem !important;
+ font-weight: 600 !important;
+ letter-spacing: 0.1em !important;
+ text-transform: uppercase !important;
+ transition: background 0.12s ease !important;
+}
+
+button[kind="primary"]:hover,
+[data-testid="stFormSubmitButton"] button:hover {
+ background: var(--brass-bright) !important;
+ border: none !important;
+}
+
+button[kind="secondary"] {
+ background: var(--ink-3) !important;
+ color: var(--fg-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+ font-family: var(--font-sans) !important;
+ font-size: 0.75rem !important;
+ font-weight: 500 !important;
+ letter-spacing: 0.06em !important;
+ text-transform: uppercase !important;
+}
+
+button[kind="secondary"]:hover {
+ background: var(--ink-4) !important;
+ border-color: var(--line-3) !important;
+ color: var(--fg-1) !important;
+}
+
+/* ── Main content area ──────────────────────────────────────────────────── */
+.block-container {
+ padding-top: 1.25rem !important;
+ padding-bottom: 3rem !important;
+}
+
+/* ── Tabs ───────────────────────────────────────────────────────────────── */
+.stTabs [data-baseweb="tab-list"] {
+ background: var(--ink-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+ padding: 2px !important;
+ gap: 2px !important;
+}
+
+.stTabs [data-baseweb="tab"] {
+ background: transparent !important;
+ border-radius: 2px !important;
+ font-family: var(--font-mono) !important;
+ font-size: 11px !important;
+ color: var(--fg-4) !important;
+ padding: 4px 10px !important;
+ border: none !important;
+ transition: color 0.1s, background 0.1s !important;
+}
+
+.stTabs [data-baseweb="tab"]:hover { color: var(--fg-2) !important; }
+
+.stTabs [aria-selected="true"] {
+ background: var(--ink-4) !important;
+ color: var(--fg-1) !important;
+}
+
+.stTabs [data-baseweb="tab-border"],
+.stTabs [data-baseweb="tab-highlight"] { display: none !important; }
+
+/* ── Metric cards ───────────────────────────────────────────────────────── */
+[data-testid="metric-container"] {
+ background: var(--ink-1) !important;
+ border: 1px solid var(--line-1) !important;
+ border-radius: 2px !important;
+ padding: 12px 16px !important;
+}
+
+[data-testid="stMetricLabel"] p {
+ font-family: var(--font-sans) !important;
+ font-size: 10px !important;
+ font-weight: 600 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.14em !important;
+ color: var(--fg-4) !important;
+}
+
+[data-testid="stMetricValue"] {
+ font-family: var(--font-mono) !important;
+ font-size: 1.125rem !important;
+ color: var(--fg-1) !important;
+ font-weight: 500 !important;
+ font-variant-numeric: tabular-nums !important;
+ letter-spacing: -0.01em !important;
+}
+
+[data-testid="stMetricDelta"] {
+ font-family: var(--font-mono) !important;
+ font-size: 0.75rem !important;
+ font-variant-numeric: tabular-nums !important;
+}
+
+[data-testid="stMetricDelta"] svg { width: 0.7rem; height: 0.7rem; }
+
+/* ── Expanders ──────────────────────────────────────────────────────────── */
+[data-testid="stExpander"] {
+ background: var(--ink-1) !important;
+ border: 1px solid var(--line-1) !important;
+ border-radius: 2px !important;
+}
+
+[data-testid="stExpander"] summary {
+ font-family: var(--font-display) !important;
+ font-size: 1.0625rem !important;
+ color: var(--fg-1) !important;
+ font-weight: 500 !important;
+ letter-spacing: -0.01em !important;
+}
+
+[data-testid="stExpander"] summary:hover { color: var(--brass-bright) !important; }
+
+/* ── Headings ───────────────────────────────────────────────────────────── */
+h1, h2, h3 {
+ font-family: var(--font-display) !important;
+ color: var(--fg-1) !important;
+ font-weight: 500 !important;
+ letter-spacing: -0.01em !important;
+}
+
+h1 { font-size: 1.875rem !important; line-height: 1.1 !important; }
+h2 { font-size: 1.5rem !important; line-height: 1.2 !important; }
+h3 { font-size: 1.125rem !important; line-height: 1.2 !important; }
+
+h4 {
+ font-family: var(--font-sans) !important;
+ font-size: 0.9375rem !important;
+ font-weight: 600 !important;
+ color: var(--fg-1) !important;
+ letter-spacing: -0.01em !important;
+}
+
+h5, h6 {
+ font-family: var(--font-sans) !important;
+ font-size: 10px !important;
+ font-weight: 600 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.14em !important;
+ color: var(--fg-4) !important;
+}
+
+/* ── Body text ──────────────────────────────────────────────────────────── */
+p, .stMarkdown p {
+ font-family: var(--font-sans) !important;
+ font-size: 0.875rem !important;
+ color: var(--fg-2) !important;
+ line-height: 1.45 !important;
+}
+
+.stCaption p {
+ font-family: var(--font-sans) !important;
+ font-size: 0.75rem !important;
+ color: var(--fg-3) !important;
+}
+
+/* ── Code ───────────────────────────────────────────────────────────────── */
+code {
+ font-family: var(--font-mono) !important;
+ background: var(--ink-3) !important;
+ color: var(--fg-1) !important;
+ padding: 2px 6px !important;
+ border-radius: 2px !important;
+ border: 1px solid var(--line-1) !important;
+ font-size: 0.875em !important;
+}
+
+/* ── Dividers ───────────────────────────────────────────────────────────── */
+hr {
+ border: none !important;
+ border-top: 1px solid var(--line-1) !important;
+ margin: 0.625rem 0 !important;
+}
+
+/* ── DataFrames ─────────────────────────────────────────────────────────── */
+[data-testid="stDataFrame"] {
+ border: 1px solid var(--line-1) !important;
+ border-radius: 2px !important;
+}
+
+/* ── Number inputs ──────────────────────────────────────────────────────── */
+.stNumberInput input {
+ background: var(--ink-2) !important;
+ border: 1px solid var(--line-2) !important;
+ border-radius: 2px !important;
+ font-family: var(--font-mono) !important;
+ font-size: 0.8125rem !important;
+ color: var(--fg-1) !important;
+ font-variant-numeric: tabular-nums !important;
+}
+
+.stNumberInput label {
+ font-family: var(--font-sans) !important;
+ font-size: 10px !important;
+ font-weight: 600 !important;
+ text-transform: uppercase !important;
+ letter-spacing: 0.12em !important;
+ color: var(--fg-4) !important;
+}
+
+/* ── Alerts ─────────────────────────────────────────────────────────────── */
+[data-testid="stAlertContainer"] {
+ border-radius: 2px !important;
+ font-family: var(--font-sans) !important;
+ font-size: 0.875rem !important;
+}
+
+/* ── Spinner ────────────────────────────────────────────────────────────── */
+.stSpinner > div { border-top-color: var(--brass) !important; }
+
+/* ── Scrollbars ─────────────────────────────────────────────────────────── */
+::-webkit-scrollbar { width: 5px; height: 5px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb { background: var(--ink-3); border-radius: 3px; }
+::-webkit-scrollbar-thumb:hover { background: var(--ink-4); }
</style>
""", unsafe_allow_html=True)
+import plotly.graph_objects as go
+import plotly.io as pio
+
+# ── Plotly theme ──────────────────────────────────────────────────────────────
+_prism_layout = go.Layout(
+ paper_bgcolor="#0B0E13",
+ plot_bgcolor="#0B0E13",
+ font=dict(family="IBM Plex Mono, SF Mono, Menlo, monospace", color="#C7C0AE", size=11),
+ title=dict(
+ font=dict(family="EB Garamond, Georgia, serif", color="#F2ECDC", size=18),
+ x=0,
+ ),
+ colorway=["#C2AA7A", "#4F8C5E", "#4A78B5", "#B5494B", "#C49545", "#8B7FBF"],
+ xaxis=dict(
+ gridcolor="#232934",
+ linecolor="#232934",
+ tickfont=dict(family="IBM Plex Mono, monospace", color="#5E5849", size=10),
+ title=dict(font=dict(color="#8E8676", size=11)),
+ showgrid=True,
+ zeroline=False,
+ ),
+ yaxis=dict(
+ gridcolor="#232934",
+ linecolor="#232934",
+ tickfont=dict(family="IBM Plex Mono, monospace", color="#5E5849", size=10),
+ title=dict(font=dict(color="#8E8676", size=11)),
+ showgrid=True,
+ zeroline=False,
+ ),
+ legend=dict(
+ bgcolor="rgba(17,21,28,0.85)",
+ bordercolor="#232934",
+ borderwidth=1,
+ font=dict(family="IBM Plex Sans, sans-serif", color="#C7C0AE", size=11),
+ ),
+ margin=dict(l=48, r=16, t=32, b=40),
+ hoverlabel=dict(
+ bgcolor="#181D26",
+ bordercolor="#2E3645",
+ font=dict(family="IBM Plex Mono, monospace", color="#F2ECDC", size=11),
+ ),
+)
+_prism_template = go.layout.Template(layout=_prism_layout)
+pio.templates["prism"] = _prism_template
+pio.templates.default = "prism"
+
from components.market_bar import render_market_bar
from components.top_movers import render_top_movers
from components.overview import render_overview
@@ -71,21 +435,49 @@ if "ticker" not in st.session_state:
st.session_state["ticker"] = None
-# ── Sidebar ──────────────────────────────────────────────────────────────────
+# ── Sidebar ───────────────────────────────────────────────────────────────────
with st.sidebar:
- col_logo, col_title = st.columns([1, 2])
- with col_logo:
- st.image("assets/logo.png", width=60)
- with col_title:
- st.markdown("### Prism")
- st.caption("Financial Analysis Dashboard")
- st.divider()
+ # Brand mark
+ st.markdown("""
+ <div style="
+ padding: 12px 16px;
+ border-bottom: 1px solid #232934;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 4px;
+ ">
+ <div style="
+ width: 28px; height: 28px;
+ border-radius: 50%;
+ border: 1px solid #C2AA7A;
+ display: flex; align-items: center; justify-content: center;
+ font-family: 'EB Garamond', Georgia, serif;
+ font-style: italic; font-size: 16px;
+ color: #C2AA7A; letter-spacing: -0.05em;
+ flex-shrink: 0;
+ ">P</div>
+ <div style="display: flex; flex-direction: column; gap: 1px; overflow: hidden;">
+ <span style="
+ font-family: 'EB Garamond', Georgia, serif;
+ font-size: 18px; color: #F2ECDC;
+ font-weight: 500; letter-spacing: -0.01em;
+ line-height: 1; white-space: nowrap;
+ ">Prism</span>
+ <span style="
+ font-family: 'IBM Plex Mono', 'SF Mono', monospace;
+ font-size: 10px; color: #5E5849;
+ letter-spacing: 0.12em; text-transform: uppercase;
+ ">Research Terminal</span>
+ </div>
+ </div>
+ """, unsafe_allow_html=True)
with st.form("ticker_search_form", clear_on_submit=False):
query = st.text_input(
- "Search company or ticker",
- placeholder="e.g. Apple, AAPL, MSFT…",
+ "Ticker or company",
+ placeholder="AAPL, Apple, MSFT…",
key="search_query",
).strip()
@@ -108,50 +500,117 @@ with st.sidebar:
if submitted and selected_symbol:
st.session_state["ticker"] = selected_symbol
- if st.session_state["ticker"]:
- st.caption(f"Currently viewing: **{st.session_state['ticker']}**")
-
ticker = st.session_state["ticker"]
- # Quick company info in sidebar
- st.divider()
+ # Company snapshot
if ticker:
+ st.divider()
info = get_company_info(ticker)
- if ticker and info:
- st.caption(info.get("longName", ticker))
- price = get_latest_price(ticker)
- prev_close = info.get("previousClose") or info.get("regularMarketPreviousClose")
- if price is not None:
- if prev_close and prev_close > 0:
- chg = price - prev_close
- chg_pct = chg / prev_close * 100
- sign = "+" if chg >= 0 else ""
- color = "#2ecc71" if chg >= 0 else "#e74c3c"
- st.markdown(
- f"<span style='font-size:1.3rem;font-weight:700'>${price:,.2f}</span>"
- f"&nbsp;<span style='font-size:0.82rem;color:{color}'>{sign}{chg:+.2f} ({sign}{chg_pct:.2f}%)</span>",
- unsafe_allow_html=True,
- )
- else:
- st.markdown(
- f"<span style='font-size:1.3rem;font-weight:700'>${price:,.2f}</span>",
- unsafe_allow_html=True,
- )
- _EXCHANGE_NAMES = {
- "NYQ": "NYSE", "NMS": "NASDAQ", "NGM": "NASDAQ",
- "NCM": "NASDAQ", "ASE": "AMEX", "PCX": "NYSE Arca",
- "BTS": "BATS", "TSX": "TSX", "LSE": "LSE",
- }
- raw_exchange = info.get("exchange", "")
- exchange = _EXCHANGE_NAMES.get(raw_exchange, raw_exchange) or "—"
- st.caption(f"Exchange: {exchange}")
- st.caption(f"Currency: {info.get('currency', 'USD')}")
- st.caption(f"Sector: {info.get('sector', '—')}")
- employees = info.get("fullTimeEmployees")
- st.caption(f"Employees: {employees:,}" if isinstance(employees, int) else "Employees: —")
- website = info.get("website")
- if website:
- st.markdown(f"[🌐 Website]({website})")
+ if info:
+ co_name = info.get("longName", ticker)
+ price = get_latest_price(ticker)
+ prev_close = info.get("previousClose") or info.get("regularMarketPreviousClose")
+
+ # Ticker + name
+ st.markdown(f"""
+ <div style="padding: 6px 0 4px;">
+ <div style="
+ font-family: 'EB Garamond', Georgia, serif;
+ font-style: italic;
+ font-size: 2rem; color: #F2ECDC;
+ line-height: 0.95; letter-spacing: -0.025em;
+ margin-bottom: 4px;
+ ">{ticker}</div>
+ <div style="
+ font-family: 'IBM Plex Sans', sans-serif;
+ font-size: 11px; color: #8E8676;
+ letter-spacing: 0.01em;
+ ">{co_name}</div>
+ </div>
+ """, unsafe_allow_html=True)
+
+ # Price + change
+ if price is not None:
+ if prev_close and prev_close > 0:
+ chg = price - prev_close
+ chg_pct = chg / prev_close * 100
+ sign = "+" if chg >= 0 else ""
+ px_color = "#4F8C5E" if chg >= 0 else "#B5494B"
+ st.markdown(f"""
+ <div style="padding: 4px 0 8px;">
+ <span style="
+ font-family: 'IBM Plex Mono', monospace;
+ font-size: 1.375rem; color: #F2ECDC;
+ font-weight: 500; font-variant-numeric: tabular-nums;
+ ">${price:,.2f}</span>
+ <span style="
+ font-family: 'IBM Plex Mono', monospace;
+ font-size: 0.6875rem; color: {px_color};
+ margin-left: 6px; font-variant-numeric: tabular-nums;
+ ">{sign}{chg_pct:.2f}%</span>
+ </div>
+ """, unsafe_allow_html=True)
+ else:
+ st.markdown(f"""
+ <div style="padding: 4px 0 8px;">
+ <span style="
+ font-family: 'IBM Plex Mono', monospace;
+ font-size: 1.375rem; color: #F2ECDC;
+ font-weight: 500; font-variant-numeric: tabular-nums;
+ ">${price:,.2f}</span>
+ </div>
+ """, unsafe_allow_html=True)
+
+ # Company meta
+ _EXCHANGE_NAMES = {
+ "NYQ": "NYSE", "NMS": "NASDAQ", "NGM": "NASDAQ",
+ "NCM": "NASDAQ", "ASE": "AMEX", "PCX": "NYSE Arca",
+ "BTS": "BATS", "TSX": "TSX", "LSE": "LSE",
+ }
+ raw_exchange = info.get("exchange", "")
+ exchange = _EXCHANGE_NAMES.get(raw_exchange, raw_exchange) or "—"
+ sector = info.get("sector", "—")
+ currency = info.get("currency", "USD")
+ employees = info.get("fullTimeEmployees")
+ emp_str = f"{employees:,}" if isinstance(employees, int) else "—"
+
+ rows = [
+ ("Exchange", exchange),
+ ("Sector", sector),
+ ("Currency", currency),
+ ("Employees", emp_str),
+ ]
+ rows_html = "".join(f"""
+ <div style="display:flex;justify-content:space-between;align-items:baseline;">
+ <span style="font-family:'IBM Plex Sans',sans-serif;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.12em;color:#5E5849;">{k}</span>
+ <span style="font-family:'IBM Plex Mono',monospace;font-size:11px;color:#C7C0AE;text-align:right;max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">{v}</span>
+ </div>
+ """ for k, v in rows)
+
+ st.markdown(f"""
+ <div style="
+ display:flex;flex-direction:column;gap:6px;
+ padding:8px 0 4px;
+ border-top:1px solid #232934;
+ ">{rows_html}</div>
+ """, unsafe_allow_html=True)
+
+ website = info.get("website", "")
+ if website:
+ st.markdown(f"""
+ <div style="padding:6px 0 0;">
+ <a href="{website}" target="_blank" style="
+ font-family:'IBM Plex Sans',sans-serif;
+ font-size:11px;color:#C2AA7A;
+ text-decoration:none;
+ border-bottom:1px solid #8F7A50;
+ padding-bottom:1px;
+ ">Website ↗</a>
+ </div>
+ """, unsafe_allow_html=True)
+
+ elif ticker:
+ st.caption(f"Viewing: **{ticker}**")
# ── Market Bar ────────────────────────────────────────────────────────────────
@@ -171,17 +630,32 @@ st.divider()
# ── Main Content ──────────────────────────────────────────────────────────────
if not ticker:
- st.info("Search for a company or ticker in the sidebar to get started.")
+ st.markdown("""
+ <div style="padding:48px 0 32px;text-align:center;">
+ <div style="
+ font-family:'EB Garamond',Georgia,serif;
+ font-style:italic;font-size:2.375rem;
+ color:#F2ECDC;font-weight:400;
+ letter-spacing:-0.01em;line-height:1.1;
+ margin-bottom:12px;
+ ">Search for a ticker to begin.</div>
+ <div style="
+ font-family:'IBM Plex Sans',sans-serif;
+ font-size:0.875rem;color:#5E5849;
+ letter-spacing:0.01em;
+ ">Enter a company name or symbol in the sidebar.</div>
+ </div>
+ """, unsafe_allow_html=True)
st.stop()
tab_overview, tab_financials, tab_valuation, tab_options, tab_insiders, tab_filings, tab_news = st.tabs([
- "📈 Overview",
- "📊 Financials",
- "💰 Valuation",
- "🎯 Options",
- "👤 Insiders",
- "📁 Filings",
- "📰 News",
+ "Overview",
+ "Financials",
+ "Valuation",
+ "Options",
+ "Insiders",
+ "Filings",
+ "News",
])
with tab_overview: