@paro.io/expert-shared-components 1.14.70 → 1.14.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/lib/components/TaxAxis/TaxAxisApi.d.ts +0 -8
  2. package/lib/components/TaxAxis/TaxAxisShell.js +13 -29
  3. package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +10 -6
  4. package/lib/tax-axis/components/clientReport/ImplementationRoadmap.d.ts +2 -1
  5. package/lib/tax-axis/components/clientReport/ImplementationRoadmap.js +11 -8
  6. package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.d.ts +2 -1
  7. package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.js +9 -3
  8. package/lib/tax-axis/components/clientReport/QuarterlyCashChart.d.ts +2 -1
  9. package/lib/tax-axis/components/clientReport/QuarterlyCashChart.js +9 -3
  10. package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +9 -2
  11. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +3 -1
  12. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +5 -2
  13. package/lib/tax-axis/components/intake/ClientParametersSection.js +14 -4
  14. package/lib/tax-axis/components/intake/IntakeCtaCards.d.ts +2 -8
  15. package/lib/tax-axis/components/intake/IntakeCtaCards.js +7 -70
  16. package/lib/tax-axis/components/intake/TaxAxisIntake.js +1 -1
  17. package/lib/tax-axis/components/intake/intakeSchema.js +1 -1
  18. package/lib/tax-axis/components/preparerWorkpaper/EngagementHeader.js +6 -2
  19. package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +3 -3
  20. package/lib/tax-axis/components/processing/TaxAxisProcessing.d.ts +1 -5
  21. package/lib/tax-axis/components/processing/TaxAxisProcessing.js +11 -27
  22. package/lib/tax-axis/components/prospectReport/ProspectNextSteps.js +1 -3
  23. package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.d.ts +1 -7
  24. package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.js +6 -8
  25. package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.d.ts +1 -33
  26. package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.js +56 -57
  27. package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +42 -58
  28. package/lib/tax-axis/lib/adapters/useEngineOutput.js +99 -35
  29. package/lib/tax-axis/lib/data/nextSteps.js +13 -13
  30. package/lib/tax-axis/lib/data/sourceDescriptions.js +6 -0
  31. package/lib/tax-axis/lib/documentFieldCatalog.d.ts +10 -5
  32. package/lib/tax-axis/lib/documentFieldCatalog.js +329 -115
  33. package/lib/tax-axis/lib/types/index.d.ts +2 -0
  34. package/package.json +1 -1
  35. package/lib/tax-axis/components/prospectReport/ProspectReportSkeleton.d.ts +0 -2
  36. package/lib/tax-axis/components/prospectReport/ProspectReportSkeleton.js +0 -78
@@ -31,21 +31,8 @@ const CRAWL_TICK_MS = 300; // interval between crawl ticks
31
31
  const CRAWL_STEP = 0.27; // % added each tick → 0.27 / 0.3s = 0.9%/s
32
32
  const FINISH_MS = 400; // ms to animate from 99% → 100% once ready
33
33
  const STAGE_ADVANCE_MS = 3200; // how often the displayed stage advances
