Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Roommate Expense Splitter</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-out; | |
| } | |
| .tabs { | |
| position: relative; | |
| } | |
| .tabs::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| background: #e2e8f0; | |
| z-index: 1; | |
| } | |
| .tab { | |
| position: relative; | |
| z-index: 2; | |
| } | |
| .tab.active { | |
| border-bottom: 3px solid #4f46e5; | |
| margin-bottom: -1px; | |
| } | |
| .chart-container { | |
| width: 100%; | |
| max-width: 300px; | |
| height: 300px; | |
| position: relative; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text 3xl md:text-4xl font-bold text-indigo-700 mb-2">Roommate Expense Splitter</h1> | |
| <p class="text-gray-600">Track and split expenses equally among Althaf, Jamzith & Rasheed</p> | |
| </header> | |
| <div class="bg-white rounded-lg shadow-md overflow-hidden mb-8"> | |
| <div class="tabs flex border-b"> | |
| <button onclick="switchTab('add')" class="tab active flex-1 py-3 px-4 text-center font-medium text-gray-700 hover:text-indigo-600 transition"> | |
| <i class="fas fa-plus-circle mr-2"></i>Add Expense | |
| </button> | |
| <button onclick="switchTab('history')" class="tab flex-1 py-3 px-4 text-center font-medium text-gray-700 hover:text-indigo-600 transition"> | |
| <i class="fas fa-history mr-2"></i>Expense History | |
| </button> | |
| <button onclick="switchTab('balance')" class="tab flex-1 py-3 px-4 text-center font-medium text-gray-700 hover:text-indigo-600 transition"> | |
| <i class="fas fa-scale-balanced mr-2"></i>Balances | |
| </button> | |
| </div> | |
| <!-- Add Expense Tab --> | |
| <div id="add-tab" class="p-6 fade-in"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Add New Expense</h2> | |
| <form id="expense-form" class="space-y-4"> | |
| <div class="grid md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Paid By</label> | |
| <select id="paid-by" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" required> | |
| <option value="" disabled selected>Select roommate</option> | |
| <option value="Althaf">Althaf</option> | |
| <option value="Jamzith">Jamzith</option> | |
| <option value="Rasheed">Rasheed</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Amount (₹)</label> | |
| <input id="amount" type="number" min="0" step="0.01" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="0.00" required> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Description</label> | |
| <input id="description" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" placeholder="What was this expense for?" required> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Date</label> | |
| <input id="expense-date" type="date" class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" required> | |
| </div> | |
| <div class="pt-2"> | |
| <button type="submit" class="w-full bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 transition"> | |
| <i class="fas fa-check-circle mr-2"></i>Add Expense | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| <!-- Expense History Tab --> | |
| <div id="history-tab" class="p-6 hidden fade-in"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Expense History</h2> | |
| <div class="mb-4 flex justify-between items-center"> | |
| <p class="text-sm text-gray-600">Total expenses: <span id="total-expenses" class="font-medium">₹0.00</span></p> | |
| <button onclick="clearAllExpenses()" class="text-sm text-red-500 hover:text-red-700 flex items-center"> | |
| <i class="fas fa-trash mr-1"></i> Clear All | |
| </button> | |
| </div> | |
| <div class="border rounded-lg overflow-hidden"> | |
| <table class="min-w-full divide-y divide-gray-200"> | |
| <thead class="bg-gray-50"> | |
| <tr> | |
| <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th> | |
| <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Paid By</th> | |
| <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th> | |
| <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th> | |
| <th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Action</th> | |
| </tr> | |
| </thead> | |
| <tbody id="expense-list" class="bg-white divide-y divide-gray-200"> | |
| <!-- Expenses will be listed here --> | |
| <tr id="no-expenses" class="text-center py-4"> | |
| <td colspan="5" class="px-4 py-4 text-sm text-gray-500">No expenses added yet</td> | |
| </tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Balances Tab --> | |
| <div id="balance-tab" class="p-6 hidden fade-in"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Balances</h2> | |
| <div class="grid md:grid-cols-2 gap-6"> | |
| <div> | |
| <h3 class="text-lg font-medium mb-3 text-gray-700">Summary</h3> | |
| <div class="bg-indigo-50 rounded-lg p-4 mb-4"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <span class="text-indigo-700">Total Spent</span> | |
| <span id="total-spent" class="font-medium">₹0.00</span> | |
| </div> | |
| <div class="flex justify-between items-center mb-2"> | |
| <span class="text-indigo-700">Equal Share</span> | |
| <span id="equal-share" class="font-medium">₹0.00</span> | |
| </div> | |
| </div> | |
| <h3 class="text-lg font-medium mb-3 text-gray-700">Individual Balances</h3> | |
| <div class="space-y-3"> | |
| <div class="bg-white border rounded-lg p-3 shadow-sm"> | |
| <div class="flex justify-between items-center"> | |
| <span class="font-medium">Althaf</span> | |
| <span id="althaf-balance" class="text-gray-700">₹0.00</span> | |
| </div> | |
| <div id="althaf-owes" class="text-xs text-gray-500 mt-1"></div> | |
| </div> | |
| <div class="bg-white border rounded-lg p-3 shadow-sm"> | |
| <div class="flex justify-between items-center"> | |
| <span class="font-medium">Jamzith</span> | |
| <span id="jamzith-balance" class="text-gray-700">₹0.00</span> | |
| </div> | |
| <div id="jamzith-owes" class="text-xs text-gray-500 mt-1"></div> | |
| </div> | |
| <div class="bg-white border rounded-lg p-3 shadow-sm"> | |
| <div class="flex justify-between items-center"> | |
| <span class="font-medium">Rasheed</span> | |
| <span id="rasheed-balance" class="text-gray-700">₹0.00</span> | |
| </div> | |
| <div id="rasheed-owes" class="text-xs text-gray-500 mt-1"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-medium mb-3 text-gray-700">Visualization</h3> | |
| <div class="chart-container mx-auto"> | |
| <canvas id="balance-chart"></canvas> | |
| </div> | |
| <div id="settlement-instructions" class="mt-4 text-sm text-gray-600 p-3 bg-gray-50 rounded-lg"> | |
| <p>No settlements needed yet. All balances are zero.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <script> | |
| // Initialize data | |
| let expenses = JSON.parse(localStorage.getItem('roommate-expenses')) || []; | |
| let balanceChart = null; | |
| // DOM elements | |
| const expenseForm = document.getElementById('expense-form'); | |
| const expenseList = document.getElementById('expense-list'); | |
| const noExpensesRow = document.getElementById('no-expenses'); | |
| const totalExpensesEl = document.getElementById('total-expenses'); | |
| const totalSpentEl = document.getElementById('total-spent'); | |
| const equalShareEl = document.getElementById('equal-share'); | |
| // Balance elements | |
| const althafBalanceEl = document.getElementById('althaf-balance'); | |
| const jamzithBalanceEl = document.getElementById('jamzith-balance'); | |
| const rasheedBalanceEl = document.getElementById('rasheed-balance'); | |
| const althafOwesEl = document.getElementById('althaf-owes'); | |
| const jamzithOwesEl = document.getElementById('jamzith-owes'); | |
| const rasheedOwesEl = document.getElementById('rasheed-owes'); | |
| const settlementInstructionsEl = document.getElementById('settlement-instructions'); | |
| // Set today's date as default | |
| document.getElementById('expense-date').valueAsDate = new Date(); | |
| // Initialize the app | |
| function init() { | |
| renderExpenseList(); | |
| calculateBalances(); | |
| } | |
| // Switch between tabs | |
| function switchTab(tabName) { | |
| document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active')); | |
| document.querySelectorAll('#add-tab, #history-tab, #balance-tab').forEach(tab => tab.classList.add('hidden')); | |
| if (tabName === 'add') { | |
| document.querySelector('.tab:nth-child(1)').classList.add('active'); | |
| document.getElementById('add-tab').classList.remove('hidden'); | |
| } else if (tabName === 'history') { | |
| document.querySelector('.tab:nth-child(2)').classList.add('active'); | |
| document.getElementById('history-tab').classList.remove('hidden'); | |
| } else if (tabName === 'balance') { | |
| document.querySelector('.tab:nth-child(3)').classList.add('active'); | |
| document.getElementById('balance-tab').classList.remove('hidden'); | |
| updateChart(); | |
| } | |
| } | |
| // Handle expense form submission | |
| expenseForm.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| const paidBy = document.getElementById('paid-by').value; | |
| const amount = parseFloat(document.getElementById('amount').value); | |
| const description = document.getElementById('description').value; | |
| const date = document.getElementById('expense-date').value; | |
| const expense = { | |
| id: Date.now().toString(), | |
| paidBy, | |
| amount, | |
| description, | |
| date, | |
| timestamp: new Date(date).getTime() | |
| }; | |
| expenses.push(expense); | |
| saveExpenses(); | |
| renderExpenseList(); | |
| calculateBalances(); | |
| // Reset form | |
| expenseForm.reset(); | |
| document.getElementById('expense-date').valueAsDate = new Date(); | |
| // Show notification | |
| showNotification('Expense added successfully!', 'success'); | |
| }); | |
| // Render the expense list | |
| function renderExpenseList() { | |
| if (expenses.length === 0) { | |
| noExpensesRow.classList.remove('hidden'); | |
| expenseList.innerHTML = ''; | |
| expenseList.appendChild(noExpensesRow); | |
| totalExpensesEl.textContent = '₹0.00'; | |
| return; | |
| } | |
| noExpensesRow.classList.add('hidden'); | |
| // Sort expenses by date (newest first) | |
| expenses.sort((a, b) => b.timestamp - a.timestamp); | |
| expenseList.innerHTML = ''; | |
| let total = 0; | |
| expenses.forEach(expense => { | |
| total += expense.amount; | |
| const row = document.createElement('tr'); | |
| row.className = 'hover:bg-gray-50'; | |
| row.innerHTML = ` | |
| <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${formatDate(expense.date)}</td> | |
| <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900"> | |
| <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-indigo-100 text-indigo-800"> | |
| ${expense.paidBy} | |
| </span> | |
| </td> | |
| <td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${expense.description}</td> | |
| <td class="px-4 py-3 whitespace-nowrap text-sm font-medium text-gray-900">₹${expense.amount.toFixed(2)}</td> | |
| <td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium"> | |
| <button onclick="deleteExpense('${expense.id}')" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-trash"></i> | |
| </button> | |
| </td> | |
| `; | |
| expenseList.appendChild(row); | |
| }); | |
| totalExpensesEl.textContent = `₹${total.toFixed(2)}`; | |
| } | |
| // Delete an expense | |
| function deleteExpense(id) { | |
| if (confirm('Are you sure you want to delete this expense?')) { | |
| expenses = expenses.filter(expense => expense.id !== id); | |
| saveExpenses(); | |
| renderExpenseList(); | |
| calculateBalances(); | |
| showNotification('Expense deleted successfully!', 'success'); | |
| } | |
| } | |
| // Clear all expenses | |
| function clearAllExpenses() { | |
| if (expenses.length === 0) return; | |
| if (confirm('Are you sure you want to delete ALL expenses? This cannot be undone.')) { | |
| expenses = []; | |
| saveExpenses(); | |
| renderExpenseList(); | |
| calculateBalances(); | |
| showNotification('All expenses cleared!', 'success'); | |
| } | |
| } | |
| // Save expenses to localStorage | |
| function saveExpenses() { | |
| localStorage.setItem('roommate-expenses', JSON.stringify(expenses)); | |
| } | |
| // Calculate balances and who owes whom | |
| function calculateBalances() { | |
| const totals = { | |
| Althaf: 0, | |
| Jamzith: 0, | |
| Rasheed: 0 | |
| }; | |
| // Calculate total spent by each person | |
| expenses.forEach(expense => { | |
| totals[expense.paidBy] += expense.amount; | |
| }); | |
| // Calculate total amount spent | |
| const totalSpent = totals.Althaf + totals.Jamzith + totals.Rasheed; | |
| totalSpentEl.textContent = `₹${totalSpent.toFixed(2)}`; | |
| // Calculate equal share | |
| const equalShare = totalSpent / 3; | |
| equalShareEl.textContent = `₹${equalShare.toFixed(2)}`; | |
| // Calculate individual balances (positive means they paid more than share, negative means they owe) | |
| const balances = { | |
| Althaf: (Math.round((totals.Althaf - equalShare) * 100) / 100), | |
| Jamzith: (Math.round((totals.Jamzith - equalShare) * 100) / 100), | |
| Rasheed: (Math.round((totals.Rasheed - equalShare) * 100) / 100) | |
| }; | |
| // Update UI with balances | |
| updateBalanceUI(balances); | |
| // Generate settlement instructions | |
| generateSettlementInstructions(balances); | |
| } | |
| // Update balance UI elements | |
| function updateBalanceUI(balances) { | |
| althafBalanceEl.textContent = formatCurrency(balances.Althaf); | |
| jamzithBalanceEl.textContent = formatCurrency(balances.Jamzith); | |
| rasheedBalanceEl.textContent = formatCurrency(balances.Rasheed); | |
| // Set text color based on balance (green for positive, red for negative) | |
| setBalanceColor(althafBalanceEl, balances.Althaf); | |
| setBalanceColor(jamzithBalanceEl, balances.Jamzith); | |
| setBalanceColor(rasheedBalanceEl, balances.Rasheed); | |
| } | |
| // Generate settlement instructions | |
| function generateSettlementInstructions(balances) { | |
| const people = [ | |
| { name: 'Althaf', balance: balances.Althaf }, | |
| { name: 'Jamzith', balance: balances.Jamzith }, | |
| { name: 'Rasheed', balance: balances.Rasheed } | |
| ]; | |
| // Sort by balance (ascending) | |
| people.sort((a, b) => a.balance - b.balance); | |
| let creditors = people.filter(p => p.balance > 0); | |
| let debtors = people.filter(p => p.balance < 0); | |
| if (creditors.length === 0 && debtors.length === 0) { | |
| settlementInstructionsEl.innerHTML = '<p class="text-green-600 font-medium">All balances are settled. No payments needed.</p>'; | |
| return; | |
| } | |
| let instructions = ''; | |
| // Generate owes text for each person | |
| people.forEach(person => { | |
| if (person.balance < 0) { | |
| const owesText = person.name + ' owes ₹' + Math.abs(person.balance).toFixed(2) + ' in total'; | |
| if (person.name === 'Althaf') althafOwesEl.textContent = owesText; | |
| else if (person.name === 'Jamzith') jamzithOwesEl.textContent = owesText; | |
| else if (person.name === 'Rasheed') rasheedOwesEl.textContent = owesText; | |
| } else if (person.balance > 0) { | |
| const owedText = person.name + ' is owed ₹' + person.balance.toFixed(2) + ' in total'; | |
| if (person.name === 'Althaf') althafOwesEl.textContent = owedText; | |
| else if (person.name === 'Jamzith') jamzithOwesEl.textContent = owedText; | |
| else if (person.name === 'Rasheed') rasheedOwesEl.textContent = owedText; | |
| } else { | |
| if (person.name === 'Althaf') althafOwesEl.textContent = 'No balance - all settled'; | |
| else if (person.name === 'Jamzith') jamzithOwesEl.textContent = 'No balance - all settled'; | |
| else if (person.name === 'Rasheed') rasheedOwesEl.textContent = 'No balance - all settled'; | |
| } | |
| }); | |
| // Generate settlement instructions | |
| if (creditors.length > 0 && debtors.length > 0) { | |
| instructions = '<h4 class="font-medium mb-2 text-gray-800">Settlement Instructions:</h4><ul class="list-disc pl-5 space-y-1">'; | |
| // Simplify debts (basic approach) | |
| while (creditors.length > 0 && debtors.length > 0) { | |
| const creditor = creditors[0]; | |
| const debtor = debtors[0]; | |
| const amount = Math.min(creditor.balance, -debtor.balance); | |
| const roundedAmount = Math.round(amount * 100) / 100; | |
| instructions += `<li>${debtor.name} should pay ${creditor.name} ₹${roundedAmount.toFixed(2)}</li>`; | |
| creditor.balance -= amount; | |
| debtor.balance += amount; | |
| if (Math.abs(creditor.balance) < 0.01) creditors.shift(); | |
| if (Math.abs(debtor.balance) < 0.01) debtors.shift(); | |
| } | |
| instructions += '</ul>'; | |
| } | |
| settlementInstructionsEl.innerHTML = instructions || '<p class="text-green-600 font-medium">All balances are settled. No payments needed.</p>'; | |
| } | |
| // Update the balance chart | |
| function updateChart() { | |
| const ctx = document.getElementById('balance-chart').getContext('2d'); | |
| const totals = { Althaf: 0, Jamzith: 0, Rasheed: 0 }; | |
| expenses.forEach(expense => { | |
| totals[expense.paidBy] += expense.amount; | |
| }); | |
| const totalSpent = totals.Althaf + totals.Jamzith + totals.Rasheed; | |
| const equalShare = totalSpent / 3; | |
| const balances = { | |
| Althaf: totals.Althaf - equalShare, | |
| Jamzith: totals.Jamzith - equalShare, | |
| Rasheed: totals.Rasheed - equalShare | |
| }; | |
| if (balanceChart) { | |
| balanceChart.destroy(); | |
| } | |
| balanceChart = new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['Althaf', 'Jamzith', 'Rasheed'], | |
| datasets: [{ | |
| label: 'Balance (₹)', | |
| data: [balances.Althaf, balances.Jamzith, balances.Rasheed], | |
| backgroundColor: [ | |
| 'rgba(79, 70, 229, 0.7)', | |
| 'rgba(99, 102, 241, 0.7)', | |
| 'rgba(129, 140, 248, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(79, 70, 229, 1)', | |
| 'rgba(99, 102, 241, 1)', | |
| 'rgba(129, 140, 248, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { | |
| y: { | |
| beginAtZero: false | |
| } | |
| }, | |
| plugins: { | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| let label = context.dataset.label || ''; | |
| if (label) { | |
| label += ': '; | |
| } | |
| const value = context.raw; | |
| label += '₹' + value.toFixed(2); | |
| if (value > 0) { | |
| label += ' (owed)'; | |
| } else if (value < 0) { | |
| label += ' (owes)'; | |
| } else { | |
| label += ' (settled)'; | |
| } | |
| return label; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Helper functions | |
| function formatDate(dateString) { | |
| const options = { year: 'numeric', month: 'short', day: 'numeric' }; | |
| return new Date(dateString).toLocaleDateString(undefined, options); | |
| } | |
| function formatCurrency(amount) { | |
| return '₹' + Math.abs(amount).toFixed(2); | |
| } | |
| function setBalanceColor(element, amount) { | |
| if (amount > 0) { | |
| element.classList.remove('text-red-600'); | |
| element.classList.add('text-green-600'); | |
| } else if (amount < 0) { | |
| element.classList.remove('text-green-600'); | |
| element.classList.add('text-red-600'); | |
| } else { | |
| element.classList.remove('text-green-600', 'text-red-600'); | |
| element.classList.add('text-gray-700'); | |
| } | |
| } | |
| function showNotification(message, type) { | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-4 right-4 z-50 px-4 py-2 rounded-md shadow-lg text-white ${ | |
| type === 'success' ? 'bg-green-500' : 'bg-red-500' | |
| } animate-fade-in-out`; | |
| notification.textContent = message; | |
| document.body.appendChild(notification); | |
| setTimeout(() => { | |
| notification.classList.add('opacity-0', 'transition-opacity', 'duration-300'); | |
| setTimeout(() => notification.remove(), 300); | |
| }, 3000); | |
| } | |
| // Initialize the app | |
| init(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Ultronprime/room-app" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |