- Fix: simple-at-maturity final payout now includes total interest (was only paying one year's interest due to dead ternary) - Fix: add type='button' to prevent form submission in WordPress - Fix: add radix 10 to parseInt - Fix: remove misleading tilde on exact average income calculation
337 lines
21 KiB
HTML
337 lines
21 KiB
HTML
<!-- =================================================================== -->
|
||
<!-- SCD Bond Calculator — WordPress Custom HTML Block -->
|
||
<!-- Self-contained widget for Stirchley Community Development's -->
|
||
<!-- bond investment calculator. All styles are inline to remain -->
|
||
<!-- portable across WordPress theme environments. -->
|
||
<!-- =================================================================== -->
|
||
|
||
<div id="scd-calc" style="max-width:640px;margin:0 auto;padding:28px 24px;font-family:inherit;color:#2d2d2d;line-height:1.7;">
|
||
|
||
<!-- Heading -->
|
||
<h3 style="margin-bottom:12px;font-size:22px;">🧮 Estimate Your Bond Return</h3>
|
||
|
||
<!-- Mandatory risk disclaimer — required for FCA compliance -->
|
||
<p style="margin-bottom:28px;font-size:16px;line-height:1.7;color:#2d2d2d;"><em>Illustrative only — not financial advice. Bonds are not FSCS-protected. You may lose some or all of your investment.</em></p>
|
||
|
||
<!-- =============================================================== -->
|
||
<!-- INPUT FIELDS -->
|
||
<!-- Three core parameters: amount, interest rate, term. Each has -->
|
||
<!-- realistic min/max constraints enforced by the HTML attributes -->
|
||
<!-- as well as by JS validation in run(). -->
|
||
<!-- =============================================================== -->
|
||
<div style="display:flex;gap:16px;flex-wrap:wrap;margin-bottom:32px;">
|
||
|
||
<div style="flex:1;min-width:180px;">
|
||
<label for="scd-amt" style="font-size:16px;font-weight:600;display:block;margin-bottom:8px;color:#2d2d2d;">💰 Amount (£)</label>
|
||
<input type="number" id="scd-amt" min="20000" max="10000000" step="1000" value="75000"
|
||
style="width:100%;padding:12px 14px;border:2px solid #9a9a9a;border-radius:8px;font-size:18px;font-family:monospace;background:#fff;box-sizing:border-box;color:#2d2d2d;" />
|
||
<span style="font-size:14px;color:#595959;display:block;margin-top:6px;">Min £20,000</span>
|
||
</div>
|
||
|
||
<div style="flex:1;min-width:140px;">
|
||
<label for="scd-rate" style="font-size:16px;font-weight:600;display:block;margin-bottom:8px;color:#2d2d2d;">💹 Rate (%)</label>
|
||
<input type="number" id="scd-rate" min="0" max="3.5" step="0.25" value="2.75"
|
||
style="width:100%;padding:12px 14px;border:2px solid #9a9a9a;border-radius:8px;font-size:18px;font-family:monospace;background:#fff;box-sizing:border-box;color:#2d2d2d;" />
|
||
<span style="font-size:14px;color:#595959;display:block;margin-top:6px;">0 – 3.5%</span>
|
||
</div>
|
||
|
||
<div style="flex:1;min-width:120px;">
|
||
<label for="scd-term" style="font-size:16px;font-weight:600;display:block;margin-bottom:8px;color:#2d2d2d;">📅 Term (yrs)</label>
|
||
<input type="number" id="scd-term" min="5" max="30" step="1" value="10"
|
||
style="width:100%;padding:12px 14px;border:2px solid #9a9a9a;border-radius:8px;font-size:18px;font-family:monospace;background:#fff;box-sizing:border-box;color:#2d2d2d;" />
|
||
<span style="font-size:14px;color:#595959;display:block;margin-top:6px;">Min 5</span>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- =============================================================== -->
|
||
<!-- INTEREST PAYOUT METHOD SELECTOR -->
|
||
<!-- Four radio buttons covering the two binary choices: -->
|
||
<!-- Compound (cm/ca) vs Simple (sm/sa) -->
|
||
<!-- At maturity (cm/sm) vs Paid yearly (ca/sa) -->
|
||
<!-- Values are two-letter codes: first letter = type (c/s), -->
|
||
<!-- second letter = schedule (m/a). -->
|
||
<!-- =============================================================== -->
|
||
<p style="font-size:16px;font-weight:600;margin-bottom:12px;color:#2d2d2d;">📈 Interest payout</p>
|
||
<div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:36px;">
|
||
<label style="cursor:pointer;padding:12px 18px;border:2px solid #9a9a9a;border-radius:8px;font-size:16px;background:#fff;display:flex;align-items:center;gap:8px;color:#2d2d2d;">
|
||
<input type="radio" name="scd-m" value="cm" checked style="accent-color:#e66d00;width:20px;height:20px;flex-shrink:0;" />
|
||
<span>Compound at maturity</span>
|
||
</label>
|
||
<label style="cursor:pointer;padding:12px 18px;border:2px solid #9a9a9a;border-radius:8px;font-size:16px;background:#fff;display:flex;align-items:center;gap:8px;color:#2d2d2d;">
|
||
<input type="radio" name="scd-m" value="ca" style="accent-color:#e66d00;width:20px;height:20px;flex-shrink:0;" />
|
||
<span>Compound paid yearly</span>
|
||
</label>
|
||
<label style="cursor:pointer;padding:12px 18px;border:2px solid #9a9a9a;border-radius:8px;font-size:16px;background:#fff;display:flex;align-items:center;gap:8px;color:#2d2d2d;">
|
||
<input type="radio" name="scd-m" value="sm" style="accent-color:#e66d00;width:20px;height:20px;flex-shrink:0;" />
|
||
<span>Simple at maturity</span>
|
||
</label>
|
||
<label style="cursor:pointer;padding:12px 18px;border:2px solid #9a9a9a;border-radius:8px;font-size:16px;background:#fff;display:flex;align-items:center;gap:8px;color:#2d2d2d;">
|
||
<input type="radio" name="scd-m" value="sa" style="accent-color:#e66d00;width:20px;height:20px;flex-shrink:0;" />
|
||
<span>Simple paid yearly</span>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- =============================================================== -->
|
||
<!-- CALCULATE BUTTON -->
|
||
<!-- Triggers the run() function which validates, computes, and -->
|
||
<!-- renders the results. -->
|
||
<!-- =============================================================== -->
|
||
<button type="button" id="scd-btn"
|
||
style="display:block;width:100%;padding:18px;background:#e66d00;color:#fff;font-size:20px;font-weight:700;border:none;border-radius:10px;cursor:pointer;margin-bottom:8px;letter-spacing:0.3px;">
|
||
Calculate
|
||
</button>
|
||
|
||
<!-- =============================================================== -->
|
||
<!-- ERROR MESSAGE CONTAINER -->
|
||
<!-- Hidden by default; shown by run() when validation fails. -->
|
||
<!-- Uses warm orange/red palette to draw attention. -->
|
||
<!-- =============================================================== -->
|
||
<div id="scd-err" style="display:none;margin-top:20px;padding:16px 20px;background:#fff8f0;border:2px solid #c45500;border-radius:8px;font-size:16px;color:#3d1f00;line-height:1.7;"></div>
|
||
|
||
<!-- =============================================================== -->
|
||
<!-- RESULTS SECTION -->
|
||
<!-- Hidden by default; revealed after a successful calculation. -->
|
||
<!-- Contains: key-figure cards, security tier, tax note, -->
|
||
<!-- year-by-year breakdown table, and contextual footnotes. -->
|
||
<!-- =============================================================== -->
|
||
<div id="scd-out" style="display:none;">
|
||
|
||
<hr style="margin:36px 0;border:none;border-top:2px solid #d9d5cc;" />
|
||
|
||
<h4 style="margin-bottom:20px;font-size:20px;color:#2d2d2d;">📊 Your Estimated Return</h4>
|
||
|
||
<!-- Key figures -->
|
||
<div style="display:grid;grid-template-columns:1fr;gap:14px;margin-bottom:24px;">
|
||
|
||
<div style="padding:20px 24px;background:#fff8f0;border-left:4px solid #e66d00;border-radius:10px;text-align:center;">
|
||
<div style="font-size:14px;color:#6b3500;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;">Total Repayment</div>
|
||
<div id="scd-r-total" style="font-size:30px;font-weight:700;color:#5a2c00;margin-top:8px;">—</div>
|
||
</div>
|
||
|
||
<div style="padding:20px 24px;background:#fff8f0;border-left:4px solid #e66d00;border-radius:10px;text-align:center;">
|
||
<div style="font-size:14px;color:#6b3500;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;">Total Interest</div>
|
||
<div id="scd-r-interest" style="font-size:30px;font-weight:700;color:#5a2c00;margin-top:8px;">—</div>
|
||
</div>
|
||
|
||
<div style="padding:20px 24px;background:#fff8f0;border-left:4px solid #e66d00;border-radius:10px;text-align:center;">
|
||
<div style="font-size:14px;color:#6b3500;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;">Per-Year Income</div>
|
||
<div id="scd-r-annual" style="font-size:30px;font-weight:700;color:#5a2c00;margin-top:8px;">—</div>
|
||
</div>
|
||
|
||
<div style="padding:20px 24px;background:#eaf2ec;border-left:4px solid #2d6a4f;border-radius:10px;text-align:center;">
|
||
<div style="font-size:14px;color:#1a3c2e;font-weight:700;text-transform:uppercase;letter-spacing:0.5px;">Effective Rate</div>
|
||
<div id="scd-r-eff" style="font-size:30px;font-weight:700;color:#1a3c2e;margin-top:8px;">—</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Security -->
|
||
<div id="scd-r-sec" style="margin:24px 0;padding:16px 20px;border-radius:8px;font-size:16px;line-height:1.7;color:#2d2d2d;">
|
||
<strong>🔒 Security:</strong> <span id="scd-r-sec-text">—</span>
|
||
</div>
|
||
|
||
<!-- UK Tax note -->
|
||
<blockquote style="margin:24px 0;padding:16px 20px;background:#f5f5f0;border-left:4px solid #e66d00;font-size:15px;color:#595959;line-height:1.7;">
|
||
<strong style="color:#2d2d2d;">🇬🇧 UK tax:</strong> Interest is your responsibility to report. SCD notifies HMRC if any investor receives >£250 interest in a year. <span id="scd-r-tax"></span>
|
||
</blockquote>
|
||
|
||
<!-- Year-by-year -->
|
||
<details style="margin:28px 0;font-size:16px;color:#2d2d2d;">
|
||
<summary style="cursor:pointer;font-weight:700;color:#6b3500;padding:10px 0;font-size:16px;">▾ Year-by-year breakdown</summary>
|
||
<div style="overflow-x:auto;margin-top:14px;-webkit-overflow-scrolling:touch;">
|
||
<table style="width:100%;border-collapse:collapse;font-size:15px;min-width:400px;">
|
||
<thead>
|
||
<tr style="background:#f5f5f0;">
|
||
<th style="padding:12px 14px;text-align:left;border-bottom:2px solid #9a9a9a;color:#2d2d2d;">Year</th>
|
||
<th style="padding:12px 14px;text-align:right;border-bottom:2px solid #9a9a9a;color:#2d2d2d;">Balance</th>
|
||
<th style="padding:12px 14px;text-align:right;border-bottom:2px solid #9a9a9a;color:#2d2d2d;">Interest</th>
|
||
<th style="padding:12px 14px;text-align:right;border-bottom:2px solid #9a9a9a;color:#2d2d2d;">Paid Out</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="scd-r-table"></tbody>
|
||
</table>
|
||
</div>
|
||
</details>
|
||
|
||
<!-- Context -->
|
||
<hr style="margin:32px 0;border:none;border-top:2px solid #d9d5cc;" />
|
||
<div style="font-size:15px;color:#595959;line-height:1.8;">
|
||
<p style="margin-bottom:8px;">🏠 At <strong style="color:#2d2d2d;">0%</strong> — a pure solidarity loan, maximising SCD's impact.</p>
|
||
<p style="margin-bottom:8px;">📈 At <strong style="color:#2d2d2d;">3.5%</strong> — a competitive return while supporting community housing.</p>
|
||
<p style="margin-bottom:8px;">🏦 Best Cash ISA rates (~4.0–4.7%) are FSCS-protected and instantly accessible — bonds are neither.</p>
|
||
<p style="margin-bottom:8px;">🔒 Bonds are non-transferable; early repayment possible but not guaranteed.</p>
|
||
<p style="margin-bottom:0;">💷 Denominated in GBP — international investors bear FX risk.</p>
|
||
</div>
|
||
|
||
<p style="font-size:15px;color:#595959;margin-top:24px;line-height:1.7;">
|
||
Refer to the <a href="https://www.stirchley.coop/wp-content/uploads/2026/05/SCD-Bond-Offer-2026-25-May-2026.pdf" style="color:#9c4d00;font-weight:600;text-decoration:underline;">Bond Offer Document</a> for full terms and risks. SCD is a Co-operative & Community Benefit Society (Reg. 4496), regulated by the Regulator of Social Housing.
|
||
</p>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- =================================================================== -->
|
||
<!-- RESPONSIVE OVERRIDE -->
|
||
<!-- On screens wider than 480px (i.e. most phones in landscape or -->
|
||
<!-- larger), switch the four key-figure cards from a single column -->
|
||
<!-- to a two-column grid for better use of horizontal space. -->
|
||
<!-- =================================================================== -->
|
||
<style>
|
||
@media(min-width:480px){
|
||
#scd-out>div:first-of-type{grid-template-columns:1fr 1fr !important;}
|
||
}
|
||
</style>
|
||
|
||
<!-- =================================================================== -->
|
||
<!-- JAVASCRIPT — CALCULATOR LOGIC -->
|
||
<!-- All computation, validation, and DOM manipulation lives inside -->
|
||
<!-- an IIFE to avoid polluting the global scope. -->
|
||
<!-- =================================================================== -->
|
||
<script>
|
||
(function(){
|
||
/* ------------------------------------------------------------------ */
|
||
/* g(n) — Format a number as GBP (e.g. 75000 → £75,000) */
|
||
/* Uses UK locale grouping with no decimal places for readability. */
|
||
/* ------------------------------------------------------------------ */
|
||
function g(n){return'\u00a3'+n.toLocaleString('en-GB',{minimumFractionDigits:0,maximumFractionDigits:0});}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* run() — Main calculation entry point */
|
||
/* 1. Read & validate inputs */
|
||
/* 2. Iterate year-by-year computing interest under the chosen model */
|
||
/* 3. Populate summary cards (total, interest, annual, effective) */
|
||
/* 4. Determine security tier from investment amount */
|
||
/* 5. Estimate UK tax at three marginal rates */
|
||
/* 6. Render the year-by-year breakdown table + totals row */
|
||
/* ------------------------------------------------------------------ */
|
||
function run(){
|
||
/* --- Read raw inputs --- */
|
||
var a=parseFloat(document.getElementById('scd-amt').value), /* Investment amount (£) */
|
||
r=parseFloat(document.getElementById('scd-rate').value), /* Annual interest rate (%) */
|
||
t=parseInt(document.getElementById('scd-term').value,10), /* Bond term (years) */
|
||
mEl=document.querySelector('input[name="scd-m"]:checked'), /* Selected payout method */
|
||
m=mEl?mEl.value:'cm', /* Default to compound-at-maturity */
|
||
e=document.getElementById('scd-err'), /* Error container */
|
||
o=document.getElementById('scd-out'); /* Results container */
|
||
|
||
/* --- Clear previous state --- */
|
||
e.style.display='none';e.textContent='';o.style.display='none';
|
||
|
||
/* --- Input validation --- */
|
||
if(isNaN(a)||a<20000){e.textContent='\u26a0 Minimum investment is \u00a320,000.';e.style.display='block';return;}
|
||
if(isNaN(r)||r<0||r>3.5){e.textContent='\u26a0 Rate must be 0\u20133.5%.';e.style.display='block';return;}
|
||
if(isNaN(t)||t<5){e.textContent='\u26a0 Minimum term is 5 years.';e.style.display='block';return;}
|
||
|
||
/* --- Decode method code --- */
|
||
/* m[0] === 'c' → compound; 's' → simple */
|
||
/* m[1] === 'a' → paid annually; 'm' → at maturity */
|
||
var cmp=m[0]==='c', /* compound? */
|
||
ann=m[1]==='a', /* annual payout? */
|
||
bal=a, /* running balance for compound calc */
|
||
totI=0, /* accumulated interest across all years */
|
||
totP=0, /* accumulated payout across all years */
|
||
rows=[]; /* year-by-year data for the table */
|
||
|
||
/* --- Year-by-year interest computation --- */
|
||
for(var y=1;y<=t;y++){
|
||
/* Interest for this year: compound uses running balance; simple always uses principal */
|
||
var i=cmp?bal*(r/100):a*(r/100);
|
||
totI+=i;
|
||
|
||
/* Payout: annual modes pay out each year; at-maturity pays everything in the final year */
|
||
var p=0;
|
||
if(ann){
|
||
p=i; /* yearly payout = this year's interest */
|
||
}else if(y===t){ /* final year for at-maturity modes */
|
||
p=cmp?bal+i:a+totI; /* cm: full accumulated balance; sm: principal + total simple interest */
|
||
}
|
||
totP+=p;
|
||
|
||
/* Compound: add this year's interest to the running balance */
|
||
if(cmp){
|
||
bal+=i;
|
||
}
|
||
|
||
rows.push({
|
||
y:y, /* year number */
|
||
i:i, /* interest earned in this year */
|
||
p:p, /* amount paid out this year */
|
||
cb:cmp?bal:(ann?a:bal) /* closing balance displayed in table */
|
||
});
|
||
}
|
||
|
||
/* Simple at maturity: final balance is just the original principal */
|
||
if(!cmp&&!ann) bal=a;
|
||
|
||
/* --- Summary calculations --- */
|
||
var total=ann?a+totP:(cmp?bal:a+totI), /* total repayment to investor */
|
||
netI=ann?totP:(cmp?bal-a:totI), /* net interest earned */
|
||
yrInc=ann?a*(r/100):netI/t, /* per-year income (actual or average) */
|
||
eff=r===0?0:(ann?r:(Math.pow(total/a,1/t)-1)*100); /* effective annual rate (CAGR) */
|
||
|
||
/* --- Security tier based on investment amount --- */
|
||
var sec=a<150000?'<strong>Unsecured</strong> \u2014 no legal charge (under \u00a3150,000)'
|
||
:a<500000?'<strong>Tertiary charge</strong> \u2014 behind primary & secondary (\u00a3150k\u2013\u00a3500k)'
|
||
:'<strong>Secondary charge</strong> \u2014 behind primary mortgage only (over \u00a3500,000)',
|
||
secBg=a<150000?'#fff8f0':a<500000?'#eaf2ec':'#eaf2ec',
|
||
secBd=a<150000?'#c45500':a<500000?'#2d6a4f':'#2d6a4f';
|
||
|
||
/* --- Populate summary cards --- */
|
||
document.getElementById('scd-r-total').textContent=g(total);
|
||
document.getElementById('scd-r-interest').textContent=g(netI);
|
||
document.getElementById('scd-r-annual').textContent=g(yrInc)+(ann?'/yr':'/yr avg');
|
||
document.getElementById('scd-r-eff').textContent=eff.toFixed(2)+'%';
|
||
|
||
/* --- Update security tier display --- */
|
||
var sb=document.getElementById('scd-r-sec');
|
||
sb.style.background=secBg;sb.style.border='2px solid '+secBd;sb.style.color='#2d2d2d';
|
||
document.getElementById('scd-r-sec-text').innerHTML=sec;
|
||
|
||
/* --- Estimate UK tax at three marginal rates --- */
|
||
/* Uses the annual income figure: actual for annual-payout modes, average for at-maturity. */
|
||
var taxI=ann?yrInc:netI;
|
||
document.getElementById('scd-r-tax').textContent='On '+g(taxI)+' interest: ~'+g(taxI*0.2)+' (basic), ~'+g(taxI*0.4)+' (higher), ~'+g(taxI*0.45)+' (additional rate) \u2014 before Personal Savings Allowance.';
|
||
|
||
/* --- Render year-by-year table --- */
|
||
var tb=document.getElementById('scd-r-table');tb.innerHTML='';
|
||
for(var j=0;j<rows.length;j++){
|
||
var d=rows[j],
|
||
isFinal=(j===rows.length-1), /* highlight the maturity row */
|
||
tr=document.createElement('tr');
|
||
tr.style.background=isFinal?'#fff8f0':(j%2?'#f5f5f0':'#fff'); /* alternate row colours */
|
||
tr.innerHTML='<td style="padding:10px 14px;border-bottom:1px solid #d9d5cc;color:#2d2d2d;font-weight:'+(isFinal?'700':'400')+';">'+d.y+(isFinal?' (maturity)':'')+'</td>'
|
||
+'<td style="padding:10px 14px;border-bottom:1px solid #d9d5cc;text-align:right;font-family:monospace;color:#2d2d2d;">'+g(d.cb)+'</td>'
|
||
+'<td style="padding:10px 14px;border-bottom:1px solid #d9d5cc;text-align:right;font-family:monospace;color:#2d2d2d;">'+g(d.i)+'</td>'
|
||
+'<td style="padding:10px 14px;border-bottom:1px solid #d9d5cc;text-align:right;font-family:monospace;color:#2d2d2d;font-weight:'+(d.p>0?'700':'400')+';">'+(d.p>0?g(d.p):'\u2014')+'</td>';
|
||
tb.appendChild(tr);
|
||
}
|
||
|
||
/* --- Totals row appended after the per-year rows --- */
|
||
var ttr=document.createElement('tr');
|
||
ttr.style.background='#f5f5f0';
|
||
ttr.innerHTML='<td style="padding:10px 14px;border-top:2px solid #9a9a9a;font-weight:700;color:#2d2d2d;">Total</td>'
|
||
+'<td style="padding:10px 14px;border-top:2px solid #9a9a9a;text-align:right;font-family:monospace;color:#2d2d2d;">\u2014</td>'
|
||
+'<td style="padding:10px 14px;border-top:2px solid #9a9a9a;text-align:right;font-family:monospace;font-weight:700;color:#2d2d2d;">'+g(totI)+'</td>'
|
||
+'<td style="padding:10px 14px;border-top:2px solid #9a9a9a;text-align:right;font-family:monospace;font-weight:700;color:#2d2d2d;">'+g(totP)+'</td>';
|
||
tb.appendChild(ttr);
|
||
|
||
/* --- Reveal results section --- */
|
||
o.style.display='block';
|
||
}
|
||
|
||
/* ------------------------------------------------------------------ */
|
||
/* Event binding — wire the Calculate button to run() */
|
||
/* Handles both synchronous (DOM already loaded) and async cases. */
|
||
/* ------------------------------------------------------------------ */
|
||
if(document.readyState==='loading'){
|
||
document.addEventListener('DOMContentLoaded',function(){
|
||
document.getElementById('scd-btn').addEventListener('click',run);
|
||
});
|
||
}else{
|
||
document.getElementById('scd-btn').addEventListener('click',run);
|
||
}
|
||
})();
|
||
</script>
|