34
- function buildStages(profile, mode) {
34
+ function buildStages(profile) {
35
35
  var _a;
36
- const entity = (profile === null || profile === void 0 ? void 0 : profile.entity) || "your business";
37
- const stateCount = ((_a = profile === null || profile === void 0 ? void 0 : profile.states) === null || _a === void 0 ? void 0 : _a.length) || 1;
38
- const bizName = (profile === null || profile === void 0 ? void 0 : profile.bizName) || "your business";
39
- if (mode === 'prospect') {
40
- return [
41
- `Classifying ${entity} profile`,
42
- "Injecting IRS 2026 + OBBBA parameters",
43
- `Screening 25 strategies across ${stateCount} state${stateCount > 1 ? "s" : ""}`,
44
- "Computing deterministic tax math",
45
- "Generating strategy narratives",
46
- `Assembling prospect report for ${bizName}`,
47
- ];
48
- }
49
36
  if (!profile) {
50
37
  return [
51
38
  "Parsing source documents",
@@ -55,6 +42,9 @@ function buildStages(profile, mode) {
55
42
  "Generating IRS citations",
56
43
  ];
57
44
  }
45
+ const entity = profile.entity || "your business";
46
+ const stateCount = ((_a = profile.states) === null || _a === void 0 ? void 0 : _a.length) || 1;
47
+ const bizName = profile.bizName || "your business";
58
48
  const base = [
59
49
  "Parsing source documents",
60
50
  "Injecting IRS 2026 parameters",
@@ -70,16 +60,10 @@ function buildStages(profile, mode) {
70
60
  base.push("Screening 30 eligible strategies", "Computing deterministic tax math", "Cross-validating source documents", "Generating IRS citations", `Assembling TaxAxis report for ${bizName}`);
71
61
  return base;
72
62
  }
73
- function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep, mode, userContext: _userContext = "expert", }) {
74
- const stages = (0, react_1.useMemo)(() => buildStages(profile, mode), [profile, mode]);
63
+ function TaxAxisProcessing({ onComplete, profile, reportReady = false, userContext: _userContext = "expert", }) {
64
+ const stages = (0, react_1.useMemo)(() => buildStages(profile), [profile]);
75
65
  const [progress, setProgress] = (0, react_1.useState)(0);
76
66
  const [stageIdx, setStageIdx] = (0, react_1.useState)(0);
77
- const effectiveCrawlStep = crawlStep !== null && crawlStep !== void 0 ? crawlStep : CRAWL_STEP;
78
- // Stable refs so effects don't restart when props change after mount
79
- const crawlStepRef = (0, react_1.useRef)(effectiveCrawlStep);
80
- const onCompleteRef = (0, react_1.useRef)(onComplete);
81
- (0, react_1.useEffect)(() => { crawlStepRef.current = effectiveCrawlStep; }, [effectiveCrawlStep]);
82
- (0, react_1.useEffect)(() => { onCompleteRef.current = onComplete; }, [onComplete]);
83
67
  const finishingRef = (0, react_1.useRef)(false);
84
68
  const completedRef = (0, react_1.useRef)(false);
85
69
  // Track current progress in a ref so the finish animation can read it synchronously
@@ -113,7 +97,7 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
113
97
  progressRef.current = v;
114
98
  setProgress(v);
115
99
  };
116
- // ── Crawl: 0 → 99% — runs once on mount, reads crawlStepRef so prop changes don't restart it ──
100
+ // ── Crawl: 0 → 80% — skipped entirely if reportReady already true on mount ──
117
101
  (0, react_1.useEffect)(() => {
118
102
  if (reportReady || finishingRef.current)
119
103
  return;
@@ -122,14 +106,14 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
122
106
  clearInterval(iv);
123
107
  return;
124
108
  }
125
- const next = Math.min(progressRef.current + crawlStepRef.current, CRAWL_CEILING);
109
+ const next = Math.min(progressRef.current + CRAWL_STEP, CRAWL_CEILING);
126
110
  setProgressSync(next);
127
111
  if (next >= CRAWL_CEILING)
128
112
  clearInterval(iv);
129
113
  }, CRAWL_TICK_MS);
130
114
  return () => clearInterval(iv);
131
- // eslint-disable-next-line react-hooks/exhaustive-deps
132
- }, []);
115
+ // reportReady intentionally in deps: if it arrives before mount effect runs, skip crawl
116
+ }, [reportReady]);
133
117
  // ── Stage labels: advance every STAGE_ADVANCE_MS ─────────────────
134
118
  (0, react_1.useEffect)(() => {
135
119
  const iv = setInterval(() => {
@@ -158,7 +142,7 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
158
142
  setProgressSync(100);
159
143
  if (!completedRef.current) {
160
144
  completedRef.current = true;
161
- setTimeout(() => onCompleteRef.current(), 150);
145
+ setTimeout(onComplete, 150);
162
146
  }
163
147
  }
164
148
  };
@@ -25,9 +25,7 @@ function ProspectNextSteps({ sectionNum, bizName, onUpgrade, onPresent, onPrint,
25
25
  bizName,
26
26
  "'s documents to run the full TaxAxis analysis \u2014 computed savings with calculation traces, IRS citations, and a client-ready report."),
27
27
  react_1.default.createElement("div", { style: { display: "flex", gap: 10, flexWrap: "wrap" } },
28
- react_1.default.createElement(GlowBtn, { onClick: () => { if (onUpgrade) {
29
- onUpgrade();
30
- } } }, "Upload Documents"),
28
+ react_1.default.createElement(GlowBtn, { onClick: onUpgrade }, "Upload Documents"),
31
29
  react_1.default.createElement(GlowBtn, { onClick: onPresent, style: { fontSize: 13 } }, "Present to Client"),
32
30
  react_1.default.createElement(GlowBtn, { secondary: true, onClick: onPrint, style: { fontSize: 13 } }, "Download PDF")),
33
31
  react_1.default.createElement("div", { style: { fontSize: 11, color: theme_1.T.text4, marginTop: 12, fontFamily: theme_1.T.body } }, "Typical turnaround: same day. No obligation until you review the results.")))),
@@ -1,16 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Strategy, ClientProfile, ComputedMap } from "../../lib/types";
3
- export interface LlmStrategyNarrative {
4
- whyItMatters: string;
5
- whatWeDo: string;
6
- }
7
3
  interface ProspectStrategyCardProps {
8
4
  strategy: Strategy;
9
5
  index: number;
10
6
  profile: ClientProfile;
11
7
  computed: ComputedMap;
12
- /** LLM-generated narrative — when present, overrides all static/computed text */
13
- llmNarrative?: LlmStrategyNarrative;
14
8
  }
15
- export declare function ProspectStrategyCard({ strategy: s, index: i, profile, computed, llmNarrative }: ProspectStrategyCardProps): React.JSX.Element;
9
+ export declare function ProspectStrategyCard({ strategy: s, index: i, profile, computed }: ProspectStrategyCardProps): React.JSX.Element;
16
10
  export {};
@@ -8,19 +8,17 @@ const react_1 = __importDefault(require("react"));
8
8
  const strategyNarrative_1 = require("../../lib/data/strategyNarrative");
9
9
  const strategyProspect_1 = require("../../lib/data/strategyProspect");
10
10
  const theme_1 = require("./theme");
11
- function ProspectStrategyCard({ strategy: s, index: i, profile, computed, llmNarrative }) {
12
- var _a, _b, _c, _d;
11
+ function ProspectStrategyCard({ strategy: s, index: i, profile, computed }) {
12
+ var _a, _b;
13
13
  const p = strategyProspect_1.STRATEGY_PROSPECT[s.rank];
14
14
  const narr = strategyNarrative_1.STRATEGY_NARRATIVE[s.rank];
15
15
  const c = computed.get(s.rank);
16
- const scaledLo = Math.round(((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) / 1000);
17
- const scaledHi = Math.round(((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi) / 1000);
18
- // When LLM narrative is provided, use it for whyItMatters and whatWeDo.
19
- // HOW SAVINGS BREAK DOWN and WHY YOU NEED A SPECIALIST remain deterministic.
20
- const whyText = (_c = llmNarrative === null || llmNarrative === void 0 ? void 0 : llmNarrative.whyItMatters) !== null && _c !== void 0 ? _c : (narr ? narr.whyMatters(profile) : strategyNarrative_1.NARRATIVE_FALLBACK.whyMatters(s));
16
+ const whyText = narr ? narr.whyMatters(profile) : strategyNarrative_1.NARRATIVE_FALLBACK.whyMatters(s);
21
17
  const breakdownText = narr ? narr.breakdown(profile, c) : strategyNarrative_1.NARRATIVE_FALLBACK.breakdown(s, c);
22
18
  const specialistText = (narr === null || narr === void 0 ? void 0 : narr.whySpecialist) || strategyNarrative_1.NARRATIVE_FALLBACK.whySpecialist(s);
23
- const nextText = (_d = llmNarrative === null || llmNarrative === void 0 ? void 0 : llmNarrative.whatWeDo) !== null && _d !== void 0 ? _d : (narr ? narr.nextSteps : strategyNarrative_1.NARRATIVE_FALLBACK.nextSteps(s));
19
+ const nextText = narr ? narr.nextSteps : strategyNarrative_1.NARRATIVE_FALLBACK.nextSteps(s);
20
+ const scaledLo = Math.round(((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) / 1000);
21
+ const scaledHi = Math.round(((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi) / 1000);
24
22
  return (react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "24px 28px", boxShadow: theme_1.T.shadow } },
25
23
  react_1.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 16 } },
26
24
  react_1.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14 } },
@@ -1,41 +1,9 @@
1
1
  import React from "react";
2
2
  import type { ClientProfile, TaxAxisScreenProps } from "../../lib/types";
3
- import type { LlmStrategyNarrative } from "./ProspectStrategyCard";
4
- /** LLM output payload shape returned by generateProspectReport mutation */
5
- export interface LlmOutputPayload {
6
- execSummary: string;
7
- strategyNarratives: Record<string, LlmStrategyNarrative>;
8
- additionalStrategiesBlurb: string;
9
- }
10
- /** Backend-computed math payload */
11
- export interface BackendComputePayload {
12
- savingsLo: number;
13
- savingsHi: number;
14
- eligibleCount: number;
15
- highPriorityCount: number;
16
- quickWinCount: number;
17
- gapBlock: {
18
- currentTax: number;
19
- optimizedTax: number;
20
- realizedSavings: number;
21
- realizedPctOfCurrent: number;
22
- fedRatePct: number;
23
- stateRatePct: number;
24
- };
25
- barData: Array<{
26
- name: string;
27
- mid: number;
28
- pct: number;
29
- }>;
30
- }
31
3
  export interface TaxAxisProspectReportProps extends TaxAxisScreenProps {
32
4
  profile: ClientProfile;
33
- /** When provided (from generateProspectReport mutation), all math comes from backend */
34
- backendComputePayload?: BackendComputePayload | null;
35
- /** LLM-generated narrative text from the mutation */
36
- outputPayload?: LlmOutputPayload | null;
37
5
  onUpgrade?: () => void;
38
6
  onPresent?: () => void;
39
7
  onReset?: () => void;
40
8
  }
41
- export declare function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload, onUpgrade, onPresent, onReset, }: TaxAxisProspectReportProps): React.JSX.Element;
9
+ export declare function TaxAxisProspectReport({ profile, onUpgrade, onPresent, onReset, }: TaxAxisProspectReportProps): React.JSX.Element;
@@ -35,48 +35,47 @@ const SampleAnalysisPreview_1 = require("./SampleAnalysisPreview");
35
35
  const ProspectDocuments_1 = require("./ProspectDocuments");
