import { useState, useEffect } from "react";
const COURT_TYPES = ["Indoor", "Outdoor", "Both indoor & outdoor"];
const SURFACES = ["Artificial turf", "Panoramic glass", "Standard glass", "No preference yet"];
const LIGHTING = ["LED competition", "LED standard", "Natural light only", "Not sure yet"];
const TIMELINES = ["ASAP (< 3 months)", "3–6 months", "6–12 months", "12+ months / exploring"];
const BUDGETS = ["< €100K", "€100K – €250K", "€250K – €500K", "€500K – €1M", "> €1M", "Not sure yet"];
const SERVICES = ["Court construction (turnkey)", "Court surfaces only", "Steel structures / canopy", "Glass walls", "Lighting systems", "Flooring / foundation", "Architecture & planning", "Financing / investment", "Consulting / feasibility"];
const CALC = { courts: 6, type: "Indoor (rented hall)", totalInvestment: "€285,000", monthlyRevenue: "€18,750", monthlyCosts: "€12,400", monthlyCashFlow: "€6,350", paybackYears: "3.7", irr: "22.4%", cashOnCash: "26.8%" };
export default function App() {
const [view, setView] = useState("results");
const [bStep, setBStep] = useState(1);
const [anim, setAnim] = useState(true);
const [brief, setBrief] = useState({ courtType: "", courts: 6, surface: "", lighting: "", location: "", country: "Germany", timeline: "", budget: "", services: [], info: "", name: "", email: "", phone: "", company: "" });
useEffect(() => { setAnim(false); const t = setTimeout(() => setAnim(true), 50); return () => clearTimeout(t); }, [view, bStep]);
const $ = {
bg: "#FFFFFF", soft: "#F8FAFC", muted: "#F1F5F9",
border: "#E2E8F0", borderDark: "#CBD5E1",
text: "#0F172A", sub: "#334155", dim: "#64748B", faint: "#94A3B8",
blue: "#1D4ED8", blueLight: "#3B82F6", bluePale: "#DBEAFE", blueGhost: "#EFF6FF", blueDark: "#1E40AF",
green: "#16A34A", greenPale: "#DCFCE7",
gold: "#D97706", goldPale: "#FEF3C7",
};
const font = "'Inter', -apple-system, BlinkMacSystemFont, sans-serif";
const mono = "'JetBrains Mono', 'SF Mono', monospace";
const Pill = ({ options, value, onChange, multi }) => (
{options.map(o => {
const a = multi ? value.includes(o) : value === o;
return onChange(o)} style={{ background: a ? $.blueGhost : $.bg, border: `1.5px solid ${a ? $.blue : $.border}`, borderRadius: "999px", padding: "8px 16px", fontSize: "13px", fontWeight: a ? 600 : 400, color: a ? $.blue : $.dim, cursor: "pointer", fontFamily: font }}>{o} ;
})}
);
const Stat = ({ label, value, highlight, sub }) => (
{label}
{value}
{sub &&
{sub}
}
);
return (
{/* Header */}
{view !== "results" &&
{ setView("results"); setBStep(1); }} style={{ background: $.bg, border: `1px solid ${$.border}`, borderRadius: "8px", padding: "6px 14px", fontSize: "13px", color: $.dim, cursor: "pointer", fontFamily: font }}>← Back to calculator }
{/* ─── RESULTS ─── */}
{view === "results" && (
Your Projection
{CALC.courts}-Court Indoor Padel Hall
Based on your inputs — {CALC.type}
{/* CTA */}
Next Step
Get quotes from verified court suppliers
Share your project specs and we'll connect you with pre-vetted suppliers who match your requirements. They'll reach out directly with tailored proposals — typically within 48 hours.
{["2–5 matched suppliers", "Direct contact, no middleman", "Free, no commitment", "Your data stays private"].map(t => (
✓ {t}
))}
setView("brief")} style={{ background: $.blue, color: "#fff", border: "none", borderRadius: "10px", padding: "13px 26px", fontSize: "15px", fontWeight: 700, cursor: "pointer", fontFamily: font, boxShadow: "0 2px 10px rgba(29,78,216,0.25)" }}>Get Supplier Quotes →
Takes ~2 minutes
{[["540+", "projects matched"], ["48h", "avg. response"], ["35+", "verified suppliers"]].map(([n, l]) => (
))}
)}
{/* ─── BRIEF ─── */}
{view === "brief" && (
{["Project", "Details", "Contact"].map((l, i) => {
const n = i + 1, on = bStep === n, done = bStep > n;
return (
);
})}
{/* Brief 1 */}
{bStep === 1 && (
Tell us about your project
This helps us match you with the right suppliers. All fields optional.
Facility type setBrief(p => ({ ...p, courtType: v }))} />
Court surface preference setBrief(p => ({ ...p, surface: v }))} />
Lighting setBrief(p => ({ ...p, lighting: v }))} />
setBStep(2)} style={{ background: $.blue, color: "#fff", border: "none", borderRadius: "10px", padding: "12px 24px", fontSize: "14px", fontWeight: 600, cursor: "pointer", fontFamily: font, boxShadow: "0 2px 8px rgba(29,78,216,0.25)" }}>Continue →
)}
{/* Brief 2 */}
{bStep === 2 && (
Project details
Help suppliers understand your timeline and scope.
City / Region setBrief(p => ({ ...p, location: e.target.value }))} style={{ width: "100%", background: $.bg, border: `1.5px solid ${$.border}`, borderRadius: "10px", padding: "11px 14px", fontSize: "14px", color: $.text, fontFamily: font, outline: "none", boxSizing: "border-box" }} onFocus={e => e.target.style.borderColor = $.blue} onBlur={e => e.target.style.borderColor = $.border} />
Country setBrief(p => ({ ...p, country: e.target.value }))} style={{ width: "100%", background: $.bg, border: `1.5px solid ${$.border}`, borderRadius: "10px", padding: "11px 14px", fontSize: "14px", color: $.text, fontFamily: font, outline: "none", boxSizing: "border-box" }}>{["Germany", "Netherlands", "France", "Italy", "Spain", "UK", "Sweden", "Belgium", "Austria", "Switzerland", "Portugal", "UAE", "USA", "Other"].map(c => {c} )}
Timeline setBrief(p => ({ ...p, timeline: v }))} />
Estimated budget setBrief(p => ({ ...p, budget: v }))} />
What do you need? (select all) setBrief(p => ({ ...p, services: p.services.includes(s) ? p.services.filter(x => x !== s) : [...p.services, s] }))} multi />
Anything else?
setBStep(1)} style={{ background: $.bg, color: $.dim, border: `1px solid ${$.border}`, borderRadius: "10px", padding: "12px 22px", fontSize: "14px", cursor: "pointer", fontFamily: font }}>← Back
setBStep(3)} style={{ background: $.blue, color: "#fff", border: "none", borderRadius: "10px", padding: "12px 24px", fontSize: "14px", fontWeight: 600, cursor: "pointer", fontFamily: font, boxShadow: "0 2px 8px rgba(29,78,216,0.25)" }}>Continue →
)}
{/* Brief 3 */}
{bStep === 3 && (
How should suppliers reach you?
Your details are only shared with matched suppliers — never published publicly.
🔒
Your privacy matters
Contact details go to 2–5 pre-vetted suppliers only. They pay to access your brief — so only serious companies reach out.
{[["name", "Your name *", "Full name"], ["email", "Email *", "you@company.com"], ["phone", "Phone (optional)", "+49 170 1234567"], ["company", "Company (optional)", "e.g. Padel Club Göttingen"]].map(([k, l, p]) => (
{l} setBrief(f => ({ ...f, [k]: e.target.value }))} style={{ width: "100%", background: $.bg, border: `1.5px solid ${$.border}`, borderRadius: "10px", padding: "11px 14px", fontSize: "14px", color: $.text, fontFamily: font, outline: "none", boxSizing: "border-box" }} onFocus={e => e.target.style.borderColor = $.blue} onBlur={e => e.target.style.borderColor = $.border} />
))}
Your project brief
{[["Type", brief.courtType || "—"], ["Courts", brief.courts], ["Surface", brief.surface || "—"], ["Lighting", brief.lighting || "—"], ["Location", brief.location ? `${brief.location}, ${brief.country}` : brief.country], ["Timeline", brief.timeline || "—"], ["Budget", brief.budget || "—"], ["Services", brief.services.length ? `${brief.services.length} selected` : "—"]].map(([l, v]) => (
{l} {v}
))}
setBStep(2)} style={{ background: $.bg, color: $.dim, border: `1px solid ${$.border}`, borderRadius: "10px", padding: "12px 22px", fontSize: "14px", cursor: "pointer", fontFamily: font }}>← Back
setView("submitted")} style={{ background: $.blue, color: "#fff", border: "none", borderRadius: "10px", padding: "13px 26px", fontSize: "15px", fontWeight: 700, cursor: "pointer", fontFamily: font, boxShadow: "0 2px 10px rgba(29,78,216,0.25)" }}>Submit & Get Matched →
By submitting you agree to our Terms & Privacy Policy. Free, no obligation.
)}
)}
{/* ─── SUBMITTED ─── */}
{view === "submitted" && (
✓
You're matched!
We've matched your {brief.courts}-court {brief.courtType.toLowerCase() || "padel"} project with 3 verified suppliers in {brief.country}. They'll reach out within 48 hours.
What happens next
{[["1", "Suppliers review your brief", "Matched suppliers receive your project specs and assess fit.", "Now"], ["2", "They contact you directly", "Expect calls or emails within 24–48 hours.", "1–2 days"], ["3", "Compare proposals", "Review quotes, ask questions, visit references.", "1–2 weeks"], ["4", "Choose your supplier", "Pick the best fit. We're here if you need help.", "Your pace"]].map(([n, t, d, time], i) => (
))}
📧
Check your email
Confirmation sent to {brief.email || "your email"}
setView("dashboard")} style={{ background: $.bg, border: `1px solid ${$.border}`, borderRadius: "10px", padding: "10px 22px", fontSize: "14px", color: $.dim, cursor: "pointer", fontFamily: font, marginTop: "4px" }}>View your project dashboard →
)}
{/* ─── DASHBOARD ─── */}
{view === "dashboard" && (
Your Project
{brief.courts}-Court {brief.courtType || "Padel"} Facility
{brief.location ? `${brief.location}, ` : ""}{brief.country} · Submitted just now
3 suppliers matched
Waiting for their first response — typically 24–48 hours
{["P", "M", "C"].map((l, i) =>
0 ? "-8px" : 0, position: "relative", zIndex: 3 - i }}>{l}
)}
Matched Suppliers
{[
{ name: "PadelCourt Europe", loc: "Cologne, Germany", spec: "Full turnkey", rating: 4.9, projects: "150+", avatar: "P", color: $.blue },
{ name: "Mondo Padel", loc: "Milan, Italy", spec: "Court surfaces & glass", rating: 4.8, projects: "200+", avatar: "M", color: $.blueLight },
{ name: "CourtTech Solutions", loc: "Amsterdam, Netherlands", spec: "Steel & lighting", rating: 4.7, projects: "85+", avatar: "C", color: $.gold },
].map((sup, i) => (
{sup.avatar}
{sup.name}
{sup.loc} · {sup.spec}
⏳ Reviewing your brief
⭐ {sup.rating} {sup.projects} projects ✓ Verified
))}
Suppliers contact you directly by email or phone once they've reviewed your brief. Reply to the confirmation email to update your specs.
)}
);
}