Spaces:
Running
Running
Anish commited on
Commit ·
ee7c0fb
1
Parent(s): 825fa02
[UI/UX] More changes to the Frontend
Browse files- backend/app/services/email_service.py +1 -1
- backend/main.py +1 -1
- frontend/app/dashboard/page.tsx +4 -4
- frontend/app/learn-more/page.tsx +144 -0
- frontend/app/login/page.tsx +7 -7
- frontend/app/page.tsx +38 -31
- frontend/components/dashboard/HistoryGrid.tsx +1 -1
backend/app/services/email_service.py
CHANGED
|
@@ -58,7 +58,7 @@ async def send_verification_email(email_to: str, raw_token: str):
|
|
| 58 |
|
| 59 |
html_content = f"""
|
| 60 |
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #333;">
|
| 61 |
-
<h2>Welcome to
|
| 62 |
<p>Please confirm your email address to activate your account.</p>
|
| 63 |
<p>Click the button below to verify. <strong>This link will expire in 24 hours.</strong></p>
|
| 64 |
<div style="text-align: center; margin: 30px 0;">
|
|
|
|
| 58 |
|
| 59 |
html_content = f"""
|
| 60 |
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; color: #333;">
|
| 61 |
+
<h2>Welcome to Spotix!</h2>
|
| 62 |
<p>Please confirm your email address to activate your account.</p>
|
| 63 |
<p>Click the button below to verify. <strong>This link will expire in 24 hours.</strong></p>
|
| 64 |
<div style="text-align: center; margin: 30px 0;">
|
backend/main.py
CHANGED
|
@@ -24,7 +24,7 @@ async def lifespan(app: FastAPI):
|
|
| 24 |
yield
|
| 25 |
|
| 26 |
app = FastAPI(
|
| 27 |
-
title="
|
| 28 |
lifespan=lifespan
|
| 29 |
)
|
| 30 |
|
|
|
|
| 24 |
yield
|
| 25 |
|
| 26 |
app = FastAPI(
|
| 27 |
+
title="Spotix",
|
| 28 |
lifespan=lifespan
|
| 29 |
)
|
| 30 |
|
frontend/app/dashboard/page.tsx
CHANGED
|
@@ -20,8 +20,8 @@ export default function DashboardPage() {
|
|
| 20 |
|
| 21 |
// Filter & Sort States
|
| 22 |
const [searchQuery, setSearchQuery] = useState("");
|
| 23 |
-
const [sortBy, setSortBy] = useState<'newest'|'confidence'>('newest');
|
| 24 |
-
const [resultFilter, setResultFilter] = useState<'All'|'AI'|'Real'|'Suspicious'>('All');
|
| 25 |
|
| 26 |
// Fetch Files & Guard
|
| 27 |
useEffect(() => {
|
|
@@ -87,8 +87,8 @@ export default function DashboardPage() {
|
|
| 87 |
// 3. Sort
|
| 88 |
result.sort((a, b) => {
|
| 89 |
if (sortBy === 'newest' || sortBy === 'date') {
|
| 90 |
-
const dateA = a.uploaded_at ? new Date(a.uploaded_at).getTime() : 0;
|
| 91 |
-
const dateB = b.uploaded_at ? new Date(b.uploaded_at).getTime() : 0;
|
| 92 |
return dateB - dateA; // Both map to newest first logic
|
| 93 |
} else {
|
| 94 |
const confA = a.confidence || 0;
|
|
|
|
| 20 |
|
| 21 |
// Filter & Sort States
|
| 22 |
const [searchQuery, setSearchQuery] = useState("");
|
| 23 |
+
const [sortBy, setSortBy] = useState<'newest'|'confidence'|'date'>('newest');
|
| 24 |
+
const [resultFilter, setResultFilter] = useState<'All'|'AI'|'Real'|'Suspicious'|'Videos'|'Images'>('All');
|
| 25 |
|
| 26 |
// Fetch Files & Guard
|
| 27 |
useEffect(() => {
|
|
|
|
| 87 |
// 3. Sort
|
| 88 |
result.sort((a, b) => {
|
| 89 |
if (sortBy === 'newest' || sortBy === 'date') {
|
| 90 |
+
const dateA = a.uploaded_at ? new Date(a.uploaded_at.endsWith('Z') ? a.uploaded_at : a.uploaded_at + 'Z').getTime() : 0;
|
| 91 |
+
const dateB = b.uploaded_at ? new Date(b.uploaded_at.endsWith('Z') ? b.uploaded_at : b.uploaded_at + 'Z').getTime() : 0;
|
| 92 |
return dateB - dateA; // Both map to newest first logic
|
| 93 |
} else {
|
| 94 |
const confA = a.confidence || 0;
|
frontend/app/learn-more/page.tsx
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client";
|
| 2 |
+
|
| 3 |
+
import React, { useEffect } from "react";
|
| 4 |
+
import { useRouter } from "next/navigation";
|
| 5 |
+
import { ArrowLeft, Upload, Shield, Activity, Database } from "lucide-react";
|
| 6 |
+
import Navbar from "@/components/shared/Navbar";
|
| 7 |
+
|
| 8 |
+
export default function LearnMorePage() {
|
| 9 |
+
const router = useRouter();
|
| 10 |
+
|
| 11 |
+
useEffect(() => {
|
| 12 |
+
// Scroll reveal logic
|
| 13 |
+
const observer = new IntersectionObserver((entries) => {
|
| 14 |
+
entries.forEach(entry => {
|
| 15 |
+
if (entry.isIntersecting) {
|
| 16 |
+
entry.target.classList.add('visible');
|
| 17 |
+
}
|
| 18 |
+
});
|
| 19 |
+
}, { threshold: 0.1 });
|
| 20 |
+
|
| 21 |
+
document.querySelectorAll('.scroll-reveal').forEach((el) => observer.observe(el));
|
| 22 |
+
return () => observer.disconnect();
|
| 23 |
+
}, []);
|
| 24 |
+
|
| 25 |
+
return (
|
| 26 |
+
<div className="min-h-screen bg-[var(--theme-bg)] text-[var(--theme-text)] font-sans relative overflow-x-hidden selection:bg-[var(--theme-text)] selection:text-[var(--theme-bg)]">
|
| 27 |
+
{/* Global Theme & Cursor overrides if needed */}
|
| 28 |
+
<style dangerouslySetInnerHTML={{
|
| 29 |
+
__html: `
|
| 30 |
+
html, body, a, button, [role="button"], input, select, textarea, .cursor-pointer {
|
| 31 |
+
cursor: none !important;
|
| 32 |
+
}
|
| 33 |
+
body {
|
| 34 |
+
background-color: var(--theme-bg);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.scroll-reveal {
|
| 38 |
+
opacity: 0;
|
| 39 |
+
transform: translateY(30px);
|
| 40 |
+
transition: all 1s cubic-bezier(0.23, 1, 0.32, 1);
|
| 41 |
+
}
|
| 42 |
+
.scroll-reveal.visible {
|
| 43 |
+
opacity: 1;
|
| 44 |
+
transform: translateY(0);
|
| 45 |
+
}
|
| 46 |
+
`
|
| 47 |
+
}} />
|
| 48 |
+
|
| 49 |
+
{/* Background Effects */}
|
| 50 |
+
<div className="absolute inset-0 bg-[var(--theme-bg)] z-0"></div>
|
| 51 |
+
<div className="absolute inset-0 opacity-40 pointer-events-none bg-[radial-gradient(circle_at_50%_50%,_#11141d_0%,_var(--theme-bg)_100%)] z-[1]"></div>
|
| 52 |
+
<div className="absolute inset-0 z-[2] opacity-[0.03] pointer-events-none" style={{ backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.90' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")` }}></div>
|
| 53 |
+
|
| 54 |
+
<Navbar />
|
| 55 |
+
|
| 56 |
+
<div className="relative z-10 max-w-4xl mx-auto px-6 py-40">
|
| 57 |
+
{/* Header */}
|
| 58 |
+
<div className="mb-20 text-center scroll-reveal">
|
| 59 |
+
<label className="uppercase tracking-[0.4em] text-[#d0c4bb] mb-6 block text-xs">Platform Guide</label>
|
| 60 |
+
<h1 className="text-5xl md:text-7xl font-black tracking-tight text-[var(--theme-text)] mb-6">
|
| 61 |
+
Master the Engine
|
| 62 |
+
</h1>
|
| 63 |
+
<p className="text-lg md:text-xl text-[var(--theme-text)]/70 font-light leading-relaxed max-w-2xl mx-auto">
|
| 64 |
+
Welcome to Spotix. Learn how to utilize our neural architecture to detect synthetic and AI-generated media with absolute precision.
|
| 65 |
+
</p>
|
| 66 |
+
</div>
|
| 67 |
+
|
| 68 |
+
{/* Steps */}
|
| 69 |
+
<div className="space-y-12">
|
| 70 |
+
{/* Step 1 */}
|
| 71 |
+
<div className="flex flex-col md:flex-row gap-8 items-start bg-[var(--theme-text)]/5 p-8 md:p-12 rounded-[2rem] border border-[var(--theme-border)] backdrop-blur-md scroll-reveal hover:bg-[var(--theme-text)]/10 transition-colors">
|
| 72 |
+
<div className="w-16 h-16 shrink-0 rounded-2xl bg-[var(--theme-text)]/10 flex items-center justify-center border border-[var(--theme-border)]">
|
| 73 |
+
<Upload className="w-8 h-8 text-[var(--theme-text)]" />
|
| 74 |
+
</div>
|
| 75 |
+
<div>
|
| 76 |
+
<h2 className="text-2xl font-bold mb-4">1. Uploading Media</h2>
|
| 77 |
+
<p className="text-[var(--theme-text)]/70 leading-relaxed text-lg">
|
| 78 |
+
Click the <strong className="text-[var(--theme-text)]">Analyze Media</strong> button on the top right of the navigation bar, or from the Landing Page. A secure uplink zone will appear. You can drag and drop your images or videos (MP4, WEBM, JPG, PNG) directly into this zone.
|
| 79 |
+
</p>
|
| 80 |
+
</div>
|
| 81 |
+
</div>
|
| 82 |
+
|
| 83 |
+
{/* Step 2 */}
|
| 84 |
+
<div className="flex flex-col md:flex-row gap-8 items-start bg-[var(--theme-text)]/5 p-8 md:p-12 rounded-[2rem] border border-[var(--theme-border)] backdrop-blur-md scroll-reveal hover:bg-[var(--theme-text)]/10 transition-colors">
|
| 85 |
+
<div className="w-16 h-16 shrink-0 rounded-2xl bg-[var(--theme-text)]/10 flex items-center justify-center border border-[var(--theme-border)]">
|
| 86 |
+
<Activity className="w-8 h-8 text-[var(--theme-text)]" />
|
| 87 |
+
</div>
|
| 88 |
+
<div>
|
| 89 |
+
<h2 className="text-2xl font-bold mb-4">2. The Analysis Phase</h2>
|
| 90 |
+
<p className="text-[var(--theme-text)]/70 leading-relaxed text-lg">
|
| 91 |
+
Once uploaded, the Spotix engine dissects the file through multiple neural pathways. We check for temporal inconsistencies, latent space noise patterns, and structural artifacts that generative AI models leave behind.
|
| 92 |
+
</p>
|
| 93 |
+
</div>
|
| 94 |
+
</div>
|
| 95 |
+
|
| 96 |
+
{/* Step 3 */}
|
| 97 |
+
<div className="flex flex-col md:flex-row gap-8 items-start bg-[var(--theme-text)]/5 p-8 md:p-12 rounded-[2rem] border border-[var(--theme-border)] backdrop-blur-md scroll-reveal hover:bg-[var(--theme-text)]/10 transition-colors">
|
| 98 |
+
<div className="w-16 h-16 shrink-0 rounded-2xl bg-[var(--theme-text)]/10 flex items-center justify-center border border-[var(--theme-border)]">
|
| 99 |
+
<Shield className="w-8 h-8 text-[var(--theme-text)]" />
|
| 100 |
+
</div>
|
| 101 |
+
<div>
|
| 102 |
+
<h2 className="text-2xl font-bold mb-4">3. Deciphering the Results</h2>
|
| 103 |
+
<p className="text-[var(--theme-text)]/70 leading-relaxed text-lg">
|
| 104 |
+
You will be routed to the Results Terminal. Here, you'll see a confidence score and a verdict (Authentic, Suspicious, or AI Detected). A heatmap visualizes exactly where the neural anomalies were found in the media.
|
| 105 |
+
</p>
|
| 106 |
+
</div>
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
{/* Step 4 */}
|
| 110 |
+
<div className="flex flex-col md:flex-row gap-8 items-start bg-[var(--theme-text)]/5 p-8 md:p-12 rounded-[2rem] border border-[var(--theme-border)] backdrop-blur-md scroll-reveal hover:bg-[var(--theme-text)]/10 transition-colors">
|
| 111 |
+
<div className="w-16 h-16 shrink-0 rounded-2xl bg-[var(--theme-text)]/10 flex items-center justify-center border border-[var(--theme-border)]">
|
| 112 |
+
<Database className="w-8 h-8 text-[var(--theme-text)]" />
|
| 113 |
+
</div>
|
| 114 |
+
<div>
|
| 115 |
+
<h2 className="text-2xl font-bold mb-4">4. Managing Your Dashboard</h2>
|
| 116 |
+
<p className="text-[var(--theme-text)]/70 leading-relaxed text-lg">
|
| 117 |
+
Access your Dashboard to view a complete history of all your analyses. You can filter by file type, sort by confidence or date, and delete historical data to maintain your privacy.
|
| 118 |
+
</p>
|
| 119 |
+
</div>
|
| 120 |
+
</div>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
{/* Back Button */}
|
| 124 |
+
<div className="mt-20 flex justify-center scroll-reveal">
|
| 125 |
+
<button
|
| 126 |
+
onClick={() => router.push('/')}
|
| 127 |
+
className="group relative overflow-hidden rounded-full bg-[var(--theme-text)] text-[var(--theme-bg)] px-10 py-4 font-bold tracking-widest text-sm uppercase transition-all duration-300 hover:shadow-[0_0_40px_rgba(253,232,214,0.3)] hover:scale-105 flex items-center gap-3 !cursor-none"
|
| 128 |
+
>
|
| 129 |
+
<ArrowLeft className="w-5 h-5 transition-transform duration-300 group-hover:-translate-x-1" />
|
| 130 |
+
<span>Return to Gateway</span>
|
| 131 |
+
</button>
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
|
| 135 |
+
{/* Footer */}
|
| 136 |
+
<footer className="bg-[var(--theme-bg)] w-full border-t border-[var(--theme-border)] relative z-10">
|
| 137 |
+
<div className="flex flex-col items-center justify-center py-12 max-w-5xl mx-auto">
|
| 138 |
+
<div className="text-xl font-black text-[var(--theme-text)] uppercase mb-2">SPOTIX</div>
|
| 139 |
+
<div className="font-['Inter'] uppercase tracking-[0.15em] text-[9px] text-[#d0c4bb] opacity-60">© 2026 SPOTIX KINETIC. ENGINEERED FOR THE ETHEREAL.</div>
|
| 140 |
+
</div>
|
| 141 |
+
</footer>
|
| 142 |
+
</div>
|
| 143 |
+
);
|
| 144 |
+
}
|
frontend/app/login/page.tsx
CHANGED
|
@@ -128,7 +128,7 @@ export default function LoginPage() {
|
|
| 128 |
<div className="fixed top-12 left-6 md:left-12 z-40">
|
| 129 |
<button onClick={() => router.push("/")} className="group flex items-center gap-3 text-[#d0c4bb] hover:text-[var(--theme-text)] transition-colors !cursor-none text-sm font-semibold tracking-wider hover-scale">
|
| 130 |
<ArrowLeft className="w-5 h-5 transition-transform group-hover:-translate-x-1 !cursor-none" />
|
| 131 |
-
RETURN TO
|
| 132 |
</button>
|
| 133 |
</div>
|
| 134 |
|
|
@@ -137,9 +137,9 @@ export default function LoginPage() {
|
|
| 137 |
<div className="absolute -top-40 -right-40 w-80 h-80 bg-theme-text/5 rounded-full blur-[80px] pointer-events-none"></div>
|
| 138 |
|
| 139 |
<h1 className="text-3xl tracking-widest font-bold mb-2 font-sans uppercase">
|
| 140 |
-
|
| 141 |
</h1>
|
| 142 |
-
<p className="text-[#d0c4bb]/60 text-sm mb-6">Authenticate to seamlessly
|
| 143 |
|
| 144 |
{error && (
|
| 145 |
<div className="bg-red-500/10 border border-red-500/20 text-red-400 text-sm p-3 rounded-lg mb-4">
|
|
@@ -186,7 +186,7 @@ export default function LoginPage() {
|
|
| 186 |
required={mode === 'signup'}
|
| 187 |
value={email}
|
| 188 |
onChange={(e) => setEmail(e.target.value)}
|
| 189 |
-
placeholder="
|
| 190 |
className="w-full bg-theme-text/5 border border-theme-border rounded-xl py-4 pl-12 pr-4 text-[var(--theme-text)] placeholder-[#d0c4bb]/40 focus:outline-none focus:border-theme-border/50 focus:bg-theme-text/10 transition-all font-mono text-sm"
|
| 191 |
/>
|
| 192 |
</div>
|
|
@@ -198,7 +198,7 @@ export default function LoginPage() {
|
|
| 198 |
required
|
| 199 |
value={password}
|
| 200 |
onChange={(e) => setPassword(e.target.value)}
|
| 201 |
-
placeholder="
|
| 202 |
className="w-full bg-theme-text/5 border border-theme-border rounded-xl py-4 pl-12 pr-4 text-[var(--theme-text)] placeholder-[#d0c4bb]/40 focus:outline-none focus:border-theme-border/50 focus:bg-theme-text/10 transition-all font-mono text-sm"
|
| 203 |
/>
|
| 204 |
</div>
|
|
@@ -209,7 +209,7 @@ export default function LoginPage() {
|
|
| 209 |
className="w-full mt-2 py-4 rounded-xl relative overflow-hidden group dash-border bg-theme-text/5 hover:bg-theme-text/10 transition"
|
| 210 |
>
|
| 211 |
<span className="relative z-10 font-bold tracking-widest text-sm uppercase flex items-center justify-center gap-2">
|
| 212 |
-
{loading && mode === 'login' ? 'Authorizing...' : loading && mode === 'signup' ? 'Registering...' : mode === 'login' ? '
|
| 213 |
<ArrowRight className={`w-4 h-4 transition-transform group-hover:translate-x-1 ${loading ? 'hidden' : ''}`} />
|
| 214 |
</span>
|
| 215 |
</button>
|
|
@@ -220,7 +220,7 @@ export default function LoginPage() {
|
|
| 220 |
onClick={() => setMode(mode === 'login' ? 'signup' : 'login')}
|
| 221 |
className="text-xs text-[#d0c4bb]/60 hover:text-[var(--theme-text)] transition uppercase tracking-widest cursor-pointer"
|
| 222 |
>
|
| 223 |
-
{mode === 'login' ? "Don't have
|
| 224 |
</button>
|
| 225 |
</div>
|
| 226 |
|
|
|
|
| 128 |
<div className="fixed top-12 left-6 md:left-12 z-40">
|
| 129 |
<button onClick={() => router.push("/")} className="group flex items-center gap-3 text-[#d0c4bb] hover:text-[var(--theme-text)] transition-colors !cursor-none text-sm font-semibold tracking-wider hover-scale">
|
| 130 |
<ArrowLeft className="w-5 h-5 transition-transform group-hover:-translate-x-1 !cursor-none" />
|
| 131 |
+
RETURN TO SPOTIX
|
| 132 |
</button>
|
| 133 |
</div>
|
| 134 |
|
|
|
|
| 137 |
<div className="absolute -top-40 -right-40 w-80 h-80 bg-theme-text/5 rounded-full blur-[80px] pointer-events-none"></div>
|
| 138 |
|
| 139 |
<h1 className="text-3xl tracking-widest font-bold mb-2 font-sans uppercase">
|
| 140 |
+
Spotix<span className="text-[var(--theme-text)]/50"> Vault</span> Access
|
| 141 |
</h1>
|
| 142 |
+
<p className="text-[#d0c4bb]/60 text-sm mb-6">Authenticate to seamlessly store and analyze.</p>
|
| 143 |
|
| 144 |
{error && (
|
| 145 |
<div className="bg-red-500/10 border border-red-500/20 text-red-400 text-sm p-3 rounded-lg mb-4">
|
|
|
|
| 186 |
required={mode === 'signup'}
|
| 187 |
value={email}
|
| 188 |
onChange={(e) => setEmail(e.target.value)}
|
| 189 |
+
placeholder="Email"
|
| 190 |
className="w-full bg-theme-text/5 border border-theme-border rounded-xl py-4 pl-12 pr-4 text-[var(--theme-text)] placeholder-[#d0c4bb]/40 focus:outline-none focus:border-theme-border/50 focus:bg-theme-text/10 transition-all font-mono text-sm"
|
| 191 |
/>
|
| 192 |
</div>
|
|
|
|
| 198 |
required
|
| 199 |
value={password}
|
| 200 |
onChange={(e) => setPassword(e.target.value)}
|
| 201 |
+
placeholder="Password"
|
| 202 |
className="w-full bg-theme-text/5 border border-theme-border rounded-xl py-4 pl-12 pr-4 text-[var(--theme-text)] placeholder-[#d0c4bb]/40 focus:outline-none focus:border-theme-border/50 focus:bg-theme-text/10 transition-all font-mono text-sm"
|
| 203 |
/>
|
| 204 |
</div>
|
|
|
|
| 209 |
className="w-full mt-2 py-4 rounded-xl relative overflow-hidden group dash-border bg-theme-text/5 hover:bg-theme-text/10 transition"
|
| 210 |
>
|
| 211 |
<span className="relative z-10 font-bold tracking-widest text-sm uppercase flex items-center justify-center gap-2">
|
| 212 |
+
{loading && mode === 'login' ? 'Authorizing...' : loading && mode === 'signup' ? 'Registering...' : mode === 'login' ? 'Login' : 'Register'}
|
| 213 |
<ArrowRight className={`w-4 h-4 transition-transform group-hover:translate-x-1 ${loading ? 'hidden' : ''}`} />
|
| 214 |
</span>
|
| 215 |
</button>
|
|
|
|
| 220 |
onClick={() => setMode(mode === 'login' ? 'signup' : 'login')}
|
| 221 |
className="text-xs text-[#d0c4bb]/60 hover:text-[var(--theme-text)] transition uppercase tracking-widest cursor-pointer"
|
| 222 |
>
|
| 223 |
+
{mode === 'login' ? "Don't have an Account? Create one." : "Already have an Account? Login."}
|
| 224 |
</button>
|
| 225 |
</div>
|
| 226 |
|
frontend/app/page.tsx
CHANGED
|
@@ -12,6 +12,13 @@ export default function LandingPage() {
|
|
| 12 |
|
| 13 |
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
// --- React State for Loader ---
|
| 16 |
const [loadProgress, setLoadProgress] = useState(0);
|
| 17 |
const [isLoaded, setIsLoaded] = useState(false);
|
|
@@ -301,7 +308,7 @@ export default function LandingPage() {
|
|
| 301 |
|
| 302 |
<div className="absolute inset-0 bg-[var(--theme-bg)] z-0"></div>
|
| 303 |
<div className="absolute inset-0 opacity-40 pointer-events-none bg-[radial-gradient(circle_at_50%_50%,_#11141d_0%,_var(--theme-bg)_100%)] z-[1]" id="hero-bg"></div>
|
| 304 |
-
<div className="absolute inset-0 z-[2] opacity-[0.03] pointer-events-none" style={{ backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.
|
| 305 |
<div className="absolute inset-0 pointer-events-none" id="hero-grid"></div>
|
| 306 |
<div className="absolute inset-0 pointer-events-none" id="hero-glow"></div>
|
| 307 |
|
|
@@ -310,11 +317,11 @@ export default function LandingPage() {
|
|
| 310 |
{/* Main Hero Content */}
|
| 311 |
<div className={`w-full transition-all duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)] ${isUploadModalOpen ? 'blur-sm opacity-50 pointer-events-none' : ''}`} id="hero-main-content">
|
| 312 |
<div className="reveal-wrap mb-6">
|
| 313 |
-
<label className="reveal-text uppercase tracking-[0.3em] text-[var(--theme-text)] block text-xs font-medium opacity-80;" style={{ transitionDelay: '0.1s' }}>Next-Gen Media
|
| 314 |
</div>
|
| 315 |
<div className="reveal-wrap mb-8">
|
| 316 |
<h1 className="reveal-text text-5xl md:text-8xl lg:text-9xl font-black tracking-[-0.04em] leading-[0.95] text-[var(--theme-text)]" style={{ transitionDelay: '0.3s' }}>
|
| 317 |
-
|
| 318 |
<span className="text-outline">Engineered for Truth.</span>
|
| 319 |
</h1>
|
| 320 |
</div>
|
|
@@ -329,7 +336,7 @@ export default function LandingPage() {
|
|
| 329 |
<span className="transition-transform duration-300 group-hover:-translate-x-2">Analyze Media</span>
|
| 330 |
<ArrowRight className="w-5 h-5 ml-2 opacity-0 -translate-x-4 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)] absolute right-8 arrow-animate" />
|
| 331 |
</button>
|
| 332 |
-
<button className="bg-[var(--theme-text)]/5 backdrop-blur-md text-[var(--theme-text)] px-10 py-5 rounded-full text-sm font-semibold hover:bg-[var(--theme-text)]/10 transition-all border-none hover-scale relative z-20">
|
| 333 |
Learn More
|
| 334 |
</button>
|
| 335 |
</div>
|
|
@@ -361,16 +368,20 @@ export default function LandingPage() {
|
|
| 361 |
<div className="max-w-[1920px] mx-auto scroll-reveal">
|
| 362 |
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 md:gap-24 items-center">
|
| 363 |
<div className="lg:col-span-5">
|
| 364 |
-
<h2 className="text-4xl md:text-5xl font-bold tracking-tight mb-8 text-[var(--theme-text)]">
|
| 365 |
-
<p className="text-[#d0c4bb] mb-10 leading-relaxed text-lg">
|
| 366 |
<div className="space-y-6">
|
| 367 |
<div className="flex items-center gap-4 text-sm text-[var(--theme-text)]/70">
|
| 368 |
<CheckCircle className="w-5 h-5 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 369 |
-
<span className="uppercase tracking-widest font-medium">
|
| 370 |
</div>
|
| 371 |
<div className="flex items-center gap-4 text-sm text-[var(--theme-text)]/70">
|
| 372 |
<CheckCircle className="w-5 h-5 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 373 |
-
<span className="uppercase tracking-widest font-medium">
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
</div>
|
| 375 |
</div>
|
| 376 |
</div>
|
|
@@ -382,23 +393,20 @@ export default function LandingPage() {
|
|
| 382 |
<div className="bg-[var(--theme-bg)]/80 backdrop-blur-md px-5 py-2.5 rounded-full border border-theme-border">
|
| 383 |
<span className="text-[10px] font-mono text-[var(--theme-text)] uppercase tracking-tighter">Status: Scanning_Buffer_04</span>
|
| 384 |
</div>
|
| 385 |
-
<div className="font-mono text-[10px] text-[#d0c4bb] text-right">
|
| 386 |
-
COORD: 34.0522° N<br />TIMESTAMP: 12:44:09:22
|
| 387 |
-
</div>
|
| 388 |
</div>
|
| 389 |
<div className="absolute top-1/2 left-0 w-full h-[2px] bg-[var(--theme-text)]/40 shadow-[0_0_20px_rgba(253,232,214,0.5)] animate-pulse"></div>
|
| 390 |
<div className="mt-auto grid grid-cols-3 gap-3 md:gap-6">
|
| 391 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 392 |
-
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">Authenticity</div>
|
| 393 |
-
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">94
|
| 394 |
</div>
|
| 395 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 396 |
-
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">Noise
|
| 397 |
-
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">0.
|
| 398 |
</div>
|
| 399 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 400 |
-
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">
|
| 401 |
-
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">
|
| 402 |
</div>
|
| 403 |
</div>
|
| 404 |
</div>
|
|
@@ -420,22 +428,22 @@ export default function LandingPage() {
|
|
| 420 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 421 |
<Fingerprint className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 422 |
</div>
|
| 423 |
-
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">
|
| 424 |
-
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">
|
| 425 |
</div>
|
| 426 |
<div className="glow-card dash-border group relative p-8 md:p-10 lg:p-12 bg-theme-text/5 backdrop-blur-sm border border-theme-border rounded-3xl transition-all duration-700 hover:bg-theme-text/10 hover:-translate-y-[10px] hover:shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] scroll-reveal tilt-card">
|
| 427 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 428 |
<Activity className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 429 |
</div>
|
| 430 |
-
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">Temporal Consistency</h3>
|
| 431 |
-
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">
|
| 432 |
</div>
|
| 433 |
<div className="glow-card dash-border group relative p-8 md:p-10 lg:p-12 bg-theme-text/5 backdrop-blur-sm border border-theme-border rounded-3xl transition-all duration-700 hover:bg-theme-text/10 hover:-translate-y-[10px] hover:shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] scroll-reveal tilt-card">
|
| 434 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 435 |
<Network className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 436 |
</div>
|
| 437 |
-
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">
|
| 438 |
-
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">
|
| 439 |
</div>
|
| 440 |
</div>
|
| 441 |
</section>
|
|
@@ -459,16 +467,16 @@ export default function LandingPage() {
|
|
| 459 |
</div>
|
| 460 |
<div className="lg:w-1/2 order-1 lg:order-2">
|
| 461 |
<label className="uppercase tracking-[0.4em] text-[#d0c4bb] mb-8 block text-xs">Transparent Logic</label>
|
| 462 |
-
<h2 className="text-6xl md:text-8xl font-black tracking-tighter mb-10 leading-[0.95] text-[var(--theme-text)]">
|
| 463 |
-
<p className="text-[#d0c4bb] text-xl leading-relaxed mb-12">
|
| 464 |
<div className="grid grid-cols-2 gap-12">
|
| 465 |
<div>
|
| 466 |
-
<div className="text-[var(--theme-text)] text-3xl font-bold mb-3">0.
|
| 467 |
<div className="text-[10px] uppercase tracking-[0.2em] text-[#d0c4bb] font-semibold">Confidence Threshold</div>
|
| 468 |
</div>
|
| 469 |
<div>
|
| 470 |
-
<div className="text-[var(--theme-text)] text-3xl font-bold mb-3">
|
| 471 |
-
<div className="text-[10px] uppercase tracking-[0.2em] text-[#d0c4bb] font-semibold">Decision
|
| 472 |
</div>
|
| 473 |
</div>
|
| 474 |
</div>
|
|
@@ -480,11 +488,10 @@ export default function LandingPage() {
|
|
| 480 |
<footer className="bg-[var(--theme-bg)] w-full border-t border-theme-border relative z-10">
|
| 481 |
<div className="flex flex-col md:flex-row justify-between items-center px-12 py-24 gap-12 max-w-[1920px] mx-auto">
|
| 482 |
<div className="flex flex-col items-center md:items-start gap-4">
|
| 483 |
-
<div className="text-2xl font-black text-[var(--theme-text)] uppercase">
|
| 484 |
-
<div className="font-['Inter'] uppercase tracking-[0.15em] text-[9px] text-[#d0c4bb] opacity-60">
|
| 485 |
</div>
|
| 486 |
<div className="flex flex-wrap justify-center gap-x-12 gap-y-6 items-center">
|
| 487 |
-
<a className="font-['Inter'] uppercase tracking-[0.2em] text-[10px] text-[#d0c4bb] hover:text-[var(--theme-text)] transition-colors duration-300" href="#">Documentation</a>
|
| 488 |
<a className="font-['Inter'] uppercase tracking-[0.2em] text-[10px] text-[#d0c4bb] transition-all duration-300 flex items-center gap-2 group hover:text-green-400 hover:drop-shadow-[0_0_8px_rgba(74,222,128,0.5)] cursor-pointer" href="#">
|
| 489 |
<span>System Status</span>
|
| 490 |
<span className="text-[9px] bg-[var(--theme-text)]/5 px-2 py-0.5 rounded-full border border-[var(--theme-text)]/10 group-hover:border-green-500/30 group-hover:bg-green-500/10 flex items-center gap-1.5 transition-colors">
|
|
|
|
| 12 |
|
| 13 |
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
| 14 |
|
| 15 |
+
// --- Footer Copyright and Dynamic Date ---
|
| 16 |
+
const [year, setYear] = useState(new Date().getFullYear());
|
| 17 |
+
|
| 18 |
+
useEffect(() =>{
|
| 19 |
+
setYear(new Date().getFullYear());
|
| 20 |
+
}, []);
|
| 21 |
+
|
| 22 |
// --- React State for Loader ---
|
| 23 |
const [loadProgress, setLoadProgress] = useState(0);
|
| 24 |
const [isLoaded, setIsLoaded] = useState(false);
|
|
|
|
| 308 |
|
| 309 |
<div className="absolute inset-0 bg-[var(--theme-bg)] z-0"></div>
|
| 310 |
<div className="absolute inset-0 opacity-40 pointer-events-none bg-[radial-gradient(circle_at_50%_50%,_#11141d_0%,_var(--theme-bg)_100%)] z-[1]" id="hero-bg"></div>
|
| 311 |
+
<div className="absolute inset-0 z-[2] opacity-[0.03] pointer-events-none" style={{ backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.90' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")` }}></div>
|
| 312 |
<div className="absolute inset-0 pointer-events-none" id="hero-grid"></div>
|
| 313 |
<div className="absolute inset-0 pointer-events-none" id="hero-glow"></div>
|
| 314 |
|
|
|
|
| 317 |
{/* Main Hero Content */}
|
| 318 |
<div className={`w-full transition-all duration-1000 ease-[cubic-bezier(0.23,1,0.32,1)] ${isUploadModalOpen ? 'blur-sm opacity-50 pointer-events-none' : ''}`} id="hero-main-content">
|
| 319 |
<div className="reveal-wrap mb-6">
|
| 320 |
+
<label className="reveal-text uppercase tracking-[0.3em] text-[var(--theme-text)] block text-xs font-medium opacity-80;" style={{ transitionDelay: '0.1s' }}>Next-Gen Media Analyzer</label>
|
| 321 |
</div>
|
| 322 |
<div className="reveal-wrap mb-8">
|
| 323 |
<h1 className="reveal-text text-5xl md:text-8xl lg:text-9xl font-black tracking-[-0.04em] leading-[0.95] text-[var(--theme-text)]" style={{ transitionDelay: '0.3s' }}>
|
| 324 |
+
Spotix Intelligence.<br />
|
| 325 |
<span className="text-outline">Engineered for Truth.</span>
|
| 326 |
</h1>
|
| 327 |
</div>
|
|
|
|
| 336 |
<span className="transition-transform duration-300 group-hover:-translate-x-2">Analyze Media</span>
|
| 337 |
<ArrowRight className="w-5 h-5 ml-2 opacity-0 -translate-x-4 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-500 ease-[cubic-bezier(0.23,1,0.32,1)] absolute right-8 arrow-animate" />
|
| 338 |
</button>
|
| 339 |
+
<button className="bg-[var(--theme-text)]/5 backdrop-blur-md text-[var(--theme-text)] px-10 py-5 rounded-full text-sm font-semibold hover:bg-[var(--theme-text)]/10 transition-all border-none hover-scale relative z-20" onClick={() => router.push('/learn-more')}>
|
| 340 |
Learn More
|
| 341 |
</button>
|
| 342 |
</div>
|
|
|
|
| 368 |
<div className="max-w-[1920px] mx-auto scroll-reveal">
|
| 369 |
<div className="grid grid-cols-1 lg:grid-cols-12 gap-16 md:gap-24 items-center">
|
| 370 |
<div className="lg:col-span-5">
|
| 371 |
+
<h2 className="text-4xl md:text-5xl font-bold tracking-tight mb-8 text-[var(--theme-text)]">AI Media Authenticity Analysis</h2>
|
| 372 |
+
<p className="text-[#d0c4bb] mb-10 leading-relaxed text-lg">Analyze images and videos using multi-signal detection. Spotix evaluates structural patterns, noise characteristics, and temporal consistency to estimate authenticity.</p>
|
| 373 |
<div className="space-y-6">
|
| 374 |
<div className="flex items-center gap-4 text-sm text-[var(--theme-text)]/70">
|
| 375 |
<CheckCircle className="w-5 h-5 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 376 |
+
<span className="uppercase tracking-widest font-medium">Noise & Frequency Analysis</span>
|
| 377 |
</div>
|
| 378 |
<div className="flex items-center gap-4 text-sm text-[var(--theme-text)]/70">
|
| 379 |
<CheckCircle className="w-5 h-5 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 380 |
+
<span className="uppercase tracking-widest font-medium">Temporal Consistency Checks</span>
|
| 381 |
+
</div>
|
| 382 |
+
<div className="flex items-center gap-4 text-sm text-[var(--theme-text)]/70">
|
| 383 |
+
<CheckCircle className="w-5 h-5 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 384 |
+
<span className="uppercase tracking-widest font-medium">Explainable Results</span>
|
| 385 |
</div>
|
| 386 |
</div>
|
| 387 |
</div>
|
|
|
|
| 393 |
<div className="bg-[var(--theme-bg)]/80 backdrop-blur-md px-5 py-2.5 rounded-full border border-theme-border">
|
| 394 |
<span className="text-[10px] font-mono text-[var(--theme-text)] uppercase tracking-tighter">Status: Scanning_Buffer_04</span>
|
| 395 |
</div>
|
|
|
|
|
|
|
|
|
|
| 396 |
</div>
|
| 397 |
<div className="absolute top-1/2 left-0 w-full h-[2px] bg-[var(--theme-text)]/40 shadow-[0_0_20px_rgba(253,232,214,0.5)] animate-pulse"></div>
|
| 398 |
<div className="mt-auto grid grid-cols-3 gap-3 md:gap-6">
|
| 399 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 400 |
+
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">Authenticity Score</div>
|
| 401 |
+
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">94%</div>
|
| 402 |
</div>
|
| 403 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 404 |
+
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">Noise Consistency</div>
|
| 405 |
+
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">0.62</div>
|
| 406 |
</div>
|
| 407 |
<div className="bg-[var(--theme-bg)]/90 backdrop-blur-md p-5 rounded-2xl border border-theme-border">
|
| 408 |
+
<div className="text-[9px] text-[#d0c4bb] uppercase tracking-widest mb-2">Detection Confidence</div>
|
| 409 |
+
<div className="text-xl md:text-3xl font-bold text-[var(--theme-text)]">High</div>
|
| 410 |
</div>
|
| 411 |
</div>
|
| 412 |
</div>
|
|
|
|
| 428 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 429 |
<Fingerprint className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 430 |
</div>
|
| 431 |
+
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">Noise & Residual Analysis</h3>
|
| 432 |
+
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">Examines pixel-level noise patterns and residual signals to identify inconsistencies typical of synthetic generation.</p>
|
| 433 |
</div>
|
| 434 |
<div className="glow-card dash-border group relative p-8 md:p-10 lg:p-12 bg-theme-text/5 backdrop-blur-sm border border-theme-border rounded-3xl transition-all duration-700 hover:bg-theme-text/10 hover:-translate-y-[10px] hover:shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] scroll-reveal tilt-card">
|
| 435 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 436 |
<Activity className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 437 |
</div>
|
| 438 |
+
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">Temporal Consistency (Video)</h3>
|
| 439 |
+
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">Analyzes frame-to-frame variation to detect unnatural motion, interpolation artifacts, and structural drift.</p>
|
| 440 |
</div>
|
| 441 |
<div className="glow-card dash-border group relative p-8 md:p-10 lg:p-12 bg-theme-text/5 backdrop-blur-sm border border-theme-border rounded-3xl transition-all duration-700 hover:bg-theme-text/10 hover:-translate-y-[10px] hover:shadow-[0_30px_60px_-15px_rgba(0,0,0,0.3)] scroll-reveal tilt-card">
|
| 442 |
<div className="mb-6 md:mb-8 lg:mb-10 inline-block p-5 rounded-2xl bg-theme-text/5 text-[var(--theme-text)] transition-transform group-hover:scale-110 duration-500">
|
| 443 |
<Network className="w-10 h-10 text-[var(--theme-text)]" strokeWidth={1.5} />
|
| 444 |
</div>
|
| 445 |
+
<h3 className="text-xl md:text-2xl font-bold mb-6 text-[var(--theme-text)] transition-colors">Frequency Domain Analysis</h3>
|
| 446 |
+
<p className="text-[#d0c4bb] leading-relaxed text-base md:text-lg">Uses Fourier-based features to detect unnatural frequency distributions and over-smooth textures common in AI-generated media.</p>
|
| 447 |
</div>
|
| 448 |
</div>
|
| 449 |
</section>
|
|
|
|
| 467 |
</div>
|
| 468 |
<div className="lg:w-1/2 order-1 lg:order-2">
|
| 469 |
<label className="uppercase tracking-[0.4em] text-[#d0c4bb] mb-8 block text-xs">Transparent Logic</label>
|
| 470 |
+
<h2 className="text-6xl md:text-8xl font-black tracking-tighter mb-10 leading-[0.95] text-[var(--theme-text)]">Explainable<br />Detection</h2>
|
| 471 |
+
<p className="text-[#d0c4bb] text-xl leading-relaxed mb-12">Spotix highlights regions that contribute most to the detection score, allowing users to visually understand why a sample is flagged.</p>
|
| 472 |
<div className="grid grid-cols-2 gap-12">
|
| 473 |
<div>
|
| 474 |
+
<div className="text-[var(--theme-text)] text-3xl font-bold mb-3">0.85</div>
|
| 475 |
<div className="text-[10px] uppercase tracking-[0.2em] text-[#d0c4bb] font-semibold">Confidence Threshold</div>
|
| 476 |
</div>
|
| 477 |
<div>
|
| 478 |
+
<div className="text-[var(--theme-text)] text-3xl font-bold mb-3">Multi-signal Fusion</div>
|
| 479 |
+
<div className="text-[10px] uppercase tracking-[0.2em] text-[#d0c4bb] font-semibold">Decision Model</div>
|
| 480 |
</div>
|
| 481 |
</div>
|
| 482 |
</div>
|
|
|
|
| 488 |
<footer className="bg-[var(--theme-bg)] w-full border-t border-theme-border relative z-10">
|
| 489 |
<div className="flex flex-col md:flex-row justify-between items-center px-12 py-24 gap-12 max-w-[1920px] mx-auto">
|
| 490 |
<div className="flex flex-col items-center md:items-start gap-4">
|
| 491 |
+
<div className="text-2xl font-black text-[var(--theme-text)] uppercase">SPOTIX</div>
|
| 492 |
+
<div className="font-['Inter'] uppercase tracking-[0.15em] text-[9px] text-[#d0c4bb] opacity-60">© {year} SPOTIX KINETIC. ENGINEERED FOR THE ETHEREAL.</div>
|
| 493 |
</div>
|
| 494 |
<div className="flex flex-wrap justify-center gap-x-12 gap-y-6 items-center">
|
|
|
|
| 495 |
<a className="font-['Inter'] uppercase tracking-[0.2em] text-[10px] text-[#d0c4bb] transition-all duration-300 flex items-center gap-2 group hover:text-green-400 hover:drop-shadow-[0_0_8px_rgba(74,222,128,0.5)] cursor-pointer" href="#">
|
| 496 |
<span>System Status</span>
|
| 497 |
<span className="text-[9px] bg-[var(--theme-text)]/5 px-2 py-0.5 rounded-full border border-[var(--theme-text)]/10 group-hover:border-green-500/30 group-hover:bg-green-500/10 flex items-center gap-1.5 transition-colors">
|
frontend/components/dashboard/HistoryGrid.tsx
CHANGED
|
@@ -95,7 +95,7 @@ export default function HistoryGrid({ files, onDeleteFile }: { files: any[], onD
|
|
| 95 |
<div className="flex-1 min-w-0 !cursor-none">
|
| 96 |
<p className="text-[var(--theme-text)] text-sm font-medium truncate group-hover:text-[var(--theme-text)]/80 transition-colors !cursor-none">{file.filename || `Analysis_${file.id.substring(0,6)}`}</p>
|
| 97 |
<p className="text-[var(--theme-text)]/30 text-xs font-mono mt-1 !cursor-none">
|
| 98 |
-
{file.uploaded_at ? new Date(file.uploaded_at.endsWith('Z') ? file.uploaded_at : file.uploaded_at + 'Z').toLocaleString(
|
| 99 |
</p>
|
| 100 |
</div>
|
| 101 |
</div>
|
|
|
|
| 95 |
<div className="flex-1 min-w-0 !cursor-none">
|
| 96 |
<p className="text-[var(--theme-text)] text-sm font-medium truncate group-hover:text-[var(--theme-text)]/80 transition-colors !cursor-none">{file.filename || `Analysis_${file.id.substring(0,6)}`}</p>
|
| 97 |
<p className="text-[var(--theme-text)]/30 text-xs font-mono mt-1 !cursor-none">
|
| 98 |
+
{file.uploaded_at ? new Date(file.uploaded_at.endsWith('Z') ? file.uploaded_at : file.uploaded_at + 'Z').toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : 'Unknown Date'}
|
| 99 |
</p>
|
| 100 |
</div>
|
| 101 |
</div>
|