36
36
  const ProspectNextSteps_1 = require("./ProspectNextSteps");
37
37
  const ProspectPrintView_1 = require("./ProspectPrintView");
38
- function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload, onUpgrade, onPresent, onReset, }) {
39
- var _a, _b, _c, _d, _e, _f, _g;
38
+ function TaxAxisProspectReport({ profile, onUpgrade, onPresent, onReset, }) {
40
39
  const bizName = profile.bizName || "Client";
41
40
  const [confirmReset, setConfirmReset] = (0, react_1.useState)(false);
42
41
  const [printMode, setPrintMode] = (0, react_1.useState)(false);
43
- // ═══ ELIGIBLE STRATEGIES (client-side, for rendering strategy cards) ═══
42
+ // ═══ ELIGIBLE STRATEGIES ═══
44
43
  const eligible = (0, react_1.useMemo)(() => (0, compute_1.filterEligibleStrategies)(profile), [profile]);
45
44
  const high = eligible.filter(s => s.priority === "HIGH");
46
45
  const quick = eligible.filter(s => s.priority === "QUICK WIN");
46
+ // Top 3 by score for the sales narrative
47
47
  const top3 = (0, react_1.useMemo)(() => [...eligible].sort((a, b) => b.score - a.score).slice(0, 3), [eligible]);
48
48
  const remaining = eligible.length - top3.length;
49
49
  const remainingNames = eligible.filter(s => !top3.includes(s)).slice(0, 4).map(s => s.name);
50
- // ═══ SAVINGS RANGE backend-authoritative when available ═══
50
+ // ═══ SAVINGS RANGE (dynamic computation) ═══
51
51
  const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(profile), [profile]);
52
- const displayLo = backendComputePayload
53
- ? Math.round(backendComputePayload.savingsLo / 1000)
54
- : Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0) / 1000);
55
- const displayHi = backendComputePayload
56
- ? Math.round(backendComputePayload.savingsHi / 1000)
57
- : Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0) / 1000);
58
- // ═══ GAP BLOCK — backend-authoritative when available ═══
59
- const fedRatePct = (_a = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.fedRatePct) !== null && _a !== void 0 ? _a : (parseFloat((profile.federalRate || "24").replace("%", "")) || 24);
60
- const stateRatePct = (_b = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.stateRatePct) !== null && _b !== void 0 ? _b : (parseFloat(profile.stateRate || "4.95") || 4.95);
61
- const currentTax = (_c = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.currentTax) !== null && _c !== void 0 ? _c : Math.round(((0, compute_1.parseNum)(profile.netIncome) || Math.round((0, compute_1.parseNum)(profile.revenue) * 0.20))
62
- * (fedRatePct + stateRatePct) / 100);
63
- const realizedSavings = (_d = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedSavings) !== null && _d !== void 0 ? _d : Math.min(Math.round((displayLo + displayHi) / 2 * 1000 * 0.325), Math.round(currentTax * 0.6));
64
- const optimizedTax = (_e = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.optimizedTax) !== null && _e !== void 0 ? _e : (currentTax - realizedSavings);
65
- const realizedPctOfCurrent = (_f = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedPctOfCurrent) !== null && _f !== void 0 ? _f : (currentTax > 0 ? Math.round((realizedSavings / currentTax) * 100) : 0);
66
- // ═══ BAR CHART DATA ═══
67
- const barData = (_g = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.barData) !== null && _g !== void 0 ? _g : (() => {
68
- const totalMid = eligible.reduce((a, s) => { var _a, _b; const c = computed.get(s.rank); return a + (((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2; }, 0);
69
- return top3.map(s => { var _a, _b; const c = computed.get(s.rank); const mid = Math.round((((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2); return { name: s.name, mid, pct: totalMid > 0 ? Math.round(mid / totalMid * 100) : 0 }; });
70
- })();
52
+ const displayLo = Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0) / 1000);
53
+ const displayHi = Math.round(eligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0) / 1000);
54
+ // Savings bar chart data top 3 strategies with midpoint + percentage
55
+ const totalMid = eligible.reduce((a, s) => { var _a, _b; const c = computed.get(s.rank); return a + (((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2; }, 0);
56
+ const barData = top3.map(s => { var _a, _b; const c = computed.get(s.rank); const mid = Math.round((((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2); return { name: s.name, mid, pct: totalMid > 0 ? Math.round(mid / totalMid * 100) : 0 }; });
71
57
  const maxMid = Math.max(...barData.map(b => b.mid), 1);
58
+ // ═══ GAP BLOCK MATH — Current vs Optimized tax position ═══
59
+ const rev = (0, compute_1.parseNum)(profile.revenue);
60
+ const netIncome = (0, compute_1.parseNum)(profile.netIncome) || Math.round(rev * 0.20);
61
+ const fedRatePct = parseFloat((profile.federalRate || "24").replace("%", "")) || 24;
62
+ const stateRatePct = parseFloat(profile.stateRate || "4.95") || 4.95;
63
+ const combinedRate = (fedRatePct + stateRatePct) / 100;
64
+ const currentTax = Math.round(netIncome * combinedRate);
65
+ const theoreticalMidK = (displayLo + displayHi) / 2;
66
+ const realizedSavings = Math.min(Math.round(theoreticalMidK * 1000 * 0.325), Math.round(currentTax * 0.6));
67
+ const optimizedTax = currentTax - realizedSavings;
68
+ const realizedPctOfCurrent = currentTax > 0 ? Math.round((realizedSavings / currentTax) * 100) : 0;
69
+ // Section numbering — shifts when Additional Strategies section is present
70
+ const sectionNums = remaining > 0
71
+ ? { recommended: "02", additional: "03", sample: "04", documents: "05", nextSteps: "06" }
72
+ : { recommended: "02", additional: null, sample: "03", documents: "04", nextSteps: "05" };
72
73
  // ═══ DOCUMENT GROUPING ═══
73
74
  const allDocsNeeded = [...new Set(eligible.flatMap(s => { var _a; return ((_a = strategyProspect_1.STRATEGY_PROSPECT[s.rank]) === null || _a === void 0 ? void 0 : _a.docsNeeded) || []; }))];
74
75
  const requiredDocs = ["Profit & Loss Statement (current year)", "Balance Sheet (current year)"];
75
76
  const recommendedDocs = ["Federal Tax Return (most recent)", "W-2 / Officer Compensation records"];
76
77
  const conditionalDocs = allDocsNeeded.filter(d => !requiredDocs.includes(d) && !recommendedDocs.includes(d)).slice(0, 5);
77
- const sectionNums = remaining > 0
78
- ? { recommended: "02", additional: "03", sample: "04", documents: "05", nextSteps: "06" }
79
- : { recommended: "02", additional: null, sample: "03", documents: "04", nextSteps: "05" };
78
+ // ═══ PRINT HANDLER ═══
80
79
  const handlePrint = () => {
81
80
  setPrintMode(true);
82
81
  requestAnimationFrame(() => {
@@ -87,16 +86,18 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
87
86
  });
88
87
  });
89
88
  };
89
+ // ═══ PRINT MODE — Light theme layout ═══
90
90
  if (printMode) {
91
91
  return (react_1.default.createElement(ProspectPrintView_1.ProspectPrintView, { profile: profile, bizName: bizName, displayLo: displayLo, displayHi: displayHi, currentTax: currentTax, optimizedTax: optimizedTax, realizedSavings: realizedSavings, realizedPctOfCurrent: realizedPctOfCurrent, fedRatePct: fedRatePct, stateRatePct: stateRatePct, eligible: eligible, high: high, quick: quick, top3: top3, computed: computed, fmtTax: compute_1.fmtTax, fmtSavings: compute_1.fmtSavings, requiredDocs: requiredDocs, recommendedDocs: recommendedDocs, conditionalDocs: conditionalDocs }));
92
92
  }
93
+ // ═══ INTERACTIVE MODE — Dark theme layout ═══
93
94
  return (react_1.default.createElement("div", null,
94
95
  react_1.default.createElement(ProspectCover_1.ProspectCover, { profile: profile, bizName: bizName, displayLo: displayLo, displayHi: displayHi }),
95
96
  react_1.default.createElement(TaxPositionGap_1.TaxPositionGap, { bizName: bizName, currentTax: currentTax, optimizedTax: optimizedTax, realizedSavings: realizedSavings, realizedPctOfCurrent: realizedPctOfCurrent, fedRatePct: fedRatePct, stateRatePct: stateRatePct, eligibleCount: eligible.length, fmtTax: compute_1.fmtTax, fmtSavings: compute_1.fmtSavings }),
96
97
  react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "24px 28px", marginBottom: 24, boxShadow: theme_1.T.shadow } },
97
98
  react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.15em", color: theme_1.T.text4, marginBottom: 16, fontFamily: theme_1.T.mono } }, "AT A GLANCE"),
98
99
  react_1.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 16 } }, [
99
- { v: `$${displayLo}K–$${displayHi}K`, l: "Est. Savings" },
100
+ { v: `$${displayLo}K\u2013$${displayHi}K`, l: "Est. Savings" },
100
101
  { v: String(eligible.length), l: "Strategies Found" },
101
102
  { v: String(high.length), l: "High Impact" },
102
103
  { v: String(quick.length), l: "Quick Wins" },
@@ -108,30 +109,29 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
108
109
  react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "01"),
109
110
  react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: theme_1.T.text4, fontFamily: theme_1.T.body } }, "EXECUTIVE SUMMARY")),
110
111
  react_1.default.createElement("div", { style: { height: 2, background: theme_1.T.accent, width: 48, marginBottom: 16 } }),
111
- (outputPayload === null || outputPayload === void 0 ? void 0 : outputPayload.execSummary) ? (react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8, marginBottom: 12 } }, outputPayload.execSummary)) : (react_1.default.createElement(react_1.default.Fragment, null,
112
- react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8, marginBottom: 12 } },
113
- "We evaluated ",
114
- bizName,
115
- "'s tax position against 25 federal strategies and identified ",
116
- react_1.default.createElement("strong", { style: { color: theme_1.T.white } },
117
- eligible.length,
118
- " applicable opportunities"),
119
- " with combined estimated savings of ",
120
- react_1.default.createElement("strong", { style: { color: theme_1.T.accentLt } },
121
- "$",
122
- displayLo,
123
- "K\u2013$",
124
- displayHi,
125
- "K annually"),
126
- ". ",
127
- high.length,
128
- " strategies could have significant impact."),
129
- react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8 } },
130
- "The top ",
131
- top3.length,
132
- " strategies are detailed below.",
133
- quick.length > 0 ? ` ${quick.length} can be implemented this week with no structural changes.` : "",
134
- " These are profile-based estimates \u2014 uploading financial documents will refine savings calculations and unlock all 25 strategies.")))),
112
+ react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8, marginBottom: 12 } },
113
+ "We evaluated ",
114
+ bizName,
115
+ "'s tax position against 25 federal strategies and identified ",
116
+ react_1.default.createElement("strong", { style: { color: theme_1.T.white } },
117
+ eligible.length,
118
+ " applicable opportunities"),
119
+ " with combined estimated savings of ",
120
+ react_1.default.createElement("strong", { style: { color: theme_1.T.accentLt } },
121
+ "$",
122
+ displayLo,
123
+ "K\u2013$",
124
+ displayHi,
125
+ "K annually"),
126
+ ". ",
127
+ high.length,
128
+ " strategies could have significant impact."),
129
+ react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8 } },
130
+ "The top ",
131
+ top3.length,
132
+ " strategies are detailed below.",
133
+ quick.length > 0 ? ` ${quick.length} can be implemented this week with no structural changes.` : "",
134
+ " These are profile-based estimates \u2014 uploading financial documents will refine savings calculations and unlock all 25 strategies.")),
135
135
  react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "20px 24px", marginBottom: 24, boxShadow: theme_1.T.shadow } },
