<!DOCTYPE html>

<html lang=”en”>

<head>

    <meta charset=”UTF-8″>

    <meta name=”viewport” content=”width=device-width, initial-scale=1.0″>

    <title>EMI Calculator</title>

    <script src=”https://cdn.tailwindcss.com”></script>

    <script src=”https://cdn.jsdelivr.net/npm/chart.js”></script>

    <link rel=”preconnect” href=”https://fonts.googleapis.com”>

    <link rel=”preconnect” href=”https://fonts.gstatic.com” crossorigin>

    <link href=”https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap” rel=”stylesheet”>

    <style>

        body {

            font-family: ‘Inter’, sans-serif;

        }

        /* Custom styles for sliders */

        input[type=”range”] {

            -webkit-appearance: none;

            appearance: none;

            width: 100%;

            height: 6px;

            background: #e5e7eb; /* gray-200 */

            border-radius: 9999px;

            outline: none;

            opacity: 0.9;

            transition: opacity .2s;

        }

        input[type=”range”]::-webkit-slider-thumb {

            -webkit-appearance: none;

            appearance: none;

            width: 20px;

            height: 20px;

            background: #16a34a; /* green-600 */

            cursor: pointer;

            border-radius: 50%;

            border: 2px solid white;

        }

        input[type=”range”]::-moz-range-thumb {

            width: 20px;

            height: 20px;

            background: #16a34a; /* green-600 */

            cursor: pointer;

            border-radius: 50%;

            border: 2px solid white;

        }

        .monthly-details td {

            padding: 0;

            background-color: #f9fafb; /* gray-50 */

        }

        .monthly-details .inner-table {

            margin: 1rem;

            width: calc(100% – 2rem);

        }

    </style>

</head>

