// ============================================================ // 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 ( {/* Y grid */} {yPcts.map(pct => { const y = sy(total * pct); const val = total * pct; const label = val >= 10 ? `$${val.toFixed(0)}` : `$${val.toFixed(1)}`; return ( {label} ); })} {/* Area fill */} {/* Full curve */} {/* ── Target marker (moves with topN) ── */} {/* Vertical line */} {/* Horizontal line from Y axis to the dot */} {/* Dot on the curve */} {/* Callout box */} top {targetLabel || targetN} {tLabel}/day {/* X axis line */} {/* X axis: only the selected top-N rank */} {targetLabel || targetN} {/* Y axis label */} cumulative USD/day ); } // ─── 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 (
Cost at scale

How costs scale with coverage.

Cumulative daily pipeline cost across companies, ranked by market cap. The curve shows how concentrated cost is at the top of any universe.

{EXPLORER_REGIONS.map(r => ( ))}
{options.map(({ n, label }) => ( ))}
{allSorted.length === 0 ?
Loading…
: }
{fmtUSDsmart(total)}/day
Total · top {topNLabel}
{fmtD(avg)}/day
Average per company
{top1 ? fmtD(top1.c) : "—"}/day
Highest · {top1 ? top1.name : "—"}
Note: The costs shown do not include the required data licence fees.
); } // ─── Main component ───────────────────────────────────────────── function LandingProduct() { const universeData = useUniverseData(); // Calculator: Top US 500 const companies = (universeData && universeData["top_us_500"]) || []; const numCompanies = companies.length || 500; const manual = manualCost(numCompanies); const pipeline = pipelineCost(companies); const savings = manual.totals.cost - pipeline.cost; const ratio = pipeline.cost > 0 ? manual.totals.cost / pipeline.cost : 0; return (
{/* Nav */} {/* Hero */}

The morning brief.
Automated.

We ran the numbers on what it costs a research desk to write morning briefs by hand. Then we built the alternative.

Open the app →
{/* Calculator — Top US 500 */}
The cost of the morning huddle

What does covering Top US 500 cost you?

{/* Manual side */}
Scenario A · Manual

Analyst writes the briefs by hand.

Scans, reads, drafts, validates. {MANUAL_DEFAULTS.analysts} analyst at ${MANUAL_DEFAULTS.hourlyRate}/hr.

{fmtUSD(manual.totals.cost)}/day
total cost · {numCompanies} co.
Per company
{Math.round(manual.perCompany.totalMin)} min
Scanning (once)
{Math.round(manual.scan.totalMin)} min
Total
{fmtHours(manual.totals.minutes)}
{/* Pipeline side */}
Scenario B · Bigdata

Bigdata runs the briefs automatically.

Cost metered per chunk, per token.

{fmtUSDsmart(pipeline.cost)}/day
total compute · {numCompanies} co.
Per company
${pipeline.perCompany.toFixed(2)}
Time per company
< 2 min
Full parallelism (optional)
~ 2 min
{/* Result strip */}
You save {fmtUSD(savings)}/day

and roughly {fmtHours(manual.totals.minutes)} of analyst time, every time the desk runs the morning brief.

{ratio >= 100 ? Math.round(ratio).toLocaleString() : ratio.toFixed(0)}×
cheaper · per run
{/* Narrative */}
The manual workflow

Open every wire. Skim every headline. Read what survives.

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.

The Bigdata workflow

Search, draft, then verify what actually moved.

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.

{/* Method */}

Three steps. One brief.

Search, cross-reference, verify. Every step is metered and grounded.

01 · Surface

Relevant news, automatically

Bigdata searches its licensed news corpus and surfaces the content relevant to each issuer. No source list to maintain, no wires to open.

02 · First screen

Fast. Catches what was already covered.

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.

03 · Deep check

Precise. Confirms what actually moved.

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.

{/* Cost Explorer */} {/* Footer CTA */}

Run the calc on your universe.

Drop us your watchlist or pick a benchmark universe and we'll show you what it costs.

Open the app →
); } window.LandingProduct = LandingProduct;