136
136
  react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, color: theme_1.T.text4, textTransform: "uppercase", letterSpacing: "0.12em", marginBottom: 12, fontFamily: theme_1.T.mono } }, "SAVINGS DISTRIBUTION"),
137
137
  barData.map((b, i) => (react_1.default.createElement("div", { key: i, style: { display: "flex", alignItems: "center", gap: 10, marginBottom: i < barData.length - 1 ? 8 : 0 } },
@@ -151,10 +151,7 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
151
151
  react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "02"),
152
152
  react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: theme_1.T.text4, fontFamily: theme_1.T.body } }, "RECOMMENDED STRATEGIES")),
153
153
  react_1.default.createElement("div", { style: { height: 2, background: theme_1.T.accent, width: 48, marginBottom: 16 } }),
154
- react_1.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 14 } }, top3.map((s, i) => {
155
- var _a;
156
- return (react_1.default.createElement(ProspectStrategyCard_1.ProspectStrategyCard, { key: s.rank, strategy: s, index: i, profile: profile, computed: computed, llmNarrative: (_a = outputPayload === null || outputPayload === void 0 ? void 0 : outputPayload.strategyNarratives) === null || _a === void 0 ? void 0 : _a[`S${s.rank}`] }));
157
- }))),
154
+ react_1.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 14 } }, top3.map((s, i) => (react_1.default.createElement(ProspectStrategyCard_1.ProspectStrategyCard, { key: s.rank, strategy: s, index: i, profile: profile, computed: computed }))))),
158
155
  remaining > 0 && (react_1.default.createElement("div", { style: { marginBottom: 24 } },
159
156
  react_1.default.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 10, marginBottom: 6 } },