<body class=”bg-gray-50 text-gray-800″>

    <div class=”container mx-auto p-4 md:p-8 max-w-7xl”>

        <h1 class=”text-3xl md:text-4xl font-bold text-center text-gray-900 mb-2″>EMI Calculator</h1>

        <p class=”text-center text-gray-500 mb-8 md:mb-12″>Calculate your loan EMI (Equated Monthly Instalment) easily.</p>

        <div class=”grid grid-cols-1 lg:grid-cols-5 gap-8″>

            <!– Left Side: Inputs –>

            <div class=”lg:col-span-3 bg-white p-6 md:p-8 rounded-2xl shadow-sm border border-gray-200″>

                <div class=”space-y-8″>

                    <!– Loan Amount –>

                    <div>

                        <div class=”flex justify-between items-center mb-2″>

                            <label for=”amount” class=”font-medium text-gray-700″>Loan Amount</label>

                            <div class=”relative”>

                                <span class=”absolute left-3 top-1/2 -translate-y-1/2 text-gray-500″>₹</span>

                                <input type=”text” id=”amountInput” class=”w-40 pl-7 pr-2 py-2 text-right font-semibold text-lg border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500″>

                            </div>

                        </div>

                        <input type=”range” id=”amountSlider” min=”100000″ max=”50000000″ step=”10000″ value=”2500000″>

                        <div class=”flex justify-between text-sm text-gray-500 mt-1″>

                            <span>₹1 Lakh</span>

                            <span>₹5 Cr</span>

                        </div>

                    </div>

                    <!– Interest Rate –>

                    <div>

                        <div class=”flex justify-between items-center mb-2″>

                            <label for=”interest” class=”font-medium text-gray-700″>Interest Rate</label>

                            <div class=”relative”>

                                <input type=”text” id=”interestInput” class=”w-28 pr-7 py-2 text-right font-semibold text-lg border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500″>

                                <span class=”absolute right-3 top-1/2 -translate-y-1/2 text-gray-500″>%</span>

                            </div>

                        </div>

                        <input type=”range” id=”interestSlider” min=”5″ max=”20″ step=”0.05″ value=”8.5″>

                        <div class=”flex justify-between text-sm text-gray-500 mt-1″>

                            <span>5%</span>

                            <span>20%</span>

                        </div>

                    </div>

                    <!– Loan Tenure –>

                    <div>

                        <div class=”flex justify-between items-center mb-2″>

                            <label for=”tenure” class=”font-medium text-gray-700″>Loan Tenure</label>

                            <div class=”relative”>

                                <input type=”text” id=”tenureInput” class=”w-28 pr-12 py-2 text-right font-semibold text-lg border border-gray-300 rounded-md focus:ring-2 focus:ring-green-500 focus:border-green-500″>

                                <span class=”absolute right-3 top-1/2 -translate-y-1/2 text-gray-500″>Yr</span>

                            </div>

                        </div>

                        <input type=”range” id=”tenureSlider” min=”1″ max=”30″ step=”1″ value=”20″>

                        <div class=”flex justify-between text-sm text-gray-500 mt-1″>

                            <span>1 Yr</span>

                            <span>30 Yrs</span>

                        </div>

                    </div>

                </div>

            </div>

            <!– Right Side: Outputs & Chart –>

            <div class=”lg:col-span-2 bg-white p-6 md:p-8 rounded-2xl shadow-sm border border-gray-200 flex flex-col justify-center items-center”>

                <div class=”w-full max-w-xs mx-auto”>

                    <canvas id=”emiChart”></canvas>

                </div>

                <div class=”w-full mt-6 text-center”>

                    <div class=”mb-4″>

                        <p class=”text-gray-500 text-lg”>Monthly EMI</p>

                        <p id=”monthlyEmi” class=”text-3xl md:text-4xl font-bold text-gray-900″>₹0</p>

                    </div>

                    <div class=”flex justify-between text-base”>

                        <div class=”text-left”>

                            <p class=”text-gray-500″>Principal Amount</p>

                            <p id=”totalPrincipal” class=”font-semibold text-lg”>₹0</p>

                        </div>

                        <div class=”text-right”>

                            <p class=”text-gray-500″>Total Interest</p>

                            <p id=”totalInterest” class=”font-semibold text-lg”>₹0</p>

                        </div>

                    </div>

                    <div class=”border-t my-3″></div>

                    <div class=”flex justify-between text-base”>

                        <div class=”text-left”>

                            <p class=”text-gray-500″>Total Payment</p>

                            <p id=”totalPayment” class=”font-semibold text-lg”>₹0</p>

                        </div>

                    </div>

                </div>

            </div>

        </div>

        <!– Amortization Schedule –>

        <div class=”mt-8 bg-white p-6 md:p-8 rounded-2xl shadow-sm border border-gray-200″>

            <h2 class=”text-2xl font-bold text-gray-900 mb-4″>Amortization Details</h2>

             <div class=”overflow-x-auto”>

                <table class=”min-w-full divide-y divide-gray-200″>

                    <thead class=”bg-gray-50″>

                        <tr>

                            <th scope=”col” class=”px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider”>Year</th>

                            <th scope=”col” class=”px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider”>Principal</th>

                            <th scope=”col” class=”px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider”>Interest</th>

                            <th scope=”col” class=”px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider”>Total Payment</th>

                            <th scope=”col” class=”px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider”>Balance</th>

                        </tr>

                    </thead>

                    <tbody id=”scheduleBody” class=”bg-white divide-y divide-gray-200″>

                        <!– Rows will be injected by JavaScript –>

                    </tbody>

                </table>

            </div>

        </div>

    </div>

    <script>

        document.addEventListener(‘DOMContentLoaded’, function () {

            // DOM Elements

            const amountSlider = document.getElementById(‘amountSlider’);

            const amountInput = document.getElementById(‘amountInput’);

            const interestSlider = document.getElementById(‘interestSlider’);

            const interestInput = document.getElementById(‘interestInput’);

            const tenureSlider = document.getElementById(‘tenureSlider’);

            const tenureInput = document.getElementById(‘tenureInput’);

            const monthlyEmiEl = document.getElementById(‘monthlyEmi’);

            const totalPrincipalEl = document.getElementById(‘totalPrincipal’);

            const totalInterestEl = document.getElementById(‘totalInterest’);

            const totalPaymentEl = document.getElementById(‘totalPayment’);

            const scheduleBody = document.getElementById(‘scheduleBody’);

            let emiChart;

            // Format numbers with commas

            const formatCurrency = (num) => `₹${Math.round(num).toLocaleString(‘en-IN’)}`;

            const formatInputCurrency = (num) => Math.round(num).toLocaleString(‘en-IN’, { useGrouping: false });

            // Chart Initialization

            const ctx = document.getElementById(’emiChart’).getContext(‘2d’);

            function createOrUpdateChart(principal, interest) {

                const data = {

                    labels: [‘Principal Amount’, ‘Total Interest’],

                    datasets: [{

                        data: [principal, interest],

                        backgroundColor: [‘#16a34a’, ‘#dcfce7’], // green-600, green-100

                        borderColor: [‘#ffffff’, ‘#ffffff’],

                        borderWidth: 2,

                        hoverOffset: 4

                    }]

                };

                if (emiChart) {

                    emiChart.data = data;

                    emiChart.update();

                } else {

                    emiChart = new Chart(ctx, {

                        type: ‘doughnut’,

                        data: data,

                        options: {

                            responsive: true,

                            cutout: ‘70%’,

                            plugins: {

                                legend: {

                                    position: ‘bottom’,

                                    labels: {

                                        usePointStyle: true,

                                        pointStyle: ‘circle’,

                                        padding: 20,

                                        boxWidth: 8,

                                        boxHeight: 8

                                    }

                                },

                                tooltip: {

                                    callbacks: {

                                        label: function (context) {

                                            let label = context.label || ”;

                                            if (label) {

                                                label += ‘: ‘;

                                            }

                                            if (context.parsed !== null) {

                                                label += formatCurrency(context.parsed);

                                            }

                                            return label;

                                        }

                                    }

                                }

                            }

                        }

                    });

                }

            }

            // Main Calculation Function

            function calculate() {

                const p = parseFloat(amountSlider.value);

                const rAnnual = parseFloat(interestSlider.value);

                const tYears = parseInt(tenureSlider.value, 10);

                if (isNaN(p) || isNaN(rAnnual) || isNaN(tYears) || p <= 0 || rAnnual <= 0 || tYears <= 0) {

                    return;

                }

                const rMonthly = rAnnual / 12 / 100;

                const nMonths = tYears * 12;

                const emi = (p * rMonthly * Math.pow(1 + rMonthly, nMonths)) / (Math.pow(1 + rMonthly, nMonths) – 1);

                const totalPayment = emi * nMonths;

                const totalInterest = totalPayment – p;

                // Update summary display

                monthlyEmiEl.textContent = formatCurrency(emi);

                totalPrincipalEl.textContent = formatCurrency(p);

                totalInterestEl.textContent = formatCurrency(totalInterest);

                totalPaymentEl.textContent = formatCurrency(totalPayment);

                // Update Chart

                createOrUpdateChart(p, totalInterest);

                // Generate and display schedule

                generateSchedule(p, rAnnual, tYears, emi);

            }

            // Generate Collapsible Amortization Schedule, grouped by calendar year

            function generateSchedule(p, rAnnual, tYears, emi) {

                scheduleBody.innerHTML = ”;

                let balance = p;

                const rMonthly = rAnnual / 12 / 100;

                const nMonths = tYears * 12;

                const monthNames = [“Jan”, “Feb”, “Mar”, “Apr”, “May”, “Jun”, “Jul”, “Aug”, “Sep”, “Oct”, “Nov”, “Dec”];

                const startDate = new Date();

                const startMonth = startDate.getMonth(); // 0-11

                const startYear = startDate.getFullYear();

                // 1. Create a flat list of all monthly payments

                const allPayments = [];

                for (let i = 0; i < nMonths; i++) {

                    const interestForMonth = balance * rMonthly;

                    const principalForMonth = emi – interestForMonth;

                    balance -= principalForMonth;

                    const monthOffset = i;

                    const displayMonthIndex = (startMonth + monthOffset) % 12;

                    const yearOffset = Math.floor((startMonth + monthOffset) / 12);

                    const displayYear = startYear + yearOffset;

                    const displayMonthName = monthNames[displayMonthIndex];

                    allPayments.push({

                        monthName: displayMonthName,

                        year: displayYear,

                        principal: principalForMonth,

                        interest: interestForMonth,

                        total: emi,

                        balance: balance < 0 ? 0 : balance,

                    });

                }

                // 2. Group payments by calendar year

                const groupedByYear = allPayments.reduce((acc, payment) => {

                    const year = payment.year;

                    if (!acc[year]) {

                        acc[year] = [];

                    }

                    acc[year].push(payment);

                    return acc;

                }, {});

                // 3. Iterate through grouped data and build HTML

                for (const year in groupedByYear) {

                    const yearPayments = groupedByYear[year];

                    const yearlyPrincipal = yearPayments.reduce((sum, p) => sum + p.principal, 0);

                    const yearlyInterest = yearPayments.reduce((sum, p) => sum + p.interest, 0);

                    const yearlyTotalPayment = yearlyPrincipal + yearlyInterest;

                    const endOfYearBalance = yearPayments[yearPayments.length – 1].balance;

                    let monthlyRowsHtml = ”;

                    yearPayments.forEach(p => {

                         monthlyRowsHtml += `

                            <tr class=”hover:bg-white”>

                                <td class=”px-6 py-2 text-sm text-gray-700 font-medium”>${p.monthName}</td>

                                <td class=”px-6 py-2 text-sm text-gray-500 text-right”>${formatCurrency(p.principal)}</td>

                                <td class=”px-6 py-2 text-sm text-gray-500 text-right”>${formatCurrency(p.interest)}</td>

                                <td class=”px-6 py-2 text-sm text-gray-500 text-right”>${formatCurrency(p.total)}</td>

                                <td class=”px-6 py-2 text-sm text-gray-500 text-right”>${formatCurrency(p.balance)}</td>

                            </tr>

                        `;

                    });

                    const yearRowHtml = `

                        <tr class=”year-summary-row cursor-pointer hover:bg-gray-50″ data-year=”${year}”>

                            <td class=”px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900″>

                                <span class=”expand-icon inline-block w-4 mr-2 text-lg font-bold text-green-600 transition-transform transform”>+</span>

                                ${year}

                            </td>

                            <td class=”px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right font-medium”>${formatCurrency(yearlyPrincipal)}</td>

                            <td class=”px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right font-medium”>${formatCurrency(yearlyInterest)}</td>

                            <td class=”px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right font-medium”>${formatCurrency(yearlyTotalPayment)}</td>

                            <td class=”px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right font-medium”>${formatCurrency(endOfYearBalance)}</td>

                        </tr>

                    `;

                    const monthlyDetailsRowHtml = `

                        <tr class=”monthly-details hidden” data-details-for-year=”${year}”>

                            <td colspan=”5″>

                                <div class=”inner-table”>

                                    <table class=”min-w-full”>

                                        <thead class=”bg-gray-200″>

                                            <tr>

                                                <th class=”px-6 py-2 text-left text-xs font-medium text-gray-600 uppercase tracking-wider”>Month</th>

                                                <th class=”px-6 py-2 text-right text-xs font-medium text-gray-600 uppercase tracking-wider”>Principal Paid</th>

                                                <th class=”px-6 py-2 text-right text-xs font-medium text-gray-600 uppercase tracking-wider”>Interest Paid</th>

                                                <th class=”px-6 py-2 text-right text-xs font-medium text-gray-600 uppercase tracking-wider”>Total Payment</th>

                                                <th class=”px-6 py-2 text-right text-xs font-medium text-gray-600 uppercase tracking-wider”>Ending Balance</th>

                                            </tr>

                                        </thead>

                                        <tbody class=”divide-y divide-gray-300″>

                                            ${monthlyRowsHtml}

                                        </tbody>

                                    </table>

                                </div>

                            </td>

                        </tr>

                    `;

                    scheduleBody.innerHTML += yearRowHtml + monthlyDetailsRowHtml;

                }

            }

            // Sync Sliders and Inputs

            function syncInputs(slider, input, isCurrency) {

                slider.addEventListener(‘input’, (e) => {

                    input.value = isCurrency ? formatInputCurrency(e.target.value) : e.target.value;

                    calculate();

                });

                input.addEventListener(‘change’, (e) => {

                    let value = parseFloat(e.target.value.replace(/,/g, ”));

                    const min = parseFloat(slider.min);

                    const max = parseFloat(slider.max);

                    if (isNaN(value)) value = min;

                    if (value < min) value = min;

                    if (value > max) value = max;

                    slider.value = value;

                    input.value = isCurrency ? formatInputCurrency(value) : value;

                    calculate();

                });

            }

            syncInputs(amountSlider, amountInput, true);

            syncInputs(interestSlider, interestInput, false);

            syncInputs(tenureSlider, tenureInput, false);

            // Event listener for accordion functionality

            scheduleBody.addEventListener(‘click’, function(e) {

                const yearRow = e.target.closest(‘.year-summary-row’);

                if (!yearRow) return;

                const year = yearRow.dataset.year;

                const detailsRow = document.querySelector(`.monthly-details[data-details-for-year=”${year}”]`);

                const icon = yearRow.querySelector(‘.expand-icon’);

                if (detailsRow) {

                    const isHidden = detailsRow.classList.contains(‘hidden’);

                    detailsRow.classList.toggle(‘hidden’);

                    icon.textContent = isHidden ? ‘−’ : ‘+’;

                    icon.style.transform = isHidden ? ‘rotate(45deg)’ : ‘rotate(0deg)’;

                }

            });

            // Initial setup

            function initialize() {

                amountInput.value = formatInputCurrency(amountSlider.value);

                interestInput.value = interestSlider.value;

                tenureInput.value = tenureSlider.value;

                calculate();

            }

            initialize();

        });

    </script>

</body>

</html>