redesign landing page: better teaser calc, value-focused CTAs
Replace toy-like sliders (rent/m², exit multiple) with more intuitive inputs (build cost per court, equity %). Replace outputs (annual revenue, equity IRR) with payback period and cash-on-cash return. Show model assumptions inline. Remove "free" messaging from all CTAs and meta tags, focus on the plan→suppliers→financing value chain instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
{% block title %}Padelnomics - Padel Court Business Plan & ROI Calculator{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<meta name="description" content="Plan your padel court investment in minutes. Free financial planner with 60+ variables, sensitivity analysis, and professional-grade projections. Indoor/outdoor, rent/buy models.">
|
||||
<meta name="description" content="Plan your padel court investment in minutes. Financial planner with 60+ variables, sensitivity analysis, and professional-grade projections. Indoor/outdoor, rent/buy models.">
|
||||
<meta property="og:title" content="Padelnomics - Padel Court Financial Planner">
|
||||
<meta property="og:description" content="The most sophisticated padel court business plan calculator. Free forever. 60+ variables, 6 analysis tabs, charts, sensitivity analysis.">
|
||||
<meta property="og:description" content="The most sophisticated padel court business plan calculator. 60+ variables, 6 analysis tabs, charts, sensitivity analysis, and supplier connections.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ config.BASE_URL }}">
|
||||
<link rel="canonical" href="{{ config.BASE_URL }}">
|
||||
@@ -122,7 +122,7 @@
|
||||
<header style="text-align: center; padding: 4rem 0 2rem;">
|
||||
<h1 style="font-size: 2.5rem; line-height: 1.15;">Plan Your Padel Business<br>in Minutes, Not Months</h1>
|
||||
<p style="font-size: 1.2rem; max-width: 640px; margin: 1rem auto 0; color: #64748B;">
|
||||
Model your padel court investment with 60+ variables, sensitivity analysis, and professional-grade projections. 100% free.
|
||||
Model your padel court investment with 60+ variables, sensitivity analysis, and professional-grade projections.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
@@ -147,16 +147,18 @@
|
||||
<span class="val" id="tv-util">40%</span>
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<label>Rent / m²</label>
|
||||
<input type="range" id="tc-rent" min="2" max="15" step="1" value="4" oninput="tCalc()">
|
||||
<span class="val" id="tv-rent">€4/m²</span>
|
||||
<label>Build Cost / Court</label>
|
||||
<input type="range" id="tc-buildcost" min="20000" max="50000" step="5000" value="30000" oninput="tCalc()">
|
||||
<span class="val" id="tv-buildcost">€30K</span>
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<label>Exit Multiple</label>
|
||||
<input type="range" id="tc-exit" min="3" max="10" step="0.5" value="6" oninput="tCalc()">
|
||||
<span class="val" id="tv-exit">6.0x</span>
|
||||
<label>Equity %</label>
|
||||
<input type="range" id="tc-equity" min="15" max="50" step="5" value="25" oninput="tCalc()">
|
||||
<span class="val" id="tv-equity">25%</span>
|
||||
</div>
|
||||
|
||||
<p style="text-align: center; font-size: 11px; color: #94A3B8; margin-top: 0.5rem; margin-bottom: 0;">Assumes €8/m² rent, 5% interest, 10-year loan, 300 m² per court</p>
|
||||
|
||||
<div class="teaser-results">
|
||||
<div class="teaser-metric">
|
||||
<div class="tm-label">Total Investment</div>
|
||||
@@ -169,23 +171,23 @@
|
||||
<div class="tm-sub">After debt service</div>
|
||||
</div>
|
||||
<div class="teaser-metric">
|
||||
<div class="tm-label">Annual Revenue</div>
|
||||
<div class="tm-value tm-blue" id="tr-rev">—</div>
|
||||
<div class="tm-sub">Net of booking fees</div>
|
||||
<div class="tm-label">Payback Period</div>
|
||||
<div class="tm-value tm-blue" id="tr-payback">—</div>
|
||||
<div class="tm-sub">Years to recover equity</div>
|
||||
</div>
|
||||
<div class="teaser-metric">
|
||||
<div class="tm-label">Equity IRR</div>
|
||||
<div class="tm-value" id="tr-irr">—</div>
|
||||
<div class="tm-sub" id="tr-irr-sub">5-year hold</div>
|
||||
<div class="tm-label">Cash-on-Cash</div>
|
||||
<div class="tm-value" id="tr-coc">—</div>
|
||||
<div class="tm-sub">Annual return on equity</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="teaser-cta">
|
||||
<p>This is a simplified estimate. The full planner models 60+ variables with monthly cash flows, sensitivity analysis, and more.</p>
|
||||
<p>Want the full picture? The planner models 60+ variables with monthly projections, sensitivity analysis, and connects you with court suppliers.</p>
|
||||
{% if user %}
|
||||
<a href="{{ url_for('planner.index') }}" role="button">Open Full Planner</a>
|
||||
<a href="{{ url_for('planner.index') }}" role="button">Start Planning</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('auth.signup') }}" role="button">Unlock the Full Planner — Free</a>
|
||||
<a href="{{ url_for('auth.signup') }}" role="button">Create Your Plan</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,7 +199,7 @@
|
||||
<div class="grid">
|
||||
<article>
|
||||
<header><strong>Plan</strong></header>
|
||||
<p>Model your padel hall investment with our free financial planner. CAPEX, operating costs, cash flow, returns, sensitivity analysis.</p>
|
||||
<p>Model your padel hall investment with our financial planner. CAPEX, operating costs, cash flow, returns, sensitivity analysis.</p>
|
||||
</article>
|
||||
<article>
|
||||
<header><strong>Finance</strong></header>
|
||||
@@ -261,11 +263,11 @@
|
||||
<!-- Final CTA -->
|
||||
<section style="text-align: center; padding: 3rem 0;">
|
||||
<h2>Start Planning Today</h2>
|
||||
<p>No credit card. No paywall. Full access to every feature.</p>
|
||||
<p>Start with your plan. Then get quotes from verified court suppliers and connect with financing partners.</p>
|
||||
{% if user %}
|
||||
<a href="{{ url_for('planner.index') }}" role="button">Open Planner</a>
|
||||
<a href="{{ url_for('planner.index') }}" role="button">Start Planning</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('auth.signup') }}" role="button">Create Free Account</a>
|
||||
<a href="{{ url_for('auth.signup') }}" role="button">Create Your Plan</a>
|
||||
{% endif %}
|
||||
</section>
|
||||
</main>
|
||||
@@ -277,72 +279,53 @@ function tCalc() {
|
||||
var courts = +document.getElementById('tc-courts').value;
|
||||
var rate = +document.getElementById('tc-rate').value;
|
||||
var util = +document.getElementById('tc-util').value;
|
||||
var rentSqm = +document.getElementById('tc-rent').value;
|
||||
var exitMult = +document.getElementById('tc-exit').value;
|
||||
var buildCost = +document.getElementById('tc-buildcost').value;
|
||||
var equityPct = +document.getElementById('tc-equity').value;
|
||||
|
||||
// Display slider values
|
||||
document.getElementById('tv-courts').textContent = courts;
|
||||
document.getElementById('tv-rate').innerHTML = '€' + rate + '/hr';
|
||||
document.getElementById('tv-util').textContent = util + '%';
|
||||
document.getElementById('tv-rent').innerHTML = '€' + rentSqm + '/m\u00B2';
|
||||
document.getElementById('tv-exit').textContent = exitMult.toFixed(1) + 'x';
|
||||
document.getElementById('tv-buildcost').innerHTML = '€' + Math.round(buildCost / 1000) + 'K';
|
||||
document.getElementById('tv-equity').textContent = equityPct + '%';
|
||||
|
||||
// Simplified model (rent model, indoor, all double courts)
|
||||
var sqmPerCourt = 300; // court + shared space
|
||||
var totalSqm = courts * sqmPerCourt;
|
||||
var courtCost = courts * 25000;
|
||||
var capex = courtCost + 60000 + 80000 + 40000 + 100000 + 50000; // elec + sanitary + fitout + planning + parking
|
||||
// CAPEX: build cost per court is the main driver
|
||||
var capex = courts * buildCost;
|
||||
|
||||
// Financing: equity % from slider, remainder is debt
|
||||
// Assumes 5% interest, 10-year amortizing loan
|
||||
var iRate = 0.05, term = 10;
|
||||
var equity = capex * (equityPct / 100);
|
||||
var debt = capex - equity;
|
||||
var mRate = iRate / 12;
|
||||
var nPay = term * 12;
|
||||
var pmt = debt > 0 ? debt * mRate / (1 - Math.pow(1 + mRate, -nPay)) : 0;
|
||||
|
||||
// Revenue
|
||||
// Assumes 16 bookable hours/day, 29 days/month, off-peak rate = 65% of peak
|
||||
var hoursDay = 16, daysMonth = 29;
|
||||
var availHours = courts * hoursDay * daysMonth;
|
||||
var bookedHours = availHours * (util / 100);
|
||||
var offPeakRate = rate * 0.65;
|
||||
var wRate = rate * 0.4 + offPeakRate * 0.6;
|
||||
var courtRev = bookedHours * wRate;
|
||||
var netRev = courtRev * 0.9; // 10% booking fee
|
||||
var peakShare = 0.4;
|
||||
var wRate = rate * peakShare + rate * 0.65 * (1 - peakShare);
|
||||
var grossRev = bookedHours * wRate;
|
||||
var netRev = grossRev * 0.9; // 10% booking platform fee
|
||||
|
||||
// Operating costs
|
||||
var rent = totalSqm * rentSqm;
|
||||
var opex = rent + courts * 400 + 350; // utilities/maint per court + marketing
|
||||
// Assumes €8/m² rent (300 m² per court), €400/court utilities, €350 marketing
|
||||
var rent = courts * 300 * 8;
|
||||
var opex = rent + courts * 400 + 350;
|
||||
|
||||
// EBITDA
|
||||
// Monthly cash flow after debt service
|
||||
var ebitda = netRev - opex;
|
||||
|
||||
// Financing (85% LTV, 5%, 10yr)
|
||||
var loanPct = 0.85, iRate = 0.05, term = 10;
|
||||
var debt = capex * loanPct;
|
||||
var equity = capex - debt;
|
||||
var mRate = iRate / 12;
|
||||
var nPay = term * 12;
|
||||
var pmt = debt * mRate / (1 - Math.pow(1 + mRate, -nPay));
|
||||
var netCF = ebitda - pmt;
|
||||
|
||||
// Annual figures
|
||||
var annualRev = netRev * 12;
|
||||
// Payback period: years to recover equity from annual net cash flow
|
||||
var annualCF = netCF * 12;
|
||||
var payback = annualCF > 0 ? equity / annualCF : Infinity;
|
||||
|
||||
// IRR approximation (Newton's method, 5yr hold)
|
||||
var hold = 5;
|
||||
var y3ebitda = ebitda * 12;
|
||||
var exitVal = y3ebitda * exitMult;
|
||||
var cfs = [-equity];
|
||||
for (var y = 1; y <= hold; y++) {
|
||||
var cf = annualCF;
|
||||
if (y === hold) cf += exitVal - debt * Math.pow((1 + mRate), 12 * hold - 12 * hold); // simplified: remaining debt ~ original for short holds
|
||||
// Simplified: remaining debt after Y years
|
||||
var remDebt = 0;
|
||||
if (y === hold) {
|
||||
// Outstanding balance after hold*12 payments
|
||||
var paid = hold * 12;
|
||||
remDebt = debt * Math.pow(1 + mRate, paid) - pmt * (Math.pow(1 + mRate, paid) - 1) / mRate;
|
||||
if (remDebt < 0) remDebt = 0;
|
||||
cf = annualCF + exitVal - remDebt;
|
||||
}
|
||||
cfs.push(cf);
|
||||
}
|
||||
|
||||
var irr = calcIRR(cfs);
|
||||
// Cash-on-cash return: annual net CF / equity invested
|
||||
var coc = equity > 0 ? annualCF / equity : 0;
|
||||
|
||||
// Format outputs
|
||||
var fmt = function(n) { return (n >= 0 ? '' : '-') + '\u20AC' + Math.abs(Math.round(n)).toLocaleString('de-DE'); };
|
||||
@@ -353,35 +336,23 @@ function tCalc() {
|
||||
cfEl.innerHTML = fmt(netCF);
|
||||
cfEl.className = 'tm-value ' + (netCF >= 0 ? 'tm-green' : 'tm-red');
|
||||
|
||||
document.getElementById('tr-rev').innerHTML = fmt(annualRev);
|
||||
|
||||
var irrEl = document.getElementById('tr-irr');
|
||||
if (irr !== null && isFinite(irr)) {
|
||||
irrEl.textContent = (irr * 100).toFixed(1) + '%';
|
||||
irrEl.className = 'tm-value ' + (irr >= 0 ? 'tm-green' : 'tm-red');
|
||||
var pbEl = document.getElementById('tr-payback');
|
||||
if (payback > 0 && payback <= 30) {
|
||||
pbEl.textContent = payback.toFixed(1) + 'yr';
|
||||
pbEl.className = 'tm-value ' + (payback <= 5 ? 'tm-blue' : 'tm-navy');
|
||||
} else {
|
||||
irrEl.innerHTML = '—';
|
||||
irrEl.className = 'tm-value tm-navy';
|
||||
}
|
||||
pbEl.innerHTML = '—';
|
||||
pbEl.className = 'tm-value tm-red';
|
||||
}
|
||||
|
||||
function calcIRR(cfs) {
|
||||
// Newton-Raphson IRR solver
|
||||
var guess = 0.15;
|
||||
for (var i = 0; i < 100; i++) {
|
||||
var npv = 0, dnpv = 0;
|
||||
for (var t = 0; t < cfs.length; t++) {
|
||||
var f = Math.pow(1 + guess, t);
|
||||
npv += cfs[t] / f;
|
||||
if (t > 0) dnpv -= t * cfs[t] / (f * (1 + guess));
|
||||
var cocEl = document.getElementById('tr-coc');
|
||||
if (isFinite(coc)) {
|
||||
cocEl.textContent = (coc * 100).toFixed(1) + '%';
|
||||
cocEl.className = 'tm-value ' + (coc >= 0 ? 'tm-green' : 'tm-red');
|
||||
} else {
|
||||
cocEl.innerHTML = '—';
|
||||
cocEl.className = 'tm-value tm-navy';
|
||||
}
|
||||
if (Math.abs(dnpv) < 1e-10) break;
|
||||
var next = guess - npv / dnpv;
|
||||
if (Math.abs(next - guess) < 1e-7) return next;
|
||||
guess = next;
|
||||
if (guess < -0.99 || guess > 10) return null;
|
||||
}
|
||||
return guess;
|
||||
}
|
||||
|
||||
// Run on load
|
||||
|
||||
Reference in New Issue
Block a user