/* global React, Homepage, StockDetail, RequestStock, CitationProvider, TweaksPanel, useTweaks, TweakSection, TweakRadio */ // app.jsx — root, routing, tweaks wiring const { useState, useEffect } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "density": "standard", "chatLayout": "rail" }/*EDITMODE-END*/; // Throughline logo mark — narrative line that bends and lands in a violet // dot. Inline SVG so it scales crisp at any size and the dot recolors with // --accent in both light/dark themes. Drawn on a 24×16 viewBox, vertically // centered against the wordmark. function ThroughlineMark({ size = 22 }) { return ( ); } function parseRoute(hash) { const h = hash ? hash.replace(/^#/, "") : ""; if (!h) return { name: "home" }; if (h.startsWith("stock:")) return { name: "stock", ticker: h.slice(6) }; if (h.startsWith("request:")) return { name: "request", query: h.slice(8) }; if (h.startsWith("chat:")) return { name: "stock", ticker: h.slice(5) }; // legacy if (h === "about" || h === "methodology" || h.startsWith("methodology?")) { // Optional ?flag=… deep link from "Flag this finding" buttons. const qIdx = h.indexOf("?"); const params = qIdx >= 0 ? new URLSearchParams(h.slice(qIdx + 1)) : null; return { name: "methodology", flag: params?.get("flag") || null }; } return { name: "home" }; } function App() { const [route, setRoute] = useState(() => parseRoute(location.hash)); const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); useEffect(() => { const onHash = () => setRoute(parseRoute(location.hash)); window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); function navigate(r) { location.hash = r; setRoute(parseRoute("#" + r)); window.scrollTo({ top: 0, behavior: "instant" }); } useEffect(() => { document.documentElement.dataset.density = tweaks.density; document.documentElement.dataset.chatLayout = tweaks.chatLayout; }, [tweaks.density, tweaks.chatLayout]); let view = null; if (route.name === "stock") view = ; else if (route.name === "request") view = ; else if (route.name === "methodology") view = ; else view = ; const data = window.CCT_DATA; return (
{view} setTweak("density", v)} options={[ { value: "standard", label: "Standard" }, { value: "compact", label: "Compact" } ]} /> setTweak("chatLayout", v)} options={[ { value: "rail", label: "Right rail" }, { value: "stacked", label: "Below brief" } ]} />
); } // Methodology page — five sections: what Throughline does, how findings are // produced, what we verify vs editorial, flag-an-error form, what we are not. // // The flag form accepts a deep-linked URL via ?flag= from the // "Flag this finding" buttons on stock pages, so we pre-fill subject + URL. const SUPPORT_EMAIL = "hello@throughline.co.in"; function decodeFlagPayload(flag) { if (!flag) return null; try { const json = atob(decodeURIComponent(flag)); const payload = JSON.parse(json); if (typeof payload !== "object" || !payload) return null; return payload; } catch { return null; } } function MethodologyPage({ navigate, flag }) { const prefill = decodeFlagPayload(flag); const [errorKind, setErrorKind] = useState(""); const [subject, setSubject] = useState(() => prefill?.subject || ""); const [findingUrl, setFindingUrl] = useState(() => prefill?.url || ""); const [details, setDetails] = useState(""); const [email, setEmail] = useState(""); const [submitted, setSubmitted] = useState(false); const flagRef = React.useRef(null); // If we landed with a deep link, scroll the form into view. useEffect(() => { if (prefill && flagRef.current) { flagRef.current.scrollIntoView({ behavior: "smooth", block: "start" }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); function buildMailto() { const subj = subject.trim() || "Flag a finding on Throughline"; const body = [ errorKind ? `Kind: ${errorKind}` : "", "", details.trim(), "", findingUrl.trim() ? `Finding URL: ${findingUrl.trim()}` : "", email.trim() ? `From: ${email.trim()}` : "", ].filter(Boolean).join("\n"); return `mailto:${SUPPORT_EMAIL}?subject=${encodeURIComponent(subj)}&body=${encodeURIComponent(body)}`; } async function submit(e) { e.preventDefault(); if (!subject.trim() && !details.trim()) return; // Try the backend (endpoint is wired but optional — we don't depend on it). if (window.CCT_USE_REAL_API) { try { const base = window.CCT_API_BASE || ""; const res = await fetch(`${base}/api/flags`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify({ kind: errorKind || null, subject: subject.trim(), finding_url: findingUrl.trim(), details: details.trim(), email: email.trim() || null, }), }); if (res.ok) { setSubmitted(true); return; } } catch { /* fall through to mailto */ } } // Fallback: open the user's mail client with everything pre-filled. Nothing // is lost — the form contents become the email body. window.location.href = buildMailto(); setSubmitted(true); } return (
Methodology Last updated · April 2026

How Throughline builds a brief.

{/* §1 — What Throughline does */}

What Throughline does

Throughline reads earnings concalls so you don't have to. For each Indian midcap we cover, we process four consecutive quarterly calls and surface the parts that matter: what management started saying, what they stopped saying, where they walked back guidance, and which questions they dodged. Every claim cites a verbatim transcript. The trade thesis stays yours; we hand you the narrative scaffolding.

{/* §2 — How findings are produced */}

How findings are produced

  1. 01

    Source

    Earnings call transcripts from BSE / NSE filings and the company's own investor relations site. Always the official PDF. Each brief on Throughline links directly to the source PDF for verification.

  2. 02

    Clean

    PDF → text with formatting normalized. Speaker names canonicalized. Boilerplate stripped. We keep the structured Q&A pairs intact.

  3. 03

    Extract

    An LLM-assisted process reads the four quarters together and proposes candidate findings — emerged topics, walked-back guidance, dodged questions, governance changes. Each candidate cites its quote. The editor reviews and selects which ones publish.

  4. 04

    Verify

    Every quote in a published brief is checked verbatim against the cleaned transcript before the brief ships. 101 of 101 quotes across the seven launch tickers have been verified character-for-character. A finding does not enter the site without its quote passing this gate.

  5. 05

    Edit

    An editor reviews proposed findings, drops the ones that don't hold up, writes the drift summary in their own voice, picks which findings lead, assigns severity (material / notable / context), and writes the "what this implies" bullets. This is where mechanical detection becomes editorial product.

{/* §3 — What we verify vs what's editorial */}

What we verify vs what's editorial

✓ Verified Mechanically checked, gate to publication
  • Verbatim quotes. Character-for-character against the cleaned transcript.
  • Quarter and section attribution. The transcript and section a quote came from.
  • Speaker. Who said it (CFO, CEO, named analyst, moderator).
  • Topic mention counts. How many times a phrase appeared across the four quarters.
  • Numerical facts when stated by management. Margin, growth, headcount numbers as quoted.
⚠ Editorial Human judgment, debatable
  • Severity. Whether a walk-back is "material," "notable," or "context."
  • Drift summary. The one-line characterization of what changed this quarter.
  • "What this implies." The bullets translating findings into thesis-level reads.
  • Topic clustering. What counts as the same topic across quarters when phrasing shifts.
  • Significance commentary. The "why this matters" prose under each finding.
{/* §4 — Flag an error */}

Flag an error

Sometimes we get things wrong. When we do, tell us — we'll fix it and credit the catch. Quote mismatches, attribution slips, mis-sectioned findings, severity calls you disagree with: all welcome.

{submitted ? (
✓ Sent

Thanks. We read every flag. If you left an email, expect a reply within a few days; if not, we'll fix it and the corrected brief will quietly update.

) : (