Property Analyzer

Property Walk Analyzer

Property Walk Analyzer :root{–teal:#1F4E5F;–teal-l:#DCEEF2;–teal-m:#3a7a92;–green:#2F5233;–green-m:#2d7a36;–red:#b91c1c;–amber:#92400e;–g50:#f9fafb;–g100:#f3f4f6;–g200:#e5e7eb;–g400:#9ca3af;–g500:#6b7280;–g700:#374151;–g900:#111827;–w:#fff;–r:10px;–rs:6px;–sh:0 1px 4px rgba(0,0,0,.08);–shm:0 4px 16px rgba(0,0,0,.10)} *{box-sizing:border-box;margin:0;padding:0} body{font-family:-apple-system,BlinkMacSystemFont,’Segoe UI’,sans-serif;background:var(–g100);color:var(–g900);min-height:100vh;font-size:15px} .hdr{background:var(–teal);padding:16px 20px;position:sticky;top:0;z-index:100;display:flex;align-items:center;justify-content:space-between;box-shadow:0 2px 8px rgba(0,0,0,.18)} .hdr h1{color:#fff;font-size:17px;font-weight:600;letter-spacing:-.2px} .hdr .sub{color:rgba(255,255,255,.6);font-size:11px;margin-top:1px} .ht-lbl{color:rgba(255,255,255,.65);font-size:11px;text-align:right} .ht-val{color:#fff;font-size:21px;font-weight:600;text-align:right} .main{max-width:860px;margin:0 auto;padding:16px 14px 60px} .card{background:var(–w);border-radius:var(–r);box-shadow:var(–sh);margin-bottom:12px;overflow:hidden} .cb{padding:14px 16px} .ct{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(–teal);margin-bottom:12px} .grid2{display:grid;grid-template-columns:1fr 1fr;gap:8px} .s2{grid-column:span 2} .fg{display:flex;flex-direction:column;gap:3px} .fg label{font-size:11px;font-weight:600;color:var(–g500);text-transform:uppercase;letter-spacing:.05em} input[type=text],input[type=number],textarea{border:1.5px solid var(–g200);border-radius:var(–rs);padding:8px 10px;font-size:14px;color:var(–g900);background:var(–g50);width:100%;font-family:inherit;transition:border-color .15s} input[type=number]{text-align:right} input:focus,textarea:focus{outline:none;border-color:var(–teal-m);background:#fff} .sec-card{margin-bottom:8px} .sec-hd{display:flex;align-items:center;justify-content:space-between;padding:13px 16px;cursor:pointer;background:var(–w);border-radius:var(–r);box-shadow:var(–sh);transition:background .1s} .sec-hd:active{background:var(–g50)} .sec-hd.open{border-radius:var(–r) var(–r) 0 0;box-shadow:none;border-bottom:1.5px solid var(–g200)} .sec-l{display:flex;align-items:center;gap:10px} .sec-ico{width:34px;height:34px;background:var(–teal-l);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:16px} .sec-nm{font-size:14px;font-weight:500} .sec-r{display:flex;align-items:center;gap:8px} .badge{background:var(–teal-l);color:var(–teal);font-size:12px;font-weight:600;padding:2px 9px;border-radius:20px;display:none} .chev{color:var(–g400);font-size:16px;transition:transform .2s;display:inline-block} .chev.open{transform:rotate(180deg)} .sec-bd{background:var(–w);border-radius:0 0 var(–r) var(–r);box-shadow:var(–sh);padding:10px 16px 14px} .col-hds{display:grid;grid-template-columns:1fr 74px 90px 70px 32px;gap:6px;padding-bottom:6px;border-bottom:1px solid var(–g200);margin-bottom:6px} .ch{font-size:11px;font-weight:600;color:var(–g400);text-transform:uppercase;letter-spacing:.04em} .ch.r{text-align:right} .line{display:grid;grid-template-columns:1fr 74px 90px 70px 32px;gap:6px;align-items:center;margin-bottom:6px} .line input[type=text]{text-align:left} .lt{font-size:13px;font-weight:500;color:var(–teal);text-align:right} .lt.z{color:var(–g400);font-weight:400} .del{background:none;border:none;cursor:pointer;color:var(–g400);font-size:16px;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:5px;transition:background .1s,color .1s} .del:hover{background:#fef2f2;color:var(–red)} .add-btn{display:flex;align-items:center;gap:5px;font-size:13px;color:var(–teal);background:none;border:1.5px dashed var(–teal-l);border-radius:var(–rs);padding:6px 10px;cursor:pointer;margin-top:4px;width:100%;font-weight:500;transition:background .1s} .add-btn:hover{background:var(–teal-l)} .notes{width:100%;margin-top:8px;border:1.5px solid var(–g200);border-radius:var(–rs);padding:7px 10px;font-size:13px;color:var(–g700);background:var(–g50);resize:none;font-family:inherit} .rep-bar{background:var(–w);border-radius:var(–r);box-shadow:var(–sh);padding:12px 16px;display:flex;justify-content:space-between;align-items:center;margin-bottom:12px} .an-row{display:flex;flex-direction:column;gap:3px;margin-bottom:10px} .an-row label{font-size:11px;font-weight:600;color:var(–g500);text-transform:uppercase;letter-spacing:.05em} .an-row input{font-size:15px;font-weight:500;padding:9px 11px} .computed{background:var(–teal-l);border-radius:var(–rs);padding:8px 12px;margin-bottom:10px} .c-lbl{font-size:11px;color:var(–teal);font-weight:600;text-transform:uppercase;letter-spacing:.04em} .c-val{font-size:15px;font-weight:600;color:var(–teal)} .an-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px} .holding-sub{background:var(–g50);border:1px solid var(–g200);border-radius:var(–rs);padding:10px 12px;margin-bottom:8px} .hs-title{font-size:11px;font-weight:700;color:var(–g500);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px} .hs-row{display:grid;grid-template-columns:1fr 90px;gap:6px;align-items:center;margin-bottom:6px} .hs-row label{font-size:12px;color:var(–g700)} .hs-row input{font-size:13px;padding:5px 8px} .cost-strip{background:var(–w);border-radius:var(–r);box-shadow:var(–sh);padding:14px 16px;margin-bottom:12px} .cr{display:flex;justify-content:space-between;padding:7px 0;border-bottom:1px solid var(–g100)} .cr:last-child{border:none} .cr-l{font-size:13px;color:var(–g500)} .cr-v{font-size:13px;font-weight:500} .tr-row{display:flex;justify-content:space-between;padding:10px 0 4px;border-top:2px solid var(–g200);margin-top:4px} .metrics{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:12px} .met{background:var(–g50);border-radius:var(–rs);padding:11px 13px;border:1px solid var(–g200)} .m-l{font-size:11px;color:var(–g500);margin-bottom:3px;font-weight:500} .m-v{font-size:19px;font-weight:600;color:var(–g900)} .offer-box{background:var(–teal);border-radius:var(–r);padding:18px 20px;margin-bottom:12px;display:flex;align-items:center;justify-content:space-between;box-shadow:var(–shm)} .offer-box.bad{background:var(–red)} .o-l{color:rgba(255,255,255,.7);font-size:12px;margin-bottom:3px} .o-v{color:#fff;font-size:34px;font-weight:700;letter-spacing:-1px} .o-sub{color:rgba(255,255,255,.7);font-size:12px;line-height:1.6;text-align:right} .o-diff{color:#fff;font-size:13px;font-weight:500;margin-top:4px;text-align:right} .btn-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:8px} .btn-full{width:100%;margin-bottom:8px;padding:13px;font-size:14px;font-weight:500;cursor:pointer;border-radius:var(–r);border:none;display:flex;align-items:center;justify-content:center;gap:7px} .sec-div{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(–g400);padding:4px 2px 8px} .save-bar{background:var(–w);border-radius:var(–r);box-shadow:var(–sh);padding:10px 14px;display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:10px} .save-status{font-size:12px;color:var(–g500);font-style:italic} .toast{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);background:var(–g900);color:#fff;padding:9px 18px;border-radius:20px;font-size:13px;font-weight:500;opacity:0;transition:opacity .3s;pointer-events:none;white-space:nowrap;box-shadow:var(–shm);z-index:999} .toast.show{opacity:1} .modal-ov{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:200;display:none;align-items:center;justify-content:center;padding:16px} .modal-ov.show{display:flex} .modal{background:var(–w);border-radius:var(–r);padding:20px;width:100%;max-width:420px;max-height:90vh;overflow-y:auto} .modal-title{font-size:15px;font-weight:600;margin-bottom:3px} .modal-sub{font-size:12px;color:var(–g500);margin-bottom:16px} .mf{display:flex;flex-direction:column;gap:3px;margin-bottom:10px} .mf label{font-size:11px;font-weight:600;color:var(–g500);text-transform:uppercase;letter-spacing:.05em} .mf input{border:1.5px solid var(–g200);border-radius:var(–rs);padding:8px 10px;font-size:14px;color:var(–g900);background:var(–g50);width:100%;font-family:inherit} .mf input[type=number]{text-align:right} .mf input:focus{outline:none;border-color:var(–teal-m);background:#fff} .mbr{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:16px} .tbd-tap{display:flex;align-items:center;gap:6px;margin-top:5px;font-size:12px;color:var(–teal);cursor:pointer;font-weight:500} @media(max-width:600px){ .an-grid{grid-template-columns:1fr} .col-hds,.line{grid-template-columns:1fr 60px 80px 64px 28px} .offer-box{flex-direction:column;align-items:flex-start;gap:10px} .o-sub{text-align:left} }

Property Walk Analyzer

Flip deal calculator
Total repairs
$0
No saved data
Property information
Address
City / State / Zip
County
Parcel ID
Beds / Baths
Sq Ft
Year Built
Asking Price ($)
Annual Property Taxes ($)Used to calculate prorated taxes during hold period
Repair estimate β€” walk order
πŸ”§ Total estimated repairs $0
Deal analyzer
After Repair Value (ARV)
Purchase price
Rehab duration (months)
Hard money rate (annual %)
Realtor commission (%)
Seller closing costs (%)
Target profit ($)
Holding cost breakdown
πŸ’° Hard money interest (auto-calculated)
Interest only on purchase + repairs
Enter purchase, repairs & months
πŸ›οΈ Property taxes (prorated)
From annual taxes Γ— hold months
Enter taxes in property info & months
⚑ Utilities during rehab
Electric ($/mo)
Gas ($/mo)
Water ($/mo)
Total utilities for hold period
Enter utility costs & months
Total holding costs (interest + taxes + utilities)
Complete inputs above
Cost to sell
Enter ARV & percentages
Full cost breakdown
Total all-in costβ€”
Gross profit at ask
β€”
ROI at ask
β€”
ARV
β€”
Repairs % of ARV
β€”
Max offer price
β€”
ARV βˆ’ repairs βˆ’ holding
βˆ’ cost to sell βˆ’ profit
Saved βœ“
const SECTIONS=[ {id:”exterior”,icon:”🏑”,label:”Exterior & roof”,items:[{name:”Roof replacement (per sq)”,unit:400},{name:”Fascia / soffit (linear ft)”,unit:8},{name:”Gutters (linear ft)”,unit:7},{name:”Siding replacement (per sq)”,unit:350},{name:”Exterior paint”,unit:3000},{name:”Exterior door (each)”,unit:600},{name:”Garage door”,unit:1400},{name:”Driveway repair / replace”,unit:3500},{name:”Walkway / steps”,unit:800}]}, {id:”landscape”,icon:”🌿”,label:”Landscaping & curb appeal”,items:[{name:”Landscaping / grading”,unit:1500},{name:”Fencing repair / replace”,unit:2000},{name:”Deck / porch repair”,unit:2500}]}, {id:”windows”,icon:”πŸͺŸ”,label:”Windows”,items:[{name:”Window replacement (each)”,unit:300}]}, {id:”demo”,icon:”πŸ—‘οΈ”,label:”Demo & cleanup”,items:[{name:”Full interior demo”,unit:3500},{name:”Dumpster / haul-off (per pull)”,unit:450},{name:”Selective demo”,unit:800}]}, {id:”structural”,icon:”πŸ—οΈ”,label:”Structural / framing”,items:[{name:”Wall removal / relocation”,unit:1200},{name:”Beam / header”,unit:2500},{name:”Subfloor repair (per room)”,unit:600},{name:”Foundation / structural repair”,unit:5000}]}, {id:”kitchen”,icon:”🍳”,label:”Kitchen”,items:[{name:”Cabinets”,unit:4500},{name:”Countertops”,unit:2000},{name:”Backsplash tile”,unit:600},{name:”Appliance package”,unit:3000},{name:”Kitchen sink & faucet”,unit:450}]}, {id:”living”,icon:”πŸ›‹οΈ”,label:”Living & dining areas”,items:[{name:”Flooring β€” LVP / hardwood (sq ft)”,unit:4},{name:”Flooring β€” carpet (sq ft)”,unit:3},{name:”Paint β€” living / dining”,unit:800},{name:”Trim / baseboards (linear ft)”,unit:4},{name:”Interior doors (each)”,unit:250},{name:”Light fixtures (each)”,unit:150}]}, {id:”bedrooms”,icon:”πŸ›οΈ”,label:”Bedrooms”,items:[{name:”Flooring per bedroom”,unit:600},{name:”Paint per bedroom”,unit:350},{name:”Closet shelving (each)”,unit:200}]}, {id:”bath”,icon:”πŸ›”,label:”Bathrooms”,items:[{name:”Full bath remodel (each)”,unit:6000},{name:”Vanity & top (each)”,unit:800},{name:”Shower tile (each)”,unit:1200},{name:”Bath floor tile (each)”,unit:500},{name:”Toilet (each)”,unit:350},{name:”Tub / shower unit”,unit:800}]}, {id:”insulation”,icon:”🧱”,label:”Insulation & drywall”,items:[{name:”Insulation (per room)”,unit:500},{name:”Drywall hang & finish (per room)”,unit:700},{name:”Drywall patch / repair”,unit:300}]}, {id:”paint”,icon:”πŸ–ŒοΈ”,label:”Interior paint”,items:[{name:”Interior paint β€” whole house”,unit:3500},{name:”Cabinet painting”,unit:1200}]}, {id:”basement”,icon:”🏚️”,label:”Basement”,items:[{name:”Waterproofing / drainage”,unit:4000},{name:”Basement wall support / bracing”,unit:5000},{name:”Egress window”,unit:2500},{name:”Finishing (per room)”,unit:3000}]}, {id:”plumbing”,icon:”πŸ’§”,label:”Plumbing”,items:[{name:”Rough plumbing rework”,unit:4500},{name:”Water heater”,unit:1200},{name:”Sink & faucet (each)”,unit:400}]}, {id:”electrical”,icon:”⚑”,label:”Electrical”,items:[{name:”Panel upgrade (100Aβ†’200A)”,unit:2500},{name:”Rough wiring”,unit:3500},{name:”Outlets & switches (each)”,unit:15},{name:”Smoke / CO detectors (each)”,unit:60}]}, {id:”hvac”,icon:”🌬️”,label:”HVAC”,items:[{name:”Full system replacement”,unit:8000},{name:”Ductwork”,unit:3000},{name:”Thermostat”,unit:250}]}, {id:”other”,icon:”πŸ“‹”,label:”Other / miscellaneous”,items:[{name:”Permits & fees”,unit:1200},{name:”Final clean”,unit:400},{name:”Contingency buffer”,unit:2000}]}, ]; const PROP_IDS=[“p-addr”,”p-city”,”p-county”,”p-parcel”,”p-beds”,”p-sqft”,”p-year”,”p-asking”,”p-taxes”]; const DEAL_IDS=[“d-arv”,”d-purchase”,”d-months”,”d-rate”,”d-comm”,”d-close”,”d-profit”,”u-electric”,”u-gas”,”u-water”]; const state={}; SECTIONS.forEach(s=>{state[s.id]={open:false,items:s.items.map(i=>({…i,qty:0})),notes:””};}); const fmt=n=>”$”+Math.round(n).toLocaleString(); const fmtP=n=>n.toFixed(1)+”%”; const iT=i=>(i.qty||0)*(i.unit||0); const sT=id=>state[id].items.reduce((a,i)=>a+iT(i),0); const repT=()=>SECTIONS.reduce((a,s)=>a+sT(s.id),0); const vv=id=>parseFloat(document.getElementById(id).value)||0; const tt=id=>(document.getElementById(id)||{value:””}).value.trim(); const col=(v,g,w)=>v>=g?”#2d7a36″:v>=w?”#92400e”:”#b91c1c”; // ── Save / Load ────────────────────────────────────────────────────────────── function saveData(){ const data={ prop:{},deal:{},sections:{},saved:new Date().toLocaleString() }; PROP_IDS.forEach(id=>data.prop[id]=document.getElementById(id).value); DEAL_IDS.forEach(id=>data.deal[id]=document.getElementById(id).value); SECTIONS.forEach(s=>{data.sections[s.id]={items:state[s.id].items.map(i=>({…i})),notes:state[s.id].notes};}); try{ localStorage.setItem(“pwa_data”,JSON.stringify(data)); document.getElementById(“save-status”).textContent=”Last saved: “+data.saved; showToast(“Saved βœ“”); } catch(e){showToast(“Save failed β€” storage unavailable”);} } function loadData(){ try{ const raw=localStorage.getItem(“pwa_data”); if(!raw)return; const data=JSON.parse(raw); if(data.prop) PROP_IDS.forEach(id=>{if(data.prop[id]!==undefined)document.getElementById(id).value=data.prop[id];}); if(data.deal) DEAL_IDS.forEach(id=>{if(data.deal[id]!==undefined)document.getElementById(id).value=data.deal[id];}); if(data.sections) SECTIONS.forEach(s=>{ if(data.sections[s.id]){ state[s.id].items=data.sections[s.id].items||state[s.id].items; state[s.id].notes=data.sections[s.id].notes||””; } }); if(data.saved) document.getElementById(“save-status”).textContent=”Last saved: “+data.saved; } catch(e){} } function confirmNew(){ if(confirm(“Start a new property? This will clear all current data. Make sure you’ve saved first.”)){ localStorage.removeItem(“pwa_data”); PROP_IDS.forEach(id=>{document.getElementById(id).value=””;}); DEAL_IDS.forEach(id=>{const el=document.getElementById(id);if(el)el.value=””;}); // reset defaults document.getElementById(“d-rate”).value=”12″; document.getElementById(“d-comm”).value=”6″; document.getElementById(“d-close”).value=”2″; SECTIONS.forEach(s=>{state[s.id].items=SECTIONS.find(x=>x.id===s.id).items.map(i=>({…i,qty:0}));state[s.id].notes=””;state[s.id].open=false;}); document.getElementById(“save-status”).textContent=”No saved data”; render();calc(); } } function showToast(msg){ const t=document.getElementById(“toast”); t.textContent=msg;t.classList.add(“show”); setTimeout(()=>t.classList.remove(“show”),2200); } // ── Calc ───────────────────────────────────────────────────────────────────── function calc(){ const repairs=repT(); const arv=vv(“d-arv”),purchase=vv(“d-purchase”),months=vv(“d-months”),rate=vv(“d-rate”)||12; const comm=vv(“d-comm”),close=vv(“d-close”),profit=vv(“d-profit”),asking=vv(“p-asking”); const taxes=vv(“p-taxes”),elec=vv(“u-electric”),gas=vv(“u-gas”),water=vv(“u-water”); const loanBase=purchase+repairs; const interest=months>0&&loanBase>0?loanBase*(rate/100)*(months/12):0; const taxHold=months>0&&taxes>0?taxes*(months/12):0; const utilTotal=months>0?(elec+gas+water)*months:0; const totalHolding=interest+taxHold+utilTotal; const sell=arv>0?arv*((comm+close)/100):0; const allIn=purchase+repairs+totalHolding+sell; const profitAtAsk=arv-allIn; const roi=allIn>0?(profitAtAsk/allIn)*100:0; const maxOffer=arv-repairs-totalHolding-sell-profit; const repPct=arv>0?(repairs/arv)*100:0; document.getElementById(“hdr-total”).textContent=fmt(repairs); document.getElementById(“repair-total”).textContent=fmt(repairs); document.getElementById(“d-holding-display”).textContent=interest>0?`${fmt(interest)} (${fmt(loanBase)} Γ— ${rate}% Γ· 12 Γ— ${months}mo)`:”Enter purchase, repairs & months”; document.getElementById(“d-tax-display”).textContent=taxHold>0?`${fmt(taxHold)} ($${taxes.toLocaleString()}/yr Γ· 12 Γ— ${months}mo)`:”Enter annual taxes & months”; document.getElementById(“d-util-display”).textContent=utilTotal>0?`${fmt(utilTotal)} ($${(elec+gas+water).toLocaleString()}/mo Γ— ${months}mo)`:”Enter utility costs & months”; document.getElementById(“d-holding-total”).textContent=totalHolding>0?`${fmt(totalHolding)} (interest ${fmt(interest)} + taxes ${fmt(taxHold)} + utilities ${fmt(utilTotal)})`:”Complete holding cost inputs above”; document.getElementById(“d-sell-display”).textContent=sell>0?`${fmt(sell)} (${comm+close}% of ${fmt(arv)})`:”Enter ARV & percentages”; const rows=[[“Purchase price”,purchase>0?fmt(purchase):”β€””],[“Rehab / repairs”,repairs>0?fmt(repairs):”β€””],[`Holding β€” interest (${rate}% Γ— ${months||”?”}mo)`,interest>0?fmt(interest):”β€””],[`Holding β€” property taxes (${months||”?”}mo)`,taxHold>0?fmt(taxHold):”β€””],[`Holding β€” utilities (${months||”?”}mo)`,utilTotal>0?fmt(utilTotal):”β€””],[`Cost to sell (${comm+close}%)`,sell>0?fmt(sell):”β€””]]; document.getElementById(“breakdown-rows”).innerHTML=rows.map(([l,v])=>`
${l}${v}
`).join(“”); document.getElementById(“b-total”).textContent=allIn>0?fmt(allIn):”β€””; const pm=document.getElementById(“m-profit”);pm.textContent=arv>0&&purchase>0?fmt(profitAtAsk):”β€””;pm.style.color=arv>0&&purchase>0?col(profitAtAsk,profit,0):”var(–g900)”; const rm=document.getElementById(“m-roi”);rm.textContent=arv>0&&purchase>0?fmtP(roi):”β€””;rm.style.color=arv>0&&purchase>0?col(roi,20,10):”var(–g900)”; document.getElementById(“m-arv”).textContent=arv>0?fmt(arv):”β€””; const rp=document.getElementById(“m-rpct”);rp.textContent=arv>0&&repairs>0?fmtP(repPct):”β€””;rp.style.color=arv>0&&repairs>0?col(100-repPct,70,50):”var(–g900)”; const ob=document.getElementById(“offer-box”),ov=document.getElementById(“offer-val”),od=document.getElementById(“offer-diff”); if(arv>0&&profit>0){ov.textContent=maxOffer>0?fmt(maxOffer):”Doesn’t pencil”;ob.className=”offer-box”+(maxOffer0&&maxOffer>0){const d=maxOffer-asking;od.textContent=d>=0?`βœ“ Ask is ${fmt(Math.abs(d))} under your max`:`βœ— Ask is ${fmt(Math.abs(d))} over your max`;}else od.textContent=””;} else{ov.textContent=”β€””;ob.className=”offer-box”;od.textContent=””;} SECTIONS.forEach(s=>{ const b=document.getElementById(“b-“+s.id),tot=sT(s.id); if(b){b.textContent=tot>0?fmt(tot):””;b.style.display=tot>0?””:”none”;} state[s.id].items.forEach((_,i)=>{const el=document.getElementById(“it-“+s.id+”-“+i);if(el){const t2=iT(state[s.id].items[i]);el.textContent=t2>0?fmt(t2):”β€””;el.className=”lt”+(t2>0?””:” z”);}}); }); } // ── Render sections ────────────────────────────────────────────────────────── function render(){ const c=document.getElementById(“sections”);c.innerHTML=””; SECTIONS.forEach(s=>{ const st=state[s.id]; const w=document.createElement(“div”);w.className=”sec-card”; const h=document.createElement(“div”);h.className=”sec-hd”+(st.open?” open”:””); h.innerHTML=`
${s.icon}
${s.label}
βŒ„
`; h.onclick=()=>{st.open=!st.open;render();calc();};w.appendChild(h); if(st.open){ const bd=document.createElement(“div”);bd.className=”sec-bd”; const ch=document.createElement(“div”);ch.className=”col-hds”;ch.innerHTML=”ItemQtyUnit $Total“;bd.appendChild(ch); st.items.forEach((item,i)=>{ const row=document.createElement(“div”);row.className=”line”; const ni=document.createElement(“input”);ni.type=”text”;ni.placeholder=”Item”;ni.value=item.name;ni.oninput=e=>{st.items[i].name=e.target.value;}; const qi=document.createElement(“input”);qi.type=”number”;qi.min=”0″;qi.placeholder=”0″;qi.value=item.qty||””;qi.oninput=e=>{st.items[i].qty=parseFloat(e.target.value)||0;calc();}; const ui=document.createElement(“input”);ui.type=”number”;ui.min=”0″;ui.placeholder=”$”;ui.value=item.unit||””;ui.oninput=e=>{st.items[i].unit=parseFloat(e.target.value)||0;calc();}; const ts=document.createElement(“span”);ts.id=”it-“+s.id+”-“+i;ts.className=”lt z”;ts.textContent=”β€””; const db=document.createElement(“button”);db.className=”del”;db.innerHTML=”βœ•”;db.setAttribute(“aria-label”,”Remove”);db.onclick=()=>{st.items.splice(i,1);render();calc();}; row.appendChild(ni);row.appendChild(qi);row.appendChild(ui);row.appendChild(ts);row.appendChild(db);bd.appendChild(row); }); const ab=document.createElement(“button”);ab.className=”add-btn”;ab.innerHTML=”+ Add item”;ab.onclick=()=>{st.items.push({name:””,unit:0,qty:0});render();};bd.appendChild(ab); const nt=document.createElement(“textarea”);nt.className=”notes”;nt.rows=2;nt.placeholder=”Notes…”;nt.value=st.notes;nt.oninput=e=>{st.notes=e.target.value;};bd.appendChild(nt); w.appendChild(bd); } c.appendChild(w); }); calc(); } // ── Print ──────────────────────────────────────────────────────────────────── function buildPrintHTML(){ const addr=tt(“p-addr”)||”Property address not entered”,city=tt(“p-city”),beds=tt(“p-beds”); const sqft=document.getElementById(“p-sqft”).value,year=document.getElementById(“p-year”).value,asking=vv(“p-asking”); const arv=vv(“d-arv”),purchase=vv(“d-purchase”),months=vv(“d-months”),rate=vv(“d-rate”)||12; const comm=vv(“d-comm”),close=vv(“d-close”),profit=vv(“d-profit”),taxes=vv(“p-taxes”); const elec=vv(“u-electric”),gas=vv(“u-gas”),water=vv(“u-water”); const repairs=repT(),loanBase=purchase+repairs; const interest=months>0&&loanBase>0?loanBase*(rate/100)*(months/12):0; const taxHold=months>0&&taxes>0?taxes*(months/12):0; const utilTotal=months>0?(elec+gas+water)*months:0; const totalHolding=interest+taxHold+utilTotal; const sell=arv>0?arv*((comm+close)/100):0; const allIn=purchase+repairs+totalHolding+sell; const profitAtAsk=arv-allIn,roi=allIn>0?(profitAtAsk/allIn)*100:0,maxOffer=arv-repairs-totalHolding-sell-profit; const today=new Date().toLocaleDateString(“en-US”,{month:”long”,day:”numeric”,year:”numeric”}); let repairRows=””; SECTIONS.forEach(s=>{const tot=sT(s.id);if(tot>0){repairRows+=`${s.label}${fmt(tot)}`;state[s.id].items.forEach(i=>{if(iT(i)>0)repairRows+=`${i.name}${i.qty}${fmt(i.unit)}Γ—${fmt(iT(i))}`;});if(state[s.id].notes)repairRows+=`Note: ${state[s.id].notes}`;}}); const dealRows=[[“Purchase price”,purchase>0?fmt(purchase):”β€””],[“Rehab / repairs”,fmt(repairs)],[`Interest (${rate}% Γ— ${months||”?”}mo)`,interest>0?fmt(interest):”β€””],[`Property taxes (${months||”?”}mo)`,taxHold>0?fmt(taxHold):”β€””],[`Utilities (${months||”?”}mo)`,utilTotal>0?fmt(utilTotal):”β€””],[`Cost to sell (${comm+close}%)`,sell>0?fmt(sell):”β€””]]; return`Rehab Estimate β€” ${addr}body{font-family:Arial,sans-serif;font-size:12px;color:#111827;margin:0;padding:0}@page{margin:.6in;size:letter}.ph{background:#1F4E5F;color:#fff;padding:16px 20px;display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px}.pt{font-size:18px;font-weight:bold;margin-bottom:3px}.ps{font-size:12px;opacity:.8}h2{font-size:13px;font-weight:bold;color:#1F4E5F;text-transform:uppercase;letter-spacing:.05em;border-bottom:2px solid #1F4E5F;padding-bottom:4px;margin:18px 0 10px}table{width:100%;border-collapse:collapse;margin-bottom:16px}.dt td{padding:6px 8px;border-bottom:1px solid #e5e7eb;font-size:12px}.dt .tr td{font-weight:bold;border-top:2px solid #1F4E5F;border-bottom:none;font-size:13px;color:#1F4E5F}.os{background:#1F4E5F;color:#fff;padding:14px 16px;display:flex;justify-content:space-between;align-items:center;margin-top:20px}.mg{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px}.mb{border:1px solid #e5e7eb;border-radius:4px;padding:8px 10px}.ss{margin-top:24px;display:flex;gap:40px}.sb{flex:1}.sl{border-top:1px solid #374151;margin-top:32px;padding-top:4px;font-size:11px;color:#6b7280}
${addr}${city?”, “+city:””}
${[beds?beds+” bd/ba”:””,sqft?sqft+” sq ft”:””,year?”Built “+year:””].filter(Boolean).join(” Β· “)}
${asking>0?`
Asking: ${fmt(asking)}
`:””}
Rehab Estimate
${today}

Repair estimate by category

${repairRows}
ItemQtyUnitTotal
Total estimated repairs${fmt(repairs)}

Deal analysis

${[arv>0?[“ARV”,fmt(arv),”#111827″]:””,allIn>0?[“Total all-in”,fmt(allIn),”#111827″]:””,arv>0&&purchase>0?[“Profit at ask”,fmt(profitAtAsk),col(profitAtAsk,profit,0)]:””,arv>0&&purchase>0?[“ROI”,fmtP(roi),col(roi,20,10)]:””].filter(Boolean).map(([l,v,c])=>`
${l}
${v}
`).join(“”)}
${dealRows.map(([l,v])=>``).join(“”)}
${l}${v}
Total all-in cost${allIn>0?fmt(allIn):”β€””}
${arv>0?`
Max offer price
${arv>0&&profit>0?(maxOffer>0?fmt(maxOffer):”Doesn’t pencil”):”β€””}
${asking>0&&maxOffer>0?`
vs. asking
${maxOffer>=asking?”βœ“ “+fmt(maxOffer-asking)+” under”:”βœ— “+fmt(asking-maxOffer)+” over”}
`:””}
`:””}
Partner A β€” Acquisitions & Materials
Partner B β€” Construction & Oversight
`; } function doPrint(){const w=window.open(“”,”_blank”);if(!w){alert(“Allow popups to print.”);return;}w.document.write(buildPrintHTML());w.document.close();w.onload=()=>{w.focus();w.print();};} // ── Copy ───────────────────────────────────────────────────────────────────── function copyReport(){ const addr=tt(“p-addr”)||”[Address not entered]”,repairs=repT(); const arv=vv(“d-arv”),purchase=vv(“d-purchase”),months=vv(“d-months”),rate=vv(“d-rate”)||12; const comm=vv(“d-comm”),close=vv(“d-close”),profit=vv(“d-profit”),asking=vv(“p-asking”),taxes=vv(“p-taxes”); const elec=vv(“u-electric”),gas=vv(“u-gas”),water=vv(“u-water”); const loanBase=purchase+repairs; const interest=months>0&&loanBase>0?loanBase*(rate/100)*(months/12):0; const taxHold=months>0&&taxes>0?taxes*(months/12):0; const utilTotal=months>0?(elec+gas+water)*months:0; const totalHolding=interest+taxHold+utilTotal; const sell=arv>0?arv*((comm+close)/100):0; const allIn=purchase+repairs+totalHolding+sell; const maxOffer=arv-repairs-totalHolding-sell-profit; let lines=[“PROPERTY WALK SUMMARY”,”=”.repeat(44),addr,””,”REPAIR BREAKDOWN:”]; SECTIONS.forEach(s=>{const tot=sT(s.id);if(tot>0){lines.push(“”,s.label.toUpperCase()+” β€” “+fmt(tot));state[s.id].items.forEach(i=>{if(iT(i)>0)lines.push(” “+i.name+” Γ— “+i.qty+” @ “+fmt(i.unit)+” = “+fmt(iT(i)));});if(state[s.id].notes)lines.push(” Note: “+state[s.id].notes);}}); lines.push(“”,”=”.repeat(44),”Total repairs: “+fmt(repairs),”Purchase price: “+(purchase>0?fmt(purchase):”β€””),”Interest (holding): “+(interest>0?fmt(interest):”β€””),”Taxes (holding): “+(taxHold>0?fmt(taxHold):”β€””),”Utilities (holding): “+(utilTotal>0?fmt(utilTotal):”β€””),”Total holding: “+(totalHolding>0?fmt(totalHolding):”β€””),”Cost to sell: “+(sell>0?fmt(sell):”β€””),”Total all-in: “+(allIn>0?fmt(allIn):”β€””),”ARV: “+(arv>0?fmt(arv):”β€””),”Profit at ask: “+(arv>0&&purchase>0?fmt(arv-allIn):”β€””)); if(asking>0)lines.push(“Asking price: “+fmt(asking)); lines.push(“Max offer: “+(maxOffer>0?fmt(maxOffer):”Doesn’t pencil”)); navigator.clipboard.writeText(lines.join(“\n”)).then(()=>showToast(“Copied βœ“”)); } // ── Offer ──────────────────────────────────────────────────────────────────── function generateOffer(){ const addr=tt(“p-addr”)||”[Property Address]”,city=tt(“p-city”)||””,county=tt(“p-county”)||”[County]”,parcel=tt(“p-parcel”); const sName=tt(“o-sname”)||”[Seller]”,sEmail=tt(“o-semail”),sPhone=tt(“o-sphone”); const bName=tt(“o-bname”)||”Adam Zeman”,bEmail=tt(“o-bemail”),bPhone=tt(“o-bphone”); const price=vv(“o-price”),expiry=tt(“o-expiry”)||”[Expiration]”,closing=tt(“o-closing”)||”TBD”,dd=tt(“o-dd”)||”7″; const today=new Date().toLocaleDateString(“en-US”,{month:”long”,day:”numeric”,year:”numeric”}); const fullAddr=addr+(city?”, “+city:””); const isTBD=closing.toLowerCase().startsWith(“tbd”); const closingText=isTBD?”Closing date is to be mutually agreed upon by both parties in writing, with reasonable advance notice provided once both parties are ready to proceed.”:`Closing will be held on or before ${closing}.`; const html=`Purchase Agreement β€” ${addr}body{font-family:”Times New Roman”,Times,serif;font-size:12pt;color:#000;margin:0;padding:0;line-height:1.7}@page{margin:1in;size:letter}.doc{max-width:7.5in;margin:0 auto;padding:1in}h1{font-size:14pt;font-weight:bold;text-align:center;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}.ctr{text-align:center;margin-bottom:24px;font-size:11pt;color:#444}.sec{margin-bottom:16px}.sn{font-weight:bold}.sb{margin-top:40px;display:flex;gap:60px}.sc{flex:1}.sl{border-top:1px solid #000;margin-top:52px;padding-top:4px;font-size:10pt}.si{font-size:10pt;margin-top:6px;color:#333;line-height:1.9}.cb{display:inline-block;width:12px;height:12px;border:1px solid #000;margin-right:4px;vertical-align:middle;text-align:center;line-height:12px;font-size:10pt}

Purchase and Sale Agreement

Date: ${today}
BE IT KNOWN, The undersigned ${bName} (buyer), offers to purchase from ${sName} (owner/seller), real estate known as ${fullAddr}${parcel?` with parcel ID: ${parcel}`:””}.
This offer shall expire on ${expiry} unless signed by the seller before its expiration.
THE PROPERTY. The parties hereby agree that the Seller will sell and the Buyer will buy the following property, located in the county of ${county}, State of Ohio, known by the street address as: ${fullAddr}${parcel?`, with the parcel ID: ${parcel}`:””}.
1. PURCHASE PRICE: The total purchase price to be paid by the buyer will be ${price>0?fmt(price):”[OFFER PRICE]”}.
2. CASH SALE: This is a cash transaction. The Buyer may obtain private capital to fund the purchase.
3. DUE DILIGENCE PERIOD: Due diligence period is ${dd} days from this agreement being signed by both parties. During this period, buyer shall have the right to inspect the property and obtain contractor and/or structural engineer estimates. Buyer may renegotiate or withdraw from this agreement based on inspection findings during this period.
4. CLOSING: ${closingText} Patriot Title (4120 Whipple Ave NW, Canton, OH 44718) will act as the title agency for this transaction.

At closing, the buyer and seller shall pay normal closing costs following local and customary costs in transferring.

The following items will be prorated at closing:
X property taxes    HOA dues    rents    security deposits will be conveyed to the buyer.
5. DEED AND TRANSFER: Seller agrees to convey title by general warranty deed. In the event that an abstract of title issued by a reputable title company reveals defects in title, the seller is obligated to cure said issues. Seller agrees to deliver possession of the property at time of title transfer.
6. VESTING: Vesting will be determined upon close of escrow. The buyer may close in a new entity or the entity of its choosing.
7. EXECUTION IN COUNTERPARTS: This agreement may be executed in counterparts and by email & digital signatures. This agreement shall become effective as of the date of the last signature.
8. LEAD INSPECTION: Buyer shall be permitted the opportunity to inspect the premises solely for the purposes of the presence of lead-based paint hazards on or before the closing date. Lead addendum required?    YES  X NO
9. TIME IS OF THE ESSENCE: All parties agree that time is of the essence in this agreement${isTBD?”, except as it relates to the mutually agreed closing date as described in Section 4″:””}.
10. ADDITIONAL TERMS:
Buyer   is X is not a licensed real estate agent in the state of Ohio. Seller must remove any/all current marketing materials regarding the sale of this property. The seller must provide reasonable access to the property for the buyer, buyer’s contractors, partners, appraisers, lenders, etc. with at least a 24-hour notice by the buyer.
11. SIGNATURES:
Seller: ${sName}
${sEmail?`Email: ${sEmail}
`:””}${sPhone?`Phone: ${sPhone}`:””}
Date: ___________________
Buyer: ${bName}
${bEmail?`Email: ${bEmail}
`:””}${bPhone?`Phone: ${bPhone}`:””}
Date: ___________________
`; const w=window.open(“”,”_blank”);if(!w){alert(“Allow popups to generate offer.”);return;}w.document.write(html);w.document.close();document.getElementById(“modal-ov”).classList.remove(“show”); } // ── Init ───────────────────────────────────────────────────────────────────── loadData(); render();