From 8fc6c8530c3a9e8b326ebf35c429f4d43959d2ec Mon Sep 17 00:00:00 2001 From: Naitik Date: Mon, 29 Jun 2026 11:59:02 +0530 Subject: [PATCH] feat: add QR code scanner with file upload and camera support --- index.html | 97 +++- script.js | 380 ++++++++------- style.css | 1335 ++++++---------------------------------------------- 3 files changed, 433 insertions(+), 1379 deletions(-) diff --git a/index.html b/index.html index 7033a3a..3fd6a34 100644 --- a/index.html +++ b/index.html @@ -55,13 +55,15 @@

CyberSheild URL Scanner

+ +
- +
+
Try: google.com @@ -79,6 +82,92 @@

CyberSheild URL Scanner

malware test
+ +
or scan a QR code
+ +
+

+ + QR code scanner +

+ +
+ +

Drop a QR code image here, or click to upload

+ Supports PNG, JPG, GIF, WebP +
+ + +
+
+ + + + + + + + + + + + + + + +
+ +
@@ -134,8 +223,6 @@

CyberSheild URL Scanner

- - @@ -169,6 +256,8 @@

Resources

+ + - + \ No newline at end of file diff --git a/script.js b/script.js index 2c902f0..70fb1b1 100644 --- a/script.js +++ b/script.js @@ -1,215 +1,245 @@ // ═══════════════════════════════════ -// THEME — init immediately to prevent flash +// QR CODE SCANNER // ═══════════════════════════════════ -(function initTheme() { - const saved = localStorage.getItem('theme') || 'dark'; - if (saved === 'light') { - document.documentElement.classList.add('light-mode'); + +let cameraStream = null; +let cameraAnimFrame = null; + +// ── Helpers ── + +function showQRError(msg) { + const el = document.getElementById('qrError'); + if (!el) return; + el.textContent = msg; + el.classList.remove('hidden'); +} + +function hideQRError() { + const el = document.getElementById('qrError'); + if (el) el.classList.add('hidden'); +} + +function resetQRDecoded() { + const decoded = document.getElementById('qrDecoded'); + const preview = document.getElementById('qrPreview'); + if (decoded) decoded.classList.add('hidden'); + if (preview) { preview.src = ''; preview.classList.add('hidden'); } +} + +// ── Core decode ── + +function decodeQRFromImageData(imageData, width, height) { + if (typeof jsQR === 'undefined') { + showQRError('QR library not loaded. Check your internet connection and reload.'); + return null; } -})(); + return jsQR(imageData, width, height); +} -// ═══════════════════════════════════ -// LOADER — skip on back-navigation -// ═══════════════════════════════════ -window.addEventListener('load', () => { - const loader = document.getElementById('loader'); - const main = document.getElementById('mainPage'); - if (!loader || !main) return; - - if (sessionStorage.getItem('introShown')) { - loader.style.display = 'none'; - main.classList.remove('hidden'); - } else { - setTimeout(() => { - loader.classList.add('fade-out'); - setTimeout(() => { - loader.style.display = 'none'; - main.classList.remove('hidden'); - sessionStorage.setItem('introShown', 'true'); - }, 500); - }, 3200); +function processQRResult(data) { + hideQRError(); + const decoded = document.getElementById('qrDecoded'); + const urlEl = document.getElementById('qrDecodedUrl'); + const inputEl = document.getElementById('urlInput'); + + if (!data) { + showQRError('No QR code detected. Try a clearer or higher-resolution image.'); + if (decoded) decoded.classList.add('hidden'); + return; } -}); - -// Handle bfcache (back/forward navigation) -window.addEventListener('pageshow', (e) => { - if (e.persisted) { - const loader = document.getElementById('loader'); - const main = document.getElementById('mainPage'); - if (loader) loader.style.display = 'none'; - if (main) { main.classList.remove('hidden'); main.style.opacity = '1'; } + + if (urlEl) urlEl.textContent = data; + if (decoded) decoded.classList.remove('hidden'); + + // Auto-fill the URL input if it looks like a URL + if (inputEl && (data.startsWith('http://') || data.startsWith('https://'))) { + inputEl.value = data; } -}); +} -// ═══════════════════════════════════ -// THEME TOGGLE -// ═══════════════════════════════════ -document.addEventListener('DOMContentLoaded', () => { - const btn = document.getElementById('themeToggle'); - if (btn) { - btn.addEventListener('click', () => { - const isLight = document.documentElement.classList.toggle('light-mode'); - localStorage.setItem('theme', isLight ? 'light' : 'dark'); - }); +function decodeQRFromImage(img) { + const canvas = document.createElement('canvas'); + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(img, 0, 0); + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const code = decodeQRFromImageData(imageData.data, canvas.width, canvas.height); + processQRResult(code ? code.data : null); +} + +// ── File upload ── + +function handleQRFileInput(event) { + const file = event.target.files[0]; + if (!file) return; + + if (!file.type.startsWith('image/')) { + showQRError('Please upload a valid image file (PNG, JPG, GIF, WebP).'); + return; } - // Build team grid - buildTeam(); + hideQRError(); + resetQRDecoded(); - // Load persisted session stats - loadStats(); -}); + const reader = new FileReader(); + reader.onload = function (e) { + const preview = document.getElementById('qrPreview'); + if (preview) { + preview.src = e.target.result; + preview.classList.remove('hidden'); + } -// ═══════════════════════════════════ -// TEAM -// ═══════════════════════════════════ -const team = [ - { name: "Mrinal Roy", img: "Mrinal.jpg" }, - { name: "Rahul Sah", img: "Rahul.jpg" }, - { name: "Swastika Shaw", img: "Swastika.jpg" }, - { name: "Arpita Roy", img: "Arpita.jpg" }, - { name: "Disha Samanta", img: "Disha.jpg" }, -]; - -function buildTeam() { - const grid = document.getElementById('teamGrid'); - if (!grid) return; - grid.innerHTML = team.map(m => { - const initials = m.name.split(' ').map(w => w[0]).join(''); - return ` -
-
- ${m.name} -
-
${m.name}
-
`; - }).join(''); + const img = new Image(); + img.onload = () => decodeQRFromImage(img); + img.onerror = () => showQRError('Could not load image. Please try a different file.'); + img.src = e.target.result; + }; + reader.readAsDataURL(file); + + // Reset so the same file can be re-selected + event.target.value = ''; } -let teamOpen = false; +// ── Drag and drop ── -function toggleTeam() { - teamOpen = !teamOpen; - const wrap = document.getElementById('teamGridWrap'); - const toggle = document.getElementById('teamToggle'); - if (!wrap || !toggle) return; - wrap.classList.toggle('open', teamOpen); - toggle.classList.toggle('open', teamOpen); - toggle.setAttribute('aria-label', teamOpen ? 'Hide team' : 'Show team'); +function handleQRDragOver(event) { + event.preventDefault(); + const zone = document.getElementById('qrDropZone'); + if (zone) zone.classList.add('drag-over'); } -// ═══════════════════════════════════ -// SCANNER -// ═══════════════════════════════════ -let totalScans = 0, safeCount = 0, dangerCount = 0; - -function loadStats() { - const history = JSON.parse(localStorage.getItem('cybershield_history') || '[]'); - totalScans = history.length; - safeCount = history.filter(r => r.status === 'safe').length; - dangerCount = history.filter(r => r.status === 'danger').length; - renderStats(); +function handleQRDragLeave(event) { + const zone = document.getElementById('qrDropZone'); + if (zone) zone.classList.remove('drag-over'); } -function renderStats() { - const t = document.getElementById('totalScans'); - const s = document.getElementById('safeCount'); - const d = document.getElementById('dangerCount'); - if (t) t.textContent = totalScans; - if (s) s.textContent = safeCount; - if (d) d.textContent = dangerCount; -} +function handleQRDrop(event) { + event.preventDefault(); + const zone = document.getElementById('qrDropZone'); + if (zone) zone.classList.remove('drag-over'); + + const file = event.dataTransfer.files[0]; + if (!file) return; -function fillExample(url) { - const input = document.getElementById('urlInput'); - if (input) { input.value = url; input.focus(); } + if (!file.type.startsWith('image/')) { + showQRError('Please drop a valid image file (PNG, JPG, GIF, WebP).'); + return; + } + + // Reuse file handler via fake event object + handleQRFileInput({ target: { files: [file], value: '' } }); } -function saveToHistory(url, status, threats) { - const history = JSON.parse(localStorage.getItem('cybershield_history') || '[]'); - history.push({ url, status, threats, timestamp: new Date().toISOString() }); - localStorage.setItem('cybershield_history', JSON.stringify(history)); +// ── Scan extracted URL ── + +function scanExtractedQRUrl() { + const urlEl = document.getElementById('qrDecodedUrl'); + const inputEl = document.getElementById('urlInput'); + if (!urlEl) return; + + const url = urlEl.textContent.trim(); + if (!url) return; + + if (inputEl) inputEl.value = url; + checkSecurity(); + + // Scroll result into view + setTimeout(() => { + const result = document.getElementById('result'); + if (result) result.scrollIntoView({ behavior: 'smooth', block: 'center' }); + }, 100); } -function showResult(type, title, desc, url, threats) { - const el = document.getElementById('result'); - if (!el) return; - el.innerHTML = ` -
-
- ${type === 'loading' - ? '
' - : `${type === 'safe' ? '✓' : type === 'danger' ? '✕' : '!'}`} -
-
-
${title}
-
${desc}
- ${url ? `
${url}
` : ''} - ${threats && threats.length - ? `
${threats.map(t => `${t}`).join('')}
` - : ''} -
-
`; +// ── Camera ── + +function toggleCamera() { + if (cameraStream) { + stopCamera(); + } else { + startCamera(); + } } -async function checkSecurity() { - const input = document.getElementById('urlInput'); - const btn = document.getElementById('scanBtn'); - const text = input ? input.value.trim() : ''; +async function startCamera() { + hideQRError(); + resetQRDecoded(); - if (!text) { - showResult('error', 'Enter a URL', 'Paste the URL you want to check above.', '', []); + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + showQRError('Camera not supported in this browser. Please upload an image instead.'); return; } - let url = text; - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = 'https://' + url; - } + try { + cameraStream = await navigator.mediaDevices.getUserMedia({ + video: { facingMode: 'environment' } + }); - if (btn) btn.disabled = true; - showResult('loading', 'Scanning...', 'Checking against threat databases…', url, []); + const video = document.getElementById('cameraFeed'); + const section = document.getElementById('cameraSection'); + const btn = document.getElementById('cameraBtn'); - try { - const apiHost = 'https://cybershield-30a3.onrender.com'; + if (video) { video.srcObject = cameraStream; } + if (section) section.classList.remove('hidden'); + if (btn) btn.textContent = 'Stop camera'; - const response = await fetch(`${apiHost}/check`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ url }) - }); + scanCameraFrame(); - if (!response.ok) throw new Error(`Server error ${response.status}`); - const data = await response.json(); - console.log("API Response:", data); - if (data.error) throw new Error(data.error); - - if (data.matches && data.matches.length > 0) { - const threats = [...new Set(data.matches.map(m => m.threatType.replace(/_/g, ' ')))]; - totalScans++; dangerCount++; - renderStats(); - saveToHistory(url, 'danger', data.matches.map(m => m.threatType)); - showResult('danger', '⚠ Threat Detected', 'This URL is flagged as dangerous. Do not visit it.', url, threats); + } catch (err) { + cameraStream = null; + if (err.name === 'NotAllowedError') { + showQRError('Camera permission denied. Please allow camera access or upload an image instead.'); } else { - totalScans++; safeCount++; - renderStats(); - saveToHistory(url, 'safe', []); - showResult('safe', '✓ URL is Safe', 'No known threats detected via Google Safe Browsing.', url, []); + showQRError('Camera unavailable. Please upload a QR code image instead.'); } + } +} - } catch (err) { - showResult('error', 'Backend Not Connected', - `Ensure your backend server is running.
Error: ${err.message}`, '', []); - } finally { - if (btn) btn.disabled = false; +function stopCamera() { + if (cameraStream) { + cameraStream.getTracks().forEach(t => t.stop()); + cameraStream = null; + } + if (cameraAnimFrame) { + cancelAnimationFrame(cameraAnimFrame); + cameraAnimFrame = null; + } + + const section = document.getElementById('cameraSection'); + const btn = document.getElementById('cameraBtn'); + const video = document.getElementById('cameraFeed'); + + if (section) section.classList.add('hidden'); + if (video) video.srcObject = null; + if (btn) { + btn.innerHTML = ` + + Use camera`; } } -document.addEventListener('DOMContentLoaded', () => { - const input = document.getElementById('urlInput'); - if (input) { - input.addEventListener('keydown', e => { - if (e.key === 'Enter') checkSecurity(); - }); +function scanCameraFrame() { + const video = document.getElementById('cameraFeed'); + const canvas = document.getElementById('cameraCanvas'); + if (!video || !canvas || !cameraStream) return; + + if (video.readyState === video.HAVE_ENOUGH_DATA) { + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const code = decodeQRFromImageData(imageData.data, canvas.width, canvas.height); + + if (code) { + stopCamera(); + processQRResult(code.data); + return; // QR found — stop scanning + } } -}); + + cameraAnimFrame = requestAnimationFrame(scanCameraFrame); +} \ No newline at end of file diff --git a/style.css b/style.css index 49e1885..537b168 100644 --- a/style.css +++ b/style.css @@ -1,1297 +1,232 @@ /* ════════════════════════════════════════ - CYBERSHIELD — DESIGN SYSTEM - Signature: Sonar-pulse scanner hero rings + QR CODE SCANNER ════════════════════════════════════════ */ -/* ── Reset ── */ -*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } - -/* ── CSS Variables — Dark (default) ── */ -:root { - --bg: #020818; - --bg-2: #060f2a; - --bg-3: #0a1638; - --card-bg: rgba(10, 22, 56, 0.7); - --card-border: rgba(0, 245, 255, 0.12); - --card-hover: rgba(0, 245, 255, 0.06); - --text: #e8f4ff; - --text-muted: #4a6580; - --text-sub: #8aa4c0; - --accent-cyan: #00f5ff; - --accent-green: #00ff88; - --accent-red: #ff3366; - --accent-yellow: #ffcc00; - --gradient-main: linear-gradient(135deg, #00f5ff 0%, #00ff88 100%); - --gradient-danger:linear-gradient(135deg, #ff3366 0%, #ff8c42 100%); - --input-bg: rgba(6, 15, 42, 0.9); - --input-border: rgba(0, 245, 255, 0.2); - --input-focus: rgba(0, 245, 255, 0.4); - --safe-bg: rgba(0, 255, 136, 0.08); - --safe-border: rgba(0, 255, 136, 0.3); - --safe-color: #00ff88; - --danger-bg: rgba(255, 51, 102, 0.08); - --danger-border: rgba(255, 51, 102, 0.3); - --danger-color: #ff3366; - --warn-bg: rgba(255, 204, 0, 0.08); - --warn-border: rgba(255, 204, 0, 0.3); - --warn-color: #ffcc00; - --shadow-glow: 0 0 40px rgba(0, 245, 255, 0.08); - --shadow-card: 0 4px 32px rgba(0, 0, 0, 0.5); - --radius-card: 20px; - --radius-sm: 12px; - --radius-pill: 100px; -} - -/* ── Light Mode ── */ -html.light-mode { - --bg: #f0f4ff; - --bg-2: #e4ecff; - --bg-3: #d8e5ff; - --card-bg: rgba(255, 255, 255, 0.85); - --card-border: rgba(0, 120, 200, 0.15); - --card-hover: rgba(0, 120, 200, 0.05); - --text: #0a1638; - --text-muted: #8099b8; - --text-sub: #3a5580; - --accent-cyan: #0070cc; - --accent-green: #008844; - --accent-red: #cc2244; - --accent-yellow: #bb8800; - --gradient-main: linear-gradient(135deg, #0070cc 0%, #008844 100%); - --gradient-danger:linear-gradient(135deg, #cc2244 0%, #cc5500 100%); - --input-bg: rgba(255, 255, 255, 0.9); - --input-border: rgba(0, 112, 204, 0.25); - --input-focus: rgba(0, 112, 204, 0.4); - --safe-bg: rgba(0, 136, 68, 0.08); - --safe-border: rgba(0, 136, 68, 0.25); - --safe-color: #008844; - --danger-bg: rgba(204, 34, 68, 0.08); - --danger-border: rgba(204, 34, 68, 0.25); - --danger-color: #cc2244; - --warn-bg: rgba(187, 136, 0, 0.08); - --warn-border: rgba(187, 136, 0, 0.25); - --warn-color: #bb8800; - --shadow-glow: 0 0 40px rgba(0, 112, 204, 0.08); - --shadow-card: 0 4px 24px rgba(0, 0, 0, 0.1); -} - -/* ── Base ── */ -html { scroll-behavior: smooth; } - -body { - background: var(--bg); - color: var(--text); - font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; - min-height: 100vh; - overflow-x: hidden; - transition: background 0.3s, color 0.3s; -} - -/* Grid texture */ -body::before { - content: ''; - position: fixed; - inset: 0; - background-image: - linear-gradient(rgba(0,245,255,0.025) 1px, transparent 1px), - linear-gradient(90deg, rgba(0,245,255,0.025) 1px, transparent 1px); - background-size: 48px 48px; - pointer-events: none; - z-index: 0; - transition: opacity 0.3s; -} - -html.light-mode body::before { - background-image: - linear-gradient(rgba(0,112,204,0.04) 1px, transparent 1px), - linear-gradient(90deg, rgba(0,112,204,0.04) 1px, transparent 1px); -} - -.hidden { display: none !important; } - -/* ════════════════════════════════════════ - LOADER -════════════════════════════════════════ */ -#loader { - position: fixed; - inset: 0; - background: var(--bg); - display: flex; - align-items: center; - justify-content: center; - z-index: 9999; -} - -.loader-inner { +.qr-divider { display: flex; - flex-direction: column; align-items: center; - gap: 18px; - animation: fadeInUp 0.5s ease; -} - -.loader-sonar { - position: relative; - width: 100px; - height: 100px; - display: flex; - align-items: center; - justify-content: center; + gap: 12px; + margin: 24px 0 16px; } -.loader-sonar::before, -.loader-sonar::after { +.qr-divider::before, +.qr-divider::after { content: ''; - position: absolute; - border-radius: 50%; - border: 1px solid var(--accent-cyan); - animation: sonarPing 2s ease-out infinite; -} - -.loader-sonar::before { width: 100%; height: 100%; animation-delay: 0s; } -.loader-sonar::after { width: 70%; height: 70%; animation-delay: 0.5s; } - -@keyframes sonarPing { - 0% { transform: scale(0.8); opacity: 0.8; } - 100% { transform: scale(1.4); opacity: 0; } -} - -.loader-core { - width: 52px; - height: 52px; - border-radius: 50%; - background: rgba(0, 245, 255, 0.08); - border: 2px solid var(--accent-cyan); - display: flex; - align-items: center; - justify-content: center; - position: relative; - z-index: 1; - box-shadow: 0 0 20px rgba(0,245,255,0.3); - animation: corePulse 2s ease-in-out infinite; -} - -@keyframes corePulse { - 0%, 100% { box-shadow: 0 0 20px rgba(0,245,255,0.3); } - 50% { box-shadow: 0 0 40px rgba(0,245,255,0.6); } -} - -.loader-brand { - font-size: 20px; - font-weight: 800; - letter-spacing: 0.15em; - text-transform: uppercase; - background: var(--gradient-main); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; + flex: 1; + height: 1px; + background: var(--card-border); } -.loader-tagline { - font-size: 12px; +.qr-divider span { + font-size: 11px; color: var(--text-muted); - letter-spacing: 0.1em; text-transform: uppercase; + letter-spacing: 0.1em; + white-space: nowrap; } -.loader-bar-wrap { - width: 200px; - height: 2px; - background: rgba(0, 245, 255, 0.1); - border-radius: 100px; - overflow: hidden; -} - -.loader-bar { - height: 100%; - background: var(--gradient-main); - border-radius: 100px; - animation: loadBar 3s cubic-bezier(0.4, 0, 0.2, 1) forwards; -} - -@keyframes loadBar { - 0% { width: 0%; } - 30% { width: 45%; } - 70% { width: 80%; } - 100% { width: 100%; } -} - -#loader.fade-out { - animation: loaderFade 0.5s ease forwards; -} - -@keyframes loaderFade { to { opacity: 0; pointer-events: none; } } - -/* ════════════════════════════════════════ - LAYOUT -════════════════════════════════════════ */ -#mainPage { - animation: pageIn 0.6s ease; - position: relative; - z-index: 1; -} - -@keyframes pageIn { - from { opacity: 0; transform: translateY(16px); } - to { opacity: 1; transform: translateY(0); } -} - -@keyframes fadeInUp { - from { opacity: 0; transform: translateY(20px); } - to { opacity: 1; transform: translateY(0); } -} - -.wrapper { - position: relative; - z-index: 1; - max-width: 820px; - margin: 0 auto; - padding: 60px 24px 80px; -} - -/* ════════════════════════════════════════ - THEME TOGGLE -════════════════════════════════════════ */ -.theme-toggle-btn { - position: fixed; - top: 20px; - right: 20px; - z-index: 1000; - width: 44px; - height: 44px; - border-radius: 50%; - background: var(--card-bg); +.qr-section { border: 1px solid var(--card-border); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - backdrop-filter: blur(12px); - transition: all 0.2s; - color: var(--text); -} - -.theme-toggle-btn:hover { - border-color: var(--accent-cyan); - box-shadow: 0 0 16px rgba(0,245,255,0.2); -} - -.toggle-icon-wrap { - position: relative; - width: 20px; - height: 20px; + border-radius: var(--radius-sm); + padding: 20px; + background: rgba(0, 245, 255, 0.02); + transition: border-color 0.2s; } -.sun-icon, .moon-icon { - position: absolute; - inset: 0; - width: 100%; - height: 100%; - transition: opacity 0.3s, transform 0.3s; +.qr-section:focus-within { + border-color: rgba(0, 245, 255, 0.2); } -html:not(.light-mode) .sun-icon { opacity: 0; transform: rotate(90deg) scale(0.5); } -html:not(.light-mode) .moon-icon { opacity: 1; transform: rotate(0deg) scale(1); } -html.light-mode .sun-icon { opacity: 1; transform: rotate(0deg) scale(1); } -html.light-mode .moon-icon { opacity: 0; transform: rotate(-90deg) scale(0.5); } - -/* ════════════════════════════════════════ - HEADER -════════════════════════════════════════ */ -.header { - text-align: center; - margin-bottom: 56px; +html.light-mode .qr-section { + background: rgba(0, 112, 204, 0.02); } -.logo-badge { - display: inline-flex; +.qr-section-title { + display: flex; align-items: center; gap: 8px; - background: rgba(0, 245, 255, 0.06); - border: 1px solid rgba(0, 245, 255, 0.2); - border-radius: var(--radius-pill); - padding: 6px 18px; font-size: 11px; font-weight: 700; - letter-spacing: 0.14em; - text-transform: uppercase; - color: var(--accent-cyan); - margin-bottom: 28px; -} - -html.light-mode .logo-badge { - background: rgba(0, 112, 204, 0.06); - border-color: rgba(0, 112, 204, 0.2); -} - -.logo-badge .dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--accent-cyan); - animation: statusPulse 2s infinite; -} - -@keyframes statusPulse { - 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(0,245,255,0.4); } - 50% { opacity: 0.6; box-shadow: 0 0 0 4px rgba(0,245,255,0); } -} - -h1 { - font-size: clamp(32px, 5vw, 54px); - font-weight: 800; - letter-spacing: -0.04em; - line-height: 1.05; - margin-bottom: 16px; - color: var(--text); -} - -h1 span { - background: var(--gradient-main); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; -} - -.subtitle { - font-size: 15px; - color: var(--text-sub); - max-width: 460px; - margin: 0 auto; - line-height: 1.7; -} - -/* ════════════════════════════════════════ - SCANNER CARD — SONAR HERO -════════════════════════════════════════ */ -.scanner-card { - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: var(--radius-card); - padding: 36px; - margin-bottom: 20px; - backdrop-filter: blur(20px); - box-shadow: var(--shadow-card), var(--shadow-glow); - position: relative; - overflow: hidden; -} - -.scanner-card::before { - content: ''; - position: absolute; - top: -60px; - right: -60px; - width: 200px; - height: 200px; - border-radius: 50%; - border: 1px solid var(--accent-cyan); - opacity: 0.06; - pointer-events: none; -} - -.scanner-card::after { - content: ''; - position: absolute; - top: -30px; - right: -30px; - width: 120px; - height: 120px; - border-radius: 50%; - border: 1px solid var(--accent-cyan); - opacity: 0.1; - pointer-events: none; -} - -.input-row { - display: flex; - gap: 12px; - align-items: stretch; -} - -.input-wrap { - flex: 1; - position: relative; -} - -.input-icon { - position: absolute; - left: 16px; - top: 50%; - transform: translateY(-50%); - width: 18px; - height: 18px; color: var(--text-muted); - pointer-events: none; -} - -input[type="text"] { - width: 100%; - background: var(--input-bg); - border: 1px solid var(--input-border); - border-radius: var(--radius-sm); - padding: 16px 16px 16px 48px; - font-size: 14px; - color: var(--text); - outline: none; - transition: border-color 0.2s, background 0.2s, box-shadow 0.2s; - font-family: 'Courier New', monospace; -} - -input[type="text"]::placeholder { color: var(--text-muted); } - -input[type="text"]:focus { - border-color: var(--input-focus); - box-shadow: 0 0 0 3px rgba(0,245,255,0.08); -} - -html.light-mode input[type="text"]:focus { - box-shadow: 0 0 0 3px rgba(0,112,204,0.1); -} - -/* ── Scan Button ── */ -.scan-btn { - background: var(--gradient-main); - border: none; - border-radius: var(--radius-sm); - padding: 16px 28px; - font-size: 14px; - font-weight: 700; - color: #020818; - cursor: pointer; - white-space: nowrap; - display: inline-flex; - align-items: center; - gap: 8px; - transition: transform 0.15s, opacity 0.15s, box-shadow 0.2s; - text-decoration: none; - letter-spacing: 0.02em; -} - -.scan-btn:hover { - transform: translateY(-2px); - box-shadow: 0 8px 24px rgba(0,245,255,0.25); -} - -.scan-btn:active { transform: translateY(0); } -.scan-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } - -/* ── Example chips ── */ -.examples { - margin-top: 18px; - display: flex; - gap: 8px; - flex-wrap: wrap; - align-items: center; -} - -.examples-label { font-size: 11px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.1em; } - -.example-chip { - background: transparent; - border: 1px solid var(--card-border); - border-radius: 8px; - padding: 5px 12px; - font-size: 12px; - color: var(--text-sub); - cursor: pointer; - transition: all 0.15s; - font-family: 'Courier New', monospace; + text-transform: uppercase; + letter-spacing: 0.12em; + margin-bottom: 14px; } -.example-chip:hover { - background: rgba(0,245,255,0.06); +.qr-section-title svg { color: var(--accent-cyan); - border-color: rgba(0,245,255,0.3); -} - -/* ════════════════════════════════════════ - RESULT CARD -════════════════════════════════════════ */ -#result { margin-top: 24px; } - -.result-card { - border-radius: 16px; - padding: 24px; - display: flex; - align-items: flex-start; - gap: 16px; - animation: slideUp 0.3s ease; -} - -@keyframes slideUp { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -} - -.result-card.safe { background: var(--safe-bg); border: 1px solid var(--safe-border); } -.result-card.danger { background: var(--danger-bg); border: 1px solid var(--danger-border); } -.result-card.loading { background: rgba(0,245,255,0.04); border: 1px solid rgba(0,245,255,0.15); } -.result-card.error { background: var(--warn-bg); border: 1px solid var(--warn-border); } - -.result-icon { - width: 48px; - height: 48px; - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; - font-size: 22px; flex-shrink: 0; } -.result-card.safe .result-icon { background: rgba(0,255,136,0.12); } -.result-card.danger .result-icon { background: rgba(255,51,102,0.12); } -.result-card.loading .result-icon { background: rgba(0,245,255,0.12); } -.result-card.error .result-icon { background: rgba(255,204,0,0.12); } - -.result-title { - font-size: 17px; - font-weight: 700; - margin-bottom: 4px; -} - -.result-card.safe .result-title { color: var(--safe-color); } -.result-card.danger .result-title { color: var(--danger-color); } -.result-card.loading .result-title { color: var(--accent-cyan); } -.result-card.error .result-title { color: var(--warn-color); } - -.result-desc { - font-size: 13px; - color: var(--text-sub); - line-height: 1.6; -} - -.result-url { - margin-top: 10px; - font-family: 'Courier New', monospace; - font-size: 11px; - color: var(--text-muted); - background: rgba(0,0,0,0.2); - border-radius: 8px; - padding: 8px 12px; - word-break: break-all; -} - -html.light-mode .result-url { background: rgba(0,0,0,0.06); } - -.threat-tags { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: 12px; -} - -.threat-tag { - background: rgba(255,51,102,0.1); - border: 1px solid rgba(255,51,102,0.25); - border-radius: 6px; - padding: 3px 10px; - font-size: 11px; - font-weight: 600; - color: var(--danger-color); - text-transform: uppercase; - letter-spacing: 0.06em; -} - -/* ── Spinner ── */ -.spinner { - width: 22px; - height: 22px; - border: 2px solid rgba(0,245,255,0.2); - border-top-color: var(--accent-cyan); - border-radius: 50%; - animation: spin 0.8s linear infinite; -} - -@keyframes spin { to { transform: rotate(360deg); } } - -/* ════════════════════════════════════════ - STATS ROW -════════════════════════════════════════ */ -.stats-row { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px; - margin-top: 20px; -} - -.stat-card { - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: 14px; - padding: 22px 16px; +/* ── Drop Zone ── */ +.qr-drop-zone { + border: 1px dashed var(--card-border); + border-radius: var(--radius-sm); + padding: 28px 16px; text-align: center; - backdrop-filter: blur(12px); - transition: border-color 0.2s, transform 0.2s; + cursor: pointer; + transition: border-color 0.2s, background 0.2s; + outline: none; } -.stat-card:hover { - border-color: rgba(0,245,255,0.3); - transform: translateY(-2px); +.qr-drop-zone:hover, +.qr-drop-zone:focus, +.qr-drop-zone.drag-over { + border-color: var(--accent-cyan); + background: rgba(0, 245, 255, 0.04); } -.stat-num { - font-size: 30px; - font-weight: 800; - background: var(--gradient-main); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; - letter-spacing: -0.02em; +html.light-mode .qr-drop-zone:hover, +html.light-mode .qr-drop-zone:focus, +html.light-mode .qr-drop-zone.drag-over { + border-color: var(--accent-cyan); + background: rgba(0, 112, 204, 0.04); } -.stat-label { - font-size: 11px; +.qr-upload-icon { color: var(--text-muted); - margin-top: 4px; - text-transform: uppercase; - letter-spacing: 0.1em; -} - -/* ════════════════════════════════════════ - FEATURES -════════════════════════════════════════ */ -.features { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 16px; - margin-top: 20px; -} - -.feature-item { - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: 14px; - padding: 22px; - backdrop-filter: blur(12px); - transition: border-color 0.2s, transform 0.2s; -} - -.feature-item:hover { - border-color: rgba(0,245,255,0.25); - transform: translateY(-2px); + margin-bottom: 8px; } -.feature-icon { font-size: 22px; margin-bottom: 12px; } - -.feature-title { +.qr-drop-zone p { font-size: 13px; - font-weight: 700; - color: var(--text); - margin-bottom: 6px; - letter-spacing: 0.01em; -} - -.feature-desc { font-size: 12px; color: var(--text-muted); line-height: 1.6; } - -/* ════════════════════════════════════════ - TEAM SECTION -════════════════════════════════════════ */ -.team-section { - margin-top: 72px; - padding-top: 48px; - border-top: 1px solid var(--card-border); + color: var(--text-sub); + margin-bottom: 4px; } -.team-header { text-align: center; margin-bottom: 28px; } - -.section-tag { - display: inline-block; - background: rgba(0,245,255,0.06); - border: 1px solid rgba(0,245,255,0.2); - border-radius: var(--radius-pill); - padding: 5px 18px; +.qr-drop-zone small { font-size: 11px; - font-weight: 700; - letter-spacing: 0.14em; - text-transform: uppercase; - color: var(--accent-cyan); - margin-bottom: 14px; -} - -.team-title { - font-size: 28px; - font-weight: 800; - color: var(--text); - margin-bottom: 8px; - letter-spacing: -0.02em; -} - -.team-sub { - font-size: 14px; color: var(--text-muted); } -.team-toggle { +/* ── Buttons inside drop zone ── */ +.qr-btn-row { display: flex; - align-items: center; + gap: 8px; justify-content: center; - width: 44px; - height: 44px; - border-radius: 50%; - background: rgba(0,245,255,0.06); - border: 1px solid rgba(0,245,255,0.2); - color: var(--accent-cyan); - cursor: pointer; - margin: 0 auto 24px; - transition: background 0.2s, transform 0.35s cubic-bezier(0.4,0,0.2,1); -} - -.team-toggle:hover { background: rgba(0,245,255,0.12); } -.team-toggle.open { transform: rotate(180deg); } - -.team-grid-wrap { - overflow: hidden; - max-height: 0; - opacity: 0; - transition: max-height 0.45s cubic-bezier(0.4,0,0.2,1), opacity 0.35s ease; + margin-top: 14px; } -.team-grid-wrap.open { max-height: 400px; opacity: 1; } - -.team-grid { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: 16px; - padding-bottom: 8px; -} - -.member-card { - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - padding: 22px 12px; - background: var(--card-bg); +.qr-btn { + background: transparent; border: 1px solid var(--card-border); - border-radius: 18px; - transition: border-color 0.2s, background 0.2s, transform 0.2s; - backdrop-filter: blur(12px); -} - -.member-card:hover { - border-color: rgba(0,245,255,0.3); - background: var(--card-hover); - transform: translateY(-4px); -} - -.member-avatar { - width: 80px; - height: 80px; - border-radius: 50%; - border: 2px solid var(--card-border); - overflow: hidden; - background: var(--bg-3); - display: flex; - align-items: center; - justify-content: center; - font-weight: 700; - font-size: 20px; - color: var(--accent-cyan); - transition: border-color 0.2s; -} - -.member-card:hover .member-avatar { border-color: var(--accent-cyan); } - -.member-avatar img { width: 100%; height: 100%; object-fit: cover; } - -.member-name { + border-radius: var(--radius-sm); + padding: 8px 16px; font-size: 12px; font-weight: 600; color: var(--text-sub); - text-align: center; - line-height: 1.4; -} - -/* ════════════════════════════════════════ - FOOTER -════════════════════════════════════════ */ -.footer { - text-align: center; - margin-top: 64px; - font-size: 12px; - color: var(--text-muted); - letter-spacing: 0.05em; -} - -.main-footer { - background: var(--bg-2); - border-top: 1px solid var(--card-border); - padding: 48px 24px 24px; - margin-top: 80px; -} - -.footer-content { - max-width: 820px; - margin: 0 auto; - display: flex; - justify-content: space-between; - gap: 40px; - flex-wrap: wrap; - margin-bottom: 32px; -} - -.footer-brand p { - font-size: 13px; - color: var(--text-muted); - margin-top: 10px; - max-width: 240px; - line-height: 1.6; -} - -.footer-links { - display: flex; - gap: 48px; - flex-wrap: wrap; -} - -.footer-col { display: flex; flex-direction: column; gap: 8px; } - -.footer-col h4 { - font-size: 11px; - font-weight: 700; - letter-spacing: 0.14em; - text-transform: uppercase; - color: var(--text-muted); - margin-bottom: 4px; -} - -.footer-col a { - font-size: 13px; - color: var(--text-sub); - text-decoration: none; - transition: color 0.15s; -} - -.footer-col a:hover { color: var(--accent-cyan); } - -.footer-bottom { - max-width: 820px; - margin: 0 auto; - padding-top: 20px; - border-top: 1px solid var(--card-border); - font-size: 12px; - color: var(--text-muted); - text-align: center; -} - -/* ════════════════════════════════════════ - DASHBOARD -════════════════════════════════════════ */ -.dashboard-cta { - display: flex; - gap: 12px; - flex-wrap: wrap; - margin-bottom: 28px; -} - -.dashboard-grid { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 16px; - margin-top: 20px; -} - -.dashboard-card { - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: var(--radius-card); - padding: 28px; - backdrop-filter: blur(12px); -} - -.dashboard-card h2 { - font-size: 16px; - font-weight: 700; - color: var(--text); - margin-bottom: 20px; - letter-spacing: 0.01em; -} - -.dashboard-note p { - font-size: 13px; - color: var(--text-muted); - line-height: 1.7; -} - -/* ════════════════════════════════════════ - CHAT PAGE -════════════════════════════════════════ */ -.chat-card { - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: var(--radius-card); - overflow: hidden; - backdrop-filter: blur(20px); - box-shadow: var(--shadow-card); -} - -.chat-container-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 24px; - border-bottom: 1px solid var(--card-border); - background: rgba(0,0,0,0.1); -} - -.chat-status { - display: flex; + cursor: pointer; + display: inline-flex; align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 600; - color: var(--accent-green); - letter-spacing: 0.05em; + gap: 7px; + transition: border-color 0.15s, color 0.15s, background 0.15s; + letter-spacing: 0.02em; + font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; } -.chat-status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--accent-green); - animation: statusPulse 2s infinite; - box-shadow: 0 0 6px var(--accent-green); +.qr-btn:hover { + border-color: rgba(0, 245, 255, 0.3); + color: var(--accent-cyan); + background: rgba(0, 245, 255, 0.04); } -.chat-clear-btn { - padding: 6px 14px; - font-size: 11px; +.qr-btn-danger { + border-color: rgba(255, 51, 102, 0.25); + color: var(--accent-red); } -.chat-history { - height: 460px; - overflow-y: auto; - padding: 24px; - display: flex; - flex-direction: column; - gap: 16px; - scroll-behavior: smooth; +.qr-btn-danger:hover { + border-color: rgba(255, 51, 102, 0.5); + background: rgba(255, 51, 102, 0.06); + color: var(--accent-red); } -.chat-history::-webkit-scrollbar { width: 4px; } -.chat-history::-webkit-scrollbar-track { background: transparent; } -.chat-history::-webkit-scrollbar-thumb { background: var(--card-border); border-radius: 4px; } - -.chat-msg { - display: flex; - gap: 12px; - align-items: flex-start; - animation: slideUp 0.3s ease; -} - -.chat-msg.user { - flex-direction: row-reverse; -} - -.chat-avatar { - width: 36px; - height: 36px; - border-radius: 50%; - background: rgba(0,245,255,0.08); - border: 1px solid var(--card-border); - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - flex-shrink: 0; +/* ── Camera section ── */ +.camera-section { + margin-top: 14px; + text-align: center; } -.chat-bubble { - background: var(--bg-3); +.camera-section video { + width: 100%; + max-height: 220px; + border-radius: var(--radius-sm); border: 1px solid var(--card-border); - border-radius: 16px; - border-top-left-radius: 4px; - padding: 14px 18px; - font-size: 13px; - color: var(--text); - line-height: 1.6; - max-width: 78%; -} - -.chat-msg.user .chat-bubble { - background: rgba(0,245,255,0.08); - border-color: rgba(0,245,255,0.2); - border-radius: 16px; - border-top-right-radius: 4px; - color: var(--text); -} - -.user-bubble { - background: rgba(0,245,255,0.06) !important; - border-color: rgba(0,245,255,0.2) !important; + background: #000; + object-fit: cover; + display: block; } -.chat-bubble strong { color: var(--accent-cyan); } -.chat-welcome-text { margin-top: 6px; color: var(--text-sub); font-size: 13px; } - -/* Typing indicator */ -.typing-msg { padding: 0 24px; } - -.typing-indicator { - display: flex; - align-items: center; - gap: 5px; - padding: 14px 18px; - background: var(--bg-3); +/* ── QR image preview ── */ +.qr-preview { + display: block; + max-height: 96px; + max-width: 100%; + border-radius: var(--radius-sm); border: 1px solid var(--card-border); - border-radius: 16px; - border-top-left-radius: 4px; - width: fit-content; -} - -.dot-bounce { - width: 7px; - height: 7px; - border-radius: 50%; - background: var(--accent-cyan); - opacity: 0.5; - animation: dotBounce 1.2s ease-in-out infinite; -} - -.dot-bounce:nth-child(1) { animation-delay: 0s; } -.dot-bounce:nth-child(2) { animation-delay: 0.2s; } -.dot-bounce:nth-child(3) { animation-delay: 0.4s; } - -@keyframes dotBounce { - 0%, 60%, 100% { transform: translateY(0); opacity: 0.5; } - 30% { transform: translateY(-8px); opacity: 1; } -} - -/* Chat input */ -.chat-input-wrap { - padding: 16px 24px; - border-top: 1px solid var(--card-border); - background: rgba(0,0,0,0.1); -} - -.chat-input-row { - display: flex; - gap: 10px; - align-items: flex-end; + margin: 14px auto 0; + object-fit: contain; } -.chat-input-textarea { - flex: 1; - background: var(--input-bg); - border: 1px solid var(--input-border); +/* ── Decoded result ── */ +.qr-decoded { + margin-top: 14px; + padding: 14px 16px; border-radius: var(--radius-sm); - padding: 12px 16px; - font-size: 13px; - color: var(--text); - outline: none; - resize: none; - font-family: inherit; - transition: border-color 0.2s, box-shadow 0.2s; - min-height: 44px; - max-height: 140px; - overflow-y: auto; - line-height: 1.5; -} - -.chat-input-textarea::placeholder { color: var(--text-muted); } - -.chat-input-textarea:focus { - border-color: var(--input-focus); - box-shadow: 0 0 0 3px rgba(0,245,255,0.06); -} - -.chat-send-btn { - padding: 12px 20px; - font-size: 13px; - flex-shrink: 0; -} - -/* Chat examples & features */ -.chat-examples-container { - margin-top: 16px; -} - -.chat-features-container { - margin-top: 16px; -} - -/* ── Report Bubble ── */ -.report-bubble { - max-width: 85% !important; - padding: 18px 20px !important; + background: var(--safe-bg); + border: 1px solid var(--safe-border); + animation: slideUp 0.3s ease; } -.classification-border-safe { border-color: var(--safe-border) !important; } -.classification-border-danger { border-color: var(--danger-border) !important; } -.classification-border-warn { border-color: var(--warn-border) !important; } -.classification-border-suspicious { border-color: var(--warn-border) !important; } - -.report-header-row { +.qr-decoded-label { display: flex; align-items: center; - gap: 10px; - margin-bottom: 12px; - flex-wrap: wrap; -} - -.report-badge { - display: inline-block; - padding: 3px 10px; - border-radius: 6px; + gap: 6px; font-size: 11px; font-weight: 700; + color: var(--safe-color); text-transform: uppercase; - letter-spacing: 0.08em; + letter-spacing: 0.1em; + margin-bottom: 8px; } -.classification-safe { background: var(--safe-bg); color: var(--safe-color); border: 1px solid var(--safe-border); } -.classification-danger { background: var(--danger-bg); color: var(--danger-color); border: 1px solid var(--danger-border); } -.classification-suspicious { background: var(--warn-bg); color: var(--warn-color); border: 1px solid var(--warn-border); } -.classification-warn { background: var(--warn-bg); color: var(--warn-color); border: 1px solid var(--warn-border); } - -.report-scam-type { +.qr-decoded-url { + font-family: 'Courier New', monospace; font-size: 12px; color: var(--text-sub); - font-weight: 600; -} - -.report-explanation { - font-size: 13px; - color: var(--text-sub); - line-height: 1.65; - margin-bottom: 14px; -} - -.report-score-section { margin-bottom: 14px; } - -.report-score-labels { - display: flex; - justify-content: space-between; - font-size: 11px; - color: var(--text-muted); - margin-bottom: 6px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.08em; -} - -.report-progress-track { - height: 6px; - background: rgba(255,255,255,0.06); - border-radius: 100px; - overflow: hidden; + word-break: break-all; + line-height: 1.5; + margin-bottom: 12px; + background: rgba(0, 0, 0, 0.15); + padding: 8px 10px; + border-radius: 8px; } -.report-progress-bar { - height: 100%; - border-radius: 100px; - transition: width 1s cubic-bezier(0.4,0,0.2,1); +html.light-mode .qr-decoded-url { + background: rgba(0, 0, 0, 0.05); } -.report-progress-bar.classification-safe { background: var(--safe-color); } -.report-progress-bar.classification-danger { background: var(--gradient-danger); } -.report-progress-bar.classification-suspicious { background: var(--accent-yellow); } - -.report-advice-box { - border-radius: 10px; - padding: 12px 14px; +.qr-scan-trigger { + padding: 10px 20px; font-size: 12px; - line-height: 1.6; -} - -.report-advice-box.classification-safe { background: var(--safe-bg); border: 1px solid var(--safe-border); color: var(--safe-color); } -.report-advice-box.classification-danger { background: var(--danger-bg); border: 1px solid var(--danger-border); color: var(--danger-color); } -.report-advice-box.classification-suspicious { background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--warn-color); } - -.report-advice-title { text-transform: uppercase; letter-spacing: 0.06em; font-size: 11px; } - -/* ════════════════════════════════════════ - DOCS / LEGAL PAGES -════════════════════════════════════════ */ -.docs-container, .legal-container { - max-width: 820px; - margin: 80px auto; - padding: 40px; - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: 24px; - backdrop-filter: blur(20px); -} - -.docs-container h1, .legal-container h1 { color: var(--accent-cyan); margin-bottom: 28px; font-size: 32px; } -.docs-container h2, .legal-container h2 { color: var(--text); margin-top: 36px; margin-bottom: 16px; border-bottom: 1px solid var(--card-border); padding-bottom: 10px; } -.docs-container h3 { color: var(--accent-cyan); margin-top: 24px; margin-bottom: 12px; font-size: 16px; } -.docs-container p, .docs-container li, .legal-container p, .legal-container li { color: var(--text-sub); line-height: 1.8; margin-bottom: 14px; font-size: 14px; } -.docs-container ul, .legal-container ul { padding-left: 20px; } - -.back-link { - display: inline-flex; - align-items: center; - gap: 6px; - margin-bottom: 28px; - color: var(--accent-cyan); - text-decoration: none; - font-weight: 600; - font-size: 13px; - transition: transform 0.2s; } -.back-link:hover { transform: translateX(-4px); } - -.code-block { - background: var(--input-bg); - padding: 18px; +/* ── Error message ── */ +.qr-error { + margin-top: 12px; + padding: 10px 14px; border-radius: var(--radius-sm); - font-family: 'Courier New', monospace; - font-size: 13px; - color: var(--accent-green); - border: 1px solid var(--input-border); - margin: 20px 0; - overflow-x: auto; -} - -.status-badge { - display: inline-block; - padding: 4px 12px; - border-radius: 6px; - font-weight: 700; - font-size: 11px; - margin-right: 8px; - letter-spacing: 0.06em; -} - -.safe-badge { background: var(--safe-bg); color: var(--safe-color); border: 1px solid var(--safe-border); } -.danger-badge { background: var(--danger-bg); color: var(--danger-color); border: 1px solid var(--danger-border); } - -/* ════════════════════════════════════════ - SHARE PAGE -════════════════════════════════════════ */ -.share-card { - width: 100%; - max-width: 700px; - background: var(--card-bg); - border: 1px solid var(--card-border); - border-radius: 20px; - padding: 36px; - box-shadow: var(--shadow-card), var(--shadow-glow); - backdrop-filter: blur(20px); + background: var(--warn-bg); + border: 1px solid var(--warn-border); + color: var(--warn-color); + font-size: 12px; + line-height: 1.5; + animation: slideUp 0.3s ease; } -/* ════════════════════════════════════════ - RESPONSIVE -════════════════════════════════════════ */ +/* ── Responsive ── */ @media (max-width: 680px) { - .wrapper { padding: 40px 16px 60px; } - .input-row { flex-direction: column; } - .stats-row { grid-template-columns: 1fr 1fr; } - .team-grid { grid-template-columns: repeat(3, 1fr); } - .dashboard-grid { grid-template-columns: 1fr; } - .report-bubble { max-width: 96% !important; } - .docs-container, .legal-container { margin: 40px 16px; padding: 24px; } - .footer-content { flex-direction: column; gap: 24px; } -} - -@media (max-width: 400px) { - .team-grid { grid-template-columns: repeat(2, 1fr); } - .stats-row { grid-template-columns: 1fr; } -} - -@media (prefers-reduced-motion: reduce) { - *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } -} + .qr-btn-row { flex-direction: column; align-items: center; } + .qr-btn { width: 100%; justify-content: center; } + .qr-scan-trigger { width: 100%; justify-content: center; } +} \ No newline at end of file