@paro.io/expert-shared-components 1.14.67 → 1.14.69

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.
@@ -70,12 +70,6 @@ const PERIOD_OPTIONS = ["Full Year", "YTD"];
70
70
  const YEAR_OPTIONS = ["2024", "2025", "2026"];
71
71
  function ClientParametersSection({ userContext = "expert", }) {
72
72
  const { control, formState: { errors }, } = (0, react_hook_form_1.useFormContext)();
73
- // TECH DEBT (post-BDO): Analysis Period dropdown is local useState only —
74
- // value does not persist to form state because ClientProfile has no `period`
75
- // field. computeAllStrategies does not read this value. Spec defect.
76
- // Resolve post-BDO by either adding `period` to ClientProfile or removing
77
- // the field entirely.
78
- const [analysisPeriod, setAnalysisPeriod] = (0, react_1.useState)("Full Year");
79
73
  const [expanded, setExpanded] = (0, react_1.useState)(true);
80
74
  return (react_1.default.createElement(TaxAxisCard_1.TaxAxisCard, null,
81
75
  react_1.default.createElement("div", { onClick: () => setExpanded((prev) => !prev), className: "flex items-center justify-between cursor-pointer", style: { marginBottom: expanded ? 14 : 0 } },
@@ -106,9 +100,12 @@ function ClientParametersSection({ userContext = "expert", }) {
106
100
  errors.industry && react_1.default.createElement("div", { className: errCls }, errors.industry.message)),
107
101
  react_1.default.createElement("div", null,
108
102
  react_1.default.createElement("label", { className: labelCls }, "Analysis Period"),
109
- react_1.default.createElement("div", { className: "relative" },
110
- react_1.default.createElement("select", { value: analysisPeriod, onChange: e => setAnalysisPeriod(e.target.value), className: selectCls }, PERIOD_OPTIONS.map(o => (react_1.default.createElement("option", { key: o, value: o, className: "bg-tax-axis-surface-2" }, o)))),
111
- react_1.default.createElement(Chevron, null))),
103
+ react_1.default.createElement(react_hook_form_1.Controller, { name: "period", control: control, render: ({ field }) => {
104
+ var _a;
105
+ return (react_1.default.createElement("div", { className: "relative" },
106
+ react_1.default.createElement("select", Object.assign({}, field, { value: (_a = field.value) !== null && _a !== void 0 ? _a : "Full Year", className: selectCls }), PERIOD_OPTIONS.map(o => (react_1.default.createElement("option", { key: o, value: o, className: "bg-tax-axis-surface-2" }, o)))),
107
+ react_1.default.createElement(Chevron, null)));
108
+ } })),
112
109
  react_1.default.createElement("div", null,
113
110
  react_1.default.createElement("label", { className: labelCls }, "Annual Revenue"),
114
111
  react_1.default.createElement(react_hook_form_1.Controller, { name: "revenue", control: control, render: ({ field }) => (react_1.default.createElement("div", { className: "relative" },
@@ -106,13 +106,34 @@ function StrategyRadar({ profile }) {
106
106
  const eligibleStrategies = (0, compute_1.filterEligibleStrategies)(safeProfile);
107
107
  const eligibleIds = new Set(eligibleStrategies.map(s => String(s.rank)));
108
108
  const stratCount = eligibleStrategies.length;
109
- const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(safeProfile), [profile]);
110
- const loSum = eligibleStrategies.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);
111
- const hiSum = eligibleStrategies.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);
109
+ // Serialize to primitive — watch() may return a reference-stable proxy,
110
+ // so [profile] as a dep would never re-fire the memo on field changes
111
+ const profileKey = JSON.stringify(profile);
112
+ const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(safeProfile), [profileKey]);
113
+ const fedRate = parseFloat((profile.federalRate || "24").replace("%", "")) / 100 || 0.24;
114
+ const stateRate = parseFloat(profile.stateRate || "4.95") / 100;
115
+ const netIncome = parseInt((profile.netIncome || "0").replace(/,/g, "")) || Math.round(rev * 0.3);
116
+ let loSum = eligibleStrategies.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);
117
+ let hiSum = eligibleStrategies.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);
118
+ // Tax-capacity cap: savings cannot exceed approximate total tax liability
119
+ const approxTaxLiability = Math.round(netIncome * (fedRate + stateRate));
120
+ if (approxTaxLiability > 0) {
121
+ hiSum = Math.min(hiSum, approxTaxLiability);
122
+ loSum = Math.min(loSum, hiSum);
123
+ // If cap squashed lo close to hi, re-spread to maintain a meaningful range
124
+ if (hiSum > 0 && loSum >= hiSum * 0.9) {
125
+ loSum = Math.round(hiSum / 3);
126
+ }
127
+ }
128
+ // Aggregate spread constraint: max 3× ratio between hi and lo
129
+ if (hiSum > 0 && loSum > 0 && hiSum / loSum > 3.0) {
130
+ loSum = Math.round(hiSum / 3.0);
131
+ }
112
132
  const highImpact = eligibleStrategies.filter(s => s.priority === "HIGH").length;
113
133
  const quickWins = eligibleStrategies.filter(s => s.priority === "QUICK WIN").length;
114
134
  const obbbaNew = eligibleStrategies.filter(s => s.cat === "obbba_new").length;
115
- const hasPTE = states.some(s => states_1.PTE_STATES.has(s));
135
+ // PTE elections are only available for pass-through entities; C-Corps are ineligible
136
+ const hasPTE = entity !== "C-Corporation" && states.some(s => states_1.PTE_STATES.has(s));
116
137
  const hasNoTax = states.some(s => states_1.NO_INCOME_TAX.has(s));
117
138
  const total = RADAR_NODES.length;
118
139
  // G4 — Sizing: compact vs expanded
@@ -31,6 +31,7 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
31
31
  hsaContributions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
32
32
  wotcHires: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
33
33
  familyEmployed: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
34
+ period: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
34
35
  }>, import("yup/lib/object").AnyObject, import("yup/lib/object").TypeOfShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
35
36
  bizName: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
36
37
  cpaName: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
@@ -62,6 +63,7 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
62
63
  hsaContributions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
63
64
  wotcHires: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
64
65
  familyEmployed: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
66
+ period: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
65
67
  }>>, import("yup/lib/object").AssertsShape<import("yup/lib/object").Assign<import("yup/lib/object").ObjectShape, {
66
68
  bizName: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
67
69
  cpaName: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
@@ -93,5 +95,6 @@ export declare const intakeSchema: Yup.ObjectSchema<import("yup/lib/object").Ass
93
95
  hsaContributions: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
94
96
  wotcHires: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
95
97
  familyEmployed: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
98
+ period: Yup.StringSchema<string, import("yup/lib/types").AnyObject, string>;
96
99
  }>>>;
97
100
  export declare const intakeDefaultValues: Partial<ClientProfile>;
@@ -56,6 +56,7 @@ exports.intakeSchema = Yup.object().shape({
56
56
  hsaContributions: Yup.string().ensure(),
57
57
  wotcHires: Yup.string().ensure(),
58
58
  familyEmployed: Yup.string().ensure(),
59
+ period: Yup.string().ensure(),
59
60
  });
60
61
  exports.intakeDefaultValues = {
61
62
  bizName: "",
@@ -88,4 +89,5 @@ exports.intakeDefaultValues = {
88
89
  hsaContributions: "0",
89
90
  wotcHires: "No",
90
91
  familyEmployed: "No",
92
+ period: "Full Year",
91
93
  };
@@ -1,151 +1,26 @@
1
1
  import type { Strategy, ComputedMap } from "../types";
2
- export interface SourceDocumentRef {
3
- field_label: string;
4
- value_display: string;
5
- source_hint: string;
6
- }
7
- export interface EngineCalculationTrace {
8
- strategy_id: string;
9
- branch_executed: string | null;
10
- formula: string;
11
- inputs: Record<string, unknown>;
12
- result_gross: number | null;
13
- confidence_interval: {
14
- low: number;
15
- high: number;
16
- basis: string;
17
- } | null;
18
- position_strength: string;
19
- irs_cite: string;
20
- data_quality_flag: string | null;
21
- forms_required: string[];
22
- deadline_flag: string | null;
23
- amt_flag: boolean;
24
- source_documents?: SourceDocumentRef[];
25
- specialist_note?: string | null;
2
+ /** Raw engine output blob from the LLM run. */
3
+ export type EngineOutput = Record<string, unknown> | null;
4
+ /** Adapter result passed to client-report / preparer-workpaper components. */
5
+ export interface EngineOutputAdapterResult {
6
+ strategies: Strategy[];
7
+ computedMap: ComputedMap;
26
8
  }
9
+ /** Single strategy analysis entry from the engine. */
27
10
  export interface EngineStrategyAnalysis {
28
11
  strategy_id: string;
29
12
  strategy_name: string;
30
- priority_tier: "HIGH" | "MEDIUM" | "LOW" | "NOT_RECOMMENDED";
13
+ priority_tier: string;
31
14
  weighted_score: number;
32
- scores: {
33
- financial_impact: number;
34
- applicability: number;
35
- complexity: number;
36
- risk: number;
37
- cash_flow: number;
38
- };
39
15
  quick_win: boolean;
40
- calculation_trace: EngineCalculationTrace;
41
16
  engagement_recommendation: string;
42
- risk_disclosure: string | null;
43
- }
44
- export interface EngineClientSummaryStrategy {
45
- slot: number;
46
- strategy_name: string;
47
- savings_range: string;
48
- why_it_applies: string;
49
- next_step: string;
50
- quick_win_badge: boolean;
51
- deadline_flag?: string | null;
17
+ calculation_trace?: Record<string, unknown>;
18
+ [key: string]: unknown;
52
19
  }
20
+ /** CPA workflow section from the engine. */
53
21
  export interface EngineCpaWorkflow {
54
22
  workpaper_codes: string[] | null;
55
- section_6694_notices: Array<{
56
- strategy_id: string;
57
- position_strength: string;
58
- notice: string;
59
- }>;
60
- prior_year_flags: string[];
61
- conflict_of_interest_checklist: Array<{
62
- strategy_id: string;
63
- conflict_prompt: string;
64
- confirmed_no_conflict: boolean;
65
- }>;
66
23
  engagement_recommendations: string[];
24
+ [key: string]: unknown;
67
25
  }
68
- export interface EngineOutput {
69
- engagement_id: string;
70
- generated_at: string;
71
- algorithm_version: string;
72
- business_profile: {
73
- entity_type: string;
74
- industry: string;
75
- industry_vertical: string;
76
- states: string[];
77
- data_years: number;
78
- confidence_tier: string;
79
- effective_tax_rate: number | null;
80
- data_quality_flags: string[];
81
- };
82
- eligibility_screening: {
83
- excluded_strategies: Array<{
84
- strategy_id: string;
85
- status: "EXCLUDED";
86
- reason: string;
87
- irs_cite: string;
88
- }>;
89
- };
90
- strategy_analysis: EngineStrategyAnalysis[];
91
- strategy_interaction_warnings: Array<{
92
- strategy_pair: [string, string];
93
- warning_text: string;
94
- }>;
95
- implementation_roadmap: {
96
- quick_wins: Array<{
97
- strategy_id: string;
98
- action: string;
99
- deadline?: string | null;
100
- }>;
101
- immediate: Array<{
102
- strategy_id: string;
103
- action: string;
104
- deadline?: string | null;
105
- }>;
106
- thirty_day: Array<{
107
- strategy_id: string;
108
- action: string;
109
- deadline?: string | null;
110
- }>;
111
- ninety_day: Array<{
112
- strategy_id: string;
113
- action: string;
114
- deadline?: string | null;
115
- }>;
116
- annual: Array<{
117
- strategy_id: string;
118
- action: string;
119
- deadline?: string | null;
120
- }>;
121
- };
122
- cpa_workflow: EngineCpaWorkflow;
123
- client_summary: {
124
- opening: string;
125
- strategies: EngineClientSummaryStrategy[];
126
- data_quality_note: string | null;
127
- interaction_warning: string | null;
128
- closing: string;
129
- };
130
- risk_disclosures: string[];
131
- client_risk_disclosures: {
132
- accuracy_penalty_warning: string | null;
133
- applies_to_strategies: string[];
134
- };
135
- amt_flags: Array<{
136
- trigger: string;
137
- strategy_id: string;
138
- flag_text: string;
139
- }>;
140
- nexus_flags: Array<{
141
- state: string;
142
- nexus_type: string;
143
- flag_text: string;
144
- }>;
145
- }
146
- export interface EngineOutputAdapterResult {
147
- strategies: Strategy[];
148
- computedMap: ComputedMap;
149
- engineOutput: EngineOutput;
150
- }
151
- export declare function useEngineOutput(engineOutput: EngineOutput | null | undefined): EngineOutputAdapterResult | null;
26
+ export declare function useEngineOutput(_engineOutput: EngineOutput): EngineOutputAdapterResult | null;
@@ -1,161 +1,12 @@
1
1
  "use strict";
2
- // ═══════════════════════════════════════════════════════════════════
3
- // useEngineOutput adapts live EngineOutput from paro-graphql-api
4
- // into the Strategy[] + ComputedMap shapes the FE components consume.
5
- //
6
- // Strategy: use the static STRATEGIES catalog as a structural template
7
- // (rank, cat, entities, authority, etc.) and override savings/priority/score
8
- // from the live engine data. Falls back to static data when a strategy_id
9
- // has no match in the catalog (future-proofing for new strategies).
10
- // ═══════════════════════════════════════════════════════════════════
2
+ // useEngineOutput — minimal stub to unblock builds.
3
+ // TODO: centralise narrative-field mapping (why_it_applies, source_documents, specialistNote) here.
11
4
  Object.defineProperty(exports, "__esModule", { value: true });
12
5
  exports.useEngineOutput = useEngineOutput;
13
6
  const react_1 = require("react");
14
- const data_1 = require("../data");
15
- // ── Strategy ID rank mapping (S1..S26 rank 1..25 catalog) ───────
16
- // Maps engine strategy_ids like "S2", "S5" etc. to catalog ranks.
17
- // Built once from the static STRATEGIES array by matching code/name heuristics.
18
- const ENGINE_ID_TO_RANK = {
19
- S1: 1, // Business Entity Structure (S-Corp election)
20
- S2: 2, // Section 179
21
- S3: 3, // Bonus Depreciation §168(k)
22
- S4: 4, // QBI §199A
23
- S5: 5, // R&D Credit §41
24
- S6: 6, // WOTC §51
25
- S7: 7, // Retirement Plan
26
- S8: 8, // Cost Segregation
27
- S9: 9, // HSA
28
- S10: 10, // Business Meals
29
- S11: 11, // Professional Services / Legal
30
- S12: 12, // Home Office
31
- S13: 13, // Health Care Credit §45R
32
- S14: 14, // Charitable / DAF
33
- S15: 15, // SALT PTE
34
- S16: 16, // §163(j) Interest
35
- S17: 17, // Income Deferral
36
- S18: 18, // Opportunity Zone §1400Z
37
- S19: 19, // §179D Energy
38
- S20: 20, // Accounting Method
39
- S21: 21, // OBBBA Overtime
40
- S22: 22, // OBBBA Tips / Overtime
41
- S23: 23, // §174A R&E catch-up
42
- S24: 24, // Childcare §45F
43
- S25: 25, // OBBBA Trump Accounts
44
- };
45
- const CATALOG_BY_RANK = new Map(data_1.STRATEGIES.map((s) => [s.rank, s]));
46
- // ── Priority tier mapping ─────────────────────────────────────────────
47
- function mapPriority(tier, quickWin) {
48
- if (quickWin)
49
- return "QUICK WIN";
50
- if (tier === "HIGH")
51
- return "HIGH";
52
- if (tier === "MEDIUM")
53
- return "MEDIUM";
54
- if (tier === "LOW")
55
- return "LOW";
56
- return "NOT RECOMMENDED";
57
- }
58
- // ── Timeline bucket from implementation_roadmap ───────────────────────
59
- function resolveTimelineBucket(strategyId, roadmap) {
60
- if (roadmap.quick_wins.some((r) => r.strategy_id === strategyId))
61
- return "now";
62
- if (roadmap.immediate.some((r) => r.strategy_id === strategyId))
63
- return "now";
64
- if (roadmap.thirty_day.some((r) => r.strategy_id === strategyId))
65
- return "30d";
66
- if (roadmap.ninety_day.some((r) => r.strategy_id === strategyId))
67
- return "90d";
68
- if (roadmap.annual.some((r) => r.strategy_id === strategyId))
69
- return "filing";
70
- return "filing";
71
- }
72
- // ── CalculationTrace → StrategyTraceStep[] ───────────────────────────
73
- function traceFromEngine(trace) {
74
- var _a, _b;
75
- const steps = [];
76
- // Emit formula as step 1
77
- if (trace.formula) {
78
- steps.push({
79
- n: 1,
80
- step: "Formula",
81
- formula: trace.formula,
82
- result: trace.result_gross != null ? `$${trace.result_gross.toLocaleString()}` : "See CI",
83
- src: trace.irs_cite || "",
84
- });
85
- }
86
- // Emit confidence interval as step 2 when present
87
- if (((_a = trace.confidence_interval) === null || _a === void 0 ? void 0 : _a.low) != null && ((_b = trace.confidence_interval) === null || _b === void 0 ? void 0 : _b.high) != null) {
88
- steps.push({
89
- n: 2,
90
- step: "Confidence range",
91
- formula: `Low × ${trace.confidence_interval.basis}`,
92
- result: `$${trace.confidence_interval.low.toLocaleString()} – $${trace.confidence_interval.high.toLocaleString()}`,
93
- src: trace.irs_cite || "",
94
- });
95
- }
96
- return steps;
97
- }
98
- function adaptEngineOutput(engineOutput) {
99
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
100
- const strategies = [];
101
- const computedMap = new Map();
102
- for (const analysis of engineOutput.strategy_analysis) {
103
- if (analysis.priority_tier === "NOT_RECOMMENDED")
104
- continue;
105
- const rank = ENGINE_ID_TO_RANK[analysis.strategy_id];
106
- const template = rank != null ? CATALOG_BY_RANK.get(rank) : undefined;
107
- const trace = analysis.calculation_trace;
108
- const ci = trace === null || trace === void 0 ? void 0 : trace.confidence_interval;
109
- const lo = (_b = (_a = ci === null || ci === void 0 ? void 0 : ci.low) !== null && _a !== void 0 ? _a : template === null || template === void 0 ? void 0 : template.lo) !== null && _b !== void 0 ? _b : 0;
110
- const hi = (_d = (_c = ci === null || ci === void 0 ? void 0 : ci.high) !== null && _c !== void 0 ? _c : template === null || template === void 0 ? void 0 : template.hi) !== null && _d !== void 0 ? _d : 0;
111
- const timelineBucket = resolveTimelineBucket(analysis.strategy_id, engineOutput.implementation_roadmap);
112
- const clientSummaryEntry = engineOutput.client_summary.strategies.find((cs) => cs.strategy_name === analysis.strategy_name);
113
- const strategy = {
114
- rank: rank !== null && rank !== void 0 ? rank : 99,
115
- code: (_e = template === null || template === void 0 ? void 0 : template.code) !== null && _e !== void 0 ? _e : analysis.strategy_id,
116
- name: analysis.strategy_name,
117
- cat: (_f = template === null || template === void 0 ? void 0 : template.cat) !== null && _f !== void 0 ? _f : "deductions",
118
- priority: mapPriority(analysis.priority_tier, analysis.quick_win),
119
- score: Math.round(analysis.weighted_score),
120
- entities: (_g = template === null || template === void 0 ? void 0 : template.entities) !== null && _g !== void 0 ? _g : ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
121
- lo,
122
- hi,
123
- timeline: (_h = template === null || template === void 0 ? void 0 : template.timeline) !== null && _h !== void 0 ? _h : timelineBucket,
124
- timelineBucket,
125
- authority: (_j = trace === null || trace === void 0 ? void 0 : trace.irs_cite) !== null && _j !== void 0 ? _j : template === null || template === void 0 ? void 0 : template.authority,
126
- forms: (_l = (_k = trace === null || trace === void 0 ? void 0 : trace.forms_required) === null || _k === void 0 ? void 0 : _k.join(", ")) !== null && _l !== void 0 ? _l : template === null || template === void 0 ? void 0 : template.forms,
127
- warning: (_o = (_m = analysis.risk_disclosure) !== null && _m !== void 0 ? _m : template === null || template === void 0 ? void 0 : template.warning) !== null && _o !== void 0 ? _o : null,
128
- abstract: analysis.engagement_recommendation,
129
- trace: traceFromEngine(trace),
130
- needsPTE: template === null || template === void 0 ? void 0 : template.needsPTE,
131
- onlyIndustry: template === null || template === void 0 ? void 0 : template.onlyIndustry,
132
- excludeIndustry: template === null || template === void 0 ? void 0 : template.excludeIndustry,
133
- preferIndustry: template === null || template === void 0 ? void 0 : template.preferIndustry,
134
- minEmployees: template === null || template === void 0 ? void 0 : template.minEmployees,
135
- needsOwnerComp: template === null || template === void 0 ? void 0 : template.needsOwnerComp,
136
- clientBrief: (_p = clientSummaryEntry === null || clientSummaryEntry === void 0 ? void 0 : clientSummaryEntry.why_it_applies) !== null && _p !== void 0 ? _p : template === null || template === void 0 ? void 0 : template.clientBrief,
137
- action: (_q = clientSummaryEntry === null || clientSummaryEntry === void 0 ? void 0 : clientSummaryEntry.next_step) !== null && _q !== void 0 ? _q : template === null || template === void 0 ? void 0 : template.action,
138
- sourceDocuments: (_r = trace === null || trace === void 0 ? void 0 : trace.source_documents) !== null && _r !== void 0 ? _r : [],
139
- quickWin: analysis.quick_win,
140
- positionStrength: (_s = trace === null || trace === void 0 ? void 0 : trace.position_strength) !== null && _s !== void 0 ? _s : undefined,
141
- specialistNote: (_t = trace === null || trace === void 0 ? void 0 : trace.specialist_note) !== null && _t !== void 0 ? _t : undefined,
142
- };
143
- strategies.push(strategy);
144
- computedMap.set(strategy.rank, {
145
- lo,
146
- hi,
147
- trace: strategy.trace,
148
- });
149
- }
150
- // Sort by weighted_score descending (same ordering the FE uses)
151
- strategies.sort((a, b) => b.score - a.score);
152
- return { strategies, computedMap, engineOutput };
153
- }
154
- // ── React hook ────────────────────────────────────────────────────────
155
- function useEngineOutput(engineOutput) {
156
- return (0, react_1.useMemo)(() => {
157
- if (!engineOutput)
158
- return null;
159
- return adaptEngineOutput(engineOutput);
160
- }, [engineOutput]);
7
+ // ---------------------------------------------------------------------------
8
+ // Hook pass-through stub (returns null, no mapping yet)
9
+ // ---------------------------------------------------------------------------
10
+ function useEngineOutput(_engineOutput) {
11
+ return (0, react_1.useMemo)(() => null, [_engineOutput]);
161
12
  }
@@ -159,5 +159,11 @@ function computeAllStrategies(profile) {
159
159
  results.set(s.rank, { lo: Math.round(s.lo * scale), hi: Math.round(s.hi * scale), sources: s.sources, trace: s.trace });
160
160
  }
161
161
  });
162
+ // Enforce max 3× spread per strategy — tighten lo toward hi if ratio is too wide
163
+ for (const [rank, v] of results) {
164
+ if (v.hi > 0 && v.lo > 0 && v.hi / v.lo > 3.0) {
165
+ results.set(rank, Object.assign(Object.assign({}, v), { lo: Math.round(v.hi / 3.0) }));
166
+ }
167
+ }
162
168
  return results;
163
169
  }
@@ -67,66 +67,66 @@ exports.STRATEGIES = [
67
67
  sources: [{ doc: "QBO", line: "Meals", value: "$8,400", field: "Annual Meals" }],
68
68
  trace: [{ n: 1, step: "Total meals", formula: "QBO", result: "$8,400", src: "QBO" }, { n: 2, step: "Business meals", formula: "Estimated", result: "$6,200", src: "Review" }, { n: 3, step: "Deduction", formula: "$6,200 × 50%", result: "$3,100", src: "§274" }],
69
69
  clientBrief: "Properly documenting business meals recovers $800–$2,200 in tax savings.", action: "Implement meal documentation protocol", cost: "$0" },
70
- { rank: 11, name: "Professional Services Deductions", code: "§162", cat: "deductions", lo: 500, hi: 3500, score: 75, priority: "MEDIUM", timeline: "File now", timelineBucket: "now",
71
- authority: "IRC §162", forms: "Schedule C / 1120S", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
72
- abstract: "Legal, accounting, consulting fees are fully deductible as ordinary and necessary business expenses.",
73
- sources: [{ doc: "QBO", line: "Professional fees", value: "$24,000", field: "Annual Fees" }],
74
- trace: [{ n: 1, step: "Total fees", formula: "QBO + invoices", result: "$24,000", src: "QBO" }, { n: 2, step: "Business allocation", formula: "Categorization review", result: "$22,500", src: "Review" }],
75
- clientBrief: "Ensure all professional service fees are properly categorized and deducted.", action: "Review and categorize all professional service invoices", cost: "$0" },
76
- { rank: 12, name: "Home Office Deduction", code: "§280A", cat: "deductions", excludeIndustry: ["Construction", "Manufacturing", "Restaurant / Hospitality"], lo: 2100, hi: 3200, score: 80, priority: "QUICK WIN", timeline: "This week", timelineBucket: "now",
70
+ { rank: 11, name: "Home Office Deduction", code: "§280A", cat: "deductions", excludeIndustry: ["Construction", "Manufacturing", "Restaurant / Hospitality"], lo: 2100, hi: 3200, score: 80, priority: "QUICK WIN", timeline: "This week", timelineBucket: "now",
77
71
  authority: "IRC §280A(c)(1) · Rev. Proc. 2013-13", forms: "Form 8829", warning: null, entities: ["S-Corporation", "Partnership/LLC", "Sole Proprietorship"],
78
72
  abstract: "Actual expense method produces higher deduction than the $5/sqft simplified method for dedicated workspace.",
79
73
  sources: [{ doc: "Intake", line: "Office sqft", value: "280 sqft", field: "Workspace" }, { doc: "Intake", line: "Total", value: "2,100 sqft", field: "Home" }, { doc: "QBO", line: "Home costs", value: "$43,200", field: "Mortgage + Utils" }],
80
74
  trace: [{ n: 1, step: "Business use %", formula: "280 ÷ 2,100", result: "13.33%", src: "Intake" }, { n: 2, step: "Simplified", formula: "280 × $5", result: "$1,400", src: "Rev. Proc." }, { n: 3, step: "Actual", formula: "13.33% × $43,200", result: "$5,747", src: "QBO" }],
81
75
  clientBrief: "Your home office qualifies for a $5,700 deduction using the actual expense method.", action: "Photograph workspace. Collect mortgage + utility bills.", cost: "$0" },
82
- { rank: 13, name: "Employee Benefit Credits", code: "§45R", cat: "benefits", minEmployees: 1, lo: 2000, hi: 12000, score: 70, priority: "MEDIUM", timeline: "30 days", timelineBucket: "30d",
83
- authority: "IRC §45R", forms: "Form 8941", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC"],
84
- abstract: "Small Business Health Care Tax Credit: up to 50% of employer-paid premiums (≤25 FTEs, avg salary ≤$58K).",
85
- sources: [{ doc: "Payroll", line: "Health premiums", value: "$36,000", field: "Employer Costs" }, { doc: "Payroll", line: "FTEs", value: "4", field: "FTE Count" }],
86
- trace: [{ n: 1, step: "FTE count", formula: "Payroll", result: "4", src: "Payroll" }, { n: 2, step: "Credit", formula: "50% × $36,000", result: "Up to $18,000", src: "§45R" }],
87
- clientBrief: "Your small business may qualify for a credit covering up to 50% of employee health insurance premiums.", action: "Verify SHOP enrollment and FTE calculations", cost: "$0" },
88
- { rank: 14, name: "Charitable Contribution Strategies", code: "§170", cat: "deductions", lo: 500, hi: 4000, score: 60, priority: "LOW", timeline: "Year-end", timelineBucket: "90d",
89
- authority: "IRC §170", forms: "Schedule A · Form 8283", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
90
- abstract: "Appreciated asset donations avoid capital gains. Bunching strategy concentrates deductions into alternating years.",
91
- sources: [{ doc: "Brokerage", line: "Appreciated stock", value: "$15,000", field: "Holdings" }],
92
- trace: [{ n: 1, step: "Current giving", formula: "Annual pattern", result: "$3,000/yr", src: "Intake" }, { n: 2, step: "Bunching", formula: "2 years → 1", result: "$6,000", src: "Strategy" }],
93
- clientBrief: "Donating appreciated stock and bunching contributions maximizes your deduction.", action: "Identify appreciated long-term holdings", cost: "$0" },
76
+ { rank: 12, name: "Accountable Plan", code: "§62", cat: "entity", lo: 1200, hi: 3500, score: 75, priority: "MEDIUM", timeline: "30 days", timelineBucket: "30d",
77
+ authority: "IRC §62 · Treas. Reg. §1.62-2", forms: "Board resolution + plan document", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC"],
78
+ abstract: "Reimburse business expenses tax-free through an accountable plan. Expenses must be substantiated and business-connected.",
79
+ sources: [{ doc: "QBO", line: "Unreimbursed expenses", value: "$8,500", field: "Employee Expenses" }],
80
+ trace: [{ n: 1, step: "Eligible expenses", formula: "Unreimbursed biz expenses", result: "$8,500", src: "QBO" }, { n: 2, step: "Tax savings", formula: "$8,500 × marginal rate", result: "$2,350", src: "Calc." }],
81
+ clientBrief: "An accountable plan lets you reimburse business expenses tax-free instead of taking them as taxable compensation.", action: "Adopt accountable plan via board resolution", cost: "$200–$500" },
82
+ { rank: 13, name: "Augusta Rule", code: "§280A(g)", cat: "deductions", lo: 1500, hi: 6000, score: 70, priority: "MEDIUM", timeline: "30 days", timelineBucket: "30d",
83
+ authority: "IRC §280A(g) · Rev. Rul. 2002-25", forms: "Rental agreement + FMV documentation", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
84
+ abstract: "Rent your personal residence to your business for up to 14 days per year tax-free. Rental income excluded from owner's return; business deducts rent.",
85
+ sources: [{ doc: "Intake", line: "Home FMV rental", value: "$500/day", field: "FMV Rate" }, { doc: "Intake", line: "Business use days", value: "12 days", field: "Meeting Days" }],
86
+ trace: [{ n: 1, step: "FMV rental rate", formula: "Comparable rental survey", result: "$500/day", src: "Market data" }, { n: 2, step: "Annual rental", formula: "12 × $500", result: "$6,000", src: "Calc." }],
87
+ clientBrief: "Renting your home to your business for board meetings creates a tax-free deduction for the company.", action: "Document FMV rental rate and schedule business use days", cost: "$0" },
88
+ { rank: 14, name: "Family Employment", code: "§73", cat: "entity", lo: 500, hi: 4000, score: 60, priority: "LOW", timeline: "Year-end", timelineBucket: "90d",
89
+ authority: "IRC §73 · IRC §3121(b)(3) · Rev. Rul. 73-348", forms: "W-4 + payroll documentation", warning: null, entities: ["S-Corporation", "Partnership/LLC", "Sole Proprietorship"],
90
+ abstract: "Hiring family members shifts income to lower brackets. Children under 18 employed by sole proprietor are exempt from FICA.",
91
+ sources: [{ doc: "Intake", line: "Family members", value: "2", field: "Eligible Family" }, { doc: "Intake", line: "Ages", value: "16, 19", field: "Children" }],
92
+ trace: [{ n: 1, step: "Eligible family", formula: "Children working in business", result: "2 family members", src: "Intake" }, { n: 2, step: "FICA savings", formula: "Under-18 exemption", result: "Up to $2,400", src: "§3121(b)(3)" }],
93
+ clientBrief: "Employing family members in your business can shift income to lower brackets and reduce employment taxes.", action: "Document bona fide job duties and arm's-length wages", cost: "$0" },
94
94
  { rank: 15, name: "SALT / PTE Optimization", code: "OBBBA §70109", cat: "state", lo: 3200, hi: 5800, score: 92, priority: "HIGH", timeline: "File now", timelineBucket: "now", needsPTE: true,
95
95
  authority: "OBBBA §70109 · IRC §164 · IRS Notice 2020-75", forms: "Schedule A · State PTE Election", warning: null, entities: ["S-Corporation", "Partnership/LLC"],
96
96
  abstract: "OBBBA raised SALT cap to $40,400 for 2026. PTE elections convert entity-level state tax into an unlimited federal business deduction. 30% phase-out above $500K MAGI (MFJ).",
97
97
  sources: [{ doc: "State Return", line: "Tax paid", value: "$9,108", field: "State Tax" }, { doc: "OBBBA §70109", line: "Table 1", value: "$40,400", field: "SALT Cap" }, { doc: "Intake", line: "Property tax", value: "$9,200", field: "Property Tax" }],
98
98
  trace: [{ n: 1, step: "PTE election", formula: "Entity-level state tax", result: "$7,029", src: "Notice 2020-75" }, { n: 2, step: "PTET bypasses cap", formula: "Entity-level deduction", result: "$7,029", src: "Notice" }, { n: 3, step: "Remaining SALT", formula: "$9,200 property tax", result: "$9,200", src: "Intake" }, { n: 4, step: "New regime", formula: "All deducted", result: "$0 lost", src: "OBBBA + PTE" }],
99
99
  clientBrief: "New federal law raised your SALT cap, and your state's PTE workaround bypasses it entirely for business income.", action: "Elect PTE for 2026. Claim full SALT cap.", cost: "$0" },
100
- { rank: 16, name: "Business Interest §163(j)", code: "§163(j)", cat: "deductions", lo: 2000, hi: 15000, score: 73, priority: "MEDIUM", timeline: "File now", timelineBucket: "now",
101
- authority: "IRC §163(j) · OBBBA §70306", forms: "Form 8990", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC"],
102
- abstract: "OBBBA restored EBITDA computation. Full deduction if gross receipts ≤$27M.",
103
- sources: [{ doc: "1120S", line: "Interest", value: "$18,000", field: "Business Interest" }, { doc: "1120S", line: "3-yr avg", value: "$480,000", field: "Gross Receipts" }],
104
- trace: [{ n: 1, step: "Gross receipts test", formula: "$480K ≤ $27M", result: "Exempt", src: "§163(j)(3)" }, { n: 2, step: "Full deduction", formula: "All interest deductible", result: "$18,000", src: "§163(j)" }],
105
- clientBrief: "Your business qualifies for full interest deduction under the small business exception.", action: "No additional action — claim full deduction", cost: "$0" },
106
- { rank: 17, name: "Income Deferral & Timing", code: "§451", cat: "income", lo: 1500, hi: 8000, score: 66, priority: "MEDIUM", timeline: "Year-end", timelineBucket: "90d",
107
- authority: "IRC §451 · IRC §448", forms: "Form 3115", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
108
- abstract: "Cash method taxpayers (≤$27M) defer income through billing timing. §451 advance payment deferral allows one-year deferral on prepaid income.",
109
- sources: [{ doc: "1120S", line: "Revenue", value: "$500,000", field: "Revenue" }, { doc: "QBO", line: "AR aging", value: "$42,000", field: "Receivables" }],
110
- trace: [{ n: 1, step: "Cash method", formula: "Revenue ≤ $27M", result: "Eligible", src: "§448" }, { n: 2, step: "Deferral", formula: "Q4 timing", result: "$20K–$40K", src: "QBO" }],
111
- clientBrief: "Strategic timing of invoicing can defer $20K–$40K of income to next year.", action: "Review Q4 billing schedule", cost: "$0" },
112
- { rank: 18, name: "Opportunity Zone §1400Z", code: "§1400Z", cat: "investment", lo: 5000, hi: 25000, score: 55, priority: "LOW", timeline: "Year-end", timelineBucket: "90d",
113
- authority: "IRC §1400Z · OBBBA transition rules", forms: "Form 8996 · Form 8997", warning: "Confirm deferred gain recognition under OBBBA transition rules.", entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
114
- abstract: "Rural QOZ improvement threshold reduced to 50%. QOF investments defer and potentially eliminate capital gains.",
115
- sources: [{ doc: "Brokerage", line: "Gains", value: "$85,000", field: "Cap Gains" }],
116
- trace: [{ n: 1, step: "Cap gains", formula: "Brokerage + sales", result: "$85,000", src: "Brokerage" }, { n: 2, step: "QOF window", formula: "180 days", result: "Up to $85K", src: "§1400Z" }],
117
- clientBrief: "Investing capital gains into an Opportunity Zone fund defers taxes and potentially eliminates gains on the new investment.", action: "Identify QOF within 180 days of gain", cost: "$1,000–$3,000" },
118
- { rank: 19, name: "Energy Credits §179D", code: "§179D", cat: "credits", preferIndustry: ["Real Estate", "Construction"], lo: 2500, hi: 25000, score: 58, priority: "MEDIUM", timeline: "90 days", timelineBucket: "90d",
100
+ { rank: 16, name: "§179D Energy Deduction", code: "§179D", cat: "credits", preferIndustry: ["Real Estate", "Construction"], lo: 2500, hi: 25000, score: 58, priority: "MEDIUM", timeline: "90 days", timelineBucket: "90d",
119
101
  authority: "IRC §179D · OBBBA §70507", forms: "Form 7205", warning: "ELIMINATED for construction after June 30, 2026.", entities: ["S-Corporation", "C-Corporation", "Partnership/LLC"],
120
102
  abstract: "$0.50–$5.00/sqft for energy-efficient improvements. Eliminated for new construction after June 30, 2026.",
121
103
  sources: [{ doc: "Intake", line: "Sqft", value: "8,000", field: "Commercial Sqft" }],
122
104
  trace: [{ n: 1, step: "Rate", formula: "$0.50–$5.00/sqft", result: "$2.50 est.", src: "Prevailing wage" }, { n: 2, step: "Credit", formula: "8,000 × $2.50", result: "$20,000", src: "Calc." }],
123
105
  clientBrief: "Energy-efficient improvements qualify for up to $5.00/sqft. This credit is being eliminated — act before June 30, 2026.", action: "Commission energy study", cost: "$3,000–$8,000" },
124
- { rank: 20, name: "Tax Method Elections", code: "§446", cat: "accounting", lo: 1000, hi: 6000, score: 62, priority: "MEDIUM", timeline: "File now", timelineBucket: "now",
125
- authority: "IRC §446 · IRC §448", forms: "Form 3115", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
126
- abstract: "Evaluate cash vs. accrual, LIFO/FIFO, §263A UNICAP exemption for businesses under $27M.",
127
- sources: [{ doc: "1120S", line: "Method", value: "Cash", field: "Current" }, { doc: "QBO", line: "Inventory", value: "$45,000", field: "Inventory" }],
128
- trace: [{ n: 1, step: "Current method", formula: "Cash basis", result: "Cash", src: "1120S" }, { n: 2, step: "UNICAP exempt", formula: "Under $27M", result: "Exempt", src: "§263A" }],
129
- clientBrief: "Your business qualifies for simplified accounting methods that can defer income.", action: "Review accounting method elections with preparer", cost: "$500–$1,500" },
106
+ { rank: 17, name: "Vehicle Expense Optimization", code: "§274", cat: "deductions", lo: 800, hi: 3200, score: 66, priority: "MEDIUM", timeline: "30 days", timelineBucket: "30d",
107
+ authority: "IRC §274(d) · Rev. Proc. 2025-32", forms: "Mileage log or actual expense records", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
108
+ abstract: "Compare standard mileage rate (70¢/mi for 2026) vs. actual expenses. Heavy SUVs (GVWR > 6,000 lbs) eligible for full §179.",
109
+ sources: [{ doc: "Mileage log", line: "Business miles", value: "12,000", field: "Annual Miles" }, { doc: "Rev. Proc.", line: "Rate", value: "$0.70/mi", field: "2026 Rate" }],
110
+ trace: [{ n: 1, step: "Mileage method", formula: "12,000 × $0.70", result: "$8,400", src: "Rev. Proc." }, { n: 2, step: "Actual method", formula: "Fuel + ins + dep", result: "$7,200", src: "QBO" }, { n: 3, step: "Better method", formula: "Higher of two", result: "$8,400 mileage", src: "Calc." }],
111
+ clientBrief: "Choosing the right vehicle expense method maximizes your deduction mileage rate wins for your usage.", action: "Maintain contemporaneous mileage log", cost: "$0" },
112
+ { rank: 18, name: "Insurance Optimization", code: "§79, §264", cat: "deductions", lo: 1500, hi: 6000, score: 55, priority: "MEDIUM", timeline: "90 days", timelineBucket: "90d",
113
+ authority: "IRC §79 · IRC §264 · IRC §162", forms: "Plan documents · Premium records", warning: null, entities: ["S-Corporation", "C-Corporation", "Partnership/LLC"],
114
+ abstract: "Group term life up to $50K is tax-free (§79). Key-person and buy-sell insurance premiums may be deductible. Proper structuring avoids AMT traps.",
115
+ sources: [{ doc: "Benefits", line: "Group term", value: "$50,000", field: "Coverage" }, { doc: "Insurance", line: "Premium", value: "$3,600", field: "Annual Premium" }],
116
+ trace: [{ n: 1, step: "Group term exclusion", formula: "≤ $50K coverage", result: "Tax-free", src: "§79" }, { n: 2, step: "Key-person premium", formula: "Deductible if structured", result: "$3,600", src: "§162" }, { n: 3, step: "Total benefit", formula: "Premium + exclusion", result: "$3,600–$6,000", src: "Calc." }],
117
+ clientBrief: "Restructuring your business insurance produces tax-free benefits and deductible premiums.", action: "Review group term life and key-person policies with advisor", cost: "$300–$800" },
118
+ { rank: 19, name: "§1031 Like-Kind Exchange", code: "§1031", cat: "deferral", preferIndustry: ["Real Estate"], lo: 5000, hi: 50000, score: 58, priority: "MEDIUM", timeline: "Year-end", timelineBucket: "90d",
119
+ authority: "IRC §1031 · Treas. Reg. §1.1031", forms: "Form 8824", warning: "Only real property qualifies post-TCJA. 45-day ID / 180-day close.", entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
120
+ abstract: "Defer capital gains on real property by exchanging into like-kind replacement property. Strict identification and closing deadlines apply.",
121
+ sources: [{ doc: "Intake", line: "Property", value: "$400,000", field: "Relinquished FMV" }, { doc: "Intake", line: "Basis", value: "$180,000", field: "Adjusted Basis" }],
122
+ trace: [{ n: 1, step: "Realized gain", formula: "$400K − $180K", result: "$220,000", src: "Intake" }, { n: 2, step: "Deferred", formula: "Full deferral if boot = 0", result: "$220,000", src: "§1031" }, { n: 3, step: "Tax avoided", formula: "$220K × 23.8%", result: "$52,360", src: "Calc." }],
123
+ clientBrief: "Exchanging your investment property into a like-kind replacement defers $52K+ in capital gains taxes.", action: "Engage qualified intermediary before closing", cost: "$1,500–$3,000" },
124
+ { rank: 20, name: "QSBS §1202 Exclusion", code: "§1202", cat: "exclusions", lo: 0, hi: 250000, score: 62, priority: "MEDIUM", timeline: "Year-end", timelineBucket: "90d",
125
+ authority: "IRC §1202 · Rev. Proc. 2025-32", forms: "Stock documentation · Form 1040 Sch D", warning: "Must hold stock ≥ 5 years. C-Corp only. $50M gross assets cap.", entities: ["C-Corporation"],
126
+ abstract: "Exclude up to 100% of gain (max $10M or 10× basis) on sale of qualified small business stock held 5+ years.",
127
+ sources: [{ doc: "Intake", line: "Entity", value: "C-Corp", field: "Entity Type" }, { doc: "Intake", line: "Assets", value: "$8M", field: "Gross Assets" }, { doc: "Intake", line: "Held since", value: "2020", field: "Acquisition Year" }],
128
+ trace: [{ n: 1, step: "QSBS eligibility", formula: "C-Corp, < $50M assets", result: "Eligible", src: "§1202(d)" }, { n: 2, step: "Holding period", formula: "2020 → 2026 = 6 yrs", result: "≥ 5 yrs ✓", src: "§1202(a)" }, { n: 3, step: "Exclusion", formula: "100% of gain", result: "Up to $10M", src: "§1202(a)(1)" }],
129
+ clientBrief: "Your C-Corp stock may qualify for 100% gain exclusion on sale — up to $10M tax-free.", action: "Document original issuance, gross asset test, and holding period", cost: "$500–$2,000" },
130
130
  { rank: 21, name: "Qualified Tips Deduction", code: "OBBBA", cat: "obbba_new", onlyIndustry: ["Restaurant / Hospitality"], lo: 3000, hi: 25000, score: 78, priority: "HIGH", timeline: "File now", timelineBucket: "now",
131
131
  authority: "OBBBA Qualified Tips Provision", forms: "Form 1040 / Schedule C", warning: "Tipping occupations only. Max $25K. Effective 2025–2028.", entities: ["Sole Proprietorship", "Partnership/LLC"],
132
132
  abstract: "New OBBBA deduction up to $25,000 for qualifying tip income. Tipping occupations only. Effective 2025–2028.",
@@ -134,7 +134,7 @@ exports.STRATEGIES = [
134
134
  trace: [{ n: 1, step: "Tip income", formula: "Payroll", result: "$32,000", src: "Payroll" }, { n: 2, step: "Cap", formula: "Lesser of tips or $25K", result: "$25,000", src: "OBBBA" }],
135
135
  clientBrief: "New OBBBA provision lets you deduct up to $25,000 of tip income through 2028.", action: "Document and categorize all tip income", cost: "$0" },
136
136
  { rank: 22, name: "Overtime Pay Deduction", code: "OBBBA", cat: "obbba_new", minEmployees: 1, lo: 2000, hi: 12500, score: 74, priority: "HIGH", timeline: "File now", timelineBucket: "now",
137
- authority: "OBBBA Overtime Provision", forms: "Form 1040", warning: "MAGI phase-out: $150K/$300K. Effective 2025–2028.", entities: ["S-Corporation", "C-Corporation", "Partnership/LLC", "Sole Proprietorship"],
137
+ authority: "OBBBA Overtime Provision", forms: "Form 1040", warning: "MAGI phase-out: $150K/$300K. Effective 2025–2028.", entities: ["S-Corporation", "Partnership/LLC", "Sole Proprietorship"],
138
138
  abstract: "New OBBBA deduction for overtime: $12,500 single / $25,000 MFJ. Phase-out at $150K/$300K MAGI.",
139
139
  sources: [{ doc: "Payroll", line: "OT hours", value: "320 hrs", field: "Overtime" }, { doc: "W-2", line: "OT comp", value: "$18,000", field: "OT Pay" }],
140
140
  trace: [{ n: 1, step: "OT compensation", formula: "W-2 overtime", result: "$18,000", src: "W-2" }, { n: 2, step: "Cap", formula: "$12,500 / $25K MFJ", result: "$12,500", src: "OBBBA" }],
@@ -159,8 +159,6 @@ exports.STRATEGIES = [
159
159
  clientBrief: "New provision allows tax-deductible contributions to employee child savings accounts.", action: "Monitor IRS guidance. Plan contributions.", cost: "$200–$500" },
160
160
  ];
161
161
  exports.ADDITIONAL = [
162
- { name: "Accountable Plan", savings: "$1,200–$3,500", priority: "MEDIUM" },
163
162
  { name: "Estimated Tax Timing", savings: "$200–$800", priority: "LOW" },
164
163
  { name: "Business Insurance Review", savings: "$400–$2,000", priority: "LOW" },
165
- { name: "Vehicle Expense Optimization", savings: "$800–$3,200", priority: "MEDIUM" },
166
164
  ];