Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>FinAudit AI - Pro Auditor</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| /* Modern Glassmorphism & Animations */ | |
| body { | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 50%, #0f172a 100%); | |
| background-size: 200% 200%; | |
| animation: gradientMove 15s ease infinite; | |
| } | |
| @keyframes gradientMove { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| .glass-panel { | |
| background: rgba(17, 24, 39, 0.7); | |
| backdrop-filter: blur(12px); | |
| -webkit-backdrop-filter: blur(12px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| /* Realistic Phone Mockup */ | |
| .phone-bezel { | |
| box-shadow: 0 0 0 2px #1f2937, 0 25px 50px -12px rgba(0, 0, 0, 0.8), inset 0 0 20px rgba(255,255,255,0.1); | |
| } | |
| .dynamic-island { | |
| position: absolute; | |
| top: 10px; left: 50%; | |
| transform: translateX(-50%); | |
| width: 100px; height: 30px; | |
| background: #000; | |
| border-radius: 20px; | |
| z-index: 40; | |
| } | |
| /* Dynamic XAI Heatmap */ | |
| .heatmap-layer { | |
| position: absolute; inset: 0; | |
| pointer-events: none; opacity: 0; | |
| transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1); | |
| z-index: 50; background: rgba(0,0,10,0.6); | |
| } | |
| .heatmap-active .heatmap-layer { opacity: 1; } | |
| .hotspot { | |
| position: absolute; border-radius: 50%; | |
| filter: blur(25px); | |
| animation: pulseGlow 2s infinite alternate; | |
| mix-blend-mode: screen; | |
| } | |
| .hotspot-severe { background: radial-gradient(circle, rgba(255,40,40,0.9) 0%, rgba(255,0,0,0) 70%); } | |
| .hotspot-warn { background: radial-gradient(circle, rgba(255,180,0,0.9) 0%, rgba(255,140,0,0) 70%); } | |
| @keyframes pulseGlow { | |
| 0% { transform: scale(1) translate(-50%, -50%); opacity: 0.8; } | |
| 100% { transform: scale(1.15) translate(-45%, -45%); opacity: 1; } | |
| } | |
| /* Custom Scrollbar */ | |
| ::-webkit-scrollbar { width: 8px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 4px; } | |
| </style> | |
| </head> | |
| <body class="text-gray-100 h-screen overflow-hidden flex font-sans"> | |
| <aside class="w-72 glass-panel flex flex-col z-10 border-r border-white/10"> | |
| <div class="p-6 border-b border-white/10"> | |
| <h1 class="text-2xl font-black bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500"> | |
| <i class="fas fa-fingerprint text-blue-400 mr-2"></i>FinAudit | |
| </h1> | |
| <p class="text-xs text-blue-200 mt-1 uppercase tracking-widest">GenAI Auditor</p> | |
| </div> | |
| <div class="p-5 flex-1 overflow-y-auto"> | |
| <h2 class="text-sm font-bold text-gray-300 mb-4 uppercase tracking-wider"><i class="fas fa-database mr-2"></i>Custom Scenario Data</h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-xs text-gray-400 mb-1">UI Headline</label> | |
| <input type="text" id="customHeadline" value="Claim Your Bonus!" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition"> | |
| </div> | |
| <div> | |
| <label class="block text-xs text-gray-400 mb-1">Primary Button Text</label> | |
| <input type="text" id="customBtn" value="Start Free Trial" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition"> | |
| </div> | |
| <div> | |
| <label class="block text-xs text-gray-400 mb-1">Fine Print (Deceptive Element)</label> | |
| <textarea id="customFinePrint" rows="2" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition">By clicking, you agree to a $149/year auto-renewing subscription.</textarea> | |
| </div> | |
| <div> | |
| <label class="block text-xs text-gray-400 mb-1">Target Dark Pattern</label> | |
| <select id="customPatternType" class="w-full bg-gray-900/50 border border-gray-700 rounded p-2 text-sm text-white focus:border-blue-500 focus:outline-none transition"> | |
| <option value="subscription_trap">Subscription Trap (Hidden Cost)</option> | |
| <option value="confirmshaming">Confirmshaming (Guilt Trip)</option> | |
| <option value="urgency">Artificial Urgency (Timers)</option> | |
| </select> | |
| </div> | |
| <button id="generateMockupBtn" class="w-full mt-4 bg-gray-800 hover:bg-gray-700 border border-gray-600 text-white px-4 py-2 rounded font-medium text-sm transition"> | |
| <i class="fas fa-sync-alt mr-2"></i>Render Custom UI | |
| </button> | |
| </div> | |
| </div> | |
| </aside> | |
| <main class="flex-1 flex flex-col h-full relative"> | |
| <header class="h-16 glass-panel border-b border-white/10 flex items-center justify-between px-8 z-10"> | |
| <div class="flex space-x-2"> | |
| <span class="px-3 py-1 bg-green-900/40 text-green-400 border border-green-800 rounded-full text-xs font-mono">ViT Model: Online</span> | |
| <span class="px-3 py-1 bg-purple-900/40 text-purple-400 border border-purple-800 rounded-full text-xs font-mono">CFPB Database: Synced</span> | |
| </div> | |
| <button id="runAuditBtn" class="bg-gradient-to-r from-blue-600 to-indigo-600 hover:from-blue-500 hover:to-indigo-500 text-white px-6 py-2 rounded-lg font-bold text-sm transition-all shadow-[0_0_15px_rgba(79,70,229,0.4)] flex items-center"> | |
| <i class="fas fa-crosshairs mr-2"></i> Execute XAI Scan | |
| </button> | |
| </header> | |
| <div class="flex-1 flex p-8 gap-8 overflow-hidden items-center justify-center"> | |
| <div class="relative group"> | |
| <div class="absolute -inset-1 bg-gradient-to-r from-blue-600 to-purple-600 rounded-[3rem] blur opacity-25 group-hover:opacity-40 transition duration-1000"></div> | |
| <div id="phoneMockup" class="w-[340px] h-[700px] bg-white text-black phone-bezel rounded-[2.5rem] border-[14px] border-black relative overflow-hidden transition-all"> | |
| <div class="dynamic-island"></div> | |
| <div id="dynamicUI" class="h-full w-full flex flex-col pt-16 pb-10 px-6 bg-slate-50 relative z-10"> | |
| <div class="mb-auto mt-8 text-center"> | |
| <div class="w-16 h-16 bg-blue-100 text-blue-600 rounded-2xl flex items-center justify-center mx-auto mb-6 text-3xl shadow-sm"><i class="fas fa-gift"></i></div> | |
| <h2 id="uiHeadline" class="text-2xl font-extrabold text-gray-900 mb-2 leading-tight">Claim Your Bonus!</h2> | |
| <p class="text-gray-500 text-sm">Exclusive offer for new users today.</p> | |
| </div> | |
| <div class="mt-auto space-y-3 relative"> | |
| <div id="uiTimer" class="hidden text-center text-red-600 font-bold mb-4 animate-pulse"> | |
| Offer Expires in 04:59 | |
| </div> | |
| <button id="uiBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-4 rounded-xl shadow-lg text-lg transition-transform active:scale-95">Start Free Trial</button> | |
| <div class="text-center pt-2"> | |
| <p id="uiFinePrint" class="text-[10px] text-gray-400 leading-tight opacity-70">By clicking, you agree to a $149/year auto-renewing subscription.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="heatmapLayer" class="heatmap-layer"></div> | |
| <div id="scanLaser" class="absolute top-0 left-0 w-full h-1 bg-blue-500 shadow-[0_0_15px_#3b82f6] z-50 hidden"></div> | |
| </div> | |
| </div> | |
| <div class="w-[450px] h-[650px] glass-panel rounded-2xl flex flex-col shadow-2xl relative overflow-hidden"> | |
| <div class="p-5 border-b border-white/10 bg-black/20 flex justify-between items-center"> | |
| <h2 class="font-bold text-white"><i class="fas fa-brain mr-2 text-purple-400"></i>Audit Results</h2> | |
| <div class="flex items-center space-x-2"> | |
| <span class="text-xs text-gray-400">Heatmap Overlay</span> | |
| <label class="relative inline-flex items-center cursor-pointer"> | |
| <input type="checkbox" id="heatmapToggle" class="sr-only peer" checked disabled> | |
| <div class="w-9 h-5 bg-gray-600 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-purple-500"></div> | |
| </label> | |
| </div> | |
| </div> | |
| <div id="resultsContainer" class="flex-1 p-6 overflow-y-auto flex flex-col"> | |
| <div id="emptyState" class="flex-1 flex flex-col items-center justify-center text-center opacity-50"> | |
| <i class="fas fa-layer-group text-5xl mb-4 text-gray-500"></i> | |
| <p class="text-sm">Configure your custom scenario on the left, then execute the XAI scan to analyze the UI structure.</p> | |
| </div> | |
| <div id="analysisOutput" class="hidden space-y-6"> | |
| <div class="bg-red-500/10 border border-red-500/30 rounded-xl p-4"> | |
| <div class="flex justify-between items-start mb-2"> | |
| <p class="text-xs text-red-200 uppercase font-bold tracking-wider">CFPB Violation Risk</p> | |
| <span class="bg-red-500 text-white text-xs px-2 py-1 rounded font-bold animate-pulse">HIGH</span> | |
| </div> | |
| <p id="patternViolationName" class="text-xl font-black text-red-400">Subscription Trap Detected</p> | |
| </div> | |
| <div> | |
| <h3 class="text-xs font-bold text-gray-400 uppercase tracking-widest mb-3 border-b border-gray-700 pb-2">Identified Anomalies</h3> | |
| <div class="space-y-3"> | |
| <div class="bg-white/5 p-3 rounded-lg border border-white/10 border-l-4 border-l-red-500"> | |
| <span class="font-bold text-sm text-gray-200 block mb-1">Visual Obfuscation</span> | |
| <p class="text-xs text-gray-400">Critical financial terms are rendered with insufficient contrast and microscopic font size relative to the primary call-to-action.</p> | |
| </div> | |
| <div class="bg-white/5 p-3 rounded-lg border border-white/10 border-l-4 border-l-yellow-500"> | |
| <span class="font-bold text-sm text-gray-200 block mb-1">Asymmetric Action</span> | |
| <p class="text-xs text-gray-400">The action to accept is highly salient (large, colored button), while the mechanism to understand the cost is suppressed.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-blue-900/20 border border-blue-500/30 rounded-xl p-4"> | |
| <h3 class="text-xs font-bold text-blue-300 uppercase tracking-widest mb-2"><i class="fas fa-network-wired mr-2"></i>Model Rationale</h3> | |
| <p id="xaiExplanationText" class="text-xs text-blue-100/70 leading-relaxed"> | |
| The Vision Transformer (ViT) saliency map indicates a 94% attention skew towards the primary button. The financial disclosure text falls outside the primary visual hierarchy, triggering a heuristic flag for deceptive pattern structuring. | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| // DOM Elements | |
| const customHeadline = document.getElementById('customHeadline'); | |
| const customBtn = document.getElementById('customBtn'); | |
| const customFinePrint = document.getElementById('customFinePrint'); | |
| const customPatternType = document.getElementById('customPatternType'); | |
| const generateMockupBtn = document.getElementById('generateMockupBtn'); | |
| const uiHeadline = document.getElementById('uiHeadline'); | |
| const uiBtn = document.getElementById('uiBtn'); | |
| const uiFinePrint = document.getElementById('uiFinePrint'); | |
| const uiTimer = document.getElementById('uiTimer'); | |
| const runAuditBtn = document.getElementById('runAuditBtn'); | |
| const phoneMockup = document.getElementById('phoneMockup'); | |
| const heatmapLayer = document.getElementById('heatmapLayer'); | |
| const scanLaser = document.getElementById('scanLaser'); | |
| const emptyState = document.getElementById('emptyState'); | |
| const analysisOutput = document.getElementById('analysisOutput'); | |
| const patternViolationName = document.getElementById('patternViolationName'); | |
| const xaiExplanationText = document.getElementById('xaiExplanationText'); | |
| const heatmapToggle = document.getElementById('heatmapToggle'); | |
| let currentPattern = 'subscription_trap'; | |
| // 1. Handle Generating Custom UI | |
| generateMockupBtn.addEventListener('click', () => { | |
| // Apply text | |
| uiHeadline.textContent = customHeadline.value; | |
| uiBtn.textContent = customBtn.value; | |
| uiFinePrint.textContent = customFinePrint.value; | |
| currentPattern = customPatternType.value; | |
| // Handle specific UI logic based on pattern | |
| if(currentPattern === 'urgency') { | |
| uiTimer.classList.remove('hidden'); | |
| uiBtn.classList.replace('bg-blue-600', 'bg-red-600'); | |
| uiBtn.classList.replace('hover:bg-blue-700', 'hover:bg-red-700'); | |
| } else { | |
| uiTimer.classList.add('hidden'); | |
| uiBtn.classList.replace('bg-red-600', 'bg-blue-600'); | |
| uiBtn.classList.replace('hover:bg-red-700', 'hover:bg-blue-700'); | |
| } | |
| // Reset states | |
| resetAuditState(); | |
| generateMockupBtn.innerHTML = '<i class="fas fa-check mr-2"></i>UI Rendered'; | |
| generateMockupBtn.classList.add('bg-green-600', 'border-green-500'); | |
| setTimeout(() => { | |
| generateMockupBtn.innerHTML = '<i class="fas fa-sync-alt mr-2"></i>Render Custom UI'; | |
| generateMockupBtn.classList.remove('bg-green-600', 'border-green-500'); | |
| }, 1500); | |
| }); | |
| // 2. Handle Running the Audit | |
| runAuditBtn.addEventListener('click', () => { | |
| resetAuditState(); | |
| // Start scan animation | |
| scanLaser.style.display = 'block'; | |
| scanLaser.style.animation = 'scan 1.5s linear infinite'; | |
| runAuditBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i> Analyzing...'; | |
| runAuditBtn.disabled = true; | |
| setTimeout(() => { | |
| // Stop animation | |
| scanLaser.style.display = 'none'; | |
| runAuditBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Scan Complete'; | |
| runAuditBtn.disabled = false; | |
| // Show Results | |
| emptyState.classList.add('hidden'); | |
| analysisOutput.classList.remove('hidden'); | |
| heatmapToggle.disabled = false; | |
| // Update text based on pattern | |
| if(currentPattern === 'subscription_trap') { | |
| patternViolationName.textContent = "Subscription Trap Detected"; | |
| xaiExplanationText.textContent = "The Vision Transformer (ViT) saliency map indicates a 94% attention skew towards the primary button. The financial disclosure text falls outside the primary visual hierarchy, triggering a heuristic flag for deceptive pattern structuring."; | |
| generateHeatmap([{top: '80%', type: 'severe'}, {top: '92%', type: 'warn', width: '250px', height: '40px'}]); | |
| } else if (currentPattern === 'confirmshaming') { | |
| patternViolationName.textContent = "Confirmshaming Detected"; | |
| xaiExplanationText.textContent = "Linguistic analysis of the fine-print detects emotive, guilt-inducing phrasing designed to manipulate user agency. Saliency maps show severe visual imbalance favoring the affirmative action."; | |
| generateHeatmap([{top: '80%', type: 'severe'}, {top: '92%', type: 'severe', width: '280px', height: '50px'}]); | |
| } else if (currentPattern === 'urgency') { | |
| patternViolationName.textContent = "Artificial Urgency Detected"; | |
| xaiExplanationText.textContent = "Heuristic scanner identified an active countdown timer paired with high-friction transaction elements. This violates transparency guidelines by artificially accelerating financial decision-making."; | |
| generateHeatmap([{top: '68%', type: 'severe', width: '200px', height: '40px'}, {top: '82%', type: 'severe'}]); | |
| } | |
| // Activate Heatmap | |
| phoneMockup.classList.add('heatmap-active'); | |
| }, 2000); | |
| }); | |
| // Toggle heatmap | |
| heatmapToggle.addEventListener('change', (e) => { | |
| if(e.target.checked) phoneMockup.classList.add('heatmap-active'); | |
| else phoneMockup.classList.remove('heatmap-active'); | |
| }); | |
| // Helper: Generate Heatmap Spots | |
| function generateHeatmap(spots) { | |
| heatmapLayer.innerHTML = spots.map(s => ` | |
| <div class="hotspot ${s.type === 'severe' ? 'hotspot-severe' : 'hotspot-warn'}" | |
| style="top: ${s.top}; left: 50%; width: ${s.width || '150px'}; height: ${s.height || '80px'};"> | |
| </div> | |
| `).join(''); | |
| } | |
| // Helper: Reset State | |
| function resetAuditState() { | |
| phoneMockup.classList.remove('heatmap-active'); | |
| heatmapLayer.innerHTML = ''; | |
| emptyState.classList.remove('hidden'); | |
| analysisOutput.classList.add('hidden'); | |
| heatmapToggle.checked = true; | |
| heatmapToggle.disabled = true; | |
| runAuditBtn.innerHTML = '<i class="fas fa-crosshairs mr-2"></i> Execute XAI Scan'; | |
| // CSS Animation dynamically added for scanner | |
| const style = document.createElement('style'); | |
| style.innerHTML = ` | |
| @keyframes scan { | |
| 0% { top: 0%; opacity: 0; } | |
| 10% { opacity: 1; } | |
| 90% { opacity: 1; } | |
| 100% { top: 100%; opacity: 0; } | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| } | |
| </script> | |
| </body> | |
| </html> |