160
157
  react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "03"),
@@ -165,8 +162,10 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
165
162
  "+ ",
166
163
  remaining,
167
164
  " more strategies identified"),
168
- react_1.default.createElement("div", { style: { fontSize: 13, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.7, marginBottom: 10 } }, (outputPayload === null || outputPayload === void 0 ? void 0 : outputPayload.additionalStrategiesBlurb)
169
- || `Including: ${remainingNames.join(", ")}${remaining > 4 ? " and more" : ""}`),
165
+ react_1.default.createElement("div", { style: { fontSize: 13, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.7, marginBottom: 10 } },
166
+ "Including: ",
167
+ remainingNames.join(", "),
168
+ remaining > 4 ? " and more" : ""),
170
169
  react_1.default.createElement("div", { style: { fontSize: 12, color: theme_1.T.text3, fontFamily: theme_1.T.body, lineHeight: 1.6, borderLeft: `3px solid ${theme_1.T.accent}`, paddingLeft: 12 } }, "Full analysis with documents unlocks specific savings estimates for every eligible strategy.")))),
171
170
  react_1.default.createElement(SampleAnalysisPreview_1.SampleAnalysisPreview, { sectionNum: sectionNums.sample, eligibleCount: eligible.length }),
172
171
  react_1.default.createElement(ProspectDocuments_1.ProspectDocuments, { sectionNum: sectionNums.documents, requiredDocs: requiredDocs, recommendedDocs: recommendedDocs, conditionalDocs: conditionalDocs }),
