import { useState, useEffect, useRef } from "react";
// ─── CONFIGURE YOUR EMAIL HERE ────────────────────────────────────────────────
const RECIPIENT_EMAIL = "adam.ghsr@gmail.com";
// ─────────────────────────────────────────────────────────────────────────────
const GREEN = "#1D9E75";
const GREEN_DARK = "#085041";
const GREEN_LIGHT = "#E8F7F2";
const GREEN_MID = "#D0EDE4";
const SECTIONS = [
{
id: "goals",
title: "Your Goals",
icon: "🎯",
questions: [
{
id: "q1", num: 1, type: "textarea",
label: "Why are you interested in purchasing an investment property?",
},
{
id: "q2", num: 2, type: "textarea",
label: "What would make this investment a success in your eyes?",
},
{
id: "q3", num: 3, type: "multicheck", max: 2,
label: "Which of the following interests you most?",
hint: "Choose up to 2",
options: [
"Monthly cash flow",
"Long-term appreciation",
"Tax benefits",
"Building retirement income",
"Eventually owning multiple properties",
"Creating passive income",
"Living in the property while building equity",
],
other: true,
},
{
id: "q4", num: 4, type: "radio",
label: "How long do you expect to hold this property?",
options: ["Less than 3 years", "3–5 years", "5–10 years", "10+ years", "Not sure"],
},
],
},
{
id: "financial",
title: "Financial Considerations",
icon: "💰",
questions: [
{
id: "q5", num: 5, type: "text",
label: "What price range are you comfortable considering?",
placeholder: "e.g. $150,000 – $300,000",
},
{
id: "q6", num: 6, type: "multitext",
label: "How much cash do you have available for:",
fields: [
{ id: "q6a", label: "Down payment", placeholder: "e.g. $40,000" },
{ id: "q6b", label: "Closing costs", placeholder: "e.g. $6,000" },
{ id: "q6c", label: "Repairs and improvements",placeholder: "e.g. $15,000" },
{ id: "q6d", label: "Emergency reserves", placeholder: "e.g. $10,000" },
],
},
{
id: "q7", num: 7, type: "rating",
label: "How comfortable are you with unexpected expenses?",
hint: "1 = Very uncomfortable · 5 = Very comfortable",
},
],
},
{
id: "property",
title: "Property Preferences",
icon: "🏠",
questions: [
{
id: "q8", num: 8, type: "multicheck",
label: "Which property types interest you?",
options: ["Single-family home","Townhome","Duplex","Triplex","Fourplex","Small multifamily","Not sure"],
},
{
id: "q9", num: 9, type: "radio",
label: "Would you consider living in one unit while renting the others?",
options: ["Yes","No","Maybe"],
},
{
id: "q10", num: 10, type: "radio",
label: "What condition are you comfortable purchasing?",
options: ["Move-in ready only","Minor cosmetic updates","Moderate renovations","Major renovations"],
},
{
id: "q11", num: 11, type: "radio",
label: "How involved do you want to be as a landlord?",
options: ["Very hands-on","Some involvement","Mostly passive","Prefer professional property management"],
},
],
},
{
id: "location",
title: "Location",
icon: "📍",
questions: [
{
id: "q12", num: 12, type: "textarea",
label: "Where are the specific cities or areas where you want to invest?",
},
{
id: "q13", num: 13, type: "radio",
label: "Are you open to investing outside your immediate area?",
options: ["Yes","No","Maybe"],
},
{
id: "q14", num: 14, type: "multicheck", max: 3,
label: "What factors matter most in choosing a location?",
hint: "Choose up to 3",
options: [
"Strong rental demand","Good schools","Job growth","Population growth",
"Lower purchase price","Higher cash flow","Lower maintenance","Future appreciation potential",
],
},
],
},
{
id: "risk",
title: "Risk & Growth",
icon: "📈",
questions: [
{
id: "q15", num: 15, type: "radio",
label: "Which statement best describes you?",
options: [
"I want the safest investment possible.",
"I am willing to accept some calculated risk for better returns.",
"I am comfortable taking risks for potential long-term growth.",
],
},
{
id: "q16", num: 16, type: "radio",
label: "If this first property performs well, what would you like to do next?",
options: [
"Buy another property within 1–2 years",
"Buy another property eventually",
"Keep only one property",
"Not sure",
],
},
],
},
{
id: "final",
title: "Final Question",
icon: "✨",
questions: [
{
id: "q17", num: 17, type: "textarea",
label: "If one year from today I were to ask you why you are thrilled with your decision to purchase, what would you say?",
tall: true,
},
],
},
];
function initAnswers() {
const a = { clientName: "", clientEmail: "", clientPhone: "" };
SECTIONS.forEach(s => s.questions.forEach(q => {
if (q.type === "multicheck") { a[q.id] = []; a[q.id + "_other"] = ""; }
else if (q.type === "multitext") q.fields.forEach(f => a[f.id] = "");
else if (q.type === "rating") a[q.id] = null;
else a[q.id] = "";
}));
return a;
}
function formatEmailBody(answers) {
let body = `RENTAL PROPERTY INVESTOR QUESTIONNAIRE\n`;
body += `${"=".repeat(50)}\n\n`;
body += `Client Name: ${answers.clientName || "—"}\n`;
body += `Email: ${answers.clientEmail || "—"}\n`;
body += `Phone: ${answers.clientPhone || "—"}\n`;
body += `Submitted: ${new Date().toLocaleDateString("en-US", { dateStyle: "long" })}\n\n`;
SECTIONS.forEach(sec => {
body += `\n${"─".repeat(50)}\n${sec.title.toUpperCase()}\n${"─".repeat(50)}\n\n`;
sec.questions.forEach(q => {
body += `Q${q.num}. ${q.label}\n`;
if (q.type === "multicheck") {
const selected = answers[q.id] || [];
body += selected.length ? selected.map(o => ` ☑ ${o}`).join("\n") : " (no selection)";
if (answers[q.id + "_other"]) body += `\n Other: ${answers[q.id + "_other"]}`;
} else if (q.type === "multitext") {
q.fields.forEach(f => body += ` ${f.label}: ${answers[f.id] || "—"}\n`);
} else if (q.type === "rating") {
body += ` Rating: ${answers[q.id] || "—"} / 5`;
} else {
body += ` ${answers[q.id] || "—"}`;
}
body += "\n\n";
});
});
return body;
}
// ─── Sub-components ───────────────────────────────────────────────────────────
function ProgressBar({ current, total }) {
const pct = Math.round((current / total) * 100);
return (
<div style={{ marginBottom: 28 }}>
<div style={{ display:"flex", justifyContent:"space-between", marginBottom:6 }}>
<span style={{ fontSize:12, color:"#6B7280", fontWeight:500 }}>
Section {current} of {total}
</span>
<span style={{ fontSize:12, color: GREEN, fontWeight:600 }}>{pct}%</span>
</div>
<div style={{ height:5, borderRadius:99, background:"#E5E7EB", overflow:"hidden" }}>
<div style={{
height:"100%", borderRadius:99,
background:`linear-gradient(90deg, ${GREEN}, ${GREEN_DARK})`,
width:`${pct}%`, transition:"width 0.4s ease",
}}/>
</div>
</div>
);
}
function SectionBadge({ icon, title }) {
return (
<div style={{
display:"flex", alignItems:"center", gap:10,
background:`linear-gradient(135deg, ${GREEN_DARK}, ${GREEN})`,
borderRadius:10, padding:"12px 18px", marginBottom:28,
}}>
<span style={{ fontSize:20 }}>{icon}</span>
<span style={{ color:"white", fontWeight:700, fontSize:15, letterSpacing:"0.04em" }}>
{title.toUpperCase()}
</span>
</div>
);
}
function QuestionWrap({ num, label, hint, children }) {
return (
<div style={{ marginBottom:26 }}>
<div style={{ display:"flex", gap:8, marginBottom:hint?4:8, alignItems:"flex-start" }}>
<span style={{
minWidth:24, height:24, borderRadius:6,
background: GREEN_LIGHT, color: GREEN_DARK,
fontWeight:700, fontSize:12, display:"flex",
alignItems:"center", justifyContent:"center", marginTop:1, flexShrink:0,
}}>{num}</span>
<span style={{ fontSize:14, fontWeight:600, color:"#111827", lineHeight:1.5 }}>{label}</span>
</div>
{hint && <p style={{ fontSize:12, color:"#9CA3AF", margin:"0 0 8px 32px", fontStyle:"italic" }}>{hint}</p>}
<div style={{ paddingLeft:32 }}>{children}</div>
</div>
);
}
const inputStyle = {
width:"100%", border:`1.5px solid #E5E7EB`, borderRadius:8,
padding:"10px 12px", fontSize:14, color:"#111827",
background:"white", outline:"none", boxSizing:"border-box",
fontFamily:"inherit", transition:"border-color 0.15s",
};
function TextInput({ value, onChange, placeholder }) {
const [focused, setFocused] = useState(false);
return (
<input
style={{ ...inputStyle, borderColor: focused ? GREEN : "#E5E7EB" }}
value={value} onChange={e => onChange(e.target.value)}
placeholder={placeholder || "Your answer…"}
onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
/>
);
}
function TextArea({ value, onChange, tall }) {
const [focused, setFocused] = useState(false);
return (
<textarea
style={{ ...inputStyle, borderColor: focused ? GREEN : "#E5E7EB",
resize:"vertical", minHeight: tall ? 120 : 80, lineHeight:1.6 }}
value={value} onChange={e => onChange(e.target.value)}
placeholder="Your answer…"
onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
/>
);
}
function RadioGroup({ options, value, onChange }) {
return (
<div style={{ display:"flex", flexDirection:"column", gap:8 }}>
{options.map(opt => {
const sel = value === opt;
return (
<label key={opt} style={{
display:"flex", alignItems:"center", gap:10, cursor:"pointer",
padding:"9px 12px", borderRadius:8, transition:"background 0.15s",
background: sel ? GREEN_LIGHT : "transparent",
border: `1.5px solid ${sel ? GREEN : "#E5E7EB"}`,
}}>
<div style={{
width:16, height:16, borderRadius:"50%", flexShrink:0,
border:`2px solid ${sel ? GREEN : "#D1D5DB"}`,
background: sel ? GREEN : "white",
display:"flex", alignItems:"center", justifyContent:"center",
transition:"all 0.15s",
}}>
{sel && <div style={{ width:6, height:6, borderRadius:"50%", background:"white" }}/>}
</div>
<span style={{ fontSize:13.5, color: sel ? GREEN_DARK : "#374151", fontWeight: sel?600:400 }}>
{opt}
</span>
<input type="radio" style={{ display:"none" }}
checked={sel} onChange={() => onChange(opt)} />
</label>
);
})}
</div>
);
}
function MultiCheck({ options, value, onChange, max, other, otherValue, onOtherChange }) {
const toggle = (opt) => {
if (value.includes(opt)) { onChange(value.filter(v => v !== opt)); return; }
if (max && value.length >= max) return;
onChange([...value, opt]);
};
return (
<div style={{ display:"flex", flexDirection:"column", gap:8 }}>
{options.map(opt => {
const sel = value.includes(opt);
const maxed = max && !sel && value.length >= max;
return (
<label key={opt} style={{
display:"flex", alignItems:"center", gap:10, cursor: maxed?"not-allowed":"pointer",
padding:"9px 12px", borderRadius:8, transition:"background 0.15s",
background: sel ? GREEN_LIGHT : "transparent",
border:`1.5px solid ${sel ? GREEN : "#E5E7EB"}`,
opacity: maxed ? 0.45 : 1,
}}>
<div style={{
width:16, height:16, borderRadius:4, flexShrink:0,
border:`2px solid ${sel ? GREEN : "#D1D5DB"}`,
background: sel ? GREEN : "white",
display:"flex", alignItems:"center", justifyContent:"center",
transition:"all 0.15s",
}}>
{sel && <svg width="9" height="7" viewBox="0 0 9 7" fill="none">
<path d="M1 3L3.5 5.5L8 1" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
</svg>}
</div>
<span style={{ fontSize:13.5, color: sel?GREEN_DARK:"#374151", fontWeight:sel?600:400 }}>{opt}</span>
<input type="checkbox" style={{ display:"none" }} checked={sel} onChange={() => toggle(opt)} readOnly />
</label>
);
})}
{other && (
<div style={{ display:"flex", alignItems:"center", gap:10, padding:"6px 12px" }}>
<div style={{
width:16, height:16, borderRadius:4, flexShrink:0,
border:`2px solid ${otherValue ? GREEN : "#D1D5DB"}`,
background: otherValue ? GREEN : "white",
}}/>
<span style={{ fontSize:13.5, color:"#6B7280" }}>Other:</span>
<input
style={{ ...inputStyle, flex:1, padding:"6px 10px", fontSize:13 }}
value={otherValue} onChange={e => onOtherChange(e.target.value)}
placeholder="Please specify…"
/>
</div>
)}
</div>
);
}
function RatingInput({ value, onChange }) {
const labels = ["Very\nuncomfortable","","Neutral","","Very\ncomfortable"];
return (
<div style={{ display:"flex", gap:10, alignItems:"flex-end" }}>
{[1,2,3,4,5].map(n => {
const sel = value === n;
return (
<div key={n} style={{ display:"flex", flexDirection:"column", alignItems:"center", gap:4, flex:1 }}>
<button
onClick={() => onChange(n)}
style={{
width:"100%", aspectRatio:"1", borderRadius:10,
border:`2px solid ${sel ? GREEN : "#E5E7EB"}`,
background: sel ? GREEN : "white",
color: sel ? "white" : "#374151",
fontSize:16, fontWeight:700, cursor:"pointer",
transition:"all 0.15s", outline:"none",
boxShadow: sel ? `0 2px 10px ${GREEN}44` : "none",
}}
>{n}</button>
<span style={{
fontSize:10, color:"#9CA3AF", textAlign:"center",
whiteSpace:"pre-line", lineHeight:1.3, minHeight:28,
}}>{labels[n-1]}</span>
</div>
);
})}
</div>
);
}
function MultiTextInput({ fields, answers, onChange }) {
return (
<div style={{ display:"flex", flexDirection:"column", gap:12 }}>
{fields.map(f => (
<div key={f.id}>
<label style={{ fontSize:13, color:"#6B7280", fontWeight:500, display:"block", marginBottom:4 }}>
{f.label}
</label>
<TextInput value={answers[f.id]} onChange={v => onChange(f.id, v)} placeholder={f.placeholder} />
</div>
))}
</div>
);
}
// ─── Submitted screen ─────────────────────────────────────────────────────────
function SubmittedScreen({ clientName, onReset }) {
return (
<div style={{ textAlign:"center", padding:"48px 24px" }}>
<div style={{
width:72, height:72, borderRadius:"50%",
background:`linear-gradient(135deg, ${GREEN}, ${GREEN_DARK})`,
display:"flex", alignItems:"center", justifyContent:"center",
margin:"0 auto 24px", fontSize:32,
}}>✓</div>
<h2 style={{ fontSize:22, fontWeight:700, color:GREEN_DARK, margin:"0 0 10px" }}>
Questionnaire Submitted!
</h2>
<p style={{ fontSize:14, color:"#6B7280", maxWidth:360, margin:"0 auto 8px" }}>
Thank you{clientName ? `, ${clientName.split(" ")[0]}` : ""}. Your answers have been prepared — your email client should have opened with the completed questionnaire ready to send.
</p>
<p style={{ fontSize:13, color:"#9CA3AF", maxWidth:340, margin:"0 auto 32px" }}>
If it didn't open automatically, please check that your browser allows mailto links, or copy and paste the answers manually.
</p>
<button onClick={onReset} style={{
background:"white", border:`2px solid ${GREEN}`, color:GREEN,
borderRadius:8, padding:"10px 24px", fontSize:14, fontWeight:600,
cursor:"pointer",
}}>Start a new questionnaire</button>
</div>
);
}
// ─── Main app ─────────────────────────────────────────────────────────────────
export default function App() {
const [step, setStep] = useState(0); // 0 = intro
const [answers, setAnswers] = useState(initAnswers);
const [submitted, setSubmitted] = useState(false);
const topRef = useRef(null);
const totalSteps = SECTIONS.length;
const sectionIndex = step - 1;
const section = SECTIONS[sectionIndex];
const set = (key, val) => setAnswers(prev => ({ ...prev, [key]: val }));
const scrollTop = () => topRef.current?.scrollIntoView({ behavior:"smooth" });
const handleSubmit = () => {
const subject = encodeURIComponent(
`Investor Questionnaire — ${answers.clientName || "New Client"}`
);
const body = encodeURIComponent(formatEmailBody(answers));
window.location.href = `mailto:${RECIPIENT_EMAIL}?subject=${subject}&body=${body}`;
setSubmitted(true);
};
useEffect(() => { scrollTop(); }, [step]);
const renderQuestion = (q) => {
switch (q.type) {
case "text":
return <TextInput value={answers[q.id]} onChange={v => set(q.id, v)} placeholder={q.placeholder} />;
case "textarea":
return <TextArea value={answers[q.id]} onChange={v => set(q.id, v)} tall={q.tall} />;
case "radio":
return <RadioGroup options={q.options} value={answers[q.id]} onChange={v => set(q.id, v)} />;
case "multicheck":
return (
<MultiCheck
options={q.options} value={answers[q.id]} max={q.max}
onChange={v => set(q.id, v)} other={q.other}
otherValue={answers[q.id + "_other"]}
onOtherChange={v => set(q.id + "_other", v)}
/>
);
case "multitext":
return <MultiTextInput fields={q.fields} answers={answers} onChange={(id,v) => set(id, v)} />;
case "rating":
return <RatingInput value={answers[q.id]} onChange={v => set(q.id, v)} />;
default: return null;
}
};
return (
<div style={{ minHeight:"100vh", background:"#F9FAFB", fontFamily:"system-ui, -apple-system, sans-serif" }}>
{/* Header */}
<div style={{
background:`linear-gradient(135deg, ${GREEN_DARK} 0%, ${GREEN} 100%)`,
padding:"20px 24px", textAlign:"center",
}}>
<h1 style={{ color:"white", fontSize:20, fontWeight:700, margin:0 }}>
Rental Property Investor Questionnaire
</h1>
</div>
<div ref={topRef} style={{ maxWidth:620, margin:"0 auto", padding:"24px 16px 48px" }}>
{submitted ? (
<div style={{ background:"white", borderRadius:14, padding:8,
boxShadow:"0 1px 4px rgba(0,0,0,0.06)" }}>
<SubmittedScreen clientName={answers.clientName} onReset={() => {
setAnswers(initAnswers()); setStep(0); setSubmitted(false);
}}/>
</div>
) : step === 0 ? (
/* ── INTRO ── */
<div style={{ background:"white", borderRadius:14, padding:"28px 24px",
boxShadow:"0 1px 4px rgba(0,0,0,0.06)" }}>
<p style={{ fontSize:14, color:"#4B5563", lineHeight:1.7, margin:"0 0 24px" }}>
Thank you for taking a few minutes to complete this questionnaire.
There are no right or wrong answers — my goal is to understand what
you're trying to accomplish so I can help identify the best
opportunities for you.
</p>
<div style={{ borderTop:"1px solid #F3F4F6", paddingTop:20, marginBottom:20 }}>
<p style={{ fontSize:13, fontWeight:600, color:"#374151", margin:"0 0 14px" }}>
Your contact information
</p>
<div style={{ display:"flex", flexDirection:"column", gap:10 }}>
{[
{ id:"clientName", label:"Full name", placeholder:"Jane Smith" },
{ id:"clientEmail", label:"Email address",placeholder:"jane@example.com" },
{ id:"clientPhone", label:"Phone number", placeholder:"(555) 000-0000 (optional)"},
].map(f => (
<div key={f.id}>
<label style={{ fontSize:12, color:"#6B7280", fontWeight:500,
display:"block", marginBottom:4 }}>{f.label}</label>
<TextInput value={answers[f.id]} onChange={v => set(f.id, v)}
placeholder={f.placeholder} />
</div>
))}
</div>
</div>
<div style={{ background:GREEN_LIGHT, borderRadius:10, padding:"12px 14px",
marginBottom:24, display:"flex", gap:10, alignItems:"flex-start" }}>
<span style={{ fontSize:16, flexShrink:0 }}>🔒</span>
<p style={{ fontSize:12, color:GREEN_DARK, margin:0, lineHeight:1.6 }}>
Your answers are confidential and will only be used to identify
the best investment opportunities for you.
</p>
</div>
<button
onClick={() => setStep(1)}
disabled={!answers.clientName}
style={{
width:"100%", padding:"13px", borderRadius:10,
background: answers.clientName
? `linear-gradient(135deg, ${GREEN}, ${GREEN_DARK})`
: "#E5E7EB",
color: answers.clientName ? "white" : "#9CA3AF",
border:"none", fontSize:15, fontWeight:700, cursor:
answers.clientName ? "pointer" : "not-allowed",
transition:"all 0.2s",
}}
>
Begin Questionnaire →
</button>
</div>
) : (
/* ── QUESTIONS ── */
<div>
<ProgressBar current={step} total={totalSteps} />
<div style={{ background:"white", borderRadius:14, padding:"22px 20px",
boxShadow:"0 1px 4px rgba(0,0,0,0.06)", marginBottom:16 }}>
<SectionBadge icon={section.icon} title={section.title} />
{section.questions.map(q => (
<QuestionWrap key={q.id} num={q.num} label={q.label} hint={q.hint}>
{renderQuestion(q)}
</QuestionWrap>
))}
</div>
{/* Nav buttons */}
<div style={{ display:"flex", gap:10 }}>
<button
onClick={() => setStep(s => s - 1)}
style={{
flex:1, padding:"12px", borderRadius:10,
background:"white", border:`2px solid #E5E7EB`,
color:"#6B7280", fontSize:14, fontWeight:600, cursor:"pointer",
}}
>← Back</button>
{step < totalSteps ? (
<button
onClick={() => setStep(s => s + 1)}
style={{
flex:2, padding:"12px", borderRadius:10,
background:`linear-gradient(135deg, ${GREEN}, ${GREEN_DARK})`,
border:"none", color:"white", fontSize:14, fontWeight:700, cursor:"pointer",
}}
>Continue →</button>
) : (
<button
onClick={handleSubmit}
style={{
flex:2, padding:"12px", borderRadius:10,
background:`linear-gradient(135deg, ${GREEN_DARK}, #053326)`,
border:"none", color:"white", fontSize:14, fontWeight:700, cursor:"pointer",
boxShadow:`0 4px 14px ${GREEN}55`,
}}
>Submit & Send ✉️</button>
)}
</div>
{/* Step dots */}
<div style={{ display:"flex", justifyContent:"center", gap:6, marginTop:18 }}>
{SECTIONS.map((_, i) => (
<div key={i} style={{
width: i + 1 === step ? 20 : 7, height:7, borderRadius:99,
background: i + 1 <= step ? GREEN : "#E5E7EB",
transition:"all 0.3s",
}}/>
))}
</div>
</div>
)}
</div>
</div>
);
}
Contact Us
Interested in working together? Fill out some info and we will be in touch shortly. We can’t wait to hear from you!