// Shared components: masthead, navigation, sparkline, theme-dot, citation popover const { useState, useEffect, useRef, useMemo } = React; // ── Global display timezone ─────────────────────────────────────────── // Change DISPLAY_TZ to adjust all time displays across the app. // Supported values: "UTC", "New York", "CET" const DISPLAY_TZ = "ET"; const _TZ_MAP = { "UTC": "UTC", "ET": "America/New_York", "CET": "Europe/Paris" }; const _TZ_LONG = { "UTC": "UTC", "ET": "ET / New York Time", "CET": "CET / Central European Time" }; function _tzIana(tz) { return _TZ_MAP[tz != null ? tz : DISPLAY_TZ] || "UTC"; } function _tzLong(tz) { return _TZ_LONG[tz != null ? tz : DISPLAY_TZ] || tz || DISPLAY_TZ; } function _tk(ticker) { return ticker || "PRIVATE"; } // Group citations by source name, then by headline within each source. // Returns [{source, date, headlineGroups: [{headline, excerpts: [str]}]}] function _groupCitations(citations) { const bySource = new Map(); for (const c of citations) { const src = c.source || ""; if (!bySource.has(src)) bySource.set(src, { source: src, date: c.date || "", headlines: new Map() }); const sg = bySource.get(src); const hl = c.headline || ""; if (!sg.headlines.has(hl)) sg.headlines.set(hl, []); const ex = String(c.excerpt != null ? c.excerpt : (c.text != null ? c.text : "")).trim(); if (ex) sg.headlines.get(hl).push(ex); } return Array.from(bySource.values()).map(sg => ({ source: sg.source, date: sg.date, headlineGroups: Array.from(sg.headlines.entries()).map(([hl, excerpts]) => ({ headline: hl, excerpts })), })); } function _fmtRunDate(iso) { if (!iso) return "—"; const d = new Date(iso); const parts = new Intl.DateTimeFormat("en-US", { year: "numeric", month: "2-digit", day: "2-digit", hour: "numeric", minute: "2-digit", hour12: true, timeZone: _tzIana(), }).formatToParts(d); const g = t => parts.find(p => p.type === t)?.value || ""; return `${g("year")}-${g("month")}-${g("day")} ${g("hour")}:${g("minute")} ${g("dayPeriod")} ${DISPLAY_TZ}`; } function _fmtWindow(start, end) { if (!start) return "—"; const zone = _tzIana(); const fmtDate = iso => new Date(iso).toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: zone }); const fmtTime = iso => new Date(iso).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", timeZone: zone, hour12: true }); const fmt = iso => `${fmtDate(iso)} ${fmtTime(iso)}`; if (!end) return `${fmt(start)} ${DISPLAY_TZ}`; const sDate = fmtDate(start), eDate = fmtDate(end); return sDate === eDate ? `${fmt(start)} → ${fmtTime(end)} ${DISPLAY_TZ}` : `${fmt(start)} → ${fmt(end)} ${DISPLAY_TZ}`; } function formatUtcClockHm() { return new Date().toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "UTC", }); } // ── Masthead ─────────────────────────────────────────────────────── function Masthead({ view, setView, theme, setTheme, headerStyle, setHeaderStyle, briefLayout, setBriefLayout }) { const [utcClock, setUtcClock] = useState(formatUtcClockHm); useEffect(() => { const tick = () => setUtcClock(formatUtcClockHm()); tick(); const id = setInterval(tick, 15_000); return () => clearInterval(id); }, []); const today = new Date(); const fmt = today.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric", timeZone: "UTC" }); return (
{fmt}
{e.preventDefault();setView("brief");}}>The Brief {e.preventDefault();setView("portfolio");}}>My Portfolio {e.preventDefault();setView("cost");}}>Costs {(() => { const summaries = window.DATA?.companySummaries || {}; const allDates = Object.values(summaries).map(s => s.lastRunDate).filter(Boolean); const latestRun = allDates.length > 0 ? allDates.sort().at(-1) : null; if (!latestRun) return null; const zone = _tzIana(); const d = new Date(latestRun); const datePart = d.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric", timeZone: zone }); const timePart = d.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true, timeZone: zone }); return ( Last run · {datePart} · {timePart} {DISPLAY_TZ} ); })()}
{(view === "overview" || view === "overview-audit" || view === "overview-archive") && (
{e.preventDefault();setView("overview");}}>Tearsheet {e.preventDefault();setView("overview-audit");}}>Audit {e.preventDefault();setView("overview-archive");}}>Archive
)}
); } // ── Masthead title lockup — three variants ────────────────────────── // "paren-lockup" (default) — "The Brief" big LEFT + "(powered by / [logo])" RIGHT in parens // "inline" — single line: "The Brief (powered by [logo])" // "stacked" — title big, "powered by" small below, "[logo]" below that, all centered function MastheadLockup({ style, theme }) { const LOGO_SRC = theme === "dark" ? "bigdata-logo-white.png" : "bigdata-logo-black.png"; if (style === "inline") { return (

The Brief

( powered by Bigdata.com by RavenPack )
); } if (style === "stacked") { return (

The Brief   

powered by Bigdata.com by RavenPack
); } // Default: "paren-lockup" return (

The Brief

powered by Bigdata.com by RavenPack
); } // ── Theme dot ─────────────────────────────────────────────────────── // Derives a vivid, stable colour from any label string via a simple hash. function hashThemeColor(label) { if (!label) return "var(--ink-mute)"; let h = 0; for (let i = 0; i < label.length; i++) { h = Math.imul(31, h) + label.charCodeAt(i) | 0; } const hue = ((h % 360) + 360) % 360; const dark = document.documentElement.dataset.theme === "dark"; return `hsl(${hue}, 70%, ${dark ? 62 : 38}%)`; } function ThemeDot({ theme, color: colorProp }) { const color = colorProp || hashThemeColor(theme); return ; } // ── Sparkline (saved bullets per day) ─────────────────────────────── function Sparkline({ data, height = 22, width = 80, color = "var(--ink-soft)", fillColor = null, showLast = false, fluid = false, minVal = null, maxVal = null, showZero = false, }) { if (!data || data.length === 0) return null; const max = maxVal !== null ? maxVal : Math.max(...data); const min = minVal !== null ? minVal : Math.min(...data); const range = Math.max(max - min, 0.0001); const span = Math.max(data.length - 1, 1); const step = width / span; const points = data.map((v, i) => { const x = i * step; const y = height - 2 - (v - min) / range * (height - 4); return `${x.toFixed(1)},${y.toFixed(1)}`; }).join(" "); const last = data[data.length - 1]; const lastY = height - 2 - (last - min) / range * (height - 4); const lastX = (data.length - 1) * step; const svgClass = "spark" + (fluid ? " spark--fluid" : ""); const common = { className: svgClass, viewBox: `0 0 ${width} ${height}`, }; const svgProps = fluid ? { ...common, preserveAspectRatio: "none", style: { width: "100%", height, display: "block" }, } : { ...common, width, height }; const zeroY = showZero && min < 0 && max > 0 ? height - 2 - (0 - min) / range * (height - 4) : null; const fillBase = zeroY !== null ? zeroY : height; return ( {fillColor && ( )} {zeroY !== null && ( )} {showLast && } ); } // ── Bar mini-chart ────────────────────────────────────────────────── function MiniBars({ data, height = 28, barWidth = 6, gap = 2, color = "var(--ink)", mutedColor = "var(--rule)" }) { if (!data || data.length === 0) return null; const max = Math.max(...data.map((d) => typeof d === "number" ? d : d.value), 1); return ( {data.map((d, i) => { const value = typeof d === "number" ? d : d.value; const muted = typeof d === "object" && d.muted; const h = value > 0 ? Math.max(value / max * (height - 2), 2) : 1; return ; })} ); } // ── Citation popover ──────────────────────────────────────────────── function CitationRef({ citation, idx }) { return ( {idx + 1} {citation.source} · {citation.date} {citation.headline} {citation.excerpt} ); } // ── Status badges ────────────────────────────────────────────────── function StatusBadge({ status }) { const map = { succeeded: { cls: "tag-novel", label: "Succeeded" }, running: { cls: "tag-running", label: "Running" }, failed: { cls: "tag-discard", label: "Failed" } }; const m = map[status] || { cls: "", label: status }; return {m.label}; } // ── DivergingSparkline — two-tone area chart for signed signals ────────────── function DivergingSparkline({ data, height = 38, width = 240, posColor = "var(--novel)", negColor = "var(--discard)", }) { if (!data || data.length === 0) return null; const uid = React.useId(); const absMax = Math.max(...data.map(v => Math.abs(v))); const ext = Math.max(absMax * 1.18, 0.005); const ymin = -ext; const ymax = ext; const range = ymax - ymin; const h = height; const w = width; const yOf = (v) => h - 2 - (v - ymin) / range * (h - 4); const zeroY = yOf(0); const step = w / Math.max(data.length - 1, 1); const pts = data.map((v, i) => [i * step, yOf(v)]); const line = pts .map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`) .join(" "); const last = pts[pts.length - 1]; const area = `${line} L${last[0].toFixed(2)},${zeroY.toFixed(2)} L0,${zeroY.toFixed(2)} Z`; return ( ); } window.Masthead = Masthead; window.MastheadLockup = MastheadLockup; window.ThemeDot = ThemeDot; window.Sparkline = Sparkline; window.DivergingSparkline = DivergingSparkline; window.MiniBars = MiniBars; window.CitationRef = CitationRef; window.StatusBadge = StatusBadge;