const fmt = (n, prefix = '') => `${prefix}${Math.round(n).toLocaleString('de-DE')}`; const fmtPct = (n) => `${n >= 0 ? '+' : ''}${n.toFixed(1)}%`; const calcROI = (amount, capitalPct, discountPct, years) => { const committed = amount * (capitalPct / 100); const appreciated = amount * Math.pow(1.07, years); const discount = amount * (discountPct / 100); const appreciation = appreciated - amount; const exitCosts = appreciated * 0.04; const netGain = discount + appreciation - exitCosts; const roi = (netGain / committed) * 100; const annualized = (Math.pow(1 + roi / 100, 1 / years) - 1) * 100; return { committed, appreciated, discount, appreciation, exitCosts, netGain, roi, annualized }; }; const Slider = ({ label, value, min, max, step, format, onChange }) => { const pct = ((value - min) / (max - min)) * 100; return React.createElement('div', { style: { marginBottom: 32 } }, React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 14 }, }, React.createElement('span', { style: { fontFamily: "'DM Mono',monospace", fontSize: 8, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'rgba(244,241,236,0.45)' }, }, label), React.createElement('span', { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: 22, fontWeight: 300, color: '#D4AF6A' }, }, format(value)) ), React.createElement('div', { style: { position: 'relative', height: 20, display: 'flex', alignItems: 'center' } }, React.createElement('div', { style: { position: 'absolute', left: 0, right: 0, height: 1, background: 'rgba(244,241,236,0.18)', borderRadius: 0 }, }), React.createElement('div', { style: { position: 'absolute', left: 0, width: `${pct}%`, height: 1, background: 'rgba(184,146,42,0.5)' }, }), React.createElement('input', { type: 'range', min, max, step, value, onChange: e => onChange(Number(e.target.value)), style: { position: 'absolute', left: 0, right: 0, width: '100%', appearance: 'none', background: 'transparent', cursor: 'pointer', height: 20, margin: 0, padding: 0, }, className: 'calc-slider', }) ) ); }; const Calculator = ({ selectedProfile }) => { const [amount, setAmount] = React.useState(300000); const [capPct, setCapPct] = React.useState(30); const [discPct, setDiscPct] = React.useState(4); const [years, setYears] = React.useState(4); const r = calcROI(amount, capPct, discPct, years); const Row = ({ label, value, accent }) => React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '10px 0', borderBottom: '1px solid rgba(244,241,236,0.07)', }, }, React.createElement('span', { style: { fontFamily: "'DM Mono',monospace", fontSize: 8, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(244,241,236,0.42)' } }, label), React.createElement('span', { style: { fontFamily: "'DM Mono',monospace", fontSize: 10, color: accent ? '#D4AF6A' : 'rgba(244,241,236,0.78)' } }, value) ); return React.createElement('div', { style: { background: '#2C2C2A', padding: '52px 48px 40px' }, }, // Header React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 48, borderBottom: '1px solid rgba(244,241,236,0.08)', paddingBottom: 28 } }, React.createElement('div', null, React.createElement('div', { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: 28, fontWeight: 300, color: '#F4F1EC', lineHeight: 1.2 } }, 'Return Calculator'), React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 8, color: 'rgba(244,241,236,0.28)', letterSpacing: '0.14em', textTransform: 'uppercase', marginTop: 8 } }, 'Indicative model · 7% annual appreciation assumed'), ), selectedProfile && React.createElement('div', { style: { border: '1px solid rgba(244,241,236,0.15)', padding: '10px 16px', textAlign: 'right' }, }, React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 7, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'rgba(244,241,236,0.35)', marginBottom: 4 } }, 'Profile'), React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 9, color: '#D4AF6A' } }, selectedProfile) ) ), // 2-col layout React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 64 } }, // Sliders React.createElement('div', null, React.createElement(Slider, { label: 'Investment amount', value: amount, min: 100000, max: 1000000, step: 10000, format: v => `€${(v/1000).toFixed(0)}K`, onChange: setAmount }), React.createElement(Slider, { label: 'Capital during construction', value: capPct, min: 20, max: 50, step: 1, format: v => `${v}%`, onChange: setCapPct }), React.createElement(Slider, { label: 'Off-market developer discount', value: discPct, min: 2, max: 8, step: 0.5, format: v => `${v}%`, onChange: setDiscPct }), React.createElement(Slider, { label: 'Holding period', value: years, min: 2, max: 8, step: 1, format: v => `${v} yr`, onChange: setYears }), ), // Output React.createElement('div', null, // Big ROI number React.createElement('div', { style: { marginBottom: 28, paddingBottom: 28, borderBottom: '1px solid rgba(244,241,236,0.08)' } }, React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 8, letterSpacing: '0.18em', textTransform: 'uppercase', color: 'rgba(244,241,236,0.28)', marginBottom: 8 } }, 'Total ROI on committed capital'), React.createElement('div', { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: 64, fontWeight: 300, color: '#D4AF6A', lineHeight: 1 } }, fmtPct(r.roi)), React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 8, color: 'rgba(244,241,236,0.35)', marginTop: 6, letterSpacing: '0.12em' } }, `${fmtPct(r.annualized)} annualised over ${years} years`), ), // Breakdown React.createElement(Row, { label: 'Entry value', value: `€${fmt(amount * (1 - discPct/100))}` }), React.createElement(Row, { label: 'Committed capital', value: `€${fmt(r.committed)}` }), React.createElement(Row, { label: 'Discount capture', value: `+€${fmt(r.discount)}`, accent: true }), React.createElement(Row, { label: `Appreciation (${years}yr)`, value: `+€${fmt(r.appreciation)}`, accent: true }), React.createElement(Row, { label: 'Exit costs (4%)', value: `−€${fmt(r.exitCosts)}` }), React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', padding: '14px 0 0' }, }, React.createElement('span', { style: { fontFamily: "'DM Mono',monospace", fontSize: 9, letterSpacing: '0.14em', textTransform: 'uppercase', color: 'rgba(244,241,236,0.7)' } }, 'Net gain'), React.createElement('span', { style: { fontFamily: "'Cormorant Garamond',serif", fontSize: 24, fontWeight: 300, color: '#D4AF6A' } }, `€${fmt(r.netGain)}`) ), React.createElement('div', { style: { fontFamily: "'DM Mono',monospace", fontSize: 7, color: 'rgba(244,241,236,0.22)', marginTop: 24, lineHeight: 1.9, letterSpacing: '0.06em' }, }, 'Indicative model. 7% annual appreciation based on historical portfolio data. Past performance does not guarantee future results. Not financial advice.') ) ) ); }; Object.assign(window, { Calculator });