PageSourceSearch

https://flipperbee.com/assets/StackBuilder-DzDPCIMF.js

js flipperbee.com collected 2026-10-06 16:58:03 UTC 16,645 bytes, 1 lines download raw bytes

1import{j as e}from"./three-MLbH8_9b.js";import{h as Q,f as X,r as f,L as Y}from"./vendor-CPSY9MWr.js";import{s as R}from"./main-BvCG_5nA.js";import{g as Z}from"./jurisdictions-zTP0Uhvd.js";import{f as i,b as g,c as C}from"./format-B9Y3pUiL.js";import{F as ee}from"./shared-Caliv3nm.js";import{D as z,c as te,e as ae}from"./pnl-DMNQ2vaK.js";import"./index-DQ16CTdq.js";function J(s,d,r,o,c){return{deal_id:s,name:"Base case",purchase_price:d,reno_budget:r,arv:o,hold_months:Math.min(30,Math.max(4,c)),senior_ltc:z.loan_ltc,senior_rate:z.interest_rate,senior_points:z.points,mezz_enabled:!1,mezz_amount:5e5,mezz_rate:.14,pref_enabled:!1,pref_amount:5e5,pref_rate:.1,interest_reserve:!1,structure:"solo",gp_coinvest_pct:.1,promote_pct:.2}}const H=[{id:"hard_money",label:"Hard money — 10.5% / 2 pts",rate:.105,points:.02},{id:"bank_bridge",label:"Bank bridge — 8.5% / 1 pt",rate:.085,points:.01},{id:"private",label:"Private — 9.5% / 1 pt",rate:.095,points:.01}];function se(s,d,r){const o=Math.max(1,s.hold_months),c=te({purchase_price:s.purchase_price,reno_budget:s.reno_budget,arv:s.arv,hold_months:o,jurisdiction:d,loan_ltc:s.senior_ltc,interest_rate:s.senior_rate,points:s.senior_points,interest_reserve:s.interest_reserve,annual_property_tax_rate:z.annual_property_tax_rate,monthly_insurance:ae(s.purchase_price,d.id,r==null?void 0:r.fhsz_zone),monthly_utilities_misc:z.monthly_utilities_misc,buy_closing_pct:z.buy_closing_pct,sale_commission_pct:z.sale_commission_pct,sale_escrow_title_pct:z.sale_escrow_title_pct,staging_marketing:z.staging_marketing}),v=Math.max(0,c.equity_required),n=s.mezz_enabled?Math.max(0,Math.min(s.mezz_amount,v)):0,x=s.pref_enabled?Math.max(0,Math.min(s.pref_amount,v-n)):0,_=Math.max(0,v-n-x),A=n*s.mezz_rate*(o/12),M=c.net_profit-A,k=x*s.pref_rate*(o/12),y=M-k,L=s.structure==="solo"?1:Math.min(1,Math.max(0,s.gp_coinvest_pct)),b=_*L,P=_-b,$=s.structure==="syndication"?Math.min(.9,Math.max(0,s.promote_pct)):0;let S=0,w=0,q=0;if(y>=0){S=y*$;const l=y-S;w=_>0?l*(P/_):0,q=S+(_>0?l*(b/_):l)}else w=_>0?y*(P/_):0,q=y-w;const U=w+k,N=P+x,h=c.loan_amount+n,a=N>0?(N+U)/N:0,u=b>0?(b+q)/b:0,B=N>0?U/N*(12/o):0,F=[{label:"Senior debt",amount:c.loan_amount},{label:"Mezzanine",amount:n},{label:"Pref equity",amount:x},{label:"GP equity",amount:b},{label:"LP equity",amount:P}].filter(l=>l.amount>.5),V=F.reduce((l,G)=>l+G.amount,0)||1,t=F.map(l=>({...l,pct:l.amount/V})),m=[{label:"Purchase",amount:s.purchase_price},{label:"Renovation",amount:s.reno_budget},{label:"Points + closing",amount:c.points_cost+c.buy_closing},...s.interest_reserve?[]:[{label:"Interest carry",amount:c.interest_cost}],{label:"Carry reserve",amount:c.carry_cost*.5}].filter(l=>l.amount>.5);return{sources:t,uses:m,pnl:c,lp_profit:U,gp_profit:q,lp_irr_annualized:B,lp_equity_multiple:a,gp_equity_multiple:u,mezz:n,mezz_interest:A,pref:x,pref_return:k,common_equity:_,lp_equity:P,gp_equity:b,lp_capital:N,total_debt:h,coverage:h>0?s.arv/h:1/0,deal_profit:M,common_profit:y,promote_amount:S}}function ne(s){return{sources:s.sources,uses:s.uses,pnl:s.pnl,lp_profit:s.lp_profit,gp_profit:s.gp_profit,lp_irr_annualized:s.lp_irr_annualized,lp_equity_multiple:s.lp_equity_multiple,gp_equity_multiple:s.gp_equity_multiple}}const re={"Senior debt":"#333d55",Mezzanine:"#e8b04b","Pref equity":"#a8843c","GP equity":"#d9b878","LP equity":"#3ecf8e"},W=["#242b3d","#c9a25a","#6b7794","#f4636f","#94a0bc"];function xe(){const{dealId:s}=Q(),d=X(),[r,o]=f.useState(null),[c,v]=f.useState(null),[n,x]=f.useState(null),[_,A]=f.useState([]),[M,k]=f.useState(""),[y,L]=f.useState("Base case"),[b,P]=f.useState(!0),[$,S]=f.useState(null),[w,q]=f.useState(!1),[U,N]=f.useState(!1);f.useEffect(()=>{(async()=>{if(!s)return;const{data:t,error:m}=await R.from("deals").select("*").eq("id",s).single();if(m){S(m.message);return}const l=t;o(l);const{data:G}=await R.from("properties").select("*").eq("id",l.property_id).single();v(G??null);const{data:T}=await R.from("capital_stacks").select("*").eq("deal_id",s).order("created_at",{ascending:!1}),D=T??[];A(D),D.length>0&&D[0].params?(x({...J(s,l.target_purchase,l.reno_budget_total,l.target_arv,l.hold_months),...D[0].params}),k(D[0].id),L(D[0].name)):x(J(s,l.target_purchase,l.reno_budget_total,l.target_arv,l.hold_months))})()},[s]);const h=c?Z(c.jurisdiction_id):null,a=f.useMemo(()=>n&&h?se(n,h,c):null,[n,h,c]);if($&&!r)return e.jsx("div",{className:"p-8 text-neg",children:$});if(!r||!n||!h||!a)return e.jsx("div",{className:"p-8 text-ink-400",children:"Loading c
1apital stack…"});const u=t=>x({...n,...t}),B=h.ula_applies&&n.arv>=h.ula_tier1_threshold,F=async(t=!1)=>{q(!0),S(null);const m={deal_id:r.id,name:y.trim()||"Scenario",params:n,snapshot:ne(a)};let l;if(M)({error:l}=await R.from("capital_stacks").update(m).eq("id",M));else{const T=await R.from("capital_stacks").insert(m).select("id").single();l=T.error,T.data&&k(T.data.id)}if(q(!1),l){S(l.message);return}const{data:G}=await R.from("capital_stacks").select("*").eq("deal_id",r.id).order("created_at",{ascending:!1});A(G??[]),N(!0),setTimeout(()=>N(!1),2500),t&&d(`/package/${r.id}`)},V=t=>{k(t);const m=_.find(l=>l.id===t);m!=null&&m.params&&(x({...n,...m.params}),L(m.name))};return e.jsxs("div",{className:"p-6 max-w-[1500px]",children:[e.jsxs("div",{className:"flex flex-wrap items-start justify-between gap-4 mb-5",children:[e.jsxs("div",{children:[e.jsxs("h1",{className:"font-display text-3xl text-ink-100",children:["Capital Stack — ",r.name]}),e.jsxs("div",{className:"text-ink-400 text-sm mt-1",children:[c==null?void 0:c.address," · ",h.name]})]}),e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx(Y,{className:"btn-ghost text-xs",to:`/deals/${r.id}`,children:"← Deal"}),e.jsxs("select",{className:"input !w-auto !py-1.5 text-xs",value:M,onChange:t=>t.target.value?V(t.target.value):(k(""),L("New scenario")),children:[e.jsx("option",{value:"",children:"— new scenario —"}),_.map(t=>e.jsx("option",{value:t.id,children:t.name},t.id))]}),e.jsx("input",{className:"input !w-40 !py-1.5 text-xs",value:y,onChange:t=>L(t.target.value),placeholder:"Scenario name"}),e.jsx("button",{className:"btn-ghost text-xs",disabled:w,onClick:()=>F(!1),children:w?"Saving…":U?"Saved ✓":"Save scenario"}),e.jsx("button",{className:"btn-gold text-xs",disabled:w,onClick:()=>F(!0),children:"Send to Pitch Deck →"})]})]}),$&&e.jsx("div",{className:"mb-4 text-sm text-neg bg-neg/10 border border-neg/30 rounded-lg px-3 py-2",children:$}),e.jsxs("div",{className:"grid grid-cols-1 xl:grid-cols-[400px_1fr] gap-6",children:[e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Deal Levers"}),e.jsx(p,{label:"Purchase price",value:n.purchase_price,min:Math.round(r.target_purchase*.8),max:Math.round(r.target_purchase*1.2),step:25e3,fmt:t=>i(t,!0),onChange:t=>u({purchase_price:t})}),e.jsx(p,{label:"Reno budget",value:n.reno_budget,min:0,max:Math.max(2e6,Math.round(r.reno_budget_total*2)),step:25e3,fmt:t=>i(t,!0),onChange:t=>u({reno_budget:t})}),e.jsx(p,{label:"ARV (exit)",value:n.arv,min:Math.round(r.target_arv*.6),max:Math.round(Math.max(r.target_arv*1.6,1e6)),step:5e4,fmt:t=>i(t,!0),onChange:t=>u({arv:t})}),e.jsx(p,{label:"Hold (months)",value:n.hold_months,min:4,max:30,step:1,fmt:t=>`${t} mo`,onChange:t=>u({hold_months:t})})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Senior Debt"}),e.jsx("label",{className:"label",children:"Preset"}),e.jsxs("select",{className:"input mb-3 text-xs",value:"",onChange:t=>{const m=H.find(l=>l.id===t.target.value);m&&u({senior_rate:m.rate,senior_points:m.points})},children:[e.jsx("option",{value:"",children:"Apply a preset…"}),H.map(t=>e.jsx("option",{value:t.id,children:t.label},t.id))]}),e.jsx(p,{label:"Senior LTC",value:n.senior_ltc,min:0,max:.9,step:.01,fmt:t=>g(t,0),onChange:t=>u({senior_ltc:t})}),e.jsx(p,{label:"Senior rate",value:n.senior_rate,min:.07,max:.15,step:.0025,fmt:t=>g(t,2),onChange:t=>u({senior_rate:t})}),e.jsx(p,{label:"Points",value:n.senior_points,min:0,max:.04,step:.0025,fmt:t=>`${(t*100).toFixed(2)} pts`,onChange:t=>u({senior_points:t})}),e.jsx(I,{label:"Interest reserve (capitalized)",on:n.interest_reserve,onChange:t=>u({interest_reserve:t})})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Junior Capital"}),e.jsx(I,{label:"Mezzanine debt",on:n.mezz_enabled,onChange:t=>u({mezz_enabled:t})}),n.mezz_enabled&&e.jsxs("div",{className:"pl-3 border-l border-ink-700 ml-1 mt-1",children:[e.jsx(p,{label:"Mezz amount",value:n.mezz_amount,min:1e5,max:2e6,step:25e3,fmt:t=>i(t,!0),onChange:t=>u({mezz_amount:t})}),e.jsx(p,{label:"Mezz rate",value:n.mezz_rate,min:.1,max:.2,step:.0025,fmt:t=>g(t,2),onChange:t=>u({mezz_rate:t})})]}),e.jsx(I,{label:"Preferred equity",on:n.pref_enabled,onChange:t=>u({pref_enabled:t})}),n.pref_enabled&&e.jsxs("div",{className:"pl-3 border-l border-ink-700 ml-1 mt-1",children:[e.jsx(p,{label:"Pref amount",value:n.pref_amount,min:1e5,max:2e6,step:25e3,fmt:t=>i(t,!0),onChange:t=>u({pref_amount:t})}),e.jsx(p,{label:"Pref rate",value:n.pref_rate,min:.06,max:.16,step:.
10025,fmt:t=>g(t,2),onChange:t=>u({pref_rate:t})})]})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Structure"}),e.jsx("label",{className:"label",children:"Ownership structure"}),e.jsxs("select",{className:"input mb-3 text-xs",value:n.structure,onChange:t=>u({structure:t.target.value}),children:[e.jsx("option",{value:"solo",children:"Solo — 100% GP"}),e.jsx("option",{value:"jv",children:"JV — pari passu, no promote"}),e.jsx("option",{value:"syndication",children:"Syndication — GP promote over LPs"})]}),n.structure!=="solo"&&e.jsx(p,{label:"GP co-invest (of common)",value:n.gp_coinvest_pct,min:0,max:.5,step:.01,fmt:t=>g(t,0),onChange:t=>u({gp_coinvest_pct:t})}),n.structure==="syndication"&&e.jsx(p,{label:"GP promote",value:n.promote_pct,min:0,max:.5,step:.01,fmt:t=>g(t,0),onChange:t=>u({promote_pct:t})}),h.ula_applies&&e.jsxs(e.Fragment,{children:[e.jsx(I,{label:"Model ULA exit",on:b,onChange:P}),b&&B&&e.jsxs("div",{className:"text-xs text-warn bg-warn/10 border border-warn/30 rounded px-2 py-1.5 mt-1",children:["ULA tax at this exit: ",e.jsx("span",{className:"font-mono",children:i(a.pnl.ula_tax)})," — included in profit below."]}),!b&&B&&e.jsxs("div",{className:"text-xs text-neg bg-neg/10 border border-neg/40 rounded px-2 py-1.5 mt-1 font-medium",children:["exit crosses $5.4M ULA cliff — profit shown assumes you eat it (",i(a.pnl.ula_tax,!0),")"]})]})]})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3",children:[e.jsx(O,{label:"Deal profit",value:i(a.deal_profit,!0),tone:a.deal_profit>=0?"pos":"neg",sub:a.mezz_interest>0?`after ${i(a.mezz_interest,!0)} mezz coupon`:"canonical P&L"}),e.jsx(O,{label:"LP profit",value:i(a.lp_profit,!0),tone:a.lp_profit>=0?"pos":"neg",sub:`${g(a.lp_irr_annualized)} ann. · ${a.lp_equity_multiple.toFixed(2)}x`}),e.jsx(O,{label:"GP profit",value:i(a.gp_profit,!0),tone:a.gp_profit>=0?"pos":"neg",sub:a.gp_equity>0?`${a.gp_equity_multiple.toFixed(2)}x on ${i(a.gp_equity,!0)}`:"promote only"}),e.jsx(O,{label:"Coverage",value:Number.isFinite(a.coverage)?`${a.coverage.toFixed(2)}x`:"∞",tone:a.coverage>=1.3?"pos":a.coverage>=1.1?"warn":"neg",sub:"ARV / total debt"})]}),e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Sources & Uses"}),e.jsx(K,{title:`Sources — ${i(a.sources.reduce((t,m)=>t+m.amount,0),!0)}`,segments:a.sources.map(t=>({label:t.label,amount:t.amount,color:re[t.label]??"#6b7794"}))}),e.jsx("div",{className:"h-3"}),e.jsx(K,{title:`Uses — ${i(a.uses.reduce((t,m)=>t+m.amount,0),!0)}`,segments:a.uses.map((t,m)=>({label:t.label,amount:t.amount,color:W[m%W.length]}))})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"card",children:[e.jsx("div",{className:"card-title",children:"Distribution Waterfall"}),e.jsx(E,{label:"Deal profit (canonical P&L − mezz coupon)",amount:a.deal_profit,bold:!0}),a.pref>0&&e.jsx(E,{label:`Pref return — ${i(a.pref,!0)} @ ${g(n.pref_rate)} × ${n.hold_months}/12`,amount:-a.pref_return,indent:!0}),e.jsx(E,{label:"Return of capital (all tranches repaid at exit)",amount:0,note:i(a.common_equity+a.pref+a.mezz,!0),indent:!0}),a.promote_amount>0&&e.jsx(E,{label:`GP promote @ ${g(n.promote_pct,0)}`,amount:a.promote_amount,indent:!0,gold:!0}),e.jsx("div",{className:"border-t border-ink-700 my-2"}),e.jsx(E,{label:`LP total (${i(a.lp_capital,!0)} in)`,amount:a.lp_profit,bold:!0}),e.jsx(E,{label:`GP total (${i(a.gp_equity,!0)} in)`,amount:a.gp_profit,bold:!0})]}),e.jsxs("div",{className:"card",children:[e.jsxs("div",{className:"card-title",children:["P&L Detail ",e.jsx("span",{className:"text-xs text-ink-500 font-sans",children:"computeDealPnL"})]}),e.jsx("table",{className:"table-base",children:e.jsxs("tbody",{children:[e.jsx(j,{k:"Total project cost",v:i(a.pnl.total_project_cost,!0)}),e.jsx(j,{k:"Senior loan",v:i(a.pnl.loan_amount,!0)}),e.jsx(j,{k:"Interest + points",v:i(a.pnl.interest_cost+a.pnl.points_cost,!0)}),e.jsx(j,{k:"Carry",v:i(a.pnl.carry_cost,!0)}),e.jsx(j,{k:"Selling costs",v:i(a.pnl.selling_costs,!0)}),e.jsx(j,{k:"— of which ULA",v:i(a.pnl.ula_tax,!0),warn:a.pnl.ula_tax>0}),e.jsx(j,{k:"Equity required",v:i(a.pnl.equity_required,!0)}),e.jsx(j,{k:"Common equity (after mezz/pref)",v:i(a.common_equity,!0)}),e.jsx(j,{k:"Breakeven ARV",v:i(a.pnl.breakeven_arv,!0)}),e.jsx(j,{k:"ROI on equity",v:g(a.pnl.roi_on_equity)})]})}),a.pnl.buy_box_flags.length>0&&e.jsx("div",{className:"mt-3 flex flex-wrap gap-2",children:a.pnl.buy_box_flags.map(t=>e.jsx("span",{className:"badge bg-warn/10 text-warn border border-warn/40",children:ee[t]??t},t))})]})]})]})]})]})}function p({label:s,value:d,min:r,max:o,step:c,fmt:v,onChange:n}){return e.jsxs("div",{className:"mb-3",children:[e.jsxs("div",{className:"flex justify-between items-baseline mb-1",children:[e.jsx("span",{className:"label !mb-0",children:s}),e.jsx("span",{className:"font-mono text-sm text-gold-300",children:v(d)})]}),e.jsx("input",{type:"range",min:r,max:o,step:c,value:Math.min(o,Math.max(r,d)),onChange:x=>n(Number(x.target.value))})]})}function I({label:s,on:d,onChange:r}){return e.jsxs("button",{type:"button",onClick:()=>r(!d),className:"flex items-center justify-between w-full py-2 text-sm text-ink-200 cursor-pointer group",children:[e.jsx("span",{children:s}),e.jsx("span",{className:C("w-9 h-5 rounded-full relative transition-colors",d?"bg-gold-500":"bg-ink-700 group-hover:bg-ink-600"),children:e.jsx("span",{className:C("absolute top-0.5 w-4 h-4 rounded-full bg-ink-100 transition-all",d?"left-[18px]":"left-0.5")})})]})}function K({title:s,segments:d}){const r=d.reduce((o,c)=>o+c.amount,0)||1;return e.jsxs("div",{children:[e.jsx("div",{className:"text-xs text-ink-400 mb-1.5",children:s}),e.jsx("div",{className:"flex h-8 rounded-lg overflow-hidden border border-ink-700",children:d.map(o=>e.jsx("div",{title:`${o.label}: ${i(o.amount)}`,className:"h-full transition-all duration-200",style:{width:`${o.amount/r*100}%`,backgroundColor:o.color}},o.label))}),e.jsx("div",{className:"flex flex-wrap gap-x-4 gap-y-1 mt-1.5",children:d.map(o=>e.jsxs("span",{className:"inline-flex items-center gap-1.5 text-[11px] text-ink-300",children:[e.jsx("span",{className:"w-2.5 h-2.5 rounded-sm",style:{backgroundColor:o.color}}),o.label," ",e.jsxs("span",{className:"font-mono text-ink-400",children:[i(o.amount,!0)," · ",g(o.amount/r,0)]})]},o.label))})]})}function E({label:s,amount:d,note:r,bold:o,indent:c,gold:v}){return e.jsxs("div",{className:C("flex items-baseline justify-between py-1",c&&"pl-4"),children:[e.jsx("span",{className:C("text-xs",o?"text-ink-100 font-medium":"text-ink-400",v&&"text-gold-300"),children:s}),e.jsx("span",{className:C("font-mono text-sm",v?"text-gold-300":r?"text-ink-400":d>=0?"text-pos":"text-neg"),children:r??i(d,!0)})]})}function j({k:s,v:d,warn:r}){return e.jsxs("tr",{children:[e.jsx("td",{className:C("text-xs",r?"text-warn":"text-ink-400"),children:s}),e.jsx("td",{className:C("text-right font-mono text-xs",r?"text-warn":"text-ink-100"),children:d})]})}function O({label:s,value:d,sub:r,tone:o}){return e.jsxs("div",{className:"card !p-4",children:[e.jsx("div",{className:"label",children:s}),e.jsx("div",{className:C("font-mono text-2xl",o==="pos"?"text-pos":o==="warn"?"text-warn":"text-neg"),children:d}),r&&e.jsx("div",{className:"text-[11px] text-ink-500 mt-0.5",children:r})]})}
1export{xe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.