Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Results - Proofly</title> | |
| <meta name="description" content="Fact-checking results with evidence and NLI analysis"> | |
| <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> | |
| <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=Outfit:wght@300;400;500;600;700;800&display=swap" | |
| rel="stylesheet"> | |
| <script src="https://unpkg.com/@phosphor-icons/web"></script> | |
| <!-- Apply saved theme immediately to prevent flash --> | |
| <script>if (localStorage.getItem('proofly-theme') === 'dark') document.documentElement.setAttribute('data-theme', 'dark');</script> | |
| <style> | |
| .results-scroll-area { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 0 3rem 3rem 3rem; | |
| } | |
| .results-header { | |
| margin-bottom: 2rem; | |
| } | |
| /* Verdict Hero Card */ | |
| .verdict-card { | |
| background: var(--bg-card); | |
| border-radius: var(--radius-lg); | |
| padding: 2.5rem; | |
| box-shadow: var(--shadow-sm); | |
| border: 1px solid var(--border-color); | |
| margin-bottom: 2rem; | |
| text-align: center; | |
| position: relative; | |
| overflow: hidden; | |
| transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| /* Coloured top accent bar */ | |
| .verdict-card::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| height: 4px; | |
| } | |
| .verdict-card.true::before { | |
| background: #10b981; | |
| } | |
| .verdict-card.false::before { | |
| background: #ef4444; | |
| } | |
| .verdict-card.uncertain::before { | |
| background: #f59e0b; | |
| } | |
| /* Coloured glow border around the whole card */ | |
| .verdict-card.true { | |
| border-color: rgba(16, 185, 129, 0.45); | |
| box-shadow: 0 0 0 1px rgba(16, 185, 129, 0.25), | |
| 0 8px 32px rgba(16, 185, 129, 0.18); | |
| } | |
| .verdict-card.false { | |
| border-color: rgba(239, 68, 68, 0.45); | |
| box-shadow: 0 0 0 1px rgba(239, 68, 68, 0.25), | |
| 0 8px 32px rgba(239, 68, 68, 0.18); | |
| } | |
| .verdict-card.uncertain { | |
| border-color: rgba(245, 158, 11, 0.45); | |
| box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.25), | |
| 0 8px 32px rgba(245, 158, 11, 0.18); | |
| } | |
| .verdict-badge { | |
| display: inline-block; | |
| padding: 0.5rem 1.5rem; | |
| border-radius: 50px; | |
| font-weight: 700; | |
| font-size: 0.9rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1em; | |
| margin-bottom: 1rem; | |
| } | |
| .verdict-badge.true { | |
| color: #10b981; | |
| background: rgba(16, 185, 129, 0.1); | |
| } | |
| .verdict-badge.false { | |
| color: #ef4444; | |
| background: rgba(239, 68, 68, 0.1); | |
| } | |
| .verdict-badge.uncertain { | |
| color: #f59e0b; | |
| background: rgba(245, 158, 11, 0.1); | |
| } | |
| .verdict-title { | |
| font-size: 2rem; | |
| font-weight: 500; | |
| color: var(--text-main); | |
| margin-bottom: 1.5rem; | |
| line-height: 1.2; | |
| } | |
| .verdict-confidence-bar { | |
| width: 260px; | |
| height: 8px; | |
| background: var(--border-color); | |
| border-radius: 99px; | |
| margin: 12px auto 0; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .verdict-confidence-fill { | |
| position: absolute; | |
| left: 0; | |
| top: 0; | |
| height: 100%; | |
| border-radius: 99px; | |
| transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| /* Fill colours driven by the parent verdict-card class */ | |
| .verdict-card.true .verdict-confidence-fill { | |
| background: linear-gradient(90deg, #059669, #10b981); | |
| } | |
| .verdict-card.false .verdict-confidence-fill { | |
| background: linear-gradient(90deg, #b91c1c, #ef4444); | |
| } | |
| .verdict-card.uncertain .verdict-confidence-fill { | |
| background: linear-gradient(90deg, #b45309, #f59e0b); | |
| } | |
| /* Evidence full-width */ | |
| .evidence-section { | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-lg); | |
| padding: 2rem; | |
| margin-bottom: 1.5rem; | |
| box-shadow: var(--shadow-sm); | |
| transition: background 0.3s ease, border-color 0.3s ease; | |
| } | |
| .section-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| margin-bottom: 1.5rem; | |
| padding-bottom: 1rem; | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .section-title { | |
| font-size: 1.25rem; | |
| font-weight: 600; | |
| color: var(--text-main); | |
| } | |
| /* Evidence/NLI Items */ | |
| .detail-item { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-md); | |
| padding: 1.5rem; | |
| margin-bottom: 1rem; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04); | |
| transition: var(--transition); | |
| } | |
| .detail-item:hover { | |
| box-shadow: var(--shadow-soft); | |
| transform: translateY(-2px); | |
| } | |
| .source-tag { | |
| font-size: 0.75rem; | |
| font-weight: 700; | |
| text-transform: uppercase; | |
| color: var(--primary); | |
| background: rgba(37, 99, 235, 0.1); | |
| padding: 0.25rem 0.75rem; | |
| border-radius: 50px; | |
| letter-spacing: 0.05em; | |
| } | |
| .nli-status { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 0.75rem; | |
| align-items: center; | |
| } | |
| .status-entailment { | |
| color: #10b981; | |
| } | |
| .status-contradiction { | |
| color: #ef4444; | |
| } | |
| .status-neutral { | |
| color: #64748b; | |
| } | |
| .nli-confidence-meter { | |
| height: 4px; | |
| background: var(--border-color); | |
| border-radius: 2px; | |
| margin-top: 1rem; | |
| overflow: hidden; | |
| } | |
| .nli-meter-fill { | |
| height: 100%; | |
| border-radius: 2px; | |
| background: currentColor; | |
| } | |
| /* Empty/Error State */ | |
| .empty-glass { | |
| background: var(--bg-input); | |
| border: 1px dashed var(--text-light); | |
| border-radius: var(--radius-md); | |
| text-align: center; | |
| padding: 3rem 2rem; | |
| color: var(--text-muted); | |
| } | |
| /* AI Analysis Toggle Button */ | |
| .ai-toggle-btn { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.6rem; | |
| background: transparent; | |
| color: var(--primary); | |
| border: 1.5px solid var(--primary); | |
| border-radius: 10px; | |
| padding: 0.65rem 1.4rem; | |
| font-size: 0.9rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| font-family: inherit; | |
| transition: all 0.2s ease; | |
| margin-bottom: 1.5rem; | |
| } | |
| .ai-toggle-btn:hover { | |
| background: var(--primary); | |
| color: white; | |
| } | |
| .ai-toggle-btn i { | |
| font-size: 1.1rem; | |
| transition: transform 0.3s ease; | |
| } | |
| .ai-toggle-btn.open i.toggle-chevron { | |
| transform: rotate(180deg); | |
| } | |
| /* AI Analysis collapsible panel */ | |
| .ai-analysis-panel { | |
| display: none; | |
| background: var(--bg-card); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-lg); | |
| padding: 2rem; | |
| margin-bottom: 1.5rem; | |
| box-shadow: var(--shadow-sm); | |
| animation: slideDown 0.25s ease; | |
| transition: background 0.3s ease, border-color 0.3s ease; | |
| } | |
| @keyframes slideDown { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-8px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .ai-analysis-panel.visible { | |
| display: block; | |
| } | |
| /* Hide scrollbar cleanly */ | |
| .results-scroll-area::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| .results-scroll-area::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .results-scroll-area::-webkit-scrollbar-thumb { | |
| background: var(--text-light); | |
| border-radius: 4px; | |
| } | |
| .results-scroll-area::-webkit-scrollbar-thumb:hover { | |
| background: var(--text-muted); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Sidebar Navigation --> | |
| <aside class="sidebar"> | |
| <div class="sidebar-top"> | |
| <a href="/" class="icon-btn" title="New Check" style="text-decoration:none;"><i | |
| class="ph ph-plus"></i></a> | |
| <div class="spacer"></div> | |
| <a href="/history" class="nav-btn" title="My History" style="text-decoration:none;"><i | |
| class="ph ph-clock-counter-clockwise"></i></a> | |
| {% if g.is_admin %} | |
| <a href="/admin" class="nav-btn" title="God Mode" | |
| style="text-decoration:none; color: var(--primary);"><i class="ph ph-shield-check"></i></a> | |
| {% endif %} | |
| </div> | |
| <div class="sidebar-bottom"> | |
| <button class="theme-toggle-btn" title="Toggle dark / light mode" onclick="toggleTheme()"> | |
| <i class="ph ph-moon icon-moon"></i> | |
| <i class="ph ph-sun icon-sun"></i> | |
| </button> | |
| <div class="profile-menu-container"> | |
| <div class="profile-btn" onclick="toggleProfileMenu()" title="{{ g.username }}" | |
| style="background: transparent; padding: 0; width: 44px; height: 44px;"> | |
| <img src="{{ url_for('static', filename='default_profile.svg') }}" alt="Profile" | |
| style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 2px solid var(--border-color); background: var(--bg-input);"> | |
| </div> | |
| <div class="profile-dropdown" id="profileDropdown"> | |
| <div class="dropdown-header"> | |
| <span class="dropdown-username">{{ g.username }}</span> | |
| </div> | |
| <a href="{{ url_for('auth.logout') }}" class="dropdown-item danger"> | |
| <i class="ph ph-sign-out"></i> Logout | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </aside> | |
| <!-- Main Content Area --> | |
| <main class="main-content"> | |
| <!-- Top Header --> | |
| <header class="top-header" style="opacity: 0.9;"> | |
| <div class="header-left"> | |
| <a href="/" style="text-decoration: none; color: inherit;"> | |
| <button class="assistant-selector"> | |
| <i class="ph ph-arrow-left"></i> Back to Search | |
| </button> | |
| </a> | |
| </div> | |
| <div class="header-center"> | |
| <span class="daily-text">Analysis Results</span> | |
| </div> | |
| <div class="header-right" style="display:flex; align-items:center; gap:1rem;"> | |
| </div> | |
| </header> | |
| <div class="results-scroll-area"> | |
| {% if result.success %} | |
| {% if result.verdict %} | |
| <div class="verdict-card {{ result.verdict|lower }}"> | |
| <span class="verdict-badge {{ result.verdict|lower }}">{{ result.verdict }}</span> | |
| <h2 class="verdict-title">"{{ result.claim }}"</h2> | |
| <div class="verdict-confidence" style="color: var(--text-muted); font-size: 0.95rem;"> | |
| Confidence: <strong style="color: inherit;">{{ (result.confidence * 100)|round(0) }}%</strong> | |
| <div class="verdict-confidence-bar"> | |
| <div class="verdict-confidence-fill" | |
| style="width: {{ (result.confidence * 100)|round(0) }}%;"></div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endif %} | |
| <!-- Evidence Section (always visible, full-width) --> | |
| <section class="evidence-section"> | |
| <div class="section-header"> | |
| <i class="ph-fill ph-magnifying-glass" style="font-size: 1.5rem; color: var(--primary);"></i> | |
| <h3 class="section-title">Evidence Sources</h3> | |
| <span | |
| style="margin-left: auto; font-size: 0.85rem; color: var(--text-light); font-weight: 500;"> | |
| {{ result.evidence|length }} found | |
| </span> | |
| </div> | |
| {% if result.evidence %} | |
| {% for evidence in result.evidence %} | |
| <div class="detail-item"> | |
| <div | |
| style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;"> | |
| <span class="source-tag">{{ evidence.source }}</span> | |
| <span style="font-size: 0.8rem; font-weight: 600; color: var(--text-light);"> | |
| <i class="ph-fill ph-target"></i> {{ (evidence.similarity * 100)|round(0) }}% match | |
| </span> | |
| </div> | |
| <p style="font-size: 0.95rem; color: var(--text-main); line-height: 1.6;">{{ evidence.text | |
| }}</p> | |
| </div> | |
| {% endfor %} | |
| {% else %} | |
| <div class="empty-glass"> | |
| <i class="ph ph-mask-sad" | |
| style="font-size: 3rem; margin-bottom: 1rem; color: var(--text-light);"></i> | |
| <p>No direct evidence found for this claim.</p> | |
| </div> | |
| {% endif %} | |
| </section> | |
| <!-- AI Analysis Toggle --> | |
| {% if result.nli_results %} | |
| <button class="ai-toggle-btn" id="aiToggleBtn" onclick="toggleAIAnalysis()"> | |
| <i class="ph-fill ph-brain"></i> | |
| <span id="aiToggleBtnText">Show AI Analysis</span> | |
| <i class="ph ph-caret-down toggle-chevron"></i> | |
| </button> | |
| <!-- AI Analysis Panel (hidden by default) --> | |
| <div class="ai-analysis-panel" id="aiAnalysisPanel"> | |
| <div class="section-header"> | |
| <i class="ph-fill ph-brain" style="font-size: 1.5rem; color: var(--primary);"></i> | |
| <h3 class="section-title">AI Analysis</h3> | |
| <span | |
| style="margin-left: auto; font-size: 0.85rem; color: var(--text-light); font-weight: 500;"> | |
| {{ result.nli_results|length }} analyses | |
| </span> | |
| </div> | |
| {% for nli in result.nli_results %} | |
| <div class="detail-item"> | |
| <div class="nli-status"> | |
| <span style="font-weight: 600; font-size: 0.9rem;" class="status-{{ nli.label|lower }}"> | |
| {% if 'entail' in nli.label|lower %} | |
| <i class="ph-fill ph-check-circle"></i> Supports | |
| {% elif 'contradict' in nli.label|lower %} | |
| <i class="ph-fill ph-x-circle"></i> Contradicts | |
| {% else %} | |
| <i class="ph-fill ph-minus-circle"></i> Neutral | |
| {% endif %} | |
| </span> | |
| <span style="font-size: 0.85rem; font-weight: 600; color: var(--text-light);"> | |
| {{ (nli.score * 100)|round(1) }}% | |
| </span> | |
| </div> | |
| <p style="font-size: 0.9rem; color: var(--text-muted); line-height: 1.5; margin-bottom: 0;"> | |
| {{ nli.evidence }} | |
| </p> | |
| <div class="nli-confidence-meter status-{{ nli.label|lower }}"> | |
| <div class="nli-meter-fill" style="width: {{ (nli.score * 100)|round(1) }}%;"></div> | |
| </div> | |
| </div> | |
| {% endfor %} | |
| </div> | |
| {% endif %} | |
| {% else %} | |
| <!-- Error State --> | |
| <div class="verdict-card false" style="max-width: 600px; margin: 4rem auto;"> | |
| <i class="ph-fill ph-warning-circle" | |
| style="font-size: 4rem; color: #ef4444; margin-bottom: 1rem;"></i> | |
| <h2 style="margin-bottom: 1rem; color: var(--text-main);">Analysis Failed</h2> | |
| <p style="color: var(--text-muted); margin-bottom: 2rem;">{{ result.error }}</p> | |
| <a href="/" | |
| style="display: inline-block; background: #0f172a; color: white; padding: 0.75rem 1.5rem; text-decoration: none; border-radius: var(--radius-sm); font-weight: 500;"> | |
| Try Again | |
| </a> | |
| </div> | |
| {% endif %} | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| function toggleTheme() { | |
| const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; | |
| if (isDark) { | |
| document.documentElement.removeAttribute('data-theme'); | |
| localStorage.setItem('proofly-theme', 'light'); | |
| } else { | |
| document.documentElement.setAttribute('data-theme', 'dark'); | |
| localStorage.setItem('proofly-theme', 'dark'); | |
| } | |
| } | |
| function toggleAIAnalysis() { | |
| const panel = document.getElementById('aiAnalysisPanel'); | |
| const btn = document.getElementById('aiToggleBtn'); | |
| const btnText = document.getElementById('aiToggleBtnText'); | |
| const isOpen = panel.classList.contains('visible'); | |
| if (isOpen) { | |
| panel.classList.remove('visible'); | |
| btn.classList.remove('open'); | |
| btnText.textContent = 'Show AI Analysis'; | |
| } else { | |
| panel.classList.add('visible'); | |
| btn.classList.add('open'); | |
| btnText.textContent = 'Hide AI Analysis'; | |
| } | |
| } | |
| function toggleProfileMenu() { | |
| const menu = document.getElementById('profileDropdown'); | |
| if (menu) menu.classList.toggle('open'); | |
| } | |
| document.addEventListener('click', (e) => { | |
| const container = document.querySelector('.profile-menu-container'); | |
| const menu = document.getElementById('profileDropdown'); | |
| if (container && menu && !container.contains(e.target)) { | |
| menu.classList.remove('open'); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |