// ============================================================
// Variation B — Product-forward landing page.
// ============================================================
// ─── Cost Explorer data ────────────────────────────────────────
const EXPLORER_REGIONS = [
{ id: "index_us", label: "US", max: 3000, maxLabel: "3k" },
{ id: "index_eu", label: "EU", max: 1500, maxLabel: "1.5k" },
{ id: "index_asia", label: "Asia", max: 4000, maxLabel: "4k" },
];
const EXPLORER_TOPN_BASE = [
{ n: 10, label: "10" },
{ n: 50, label: "50" },
{ n: 100, label: "100" },
{ n: 250, label: "250" },
{ n: 500, label: "500" },
{ n: 1000, label: "1k" },
{ n: 2000, label: "2k" },
{ n: 3000, label: "3k" },
];
// ─── Cumulative chart ──────────────────────────────────────────
// companies = full universe sorted descending by cost
// targetN = selected top-N (actual slice count, moves the marker)
// targetLabel = display string for the callout (e.g. "3k" or "100")
function CumulativeCostChart({ companies, targetN, targetLabel }) {
if (!companies || companies.length === 0) return null;
const W = 760, H = 260;
const PAD = { t: 24, r: 72, b: 40, l: 64 };
const iW = W - PAD.l - PAD.r;
const iH = H - PAD.t - PAD.b;
const n = companies.length;
// Cumulative cost over the full universe
const cum = [];
let running = 0;
for (const co of companies) { running += co.c; cum.push(running); }
const total = cum[n - 1] || 1;
// X axis scaled on actual rank values from the CSV
const minRank = companies[0].r || 1;
const maxRank = companies[n - 1].r || n;
const sx = (rank) => PAD.l + ((rank - minRank) / Math.max(maxRank - minRank, 1)) * iW;
const sy = (cost) => PAD.t + iH - (cost / total) * iH;
// Full-curve SVG path — X position from actual rank
const pts = cum.map((c, i) => `${sx(companies[i].r).toFixed(1)},${sy(c).toFixed(1)}`);
const linePath = "M " + pts.join(" L ");
const areaPath = `M ${PAD.l.toFixed(1)},${(PAD.t + iH).toFixed(1)} L ${pts.join(" L ")} L ${sx(maxRank).toFixed(1)},${(PAD.t + iH).toFixed(1)} Z`;
// Y grid
const yPcts = [0.25, 0.5, 0.75, 1.0];
// X axis ticks (always show 1 and n, plus a few intermediate)
const xTicks = (() => {
if (n <= 30) return [1, 10, 25, n];
if (n <= 60) return [1, 10, 25, 50, n];
if (n <= 120) return [1, 25, 50, 100, n];
if (n <= 260) return [1, 50, 100, 200, 250, n];
return [1, 100, 200, 300, 400, 500];
})().filter((v, i, a) => a.indexOf(v) === i && v <= n);
// Target marker — targetN = number of companies selected, use their actual rank
const tIdx = Math.min(targetN, n) - 1;
const tRank = companies[tIdx] ? companies[tIdx].r : maxRank;
const tX = sx(tRank);
const tCost = cum[tIdx] || 0;
const tY = sy(tCost);
const tLabel = tCost >= 10 ? `$${tCost.toFixed(1)}` : `$${tCost.toFixed(2)}`;
// Place callout left or right depending on position
const calloutRight = tIdx < n * 0.72;
const gradId = "ccc-grad";
return (
);
}
// ─── Cost Explorer section ──────────────────────────────────────
function CostExplorer({ universeData }) {
const [regionId, setRegionId] = React.useState("index_us");
const [topN, setTopN] = React.useState(100);
const currentRegion = EXPLORER_REGIONS.find(r => r.id === regionId);
// Full universe — already sorted descending by cost in the JSON
const allSorted = (universeData && universeData[regionId]) || [];
// Options: standard breakpoints below max, then the rounded max
const options = React.useMemo(() => {
const base = EXPLORER_TOPN_BASE.filter(o => o.n < currentRegion.max);
return [...base, { n: currentRegion.max, label: currentRegion.maxLabel }];
}, [regionId]);
React.useEffect(() => { setTopN(100); }, [regionId]);
// For the chart marker: clamp to actual data length
const markerN = Math.min(topN, allSorted.length);
// Display label for the current selection
const topNLabel = (options.find(o => o.n === topN) || {}).label || String(topN);
// Stats: slice to the actual clamped count
const selected = allSorted.slice(0, markerN);
const total = selected.reduce((s, c) => s + c.c, 0);
const avg = selected.length > 0 ? total / selected.length : 0;
const top1 = allSorted[0];
const fmtD = (v) => v >= 10 ? `$${v.toFixed(1)}` : `$${v.toFixed(2)}`;
return (
Cumulative daily pipeline cost across companies, ranked by market cap.
The curve shows how concentrated cost is at the top of any universe.
How costs scale with coverage.
We ran the numbers on what it costs a research desk to write morning briefs by hand. Then we built the alternative.
Scans, reads, drafts, validates. {MANUAL_DEFAULTS.analysts} analyst at ${MANUAL_DEFAULTS.hourlyRate}/hr.
Cost metered per chunk, per token.
and roughly {fmtHours(manual.totals.minutes)} of analyst time, every time the desk runs the morning brief.
Once a day, the analyst opens {MANUAL_DEFAULTS.numSources} sources one by one, about {MANUAL_DEFAULTS.secondsScanPerSource}s per source, scanning headlines across the full universe, which alone takes{" "} {fmtMinShort(manual.scan.totalMin)}.
Then, for each of the {numCompanies} companies, the analyst reads up to {MANUAL_DEFAULTS.maxArticlesRead} articles, roughly {MANUAL_DEFAULTS.avgWordsPerArticle} words each. At {MANUAL_DEFAULTS.readingSpeed} wpm, that is another{" "} {fmtMinShort(manual.perCompany.readMin)} per company. Finally, briefs are drafted and validated for each name.
All in: {fmtUSD(manual.totals.cost)}/day and{" "} {fmtHours(manual.totals.minutes)} of analyst time to cover{" "} {numCompanies} companies once.
Bigdata searches for relevant news on each company. No source list to maintain, no wires to open. The briefs are drafted, then tested against the desk's prior output and confirmed against the historical news record. Only what has materially changed since the last run is published.
Every claim is grounded back to its source. Compute is metered per token and per chunk, so the bill is honest down to the cent.
All in: {fmtUSDsmart(pipeline.cost)}/day for the same{" "} {numCompanies} companies, and the analyst spends the morning thinking, not scrolling.
Search, cross-reference, verify. Every step is metered and grounded.
Bigdata searches its licensed news corpus and surfaces the content relevant to each issuer. No source list to maintain, no wires to open.
Every item is quickly checked against the desk's prior briefs. Anything already covered is set aside. Fast and efficient: removes the obvious before the deeper check runs.
A thorough search through the historical news record confirms whether each remaining item is genuinely new. Slower and more expensive than the first pass, but precise. What survives is grounded, attributed, and ready to read.
Drop us your watchlist or pick a benchmark universe and we'll show you what it costs.