@@ -1,65 +1,49 @@
1
1
  import type { Strategy, ComputedMap } from "../types";
2
- export interface EngineStrategyAnalysis {
3
- strategy_id?: string;
4
- strategy_name?: string;
5
- lo?: number;
6
- hi?: number;
7
- estimatedSavings?: {
8
- min?: number;
9
- max?: number;
10
- };
11
- priority?: string;
12
- score?: number;
13
- weighted_score?: number;
14
- quick_win?: boolean;
15
- timeline_bucket?: string;
16
- calculation_trace?: {
17
- irs_cite?: string;
18
- forms_required?: string[];
19
- source_documents?: Array<{
20
- field_label: string;
21
- value_display: string;
22
- source_hint: string;
23
- }>;
24
- position_strength?: string;
25
- specialist_note?: string;
26
- };
27
- why_it_applies?: string;
28
- next_step?: string;
29
- risk_disclosure?: string;
30
- }
31
- export interface EngineCpaWorkflow {
32
- engagement_recommendations?: string[];
2
+ /** Raw engine output blob from the LLM run. */
3
+ export type EngineOutput = Record<string, unknown> | null;
4
+ /** Pre-computed formula engine results from outputPayload.meta.preComputed. */
5
+ export interface PreComputedResults {
6
+ results: Record<string, PreComputedStrategyResult>;
7
+ completedCount?: number;
8
+ excludedCount?: number;
9
+ failedCount?: number;
10
+ totalSavingsLow?: number;
11
+ totalSavingsHigh?: number;
12
+ computedAt?: string;
33
13
  }
34
- export interface EngineOutput {
35
- strategy_analysis?: EngineStrategyAnalysis[];
36
- cpa_workflow?: EngineCpaWorkflow;
37
- client_summary?: {
38
- opening?: string;
39
- closing?: string;
40
- data_quality_note?: string;
41
- interaction_warning?: string;
42
- strategies?: Array<{
43
- strategy_name?: string;
44
- why_it_applies?: string;
45
- next_step?: string;
46
- }>;
47
- };
48
- nexus_flags?: Array<{
49
- state: string;
50
- nexus_type: string;
51
- flag_text: string;
52
- }>;
53
- eligibility_screening?: {
54
- excluded_strategies?: Array<{
55
- strategy_id: string;
56
- reason: string;
57
- irs_cite: string;
58
- }>;
59
- };
14
+ export interface PreComputedStrategyResult {
15
+ strategy_id: string;
16
+ status: "COMPLETE" | "EXCLUDED" | "FAILED";
17
+ result_gross: number | null;
18
+ confidence_interval?: {
19
+ low: number;
20
+ high: number;
21
+ basis?: string;
22
+ } | null;
23
+ trace?: Record<string, unknown>;
60
24
  }
25
+ /** Adapter result passed to client-report / preparer-workpaper components. */
61
26
  export interface EngineOutputAdapterResult {
62
27
  strategies: Strategy[];
63
28
  computedMap: ComputedMap;
64
29
  }
65
- export declare function useEngineOutput(engineOutput: EngineOutput | null | undefined): EngineOutputAdapterResult | null;
30
+ /** Single strategy analysis entry from the engine. */
31
+ export interface EngineStrategyAnalysis {
32
+ strategy_id: string;
33
+ strategy_name: string;
34
+ priority_tier: string;
35
+ weighted_score: number;
36
+ quick_win: boolean;
37
+ engagement_recommendation: string;
38
+ risk_disclosure?: string | null;
39
+ calculation_trace?: Record<string, unknown>;
40
+ _reconciledPreComputed?: boolean;
41
+ [key: string]: unknown;
42
+ }
43
+ /** CPA workflow section from the engine. */
44
+ export interface EngineCpaWorkflow {
45
+ workpaper_codes: string[] | null;
46
+ engagement_recommendations: string[];
47
+ [key: string]: unknown;
48
+ }
49
+ export declare function useEngineOutput(engineOutput: EngineOutput, preComputed?: PreComputedResults | null): EngineOutputAdapterResult | null;