diff --git a/frontend/src/pages/CompanyDetail.tsx b/frontend/src/pages/CompanyDetail.tsx
index ec54a30..fa97ed3 100644
--- a/frontend/src/pages/CompanyDetail.tsx
+++ b/frontend/src/pages/CompanyDetail.tsx
@@ -1,5 +1,5 @@
import { useParams, useNavigate, Link } from '@tanstack/react-router';
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
import {
useCompany,
useCompanySources,
@@ -750,6 +750,34 @@ function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices, range90
const hasPrice = chartData.some((pt) => pt.price != null);
+ // Compute market open/close vertical markers for intraday and 1d windows
+ const showMarketMarkers = selectedWindow === 'intraday' || selectedWindow === '1d';
+ const marketMarkers: { ts: number; label: string }[] = [];
+ if (showMarketMarkers && chartData.length > 0) {
+ const minTs = chartData[0].timestamp;
+ const maxTs = chartData[chartData.length - 1].timestamp;
+ // Walk each day in the range and compute 9:30 AM ET (open) and 4:00 PM ET (close)
+ const dayMs = 86400_000;
+ const startDay = new Date(minTs);
+ startDay.setUTCHours(0, 0, 0, 0);
+ for (let d = startDay.getTime(); d <= maxTs + dayMs; d += dayMs) {
+ const date = new Date(d);
+ const dow = date.getUTCDay();
+ if (dow === 0 || dow === 6) continue; // skip weekends
+ // ET offset: EDT = UTC-4, EST = UTC-5. Approximate with -4 (summer).
+ // 9:30 AM ET = 13:30 UTC (EDT)
+ const openTs = d + 13 * 3600_000 + 30 * 60_000;
+ // 4:00 PM ET = 20:00 UTC (EDT)
+ const closeTs = d + 20 * 3600_000;
+ if (openTs >= minTs && openTs <= maxTs) {
+ marketMarkers.push({ ts: openTs, label: 'Open' });
+ }
+ if (closeTs >= minTs && closeTs <= maxTs) {
+ marketMarkers.push({ ts: closeTs, label: 'Close' });
+ }
+ }
+ }
+
// Available windows from the data (check both history and latest)
const allTrends = [...(trends ?? []), ...(latestTrends ?? [])];
const availableWindows = [...new Set(allTrends.filter((t) => t.entity_id === ticker).map((t) => t.window))];
@@ -761,6 +789,93 @@ function TrendHistoryChart({ trends, latestTrends, ticker, marketPrices, range90
.sort((a, b) => new Date(b.generated_at).getTime() - new Date(a.generated_at).getTime());
const latest = latestForWindow[0] ?? (filtered.length > 0 ? filtered[filtered.length - 1] : null);
+ const [fullscreen, setFullscreen] = useState(false);
+
+ // Close fullscreen on Escape key
+ useEffect(() => {
+ if (!fullscreen) return;
+ const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') setFullscreen(false); };
+ window.addEventListener('keydown', handler);
+ return () => window.removeEventListener('keydown', handler);
+ }, [fullscreen]);
+
+ // Shared chart content — rendered at different sizes
+ const chartContent = (height: number) => (
+