phishguard-api / popup.html
prashanth135's picture
Upload 38 files
bebe233 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PhishGuard AI</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-primary: #0F0F14;
--bg-secondary: #1A1A24;
--bg-card: #22222E;
--bg-hover: #2A2A38;
--text-primary: #EAEAF0;
--text-secondary: #8888A0;
--text-muted: #5A5A72;
--accent: #534AB7;
--accent-glow: rgba(83, 74, 183, 0.35);
--safe: #22C55E;
--safe-glow: rgba(34, 197, 94, 0.25);
--danger: #EF4444;
--danger-glow: rgba(239, 68, 68, 0.25);
--warning: #F59E0B;
--warning-glow: rgba(245, 158, 11, 0.25);
--border: rgba(255,255,255,0.06);
--radius: 12px;
--radius-sm: 8px;
}
body {
width: 380px;
min-height: 480px;
max-height: 640px;
font-family: 'Inter', -apple-system, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--bg-hover) transparent;
}
body::-webkit-scrollbar { width: 4px; }
body::-webkit-scrollbar-thumb { background: var(--bg-hover); border-radius: 4px; }
/* ── Header ──────────────────────────────────────────── */
.header {
display: flex; align-items: center; gap: 10px;
padding: 14px 20px 10px;
border-bottom: 1px solid var(--border);
}
.header-logo {
width: 28px; height: 28px;
background: linear-gradient(135deg, var(--accent), #7C6BDB);
border-radius: var(--radius-sm);
display: flex; align-items: center; justify-content: center;
font-size: 14px;
}
.header h1 { font-size: 15px; font-weight: 700; letter-spacing: -0.3px; }
.header h1 span { color: var(--accent); }
.header-badge {
margin-left: auto;
font-size: 10px; padding: 3px 8px;
background: var(--bg-card); border: 1px solid var(--border);
border-radius: 20px; color: var(--text-secondary); font-weight: 500;
}
/* ── URL Bar ──────────────────────────────────────────── */
.url-bar {
padding: 8px 20px;
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
}
.url-text {
font-size: 11px; color: var(--text-muted);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
font-family: 'SF Mono', 'Fira Code', monospace;
}
/* ── Loading ──────────────────────────────────────────── */
.loading-container {
display: flex; flex-direction: column; align-items: center;
justify-content: center; padding: 40px 20px; gap: 14px;
}
.spinner {
width: 40px; height: 40px;
border: 3px solid var(--bg-hover);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text {
font-size: 13px; color: var(--text-secondary);
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.5; } }
/* ── Result Panel ────────────────────────────────────── */
.result-panel { padding: 16px 20px; }
.result-hero {
display: flex; align-items: center; gap: 16px;
margin-bottom: 16px;
}
.score-ring-wrap {
position: relative; width: 80px; height: 80px; flex-shrink: 0;
}
.score-ring-bg, .score-ring-fg {
fill: none; stroke-width: 6;
}
.score-ring-bg { stroke: var(--bg-hover); }
.score-ring-fg {
stroke-linecap: round;
transform: rotate(-90deg); transform-origin: center;
transition: stroke-dashoffset 1s ease, stroke 0.5s;
stroke-dasharray: 213; stroke-dashoffset: 213;
}
.score-label {
position: absolute; inset: 0;
display: flex; flex-direction: column;
align-items: center; justify-content: center;
}
.score-pct { font-size: 20px; font-weight: 700; line-height: 1; }
.score-sub {
font-size: 9px; color: var(--text-muted);
margin-top: 2px; text-transform: uppercase; letter-spacing: 0.5px;
}
.shield-icon {
font-size: 28px;
animation: shieldPop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes shieldPop {
0% { transform: scale(0.3) rotate(-15deg); opacity: 0; }
60% { transform: scale(1.15) rotate(3deg); }
100% { transform: scale(1) rotate(0); opacity: 1; }
}
.result-verdict { flex: 1; }
.verdict-label { font-size: 16px; font-weight: 700; line-height: 1.2; }
.verdict-detail { font-size: 11px; color: var(--text-secondary); margin-top: 3px; }
.status-safe { color: var(--safe); }
.status-danger { color: var(--danger); }
.status-warn { color: var(--warning); }
/* ── Tier Rows ───────────────────────────────────────── */
.tier-section { margin-top: 4px; }
.tier-row {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
margin-bottom: 5px; overflow: hidden;
transition: border-color 0.2s;
}
.tier-row:hover { border-color: rgba(255,255,255,0.1); }
.tier-header {
display: flex; align-items: center;
padding: 8px 12px; cursor: pointer;
user-select: none; gap: 8px;
}
.tier-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.tier-name { font-size: 11px; font-weight: 600; flex: 1; }
.tier-score {
font-size: 11px; font-weight: 600;
font-family: 'SF Mono', 'Fira Code', monospace;
}
.tier-chevron {
font-size: 9px; color: var(--text-muted);
transition: transform 0.2s;
}
.tier-row.open .tier-chevron { transform: rotate(180deg); }
.tier-body {
max-height: 0; overflow: hidden;
transition: max-height 0.3s ease; padding: 0 12px;
}
.tier-row.open .tier-body { max-height: 200px; padding: 4px 12px 10px; }
.tier-detail { font-size: 10px; color: var(--text-secondary); line-height: 1.6; }
.flag-badge {
display: inline-block; padding: 1px 6px;
background: rgba(239,68,68,0.12); color: var(--danger);
border-radius: 4px; font-size: 10px; margin: 2px 2px 2px 0;
}
/* ── Feedback Section ────────────────────────────────── */
.feedback-section {
padding: 0 20px 12px; margin-top: 8px;
}
.feedback-prompt {
font-size: 12px; color: var(--text-secondary);
margin-bottom: 8px; text-align: center;
}
.feedback-buttons { display: flex; gap: 8px; }
.fb-btn {
flex: 1; padding: 8px 0;
border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--bg-card); color: var(--text-primary);
font-size: 13px; font-weight: 600; cursor: pointer;
transition: all 0.2s; font-family: inherit;
}
.fb-btn:hover { background: var(--bg-hover); }
.fb-btn-correct:hover { border-color: var(--safe); background: rgba(34,197,94,0.08); }
.fb-btn-wrong:hover { border-color: var(--danger); background: rgba(239,68,68,0.08); }
.fb-btn.selected {
opacity: 1 !important;
}
.fb-btn.dimmed {
opacity: 0.3; pointer-events: none;
}
.fb-btn-correct.selected {
border-color: var(--safe); background: rgba(34,197,94,0.15);
color: var(--safe);
}
.fb-btn-wrong.selected {
border-color: var(--danger); background: rgba(239,68,68,0.15);
color: var(--danger);
}
.thank-you {
display: none; text-align: center; padding: 10px;
font-size: 12px; color: var(--safe);
animation: slideDown 0.3s ease;
}
.thank-you.show { display: block; }
@keyframes slideDown {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
/* ── Retraining Status ───────────────────────────────── */
.retrain-section {
padding: 8px 20px 12px;
border-top: 1px solid var(--border);
margin-top: 4px;
}
.retrain-row {
display: flex; align-items: center; gap: 6px;
font-size: 11px; color: var(--text-muted);
margin-bottom: 4px;
}
.retrain-row .icon { font-size: 12px; }
.retrain-progress {
height: 3px; background: var(--bg-hover);
border-radius: 2px; margin: 6px 0 4px;
overflow: hidden;
}
.retrain-progress-bar {
height: 100%; background: linear-gradient(90deg, var(--accent), #7C6BDB);
border-radius: 2px; transition: width 0.5s ease;
}
/* ── Session Stats ───────────────────────────────────── */
.stats-row {
display: flex; justify-content: space-between;
padding: 6px 20px;
border-top: 1px solid var(--border);
font-size: 11px; color: var(--text-muted);
}
/* ── Blocked Overlay ─────────────────────────────────── */
.blocked-overlay {
display: none; padding: 28px 24px; text-align: center;
}
.blocked-overlay.show {
display: flex; flex-direction: column;
align-items: center; gap: 10px;
}
.blocked-shield { font-size: 48px; animation: shieldPop 0.6s ease; }
.blocked-title { font-size: 18px; font-weight: 700; color: var(--danger); }
.blocked-url {
font-size: 11px; color: var(--text-muted);
word-break: break-all; max-width: 300px;
}
.blocked-method { font-size: 12px; color: var(--text-secondary); }
.proceed-btn {
margin-top: 8px; padding: 8px 20px;
background: transparent; border: 1px solid rgba(239,68,68,0.3);
border-radius: var(--radius-sm); color: var(--text-secondary);
font-size: 12px; cursor: pointer; font-family: inherit;
transition: all 0.2s;
}
.proceed-btn:hover {
background: rgba(239,68,68,0.08);
border-color: var(--danger); color: var(--danger);
}
.offline-banner {
display: none; padding: 8px 16px;
background: rgba(245, 158, 11, 0.08);
border: 1px solid rgba(245, 158, 11, 0.2);
border-radius: var(--radius-sm);
margin: 8px 20px 0; font-size: 11px;
color: var(--warning); text-align: center;
}
.offline-banner.show { display: block; }
</style>
</head>
<body>
<!-- Header -->
<div class="header">
<div class="header-logo">πŸ›‘οΈ</div>
<h1>Phish<span>Guard</span> AI</h1>
<span class="header-badge" id="versionBadge">v3.0</span>
</div>
<!-- URL Bar -->
<div class="url-bar">
<div class="url-text" id="currentUrl">Analyzing...</div>
</div>
<!-- Server offline banner -->
<div class="offline-banner" id="offlineBanner">
⚠️ Server offline β€” local heuristic only
</div>
<!-- Loading State -->
<div class="loading-container" id="loadingState">
<div class="spinner"></div>
<div class="loading-text">Analyzing with AI ensemble...</div>
</div>
<!-- Result Panel -->
<div class="result-panel" id="resultPanel" style="display:none;">
<div class="result-hero">
<div class="score-ring-wrap">
<svg width="80" height="80" viewBox="0 0 80 80">
<circle class="score-ring-bg" cx="40" cy="40" r="34" />
<circle class="score-ring-fg" id="scoreRing" cx="40" cy="40" r="34" />
</svg>
<div class="score-label">
<div class="score-pct" id="scorePct">0%</div>
<div class="score-sub" id="scoreSub">RISK</div>
</div>
</div>
<div style="display:flex; flex-direction:column; align-items:center; gap:4px">
<div class="shield-icon" id="shieldIcon">πŸ›‘οΈ</div>
</div>
<div class="result-verdict">
<div class="verdict-label" id="verdictLabel">Analyzing</div>
<div class="verdict-detail" id="verdictDetail">Please wait...</div>
</div>
</div>
<!-- Tier Rows -->
<div class="tier-section" id="tierSection">
<div class="tier-row" data-tier="1">
<div class="tier-header" onclick="toggleTier(this)">
<div class="tier-dot" id="t1Dot"></div>
<div class="tier-name">Tier 1 Β· Whitelist</div>
<div class="tier-score" id="t1Score">β€”</div>
<div class="tier-chevron">β–Ό</div>
</div>
<div class="tier-body"><div class="tier-detail" id="t1Detail">O(1) domain lookup</div></div>
</div>
<div class="tier-row" data-tier="2">
<div class="tier-header" onclick="toggleTier(this)">
<div class="tier-dot" id="t2Dot"></div>
<div class="tier-name">Tier 2 Β· Heuristics</div>
<div class="tier-score" id="t2Score">β€”</div>
<div class="tier-chevron">β–Ό</div>
</div>
<div class="tier-body"><div class="tier-detail" id="t2Detail">15 regex/math signals</div></div>
</div>
<div class="tier-row" data-tier="3">
<div class="tier-header" onclick="toggleTier(this)">
<div class="tier-dot" id="t3Dot"></div>
<div class="tier-name">Tier 3 Β· BERT + GNN</div>
<div class="tier-score" id="t3Score">β€”</div>
<div class="tier-chevron">β–Ό</div>
</div>
<div class="tier-body"><div class="tier-detail" id="t3Detail">Parallel NLP + graph analysis</div></div>
</div>
<div class="tier-row" data-tier="4">
<div class="tier-header" onclick="toggleTier(this)">
<div class="tier-dot" id="t4Dot"></div>
<div class="tier-name">Tier 4 Β· CNN Visual</div>
<div class="tier-score" id="t4Score">β€”</div>
<div class="tier-chevron">β–Ό</div>
</div>
<div class="tier-body"><div class="tier-detail" id="t4Detail">Screenshot + brand detection</div></div>
</div>
</div>
</div>
<!-- Feedback Section -->
<div class="feedback-section" id="feedbackSection" style="display:none;">
<div class="feedback-prompt">Was this correct?</div>
<div class="feedback-buttons">
<button class="fb-btn fb-btn-correct" id="btnCorrect">πŸ‘ Correct</button>
<button class="fb-btn fb-btn-wrong" id="btnWrong">πŸ‘Ž Incorrect</button>
</div>
<div class="thank-you" id="thankYou">βœ“ Thanks! Helps us improve 🎯</div>
</div>
<!-- Retraining Status -->
<div class="retrain-section" id="retrainSection" style="display:none;">
<div class="retrain-row">
<span class="icon">πŸ”„</span>
<span id="retrainStatus">Next retrain: calculating...</span>
</div>
<div class="retrain-progress">
<div class="retrain-progress-bar" id="retrainProgressBar" style="width: 0%"></div>
</div>
<div class="retrain-row">
<span class="icon">πŸ“ˆ</span>
<span id="retrainLast">No retraining yet</span>
</div>
</div>
<!-- Session Stats -->
<div class="stats-row" id="statsRow" style="display:none;">
<span id="statScanned">πŸ“Š 0 scanned</span>
<span id="statFeedback">πŸ’¬ 0 feedback</span>
<span id="statVersion">🏷️ v0</span>
</div>
<!-- Blocked Page Overlay -->
<div class="blocked-overlay" id="blockedOverlay">
<div class="blocked-shield">🚨</div>
<div class="blocked-title">Phishing Detected!</div>
<div class="blocked-url" id="blockedUrl"></div>
<div class="blocked-method" id="blockedMethod"></div>
<button class="proceed-btn" id="proceedBtn">Proceed Anyway (Unsafe)</button>
</div>
<script src="popup.js"></script>
</body>
</html>