Deepfake Authenticator commited on
Commit
3337c5d
·
1 Parent(s): 4c0afe5

Initial Authrix deployment

Browse files
.dockerignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .git/
6
+ .gitignore
7
+ node_modules/
8
+ frontend/node_modules/
9
+ frontend/dist/
10
+ *.log
11
+ uploads/
12
+ *.mp4
13
+ *.webm
14
+ *.avi
15
+ .env
16
+ api_keys.json
17
+ usage.json
BUSINESS_MODEL.md ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 💰 Authrix Business Model & Monetization Guide
2
+
3
+ ## Revenue Streams
4
+
5
+ ### 1. **SaaS Subscription Tiers**
6
+
7
+ | Tier | Price | Analyses/Month | Features |
8
+ |------|-------|----------------|----------|
9
+ | **Free** | $0 | 10 | Browser extension, 2-min videos, community support |
10
+ | **Pro** | $9.99/mo | 100 | 10-min videos, API access (100 calls), email support |
11
+ | **Business** | $49/mo | 1,000 | Unlimited length, API (5K calls), white-label reports |
12
+ | **Enterprise** | Custom | Unlimited | On-premise, custom training, SLA, dedicated support |
13
+
14
+ ### 2. **Pay-Per-Use API**
15
+
16
+ - $0.05 per video (< 5 min)
17
+ - $0.10 per video (5-15 min)
18
+ - $0.25 per video (> 15 min)
19
+
20
+ **Target Markets:**
21
+ - Social media platforms (Facebook, TikTok, Instagram)
22
+ - News organizations (CNN, BBC, Reuters)
23
+ - Content moderation companies (Spectrum Labs, ActiveFence)
24
+ - Dating apps (Tinder, Bumble, Hinge)
25
+ - Video platforms (YouTube, Vimeo, Twitch)
26
+
27
+ ### 3. **Browser Extension Premium**
28
+
29
+ - **Free:** 5 analyses/day
30
+ - **Premium:** $4.99/month - unlimited analyses
31
+
32
+ **Distribution:**
33
+ - Chrome Web Store
34
+ - Firefox Add-ons
35
+ - Edge Add-ons
36
+
37
+ ### 4. **B2B Enterprise Solutions**
38
+
39
+ #### **Social Media Platforms**
40
+ - Real-time API integration
41
+ - Bulk scanning (millions of videos/day)
42
+ - Custom model training on platform-specific content
43
+ - **Pricing:** $5,000-$50,000/month
44
+
45
+ #### **News Organizations**
46
+ - Verification dashboard
47
+ - Team collaboration
48
+ - Audit trails & reports
49
+ - **Pricing:** $500-$5,000/month
50
+
51
+ #### **Law Enforcement**
52
+ - Forensic-grade reports
53
+ - Chain of custody
54
+ - Expert witness support
55
+ - **Pricing:** $10,000-$100,000/year
56
+
57
+ ### 5. **White-Label Licensing**
58
+
59
+ License the technology to:
60
+ - Security companies
61
+ - Social media platforms
62
+ - Government agencies
63
+
64
+ **Pricing:** $50,000-$500,000 one-time + 10-20% revenue share
65
+
66
+ ### 6. **Training & Certification**
67
+
68
+ - **Online Courses:** "Deepfake Detection Fundamentals" - $299
69
+ - **Professional Certification:** "Certified Deepfake Analyst" - $999
70
+ - **Corporate Training:** Custom workshops - $5,000-$20,000
71
+
72
+ ### 7. **Consulting Services**
73
+
74
+ - Custom model training: $10,000-$50,000
75
+ - Integration support: $200-$500/hour
76
+ - Security audits: $5,000-$25,000
77
+
78
+ ### 8. **Data Marketplace**
79
+
80
+ - Sell anonymized deepfake datasets to researchers
81
+ - **Pricing:** $5,000-$50,000 per dataset
82
+
83
+ ---
84
+
85
+ ## 📈 Growth Strategy
86
+
87
+ ### Phase 1: Launch (Months 1-3)
88
+ - Launch free tier + browser extension
89
+ - Get 1,000 users
90
+ - Focus on product-market fit
91
+ - **Goal:** Validate demand
92
+
93
+ ### Phase 2: Monetization (Months 4-6)
94
+ - Launch Pro tier
95
+ - Add API access
96
+ - Target first 10 paying customers
97
+ - **Goal:** $5,000 MRR
98
+
99
+ ### Phase 3: Scale (Months 7-12)
100
+ - Launch Business tier
101
+ - B2B sales to news orgs
102
+ - First enterprise deal
103
+ - **Goal:** $50,000 MRR
104
+
105
+ ### Phase 4: Enterprise (Year 2)
106
+ - On-premise deployments
107
+ - White-label licensing
108
+ - International expansion
109
+ - **Goal:** $500,000 MRR
110
+
111
+ ---
112
+
113
+ ## 🎯 Target Customer Segments
114
+
115
+ ### 1. **Individual Users** (Free/Pro)
116
+ - Journalists
117
+ - Content creators
118
+ - Researchers
119
+ - Privacy-conscious users
120
+
121
+ ### 2. **Small Businesses** (Pro/Business)
122
+ - Marketing agencies
123
+ - PR firms
124
+ - Small news outlets
125
+ - Influencer agencies
126
+
127
+ ### 3. **Enterprise** (Business/Enterprise)
128
+ - Social media platforms
129
+ - News organizations
130
+ - Government agencies
131
+ - Law enforcement
132
+ - Financial institutions (KYC/AML)
133
+
134
+ ---
135
+
136
+ ## 💡 Marketing Channels
137
+
138
+ ### 1. **Content Marketing**
139
+ - Blog: "How to Spot Deepfakes"
140
+ - YouTube tutorials
141
+ - Case studies
142
+ - Whitepapers
143
+
144
+ ### 2. **SEO**
145
+ - Target keywords: "deepfake detector", "AI video verification"
146
+ - Build backlinks from tech blogs
147
+
148
+ ### 3. **Partnerships**
149
+ - Integrate with video platforms
150
+ - Partner with news organizations
151
+ - Academic collaborations
152
+
153
+ ### 4. **Paid Advertising**
154
+ - Google Ads (high-intent keywords)
155
+ - LinkedIn Ads (B2B)
156
+ - Twitter/X (tech audience)
157
+
158
+ ### 5. **PR & Media**
159
+ - Press releases for major features
160
+ - Guest posts on tech blogs
161
+ - Conference speaking
162
+
163
+ ---
164
+
165
+ ## 📊 Financial Projections
166
+
167
+ ### Year 1 (Conservative)
168
+ | Revenue Stream | Monthly | Annual |
169
+ |----------------|---------|--------|
170
+ | Pro subscriptions (50 users) | $500 | $6,000 |
171
+ | Business subscriptions (10) | $490 | $5,880 |
172
+ | Enterprise (5 deals) | $25,000 | $300,000 |
173
+ | API usage | $2,000 | $24,000 |
174
+ | **Total** | **$27,990** | **$335,880** |
175
+
176
+ ### Year 1 (Optimistic)
177
+ | Revenue Stream | Monthly | Annual |
178
+ |----------------|---------|--------|
179
+ | Pro subscriptions (500 users) | $5,000 | $60,000 |
180
+ | Business subscriptions (50) | $2,450 | $29,400 |
181
+ | Enterprise (20 deals) | $100,000 | $1,200,000 |
182
+ | API usage | $20,000 | $240,000 |
183
+ | **Total** | **$127,450** | **$1,529,400** |
184
+
185
+ ### Year 2 Target
186
+ - **$5M ARR**
187
+ - 50 enterprise customers
188
+ - 10,000 paying users
189
+ - Series A funding ($5-10M)
190
+
191
+ ---
192
+
193
+ ## 🚀 Quick Start: Monetization Setup
194
+
195
+ ### 1. **Get Your API Key**
196
+ ```bash
197
+ cd backend
198
+ python -c "from auth import create_api_key; print(create_api_key('your@email.com', 'pro'))"
199
+ ```
200
+
201
+ ### 2. **Use API with Key**
202
+ ```bash
203
+ curl -X POST http://localhost:8000/analyze \
204
+ -H "X-API-Key: authrix_YOUR_KEY_HERE" \
205
+ -F "file=@video.mp4"
206
+ ```
207
+
208
+ ### 3. **Check Usage**
209
+ ```bash
210
+ python -c "from auth import check_usage_limit; print(check_usage_limit('authrix_YOUR_KEY'))"
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 📞 Next Steps to Start Earning
216
+
217
+ ### Immediate (Week 1):
218
+ 1. ✅ Set up Stripe/PayPal for payments
219
+ 2. ✅ Create landing page with pricing
220
+ 3. ✅ Launch on Product Hunt
221
+ 4. ✅ Post on HackerNews, Reddit
222
+
223
+ ### Short-term (Month 1):
224
+ 1. Get first 10 paying customers
225
+ 2. Set up customer support (email/chat)
226
+ 3. Create demo videos
227
+ 4. Reach out to 50 potential B2B customers
228
+
229
+ ### Medium-term (Months 2-3):
230
+ 1. Launch Chrome Web Store listing
231
+ 2. Write 10 blog posts for SEO
232
+ 3. Close first enterprise deal
233
+ 4. Build referral program
234
+
235
+ ### Long-term (Months 4-12):
236
+ 1. Raise seed funding ($500K-$2M)
237
+ 2. Hire sales team
238
+ 3. Expand to international markets
239
+ 4. Build mobile apps
240
+
241
+ ---
242
+
243
+ ## 🎓 Resources
244
+
245
+ - **Stripe Integration:** https://stripe.com/docs/api
246
+ - **Landing Page Builder:** https://carrd.co, https://webflow.com
247
+ - **Email Marketing:** https://mailchimp.com, https://sendgrid.com
248
+ - **Customer Support:** https://intercom.com, https://zendesk.com
249
+ - **Analytics:** https://mixpanel.com, https://amplitude.com
250
+
251
+ ---
252
+
253
+ ## 📧 Contact for Enterprise Sales
254
+
255
+ **Email:** enterprise@authrix.ai
256
+ **Demo:** https://authrix.ai/demo
257
+ **Pricing:** https://authrix.ai/pricing
258
+ **API Docs:** https://docs.authrix.ai
259
+
260
+ ---
261
+
262
+ **Built with ❤️ by the Authrix Team**
DEPLOY_HF.md ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deploy Authrix to Hugging Face Spaces (Free)
2
+
3
+ ## Why HF Spaces?
4
+ - FREE 16GB RAM
5
+ - FREE CPU (and optional GPU)
6
+ - Always-on (no sleep on free tier for public spaces)
7
+ - Built for AI apps exactly like this
8
+
9
+ ---
10
+
11
+ ## Step-by-Step Deployment
12
+
13
+ ### 1. Create HuggingFace Account
14
+ Go to https://huggingface.co/join and sign up (free)
15
+
16
+ ### 2. Create a New Space
17
+ 1. Go to https://huggingface.co/new-space
18
+ 2. Fill in:
19
+ - **Space name:** authrix (or authrix-deepfake-detector)
20
+ - **License:** MIT
21
+ - **SDK:** Docker
22
+ - **Visibility:** Public (required for free tier)
23
+ 3. Click **Create Space**
24
+
25
+ ### 3. Push Your Code
26
+
27
+ Install Git LFS first:
28
+ ```bash
29
+ # Windows
30
+ winget install Git.Git
31
+ git lfs install
32
+ ```
33
+
34
+ Then push:
35
+ ```bash
36
+ # In your project root (E:\DeepFake Detect)
37
+ git init
38
+ git add .
39
+ git commit -m "Initial deployment"
40
+
41
+ # Add HuggingFace remote (replace YOUR_USERNAME)
42
+ git remote add hf https://huggingface.co/spaces/YOUR_USERNAME/authrix
43
+
44
+ # Push (will ask for HF username + token)
45
+ git push hf main
46
+ ```
47
+
48
+ Get your HF token at: https://huggingface.co/settings/tokens
49
+ (Create a token with "write" permission)
50
+
51
+ ### 4. Wait for Build
52
+ - Build takes 10-15 minutes (downloading models)
53
+ - Watch progress at: https://huggingface.co/spaces/YOUR_USERNAME/authrix
54
+
55
+ ### 5. Update Extension API URL
56
+ Once deployed, your app will be at:
57
+ `https://YOUR_USERNAME-authrix.hf.space`
58
+
59
+ Update `extension/background.js`:
60
+ ```javascript
61
+ const API_BASE = 'https://YOUR_USERNAME-authrix.hf.space';
62
+ ```
63
+
64
+ Update `extension/popup.js`:
65
+ ```javascript
66
+ const API_BASE = 'https://YOUR_USERNAME-authrix.hf.space';
67
+ ```
68
+
69
+ Update `extension/content.js` (Open Authrix App button):
70
+ ```javascript
71
+ window.open('https://YOUR_USERNAME-authrix.hf.space', '_blank');
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Limitations of Free HF Spaces
77
+
78
+ | Feature | Free Tier |
79
+ |---------|-----------|
80
+ | RAM | 16GB ✅ |
81
+ | CPU | 2 vCPUs ✅ |
82
+ | GPU | ❌ (CPU only) |
83
+ | Storage | 50GB ✅ |
84
+ | Sleep | Never (public spaces) ✅ |
85
+ | Custom domain | ❌ (need Pro $9/mo) |
86
+ | Private space | ❌ (need Pro) |
87
+
88
+ ## Upgrade Options
89
+ - **HF Pro ($9/mo):** Custom domain, private spaces, more storage
90
+ - **HF GPU Space ($0.60/hr):** 10x faster inference with T4 GPU
91
+
92
+ ---
93
+
94
+ ## After Deployment
95
+
96
+ Your app will be live at:
97
+ `https://YOUR_USERNAME-authrix.hf.space`
98
+
99
+ Share this URL with anyone — no installation needed!
100
+
101
+ The browser extension will also work with this URL once you update `API_BASE`.
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ # System dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ libgl1-mesa-glx \
6
+ libglib2.0-0 \
7
+ libsm6 \
8
+ libxext6 \
9
+ libxrender-dev \
10
+ libgomp1 \
11
+ ffmpeg \
12
+ git \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # HuggingFace Spaces runs as user 1000
16
+ RUN useradd -m -u 1000 user
17
+ USER user
18
+ ENV HOME=/home/user \
19
+ PATH=/home/user/.local/bin:$PATH
20
+
21
+ WORKDIR /home/user/app
22
+
23
+ # Copy files
24
+ COPY --chown=user backend/ ./backend/
25
+ COPY --chown=user frontend-vanilla/ ./frontend-vanilla/
26
+
27
+ # Install dependencies
28
+ RUN pip install --no-cache-dir --upgrade pip && \
29
+ pip install --no-cache-dir -r backend/requirements.txt
30
+
31
+ # Pre-cache HuggingFace models
32
+ RUN python -c "\
33
+ from transformers import ViTForImageClassification, ViTImageProcessor; \
34
+ ViTImageProcessor.from_pretrained('dima806/deepfake_vs_real_image_detection'); \
35
+ ViTForImageClassification.from_pretrained('dima806/deepfake_vs_real_image_detection'); \
36
+ ViTImageProcessor.from_pretrained('prithivMLmods/Deep-Fake-Detector-v2-Model'); \
37
+ ViTForImageClassification.from_pretrained('prithivMLmods/Deep-Fake-Detector-v2-Model'); \
38
+ print('Models cached')"
39
+
40
+ WORKDIR /home/user/app/backend
41
+
42
+ RUN mkdir -p uploads
43
+
44
+ # HuggingFace Spaces uses port 7860
45
+ EXPOSE 7860
46
+
47
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,126 +1,19 @@
1
- # 🎯 Deepfake Authenticator
 
 
 
 
 
 
 
 
2
 
3
- An AI-powered web app that detects whether an uploaded video is real or deepfake.
4
 
5
- ## Features
6
-
7
- - **Prediction** — REAL or FAKE verdict
8
- - **Confidence Score** — 0–100% probability
9
- - **Explainable Insights** — Human-readable analysis details
10
- - **Frame Timeline** — Per-frame fake probability visualization
11
- - **Agent Architecture** — Modular pipeline of 4 specialized agents
12
-
13
- ## Agent Pipeline
14
-
15
- ```
16
- Video Upload
17
-
18
-
19
- ┌─────────────────────┐
20
- │ Frame Analyzer │ ← Extracts every 10th frame via OpenCV
21
- └─────────────────────┘
22
-
23
-
24
- ┌─────────────────────┐
25
- │ Face Detector │ ← Detects & crops faces via MediaPipe
26
- └─────────────────────┘
27
-
28
-
29
- ┌─────────────────────┐
30
- │ Decision Agent │ ← HuggingFace model OR heuristic fallback
31
- └─────────────────────┘
32
-
33
-
34
- ┌─────────────────────┐
35
- │ Report Generator │ ← Builds verdict + explanation
36
- └─────────────────────┘
37
-
38
-
39
- JSON Response → Frontend
40
- ```
41
-
42
- ## Tech Stack
43
-
44
- | Layer | Technology |
45
- |----------|-------------------------------------------------|
46
- | Backend | Python, FastAPI, OpenCV, MediaPipe |
47
- | AI Model | HuggingFace `prithivMLmods/Deepfake-Detection-Model` (or heuristic fallback) |
48
- | Frontend | HTML, TailwindCSS, Vanilla JS |
49
-
50
- ## Quick Start
51
-
52
- ### Windows
53
- ```bat
54
- setup.bat
55
- venv\Scripts\activate.bat
56
- cd backend
57
- python main.py
58
- ```
59
-
60
- ### macOS / Linux
61
- ```bash
62
- chmod +x setup.sh && ./setup.sh
63
- source venv/bin/activate
64
- cd backend && python main.py
65
- ```
66
-
67
- Then open **http://localhost:8000** in your browser.
68
-
69
- ## API
70
 
71
- ### `POST /analyze`
72
-
73
- Upload a video file for analysis.
74
-
75
- **Request:** `multipart/form-data` with `file` field
76
-
77
- **Response:**
78
- ```json
79
- {
80
- "result": "FAKE",
81
- "confidence": 78.4,
82
- "details": [
83
- "Significant facial manipulation artifacts identified",
84
- "Inconsistent manipulation across frames — typical of face-swap deepfakes",
85
- "Unnatural texture blending detected at facial boundaries",
86
- "High-frequency noise patterns inconsistent with natural video compression"
87
- ],
88
- "frame_timeline": [
89
- { "frame": 0, "fake_pct": 72.1 },
90
- { "frame": 1, "fake_pct": 81.3 }
91
- ],
92
- "metadata": {
93
- "frames_analyzed": 24,
94
- "frames_with_faces": 20,
95
- "video_duration_sec": 8.5,
96
- "video_fps": 30.0,
97
- "resolution": "1280x720"
98
- },
99
- "processing_time_sec": 4.2
100
- }
101
- ```
102
-
103
- ### `GET /health`
104
-
105
- Returns server status and active model type.
106
-
107
- ## Detection Logic
108
-
109
- **Scoring:** Average fake probability across all detected faces and frames.
110
- - `> 60%` → **FAKE**
111
- - `≤ 60%` → **REAL**
112
-
113
- **Heuristic fallback** (when HuggingFace model is unavailable) analyzes:
114
- 1. High-frequency noise patterns (Laplacian variance)
115
- 2. Color channel inconsistency
116
- 3. DCT frequency artifacts (GAN compression signatures)
117
- 4. Skin tone uniformity
118
- 5. Edge coherence anomalies
119
-
120
- ## Constraints
121
-
122
- - Runs fully locally — no data sent to external servers
123
- - Free/open-source models only
124
- - Supports: MP4, AVI, MOV, MKV, WebM, WMV
125
- - Max file size: 100 MB
126
- - Target inference time: < 10 seconds for short videos
 
1
+ ---
2
+ title: Authrix Deepfake Detector
3
+ emoji: 🔍
4
+ colorFrom: green
5
+ colorTo: emerald
6
+ sdk: docker
7
+ pinned: false
8
+ app_port: 7860
9
+ ---
10
 
11
+ # Authrix AI Deepfake Detection Engine
12
 
13
+ Upload a video and get an instant REAL / FAKE verdict powered by an ensemble of ViT models.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ ## Features
16
+ - Visual deepfake detection (2x ViT ensemble)
17
+ - Audio analysis (AI voice detection)
18
+ - Browser extension support
19
+ - REST API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
STRIPE_SETUP.md ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 💳 Stripe Subscription Setup Guide
2
+
3
+ ## Your Owner API Key
4
+
5
+ ```
6
+ 🔑 API Key: authrix_vx5b5HqXIEtAuhUJw92p-aU7Ucz34RtWHzpBCzbKqKE
7
+ ⭐ Tier: Owner (Unlimited Access)
8
+ ```
9
+
10
+ **Save this key securely!** You have unlimited analyses.
11
+
12
+ ---
13
+
14
+ ## Quick Start: Accept Payments in 30 Minutes
15
+
16
+ ### Step 1: Create Stripe Account (5 min)
17
+
18
+ 1. Go to https://dashboard.stripe.com/register
19
+ 2. Sign up with your email
20
+ 3. Complete business verification
21
+
22
+ ### Step 2: Get API Keys (2 min)
23
+
24
+ 1. Go to https://dashboard.stripe.com/apikeys
25
+ 2. Copy your **Secret Key** (starts with `sk_test_` or `sk_live_`)
26
+ 3. Add to your environment:
27
+
28
+ ```bash
29
+ # Windows
30
+ set STRIPE_SECRET_KEY=sk_test_YOUR_KEY_HERE
31
+
32
+ # Linux/Mac
33
+ export STRIPE_SECRET_KEY=sk_test_YOUR_KEY_HERE
34
+ ```
35
+
36
+ ### Step 3: Create Products & Prices (10 min)
37
+
38
+ 1. Go to https://dashboard.stripe.com/products
39
+ 2. Click **+ Add product**
40
+
41
+ **Create these products:**
42
+
43
+ #### Product 1: Authrix Pro
44
+ - **Name:** Authrix Pro
45
+ - **Description:** 100 video analyses per month
46
+ - **Pricing:**
47
+ - Monthly: $9.99/month (recurring)
48
+ - Yearly: $99/year (recurring)
49
+ - Copy the **Price ID** (starts with `price_`)
50
+
51
+ #### Product 2: Authrix Business
52
+ - **Name:** Authrix Business
53
+ - **Description:** 1,000 video analyses per month
54
+ - **Pricing:**
55
+ - Monthly: $49/month (recurring)
56
+ - Yearly: $490/year (recurring)
57
+ - Copy the **Price ID**
58
+
59
+ ### Step 4: Update Price IDs (2 min)
60
+
61
+ Edit `backend/stripe_integration.py`:
62
+
63
+ ```python
64
+ PRICE_IDS = {
65
+ "pro_monthly": "price_YOUR_PRO_MONTHLY_ID",
66
+ "pro_yearly": "price_YOUR_PRO_YEARLY_ID",
67
+ "business_monthly": "price_YOUR_BUSINESS_MONTHLY_ID",
68
+ "business_yearly": "price_YOUR_BUSINESS_YEARLY_ID",
69
+ }
70
+ ```
71
+
72
+ ### Step 5: Set Up Webhooks (5 min)
73
+
74
+ 1. Go to https://dashboard.stripe.com/webhooks
75
+ 2. Click **+ Add endpoint**
76
+ 3. **Endpoint URL:** `https://your-domain.com/api/stripe/webhook`
77
+ 4. **Events to send:**
78
+ - `checkout.session.completed`
79
+ - `customer.subscription.updated`
80
+ - `customer.subscription.deleted`
81
+ 5. Copy the **Signing secret** (starts with `whsec_`)
82
+ 6. Add to environment:
83
+
84
+ ```bash
85
+ set STRIPE_WEBHOOK_SECRET=whsec_YOUR_SECRET_HERE
86
+ ```
87
+
88
+ ### Step 6: Test Payment (5 min)
89
+
90
+ ```bash
91
+ # Start backend
92
+ python -m uvicorn main:app --port 8000
93
+
94
+ # Test checkout (in another terminal)
95
+ curl -X POST http://localhost:8000/api/stripe/create-checkout-session \
96
+ -H "Content-Type: application/json" \
97
+ -d '{
98
+ "email": "test@example.com",
99
+ "plan": "pro_monthly",
100
+ "success_url": "http://localhost:8000/success",
101
+ "cancel_url": "http://localhost:8000/pricing"
102
+ }'
103
+ ```
104
+
105
+ You'll get a `checkout_url` - open it in your browser!
106
+
107
+ **Test Card Numbers:**
108
+ - Success: `4242 4242 4242 4242`
109
+ - Decline: `4000 0000 0000 0002`
110
+ - Any future expiry date, any CVC
111
+
112
+ ---
113
+
114
+ ## Integration Examples
115
+
116
+ ### Frontend: Create Checkout
117
+
118
+ ```javascript
119
+ // When user clicks "Subscribe to Pro"
120
+ async function subscribeToPro() {
121
+ const response = await fetch('http://localhost:8000/api/stripe/create-checkout-session', {
122
+ method: 'POST',
123
+ headers: { 'Content-Type': 'application/json' },
124
+ body: JSON.stringify({
125
+ email: 'user@example.com',
126
+ plan: 'pro_monthly',
127
+ success_url: window.location.origin + '/success',
128
+ cancel_url: window.location.origin + '/pricing'
129
+ })
130
+ });
131
+
132
+ const { checkout_url } = await response.json();
133
+ window.location.href = checkout_url; // Redirect to Stripe
134
+ }
135
+ ```
136
+
137
+ ### Backend: Use API with Key
138
+
139
+ ```python
140
+ import requests
141
+
142
+ response = requests.post(
143
+ 'http://localhost:8000/analyze',
144
+ headers={'X-API-Key': 'authrix_YOUR_KEY_HERE'},
145
+ files={'file': open('video.mp4', 'rb')}
146
+ )
147
+
148
+ print(response.json())
149
+ ```
150
+
151
+ ### Extension: Add API Key
152
+
153
+ Edit `extension/background.js`:
154
+
155
+ ```javascript
156
+ const API_KEY = 'authrix_vx5b5HqXIEtAuhUJw92p-aU7Ucz34RtWHzpBCzbKqKE';
157
+
158
+ async function submitBlob(chunks, mimeType, totalSize) {
159
+ // ... existing code ...
160
+
161
+ const res = await fetch(`${API_BASE}/analyze`, {
162
+ method: 'POST',
163
+ headers: {
164
+ 'X-API-Key': API_KEY // Add this line
165
+ },
166
+ body: fd
167
+ });
168
+
169
+ // ... rest of code ...
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Pricing Page HTML
176
+
177
+ Create `pricing.html`:
178
+
179
+ ```html
180
+ <!DOCTYPE html>
181
+ <html>
182
+ <head>
183
+ <title>Authrix Pricing</title>
184
+ <script src="https://js.stripe.com/v3/"></script>
185
+ </head>
186
+ <body>
187
+ <h1>Choose Your Plan</h1>
188
+
189
+ <div class="pricing-cards">
190
+ <!-- Free -->
191
+ <div class="card">
192
+ <h2>Free</h2>
193
+ <p class="price">$0<span>/month</span></p>
194
+ <ul>
195
+ <li>10 analyses/month</li>
196
+ <li>Browser extension</li>
197
+ <li>2-min videos</li>
198
+ </ul>
199
+ <button onclick="window.location.href='/signup'">Get Started</button>
200
+ </div>
201
+
202
+ <!-- Pro -->
203
+ <div class="card popular">
204
+ <h2>Pro</h2>
205
+ <p class="price">$9.99<span>/month</span></p>
206
+ <ul>
207
+ <li>100 analyses/month</li>
208
+ <li>10-min videos</li>
209
+ <li>API access</li>
210
+ <li>Email support</li>
211
+ </ul>
212
+ <button onclick="subscribe('pro_monthly')">Subscribe</button>
213
+ </div>
214
+
215
+ <!-- Business -->
216
+ <div class="card">
217
+ <h2>Business</h2>
218
+ <p class="price">$49<span>/month</span></p>
219
+ <ul>
220
+ <li>1,000 analyses/month</li>
221
+ <li>Unlimited length</li>
222
+ <li>White-label reports</li>
223
+ <li>Priority support</li>
224
+ </ul>
225
+ <button onclick="subscribe('business_monthly')">Subscribe</button>
226
+ </div>
227
+ </div>
228
+
229
+ <script>
230
+ async function subscribe(plan) {
231
+ const email = prompt('Enter your email:');
232
+ if (!email) return;
233
+
234
+ const response = await fetch('http://localhost:8000/api/stripe/create-checkout-session', {
235
+ method: 'POST',
236
+ headers: { 'Content-Type': 'application/json' },
237
+ body: JSON.stringify({
238
+ email,
239
+ plan,
240
+ success_url: window.location.origin + '/success',
241
+ cancel_url: window.location.origin + '/pricing'
242
+ })
243
+ });
244
+
245
+ const { checkout_url } = await response.json();
246
+ window.location.href = checkout_url;
247
+ }
248
+ </script>
249
+ </body>
250
+ </html>
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Go Live Checklist
256
+
257
+ ### Before Launch:
258
+ - [ ] Switch to **Live mode** in Stripe Dashboard
259
+ - [ ] Update `STRIPE_SECRET_KEY` with live key (`sk_live_...`)
260
+ - [ ] Update webhook endpoint to production URL
261
+ - [ ] Test with real card (will charge!)
262
+ - [ ] Set up email notifications (welcome, payment failed, etc.)
263
+ - [ ] Add terms of service & privacy policy
264
+ - [ ] Set up customer support email
265
+
266
+ ### After Launch:
267
+ - [ ] Monitor Stripe Dashboard for payments
268
+ - [ ] Set up Stripe Radar for fraud prevention
269
+ - [ ] Enable 3D Secure for EU customers
270
+ - [ ] Set up tax collection (Stripe Tax)
271
+ - [ ] Create refund policy
272
+
273
+ ---
274
+
275
+ ## Revenue Tracking
276
+
277
+ ### Check Earnings:
278
+ ```bash
279
+ # Get all API keys and their tiers
280
+ python -c "from auth import load_api_keys; import json; print(json.dumps(load_api_keys(), indent=2))"
281
+
282
+ # Check usage for a key
283
+ python -c "from auth import check_usage_limit; print(check_usage_limit('authrix_YOUR_KEY'))"
284
+ ```
285
+
286
+ ### Stripe Dashboard:
287
+ - **Revenue:** https://dashboard.stripe.com/revenue
288
+ - **Customers:** https://dashboard.stripe.com/customers
289
+ - **Subscriptions:** https://dashboard.stripe.com/subscriptions
290
+
291
+ ---
292
+
293
+ ## Support & Resources
294
+
295
+ - **Stripe Docs:** https://stripe.com/docs
296
+ - **Stripe Testing:** https://stripe.com/docs/testing
297
+ - **Webhook Testing:** https://stripe.com/docs/webhooks/test
298
+ - **Stripe CLI:** https://stripe.com/docs/stripe-cli
299
+
300
+ ---
301
+
302
+ ## Next Steps
303
+
304
+ 1. **Deploy to production** (Heroku, AWS, DigitalOcean)
305
+ 2. **Get a domain** (authrix.ai, authrix.com)
306
+ 3. **Set up SSL** (Let's Encrypt, Cloudflare)
307
+ 4. **Create landing page** (Webflow, Carrd, custom)
308
+ 5. **Launch on Product Hunt**
309
+ 6. **Start marketing!**
310
+
311
+ ---
312
+
313
+ **Questions?** Open an issue or email support@authrix.ai
314
+
315
+ **Built with ❤️ by the Authrix Team**
backend/api_keys.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "c522f03ba084fc1150f9ab5bd78bf23e295dbf15cf75d232f3cf0dc3afad3641": {
3
+ "email": "demo@authrix.ai",
4
+ "tier": "pro",
5
+ "created_at": "2026-04-25T20:13:27.568274",
6
+ "active": true
7
+ },
8
+ "b35e8f8fb3db9ee0f6c4bc3b5a01112861de233b93610e5eeca87b75563ab030": {
9
+ "email": "owner@authrix.ai",
10
+ "tier": "owner",
11
+ "created_at": "2026-04-25T20:16:53.342074",
12
+ "active": true,
13
+ "unlimited": true
14
+ }
15
+ }
backend/auth.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Authrix - API Key Authentication & Usage Tracking
3
+ """
4
+
5
+ import secrets
6
+ import hashlib
7
+ from datetime import datetime, timedelta
8
+ from typing import Optional
9
+ import json
10
+ from pathlib import Path
11
+
12
+ # Simple file-based storage (replace with database in production)
13
+ API_KEYS_FILE = Path("api_keys.json")
14
+ USAGE_FILE = Path("usage.json")
15
+
16
+ # Tier limits (analyses per month)
17
+ TIER_LIMITS = {
18
+ "free": 10,
19
+ "pro": 100,
20
+ "business": 1000,
21
+ "enterprise": 999999,
22
+ "owner": 999999, # Owner has unlimited access
23
+ }
24
+
25
+ def generate_api_key() -> str:
26
+ """Generate a secure API key."""
27
+ return f"authrix_{secrets.token_urlsafe(32)}"
28
+
29
+ def hash_key(api_key: str) -> str:
30
+ """Hash API key for storage."""
31
+ return hashlib.sha256(api_key.encode()).hexdigest()
32
+
33
+ def create_api_key(email: str, tier: str = "free") -> str:
34
+ """Create a new API key for a user."""
35
+ api_key = generate_api_key()
36
+ key_hash = hash_key(api_key)
37
+
38
+ keys = load_api_keys()
39
+ keys[key_hash] = {
40
+ "email": email,
41
+ "tier": tier,
42
+ "created_at": datetime.now().isoformat(),
43
+ "active": True,
44
+ }
45
+ save_api_keys(keys)
46
+
47
+ return api_key
48
+
49
+ def validate_api_key(api_key: str) -> Optional[dict]:
50
+ """Validate API key and return user info."""
51
+ if not api_key:
52
+ return None
53
+
54
+ key_hash = hash_key(api_key)
55
+ keys = load_api_keys()
56
+
57
+ if key_hash not in keys:
58
+ return None
59
+
60
+ key_data = keys[key_hash]
61
+ if not key_data.get("active", False):
62
+ return None
63
+
64
+ return key_data
65
+
66
+ def check_usage_limit(api_key: str) -> tuple[bool, int, int]:
67
+ """
68
+ Check if user has exceeded their monthly limit.
69
+ Returns (allowed, used, limit).
70
+ """
71
+ key_data = validate_api_key(api_key)
72
+ if not key_data:
73
+ return False, 0, 0
74
+
75
+ tier = key_data.get("tier", "free")
76
+
77
+ # Owner has unlimited access
78
+ if tier == "owner" or key_data.get("unlimited", False):
79
+ return True, 0, 999999
80
+
81
+ limit = TIER_LIMITS.get(tier, 10)
82
+
83
+ # Get current month usage
84
+ key_hash = hash_key(api_key)
85
+ usage = load_usage()
86
+ current_month = datetime.now().strftime("%Y-%m")
87
+
88
+ if key_hash not in usage:
89
+ usage[key_hash] = {}
90
+
91
+ used = usage[key_hash].get(current_month, 0)
92
+
93
+ return used < limit, used, limit
94
+
95
+ def increment_usage(api_key: str):
96
+ """Increment usage counter for the current month."""
97
+ key_hash = hash_key(api_key)
98
+ usage = load_usage()
99
+ current_month = datetime.now().strftime("%Y-%m")
100
+
101
+ if key_hash not in usage:
102
+ usage[key_hash] = {}
103
+
104
+ usage[key_hash][current_month] = usage[key_hash].get(current_month, 0) + 1
105
+ save_usage(usage)
106
+
107
+ def load_api_keys() -> dict:
108
+ """Load API keys from file."""
109
+ if not API_KEYS_FILE.exists():
110
+ return {}
111
+ return json.loads(API_KEYS_FILE.read_text())
112
+
113
+ def save_api_keys(keys: dict):
114
+ """Save API keys to file."""
115
+ API_KEYS_FILE.write_text(json.dumps(keys, indent=2))
116
+
117
+ def load_usage() -> dict:
118
+ """Load usage data from file."""
119
+ if not USAGE_FILE.exists():
120
+ return {}
121
+ return json.loads(USAGE_FILE.read_text())
122
+
123
+ def save_usage(usage: dict):
124
+ """Save usage data to file."""
125
+ USAGE_FILE.write_text(json.dumps(usage, indent=2))
126
+
127
+ # Create a demo API key on first run
128
+ if not API_KEYS_FILE.exists():
129
+ demo_key = create_api_key("demo@authrix.ai", "pro")
130
+ print(f"\n🔑 Demo API Key created: {demo_key}")
131
+ print(f" Tier: Pro (100 analyses/month)")
132
+ print(f" Add to requests: X-API-Key: {demo_key}\n")
backend/create_owner_key.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Create owner API key with unlimited access
3
+ """
4
+
5
+ from auth import create_api_key, load_api_keys, save_api_keys, hash_key
6
+
7
+ # Create owner key
8
+ owner_email = "owner@authrix.ai"
9
+ owner_key = create_api_key(owner_email, "owner")
10
+
11
+ # Update to unlimited tier
12
+ keys = load_api_keys()
13
+ key_hash = hash_key(owner_key)
14
+ keys[key_hash]["tier"] = "owner"
15
+ keys[key_hash]["unlimited"] = True
16
+ save_api_keys(keys)
17
+
18
+ print("\n" + "="*60)
19
+ print("🎉 OWNER API KEY CREATED")
20
+ print("="*60)
21
+ print(f"\n📧 Email: {owner_email}")
22
+ print(f"🔑 API Key: {owner_key}")
23
+ print(f"⭐ Tier: Owner (Unlimited)")
24
+ print(f"\n💡 Add this to your requests:")
25
+ print(f' X-API-Key: {owner_key}')
26
+ print(f"\n📝 Save this key securely - it won't be shown again!")
27
+ print("="*60 + "\n")
backend/detector.py CHANGED
@@ -25,11 +25,10 @@ class FrameAnalyzerAgent:
25
  """
26
  self.sample_rate = sample_rate
27
 
28
- def extract_frames(self, video_path: str, max_frames: int = 40) -> list[np.ndarray]:
29
  """
30
  Extract sampled frames spread evenly across the full video duration.
31
- Uses uniform temporal sampling instead of fixed-interval to ensure
32
- coverage of the whole video regardless of length.
33
  """
34
  frames = []
35
  cap = cv2.VideoCapture(video_path)
@@ -86,7 +85,7 @@ class FrameAnalyzerAgent:
86
  # Detects and crops faces using MediaPipe
87
  # ─────────────────────────────────────────────
88
  class FaceDetectorAgent:
89
- def __init__(self, min_detection_confidence: float = 0.5):
90
  self.mp_face_detection = mp.solutions.face_detection
91
  self.min_confidence = min_detection_confidence
92
 
@@ -335,41 +334,47 @@ class DecisionAgent:
335
  ) -> dict:
336
  """
337
  Aggregate predictions with adaptive scoring.
338
-
339
- Key insight: deepfakes have CONSISTENTLY elevated scores across many
340
- frames, while false positives on real videos tend to have a few
341
- outlier frames with high scores but low overall consistency.
342
-
343
- Strategy:
344
- - Quality-gate blurry crops
345
- - Per-frame: mean of valid face scores
346
- - Final: weighted blend of mean + median (robust to outliers)
347
- - Also return consistency metrics for adaptive thresholding
348
  """
349
  frame_scores = []
350
  frames_with_faces = 0
351
  frames_skipped_quality = 0
352
-
353
- for i, crops in enumerate(face_crops_per_frame):
354
- if not crops:
355
- continue
356
-
357
- valid_probs = []
358
- for crop in crops:
359
- score = self.analyze_face(crop)
 
360
  if score is not None:
361
- valid_probs.append(score)
 
 
 
 
 
 
 
 
362
 
363
- if not valid_probs:
364
- frames_skipped_quality += 1
365
- continue
 
 
366
 
367
- frames_with_faces += 1
368
- frame_score = float(np.mean(valid_probs))
369
- frame_scores.append({"frame_index": i, "fake_probability": round(frame_score, 4)})
 
 
 
 
370
 
371
  if frames_skipped_quality > 0:
372
- logger.info(f"Skipped {frames_skipped_quality} frames due to low face quality")
373
 
374
  if not frame_scores:
375
  return {
@@ -388,15 +393,11 @@ class DecisionAgent:
388
  else:
389
  mean_prob = float(np.mean(probs))
390
  median_prob = float(np.median(probs))
391
- # Mean+median blend: robust to both outliers and sparse fakes
392
  overall = mean_prob * 0.65 + median_prob * 0.35
393
 
394
  overall = round(float(np.clip(overall, 0.0, 1.0)), 4)
395
 
396
- # Consistency: fraction of frames above 0.50 — high for real deepfakes
397
  consistency = sum(1 for p in probs if p > 0.50) / len(probs)
398
-
399
- # Face coverage: how much of the video had detectable faces
400
  face_coverage = frames_with_faces / max(len(frames), 1)
401
 
402
  logger.info(
@@ -422,8 +423,9 @@ class DecisionAgent:
422
  # Builds the final human-readable report
423
  # ─────────────────────────────────────────────
424
  class ReportGeneratorAgent:
425
- # Base threshold adjusted adaptively per video
426
- BASE_THRESHOLD = 0.58
 
427
 
428
  def generate(self, analysis: dict, metadata: dict, audio: dict | None = None) -> dict:
429
  prob = analysis["overall_fake_probability"]
 
25
  """
26
  self.sample_rate = sample_rate
27
 
28
+ def extract_frames(self, video_path: str, max_frames: int = 50) -> list[np.ndarray]:
29
  """
30
  Extract sampled frames spread evenly across the full video duration.
31
+ Increased max_frames from 40 to 50 for better coverage of extension captures.
 
32
  """
33
  frames = []
34
  cap = cv2.VideoCapture(video_path)
 
85
  # Detects and crops faces using MediaPipe
86
  # ─────────────────────────────────────────────
87
  class FaceDetectorAgent:
88
+ def __init__(self, min_detection_confidence: float = 0.3): # Lowered from 0.5 for compressed video
89
  self.mp_face_detection = mp.solutions.face_detection
90
  self.min_confidence = min_detection_confidence
91
 
 
334
  ) -> dict:
335
  """
336
  Aggregate predictions with adaptive scoring.
337
+ If no faces detected, falls back to full-frame analysis.
 
 
 
 
 
 
 
 
 
338
  """
339
  frame_scores = []
340
  frames_with_faces = 0
341
  frames_skipped_quality = 0
342
+ total_faces_detected = sum(len(crops) for crops in face_crops_per_frame)
343
+
344
+ # Fallback: if very few faces detected, analyze full frames instead
345
+ if total_faces_detected < 5:
346
+ logger.warning(f"Only {total_faces_detected} faces detected — using full-frame analysis")
347
+ for i, frame in enumerate(frames):
348
+ # Resize frame to 224x224 for model input
349
+ frame_resized = cv2.resize(frame, (224, 224))
350
+ score = self.analyze_face(frame_resized)
351
  if score is not None:
352
+ frames_with_faces += 1
353
+ frame_scores.append({"frame_index": i, "fake_probability": round(score, 4)})
354
+ else:
355
+ frames_skipped_quality += 1
356
+ else:
357
+ # Normal face-based analysis
358
+ for i, crops in enumerate(face_crops_per_frame):
359
+ if not crops:
360
+ continue
361
 
362
+ valid_probs = []
363
+ for crop in crops:
364
+ score = self.analyze_face(crop)
365
+ if score is not None:
366
+ valid_probs.append(score)
367
 
368
+ if not valid_probs:
369
+ frames_skipped_quality += 1
370
+ continue
371
+
372
+ frames_with_faces += 1
373
+ frame_score = float(np.mean(valid_probs))
374
+ frame_scores.append({"frame_index": i, "fake_probability": round(frame_score, 4)})
375
 
376
  if frames_skipped_quality > 0:
377
+ logger.info(f"Skipped {frames_skipped_quality} frames due to low quality")
378
 
379
  if not frame_scores:
380
  return {
 
393
  else:
394
  mean_prob = float(np.mean(probs))
395
  median_prob = float(np.median(probs))
 
396
  overall = mean_prob * 0.65 + median_prob * 0.35
397
 
398
  overall = round(float(np.clip(overall, 0.0, 1.0)), 4)
399
 
 
400
  consistency = sum(1 for p in probs if p > 0.50) / len(probs)
 
 
401
  face_coverage = frames_with_faces / max(len(frames), 1)
402
 
403
  logger.info(
 
423
  # Builds the final human-readable report
424
  # ─────────────────────────────────────────────
425
  class ReportGeneratorAgent:
426
+ # Lowered threshold for compressed video captures (extension use case)
427
+ # Original files: 0.58, Compressed captures: 0.54
428
+ BASE_THRESHOLD = 0.54
429
 
430
  def generate(self, analysis: dict, metadata: dict, audio: dict | None = None) -> dict:
431
  prob = analysis["overall_fake_probability"]
backend/main.py CHANGED
@@ -6,22 +6,85 @@ import os
6
  import uuid
7
  import logging
8
  import shutil
 
9
  from pathlib import Path
10
 
11
- from fastapi import FastAPI, File, UploadFile, HTTPException
12
  from fastapi.middleware.cors import CORSMiddleware
13
  from fastapi.staticfiles import StaticFiles
14
  from fastapi.responses import FileResponse
 
15
 
16
- from detector import DeepfakeAuthenticator
17
-
18
- # ── Logging ──────────────────────────────────
19
  logging.basicConfig(
20
  level=logging.INFO,
21
  format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
22
  )
23
  logger = logging.getLogger(__name__)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # ── App setup ────────────────────────────────
26
  app = FastAPI(
27
  title="Deepfake Authenticator API",
@@ -43,8 +106,9 @@ UPLOAD_DIR.mkdir(exist_ok=True)
43
  ALLOWED_EXTENSIONS = {".mp4", ".avi", ".mov", ".mkv", ".webm", ".wmv"}
44
  MAX_FILE_SIZE_MB = 100
45
 
46
- # ── Singleton authenticator (lazy-loaded on first request) ───
47
- authenticator: DeepfakeAuthenticator | None = None
 
48
 
49
  @app.on_event("startup")
50
  async def startup_event():
@@ -85,18 +149,17 @@ async def analyze_from_url(payload: dict):
85
  if not video_url:
86
  raise HTTPException(status_code=400, detail="No URL provided")
87
 
88
- # Use a unique prefix — yt-dlp appends its own extension
89
  tmp_prefix = UPLOAD_DIR / f"ext_{uuid.uuid4().hex}"
90
  actual_path = None
91
  downloaded = False
92
 
93
  try:
94
- # ── yt-dlp: handles YouTube, Twitter, Instagram, TikTok ─────────────
95
  try:
96
  import yt_dlp
97
  ydl_opts = {
98
  "format": "bestvideo[ext=mp4][height<=720]+bestaudio[ext=m4a]/best[ext=mp4][height<=720]/best",
99
- "outtmpl": str(tmp_prefix) + ".%(ext)s", # yt-dlp adds extension
100
  "quiet": True,
101
  "no_warnings": True,
102
  "merge_output_format": "mp4",
@@ -104,7 +167,6 @@ async def analyze_from_url(payload: dict):
104
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
105
  ydl.download([video_url])
106
 
107
- # Find whatever file yt-dlp created
108
  for ext in (".mp4", ".webm", ".mkv", ".avi", ".mov"):
109
  candidate = Path(str(tmp_prefix) + ext)
110
  if candidate.exists() and candidate.stat().st_size > 1000:
@@ -113,7 +175,6 @@ async def analyze_from_url(payload: dict):
113
  logger.info(f"yt-dlp: {actual_path.name} ({actual_path.stat().st_size // 1024}KB)")
114
  break
115
 
116
- # Glob fallback in case yt-dlp used a different naming scheme
117
  if not downloaded:
118
  for f in sorted(UPLOAD_DIR.glob(f"{tmp_prefix.name}*")):
119
  if f.stat().st_size > 1000:
@@ -127,7 +188,7 @@ async def analyze_from_url(payload: dict):
127
  except Exception as e:
128
  logger.warning(f"yt-dlp failed ({e}) — trying direct fetch")
129
 
130
- # ── Fallback: direct HTTP fetch for plain video URLs ─────────────────
131
  if not downloaded:
132
  try:
133
  import httpx
@@ -144,11 +205,14 @@ async def analyze_from_url(payload: dict):
144
  if not downloaded or actual_path is None:
145
  raise HTTPException(
146
  status_code=400,
147
- detail="Could not download video. For YouTube, ensure yt-dlp is installed: pip install yt-dlp"
148
  )
149
 
150
- # Analyze no file extension check needed here (yt-dlp handles format)
151
- result = authenticator.analyze(str(actual_path))
 
 
 
152
  return result
153
 
154
  except HTTPException:
@@ -157,7 +221,6 @@ async def analyze_from_url(payload: dict):
157
  logger.exception(f"analyze-url failed: {e}")
158
  raise HTTPException(status_code=500, detail=str(e))
159
  finally:
160
- # Clean up all files with our prefix
161
  for f in UPLOAD_DIR.glob(f"{tmp_prefix.name}*"):
162
  try:
163
  f.unlink()
@@ -166,21 +229,34 @@ async def analyze_from_url(payload: dict):
166
 
167
 
168
  @app.post("/analyze")
169
- async def analyze_video(file: UploadFile = File(...)):
 
 
 
170
  """
171
  Analyze an uploaded video for deepfake content.
172
-
173
- Returns:
174
- result: "REAL" or "FAKE"
175
- confidence: 0–100 percentage
176
- details: list of human-readable explanations
177
- frame_timeline: per-frame fake probability for visualization
178
- metadata: video info and processing stats
179
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  if not authenticator:
181
  raise HTTPException(status_code=503, detail="Server is still initializing, please retry.")
182
 
183
- # Validate file extension
184
  suffix = Path(file.filename).suffix.lower()
185
  if suffix not in ALLOWED_EXTENSIONS:
186
  raise HTTPException(
@@ -188,49 +264,68 @@ async def analyze_video(file: UploadFile = File(...)):
188
  detail=f"Unsupported file type '{suffix}'. Allowed: {', '.join(ALLOWED_EXTENSIONS)}",
189
  )
190
 
191
- # Save uploaded file with unique name
192
  unique_name = f"{uuid.uuid4().hex}{suffix}"
193
  save_path = UPLOAD_DIR / unique_name
 
194
 
195
  try:
196
- with save_path.open("wb") as f:
197
- content = await file.read()
198
-
199
- # Check file size
200
- size_mb = len(content) / (1024 * 1024)
201
- if size_mb > MAX_FILE_SIZE_MB:
202
- raise HTTPException(
203
- status_code=413,
204
- detail=f"File too large ({size_mb:.1f} MB). Max allowed: {MAX_FILE_SIZE_MB} MB",
205
- )
206
-
207
- f.write(content)
208
 
 
209
  logger.info(f"Saved upload: {unique_name} ({size_mb:.1f} MB)")
210
 
211
- # Run analysis
212
- result = authenticator.analyze(str(save_path))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  return result
214
 
215
  except HTTPException:
216
  raise
217
  except Exception as e:
218
  logger.exception(f"Analysis failed for {unique_name}: {e}")
 
 
 
 
219
  raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")
220
  finally:
221
- # Clean up uploaded file
222
- if save_path.exists():
223
- save_path.unlink()
224
- logger.info(f"Cleaned up: {unique_name}")
 
 
 
225
 
226
 
227
  # ── Serve frontend ────────────────────────────
228
- # Prefer built React dist, fall back to vanilla HTML
229
- _react_dist = Path(__file__).parent.parent / "frontend-dist"
230
- _vanilla_dir = Path(__file__).parent.parent / "frontend-vanilla"
231
 
232
  if _react_dist.exists():
233
- # Serve React SPA
234
  app.mount("/assets", StaticFiles(directory=str(_react_dist / "assets")), name="assets")
235
 
236
  @app.get("/")
@@ -240,7 +335,6 @@ if _react_dist.exists():
240
  headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
241
  )
242
 
243
- # Catch-all for React Router (SPA fallback)
244
  @app.get("/{full_path:path}")
245
  async def spa_fallback(full_path: str):
246
  index = _react_dist / "index.html"
@@ -249,7 +343,6 @@ if _react_dist.exists():
249
  return {"detail": "Not found"}
250
 
251
  elif _vanilla_dir.exists():
252
- # Fallback: vanilla HTML
253
  @app.get("/script.js")
254
  async def serve_script():
255
  return FileResponse(
@@ -258,6 +351,13 @@ elif _vanilla_dir.exists():
258
  headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
259
  )
260
 
 
 
 
 
 
 
 
261
  @app.get("/")
262
  async def serve_index():
263
  return FileResponse(
 
6
  import uuid
7
  import logging
8
  import shutil
9
+ import subprocess
10
  from pathlib import Path
11
 
12
+ from fastapi import FastAPI, File, UploadFile, HTTPException, Header
13
  from fastapi.middleware.cors import CORSMiddleware
14
  from fastapi.staticfiles import StaticFiles
15
  from fastapi.responses import FileResponse
16
+ from typing import Optional
17
 
18
+ # ── Logging (must be set up before any logger usage) ─────────────────────────
 
 
19
  logging.basicConfig(
20
  level=logging.INFO,
21
  format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
22
  )
23
  logger = logging.getLogger(__name__)
24
 
25
+ from detector import DeepfakeAuthenticator
26
+ from auth import validate_api_key, check_usage_limit, increment_usage
27
+
28
+ # ── Video conversion helper ───────────────────────────────────────────────────
29
+ def convert_to_mp4(src: Path):
30
+ """
31
+ Convert a video file to mp4 using the bundled ffmpeg binary.
32
+ Returns the converted Path, or None if conversion failed.
33
+ """
34
+ if src.suffix.lower() == ".mp4":
35
+ return None # already mp4
36
+
37
+ dst = src.with_suffix(".mp4")
38
+
39
+ # Use imageio-ffmpeg bundled binary (always available, no system install needed)
40
+ ffmpeg_bin = "ffmpeg"
41
+ try:
42
+ import imageio_ffmpeg
43
+ ffmpeg_bin = imageio_ffmpeg.get_ffmpeg_exe()
44
+ logger.info(f"Using bundled ffmpeg: {ffmpeg_bin}")
45
+ except Exception as e:
46
+ logger.warning(f"imageio_ffmpeg not available, trying system ffmpeg: {e}")
47
+
48
+ try:
49
+ result = subprocess.run(
50
+ [
51
+ ffmpeg_bin, "-y", "-i", str(src),
52
+ "-c:v", "libx264", "-preset", "ultrafast", "-crf", "28",
53
+ "-c:a", "aac", "-movflags", "+faststart",
54
+ str(dst),
55
+ ],
56
+ capture_output=True,
57
+ timeout=120,
58
+ )
59
+ if result.returncode == 0 and dst.exists() and dst.stat().st_size > 1000:
60
+ logger.info(f"Converted {src.name} -> {dst.name} ({dst.stat().st_size // 1024} KB)")
61
+ return dst
62
+ stderr = result.stderr.decode(errors="ignore")[-500:]
63
+ logger.warning(f"ffmpeg exit {result.returncode}: {stderr}")
64
+ except Exception as e:
65
+ logger.warning(f"ffmpeg conversion failed: {e}")
66
+
67
+ # Fallback: moviepy
68
+ try:
69
+ try:
70
+ from moviepy import VideoFileClip
71
+ except ImportError:
72
+ from moviepy.editor import VideoFileClip
73
+ clip = VideoFileClip(str(src))
74
+ clip.write_videofile(
75
+ str(dst), codec="libx264", audio_codec="aac",
76
+ logger=None, preset="ultrafast",
77
+ )
78
+ clip.close()
79
+ if dst.exists() and dst.stat().st_size > 1000:
80
+ logger.info(f"moviepy converted {src.name} -> {dst.name}")
81
+ return dst
82
+ except Exception as e:
83
+ logger.warning(f"moviepy conversion also failed: {e}")
84
+
85
+ return None
86
+
87
+
88
  # ── App setup ────────────────────────────────
89
  app = FastAPI(
90
  title="Deepfake Authenticator API",
 
106
  ALLOWED_EXTENSIONS = {".mp4", ".avi", ".mov", ".mkv", ".webm", ".wmv"}
107
  MAX_FILE_SIZE_MB = 100
108
 
109
+ # ── Singleton authenticator ───────────────────
110
+ authenticator = None
111
+
112
 
113
  @app.on_event("startup")
114
  async def startup_event():
 
149
  if not video_url:
150
  raise HTTPException(status_code=400, detail="No URL provided")
151
 
 
152
  tmp_prefix = UPLOAD_DIR / f"ext_{uuid.uuid4().hex}"
153
  actual_path = None
154
  downloaded = False
155
 
156
  try:
157
+ # yt-dlp: handles YouTube, Twitter, Instagram, TikTok
158
  try:
159
  import yt_dlp
160
  ydl_opts = {
161
  "format": "bestvideo[ext=mp4][height<=720]+bestaudio[ext=m4a]/best[ext=mp4][height<=720]/best",
162
+ "outtmpl": str(tmp_prefix) + ".%(ext)s",
163
  "quiet": True,
164
  "no_warnings": True,
165
  "merge_output_format": "mp4",
 
167
  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
168
  ydl.download([video_url])
169
 
 
170
  for ext in (".mp4", ".webm", ".mkv", ".avi", ".mov"):
171
  candidate = Path(str(tmp_prefix) + ext)
172
  if candidate.exists() and candidate.stat().st_size > 1000:
 
175
  logger.info(f"yt-dlp: {actual_path.name} ({actual_path.stat().st_size // 1024}KB)")
176
  break
177
 
 
178
  if not downloaded:
179
  for f in sorted(UPLOAD_DIR.glob(f"{tmp_prefix.name}*")):
180
  if f.stat().st_size > 1000:
 
188
  except Exception as e:
189
  logger.warning(f"yt-dlp failed ({e}) — trying direct fetch")
190
 
191
+ # Fallback: direct HTTP fetch
192
  if not downloaded:
193
  try:
194
  import httpx
 
205
  if not downloaded or actual_path is None:
206
  raise HTTPException(
207
  status_code=400,
208
+ detail="Could not download video. For YouTube, ensure yt-dlp is installed: pip install yt-dlp",
209
  )
210
 
211
+ # Convert if needed
212
+ converted = convert_to_mp4(actual_path)
213
+ analyze_path = converted if converted else actual_path
214
+
215
+ result = authenticator.analyze(str(analyze_path))
216
  return result
217
 
218
  except HTTPException:
 
221
  logger.exception(f"analyze-url failed: {e}")
222
  raise HTTPException(status_code=500, detail=str(e))
223
  finally:
 
224
  for f in UPLOAD_DIR.glob(f"{tmp_prefix.name}*"):
225
  try:
226
  f.unlink()
 
229
 
230
 
231
  @app.post("/analyze")
232
+ async def analyze_video(
233
+ file: UploadFile = File(...),
234
+ x_api_key: Optional[str] = Header(None, alias="X-API-Key")
235
+ ):
236
  """
237
  Analyze an uploaded video for deepfake content.
238
+ Requires API key for usage tracking and tier limits.
 
 
 
 
 
 
239
  """
240
+ # Check API key (allow localhost without key for development)
241
+ if x_api_key:
242
+ key_data = validate_api_key(x_api_key)
243
+ if not key_data:
244
+ raise HTTPException(status_code=401, detail="Invalid API key")
245
+
246
+ allowed, used, limit = check_usage_limit(x_api_key)
247
+ if not allowed:
248
+ raise HTTPException(
249
+ status_code=429,
250
+ detail=f"Monthly limit exceeded ({used}/{limit}). Upgrade your plan at https://authrix.ai/pricing"
251
+ )
252
+
253
+ logger.info(f"API request from {key_data['email']} ({key_data['tier']}) - {used+1}/{limit}")
254
+ else:
255
+ logger.info("Local request (no API key)")
256
+
257
  if not authenticator:
258
  raise HTTPException(status_code=503, detail="Server is still initializing, please retry.")
259
 
 
260
  suffix = Path(file.filename).suffix.lower()
261
  if suffix not in ALLOWED_EXTENSIONS:
262
  raise HTTPException(
 
264
  detail=f"Unsupported file type '{suffix}'. Allowed: {', '.join(ALLOWED_EXTENSIONS)}",
265
  )
266
 
 
267
  unique_name = f"{uuid.uuid4().hex}{suffix}"
268
  save_path = UPLOAD_DIR / unique_name
269
+ converted_path = None
270
 
271
  try:
272
+ content = await file.read()
273
+ size_mb = len(content) / (1024 * 1024)
274
+ if size_mb > MAX_FILE_SIZE_MB:
275
+ raise HTTPException(
276
+ status_code=413,
277
+ detail=f"File too large ({size_mb:.1f} MB). Max allowed: {MAX_FILE_SIZE_MB} MB",
278
+ )
 
 
 
 
 
279
 
280
+ save_path.write_bytes(content)
281
  logger.info(f"Saved upload: {unique_name} ({size_mb:.1f} MB)")
282
 
283
+ # Convert webm/mkv/etc to mp4 — OpenCV on Windows cannot decode webm natively
284
+ analyze_path = save_path
285
+ if suffix in (".webm", ".mkv", ".avi", ".wmv"):
286
+ logger.info(f"File has {suffix} extension — conversion needed")
287
+ converted_path = convert_to_mp4(save_path)
288
+ if converted_path:
289
+ analyze_path = converted_path
290
+ logger.info(f"✓ Conversion successful — using {analyze_path.name}")
291
+ else:
292
+ logger.error(f"✗ Conversion FAILED for {suffix} — will attempt direct analysis (likely to fail)")
293
+ else:
294
+ logger.info(f"File is {suffix} — no conversion needed")
295
+
296
+ logger.info(f"Calling authenticator.analyze({analyze_path})")
297
+ result = authenticator.analyze(str(analyze_path))
298
+
299
+ # Increment usage counter if API key provided
300
+ if x_api_key:
301
+ increment_usage(x_api_key)
302
+
303
  return result
304
 
305
  except HTTPException:
306
  raise
307
  except Exception as e:
308
  logger.exception(f"Analysis failed for {unique_name}: {e}")
309
+ # Write detailed error to file for debugging
310
+ error_log = UPLOAD_DIR / "last_error.txt"
311
+ import traceback
312
+ error_log.write_text(f"File: {unique_name}\nError: {e}\n\n{traceback.format_exc()}")
313
  raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")
314
  finally:
315
+ for p in [save_path, converted_path]:
316
+ if p is not None and p.exists():
317
+ try:
318
+ p.unlink()
319
+ logger.info(f"Cleaned up: {p.name}")
320
+ except Exception:
321
+ pass
322
 
323
 
324
  # ── Serve frontend ────────────────────────────
325
+ _react_dist = Path(__file__).parent.parent / "frontend-dist"
326
+ _vanilla_dir = Path(__file__).parent.parent / "frontend-vanilla"
 
327
 
328
  if _react_dist.exists():
 
329
  app.mount("/assets", StaticFiles(directory=str(_react_dist / "assets")), name="assets")
330
 
331
  @app.get("/")
 
335
  headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
336
  )
337
 
 
338
  @app.get("/{full_path:path}")
339
  async def spa_fallback(full_path: str):
340
  index = _react_dist / "index.html"
 
343
  return {"detail": "Not found"}
344
 
345
  elif _vanilla_dir.exists():
 
346
  @app.get("/script.js")
347
  async def serve_script():
348
  return FileResponse(
 
351
  headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
352
  )
353
 
354
+ @app.get("/pricing")
355
+ async def serve_pricing():
356
+ return FileResponse(
357
+ str(_vanilla_dir / "pricing.html"),
358
+ headers={"Cache-Control": "no-store, no-cache, must-revalidate"},
359
+ )
360
+
361
  @app.get("/")
362
  async def serve_index():
363
  return FileResponse(
backend/requirements.txt CHANGED
@@ -1,12 +1,12 @@
1
  fastapi==0.111.0
2
  uvicorn[standard]==0.29.0
3
  python-multipart==0.0.9
4
- opencv-python==4.9.0.80
5
  mediapipe==0.10.14
6
  numpy==1.26.4
7
  Pillow==10.3.0
8
 
9
- # HuggingFace models
10
  transformers>=4.41.0
11
  torch>=2.3.0
12
  torchvision
@@ -17,6 +17,8 @@ moviepy>=1.0.3
17
  librosa>=0.10.0
18
  soundfile>=0.12.1
19
 
20
- # Browser extension support
 
21
  yt-dlp>=2024.1.0
22
  httpx>=0.27.0
 
 
1
  fastapi==0.111.0
2
  uvicorn[standard]==0.29.0
3
  python-multipart==0.0.9
4
+ opencv-python-headless==4.9.0.80
5
  mediapipe==0.10.14
6
  numpy==1.26.4
7
  Pillow==10.3.0
8
 
9
+ # HuggingFace models (CPU only for deployment)
10
  transformers>=4.41.0
11
  torch>=2.3.0
12
  torchvision
 
17
  librosa>=0.10.0
18
  soundfile>=0.12.1
19
 
20
+ # Deployment extras
21
+ imageio-ffmpeg>=0.4.9
22
  yt-dlp>=2024.1.0
23
  httpx>=0.27.0
24
+ stripe>=8.0.0
backend/stripe_integration.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Authrix - Stripe Subscription Integration
3
+ """
4
+
5
+ import os
6
+ from typing import Optional
7
+ import stripe
8
+ from fastapi import APIRouter, HTTPException, Request
9
+ from pydantic import BaseModel
10
+ import logging
11
+
12
+ from auth import create_api_key, load_api_keys, save_api_keys, hash_key
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Set your Stripe secret key (get from https://dashboard.stripe.com/apikeys)
17
+ stripe.api_key = os.getenv("STRIPE_SECRET_KEY", "sk_test_YOUR_KEY_HERE")
18
+ STRIPE_WEBHOOK_SECRET = os.getenv("STRIPE_WEBHOOK_SECRET", "whsec_YOUR_WEBHOOK_SECRET")
19
+
20
+ router = APIRouter(prefix="/api/stripe", tags=["stripe"])
21
+
22
+ # Stripe Price IDs (create these in Stripe Dashboard)
23
+ PRICE_IDS = {
24
+ "pro_monthly": "price_PRO_MONTHLY_ID", # $9.99/month
25
+ "pro_yearly": "price_PRO_YEARLY_ID", # $99/year (2 months free)
26
+ "business_monthly": "price_BUSINESS_MONTHLY_ID", # $49/month
27
+ "business_yearly": "price_BUSINESS_YEARLY_ID", # $490/year
28
+ }
29
+
30
+ class CheckoutRequest(BaseModel):
31
+ email: str
32
+ plan: str # "pro_monthly", "pro_yearly", "business_monthly", "business_yearly"
33
+ success_url: str
34
+ cancel_url: str
35
+
36
+ class PortalRequest(BaseModel):
37
+ customer_id: str
38
+ return_url: str
39
+
40
+ @router.post("/create-checkout-session")
41
+ async def create_checkout_session(request: CheckoutRequest):
42
+ """
43
+ Create a Stripe Checkout session for subscription.
44
+
45
+ Usage:
46
+ POST /api/stripe/create-checkout-session
47
+ {
48
+ "email": "user@example.com",
49
+ "plan": "pro_monthly",
50
+ "success_url": "https://authrix.ai/success",
51
+ "cancel_url": "https://authrix.ai/pricing"
52
+ }
53
+ """
54
+ try:
55
+ price_id = PRICE_IDS.get(request.plan)
56
+ if not price_id:
57
+ raise HTTPException(status_code=400, detail=f"Invalid plan: {request.plan}")
58
+
59
+ # Create Stripe Checkout Session
60
+ session = stripe.checkout.Session.create(
61
+ customer_email=request.email,
62
+ payment_method_types=["card"],
63
+ line_items=[{
64
+ "price": price_id,
65
+ "quantity": 1,
66
+ }],
67
+ mode="subscription",
68
+ success_url=request.success_url + "?session_id={CHECKOUT_SESSION_ID}",
69
+ cancel_url=request.cancel_url,
70
+ metadata={
71
+ "email": request.email,
72
+ "plan": request.plan,
73
+ },
74
+ )
75
+
76
+ return {"checkout_url": session.url, "session_id": session.id}
77
+
78
+ except stripe.error.StripeError as e:
79
+ logger.error(f"Stripe error: {e}")
80
+ raise HTTPException(status_code=400, detail=str(e))
81
+
82
+ @router.post("/create-portal-session")
83
+ async def create_portal_session(request: PortalRequest):
84
+ """
85
+ Create a Stripe Customer Portal session for managing subscription.
86
+
87
+ Usage:
88
+ POST /api/stripe/create-portal-session
89
+ {
90
+ "customer_id": "cus_XXXXX",
91
+ "return_url": "https://authrix.ai/account"
92
+ }
93
+ """
94
+ try:
95
+ session = stripe.billing_portal.Session.create(
96
+ customer=request.customer_id,
97
+ return_url=request.return_url,
98
+ )
99
+ return {"portal_url": session.url}
100
+
101
+ except stripe.error.StripeError as e:
102
+ logger.error(f"Stripe error: {e}")
103
+ raise HTTPException(status_code=400, detail=str(e))
104
+
105
+ @router.post("/webhook")
106
+ async def stripe_webhook(request: Request):
107
+ """
108
+ Handle Stripe webhook events.
109
+
110
+ Events handled:
111
+ - checkout.session.completed: Create API key when subscription starts
112
+ - customer.subscription.updated: Update tier when subscription changes
113
+ - customer.subscription.deleted: Downgrade to free when subscription cancels
114
+ """
115
+ payload = await request.body()
116
+ sig_header = request.headers.get("stripe-signature")
117
+
118
+ try:
119
+ event = stripe.Webhook.construct_event(
120
+ payload, sig_header, STRIPE_WEBHOOK_SECRET
121
+ )
122
+ except ValueError:
123
+ raise HTTPException(status_code=400, detail="Invalid payload")
124
+ except stripe.error.SignatureVerificationError:
125
+ raise HTTPException(status_code=400, detail="Invalid signature")
126
+
127
+ # Handle the event
128
+ if event["type"] == "checkout.session.completed":
129
+ session = event["data"]["object"]
130
+ await handle_checkout_completed(session)
131
+
132
+ elif event["type"] == "customer.subscription.updated":
133
+ subscription = event["data"]["object"]
134
+ await handle_subscription_updated(subscription)
135
+
136
+ elif event["type"] == "customer.subscription.deleted":
137
+ subscription = event["data"]["object"]
138
+ await handle_subscription_deleted(subscription)
139
+
140
+ return {"status": "success"}
141
+
142
+ async def handle_checkout_completed(session: dict):
143
+ """Create API key when checkout completes."""
144
+ email = session["metadata"]["email"]
145
+ plan = session["metadata"]["plan"]
146
+ customer_id = session["customer"]
147
+ subscription_id = session["subscription"]
148
+
149
+ # Determine tier from plan
150
+ tier = "pro" if "pro" in plan else "business"
151
+
152
+ # Create API key
153
+ api_key = create_api_key(email, tier)
154
+
155
+ # Store Stripe customer ID and subscription ID
156
+ keys = load_api_keys()
157
+ key_hash = hash_key(api_key)
158
+ keys[key_hash]["stripe_customer_id"] = customer_id
159
+ keys[key_hash]["stripe_subscription_id"] = subscription_id
160
+ save_api_keys(keys)
161
+
162
+ logger.info(f"Created {tier} API key for {email} (customer: {customer_id})")
163
+
164
+ # TODO: Send welcome email with API key
165
+ # send_email(email, "Welcome to Authrix!", f"Your API key: {api_key}")
166
+
167
+ async def handle_subscription_updated(subscription: dict):
168
+ """Update tier when subscription changes."""
169
+ customer_id = subscription["customer"]
170
+ status = subscription["status"]
171
+
172
+ # Find API key by customer ID
173
+ keys = load_api_keys()
174
+ for key_hash, key_data in keys.items():
175
+ if key_data.get("stripe_customer_id") == customer_id:
176
+ if status == "active":
177
+ # Subscription is active - ensure tier is correct
178
+ logger.info(f"Subscription active for {key_data['email']}")
179
+ elif status in ("past_due", "unpaid"):
180
+ # Payment failed - send warning
181
+ logger.warning(f"Payment issue for {key_data['email']}")
182
+ break
183
+
184
+ async def handle_subscription_deleted(subscription: dict):
185
+ """Downgrade to free when subscription cancels."""
186
+ customer_id = subscription["customer"]
187
+
188
+ # Find API key and downgrade to free
189
+ keys = load_api_keys()
190
+ for key_hash, key_data in keys.items():
191
+ if key_data.get("stripe_customer_id") == customer_id:
192
+ keys[key_hash]["tier"] = "free"
193
+ keys[key_hash]["active"] = False # Deactivate key
194
+ save_api_keys(keys)
195
+ logger.info(f"Downgraded {key_data['email']} to free tier")
196
+ break
197
+
198
+ # Pricing plans for frontend
199
+ PRICING_PLANS = {
200
+ "free": {
201
+ "name": "Free",
202
+ "price": 0,
203
+ "interval": "month",
204
+ "analyses": 10,
205
+ "features": [
206
+ "10 video analyses per month",
207
+ "Browser extension",
208
+ "Max 2-minute videos",
209
+ "Community support",
210
+ ],
211
+ },
212
+ "pro": {
213
+ "name": "Pro",
214
+ "price_monthly": 9.99,
215
+ "price_yearly": 99,
216
+ "interval": "month",
217
+ "analyses": 100,
218
+ "features": [
219
+ "100 analyses per month",
220
+ "Up to 10-minute videos",
221
+ "API access (100 calls/month)",
222
+ "Priority processing",
223
+ "Email support",
224
+ "Batch upload",
225
+ ],
226
+ "popular": True,
227
+ },
228
+ "business": {
229
+ "name": "Business",
230
+ "price_monthly": 49,
231
+ "price_yearly": 490,
232
+ "interval": "month",
233
+ "analyses": 1000,
234
+ "features": [
235
+ "1,000 analyses per month",
236
+ "Unlimited video length",
237
+ "API access (5,000 calls/month)",
238
+ "White-label reports",
239
+ "Slack/Teams integration",
240
+ "Priority support",
241
+ "Custom branding",
242
+ ],
243
+ },
244
+ "enterprise": {
245
+ "name": "Enterprise",
246
+ "price": "Custom",
247
+ "interval": "month",
248
+ "analyses": "Unlimited",
249
+ "features": [
250
+ "Unlimited analyses",
251
+ "On-premise deployment",
252
+ "Custom model training",
253
+ "SLA guarantees",
254
+ "Dedicated support",
255
+ "Multi-user accounts",
256
+ ],
257
+ "contact": True,
258
+ },
259
+ }
260
+
261
+ @router.get("/pricing")
262
+ async def get_pricing():
263
+ """Get pricing plans."""
264
+ return PRICING_PLANS
extension/README.md CHANGED
@@ -1,11 +1,11 @@
1
  # Authrix Browser Extension
2
 
3
- Detect deepfake videos on any webpage without downloading them.
4
 
5
  ## Install (Chrome / Edge / Brave)
6
 
7
  1. Open `chrome://extensions`
8
- 2. Enable **Developer Mode** (top right toggle)
9
  3. Click **Load unpacked**
10
  4. Select this `extension/` folder
11
  5. The Authrix icon appears in your toolbar
@@ -13,6 +13,7 @@ Detect deepfake videos on any webpage without downloading them.
13
  ## Requirements
14
 
15
  The Authrix backend must be running locally:
 
16
  ```bash
17
  cd backend
18
  python -m uvicorn main:app --host 0.0.0.0 --port 8000
@@ -20,28 +21,47 @@ python -m uvicorn main:app --host 0.0.0.0 --port 8000
20
 
21
  ## Usage
22
 
23
- ### Method 1 — Right-click any video
24
- Right-click on a video element on any webpage → **"🔍 Analyze with Authrix"**
 
 
 
 
 
25
 
26
- ### Method 2 Toolbar popup
27
- Click the Authrix icon in the toolbar:
28
- - Auto-detects videos on the current page
29
- - Paste any direct video URL manually
30
- - Shows last analysis result
31
 
32
- ### Method 3 — Right-click a link
33
- Right-click any link to a video file → **"🔍 Analyze video URL with Authrix"**
 
34
 
35
- ## How it works
 
 
 
 
36
 
37
- 1. Extension captures the video URL from the page
38
- 2. Background service worker fetches the video and sends it to `localhost:8000/analyze`
39
- 3. FastAPI backend runs the full 5-agent pipeline (visual + audio)
40
- 4. Result overlay appears on the page with verdict, confidence, and insights
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  ## Limitations
43
 
44
- - **DRM-protected videos** (Netflix, Disney+) cannot be analyzed
45
- - **YouTube** the extension detects the page URL but YouTube's video streams require yt-dlp; direct URL analysis works for non-DRM videos
46
  - Backend must be running on `localhost:8000`
47
- - Large videos (>100MB) may time out
 
1
  # Authrix Browser Extension
2
 
3
+ Detect deepfake videos on any webpage no download required.
4
 
5
  ## Install (Chrome / Edge / Brave)
6
 
7
  1. Open `chrome://extensions`
8
+ 2. Enable **Developer Mode** (top-right toggle)
9
  3. Click **Load unpacked**
10
  4. Select this `extension/` folder
11
  5. The Authrix icon appears in your toolbar
 
13
  ## Requirements
14
 
15
  The Authrix backend must be running locally:
16
+
17
  ```bash
18
  cd backend
19
  python -m uvicorn main:app --host 0.0.0.0 --port 8000
 
21
 
22
  ## Usage
23
 
24
+ ### Method 1 — Toolbar popup (recommended)
25
+ Click the Authrix icon → **Capture & Analyze Video**
26
+
27
+ Records 12 seconds of the currently playing video, sends it to the local AI, and shows the verdict as an overlay on the page.
28
+
29
+ ### Method 2 — Paste a URL
30
+ Click the Authrix icon → paste a direct video URL → **Analyze**
31
 
32
+ Works with direct `.mp4` / `.webm` links. For YouTube, Twitter, TikTok etc., `yt-dlp` must be installed on the backend (`pip install yt-dlp`).
 
 
 
 
33
 
34
+ ### Method 3 — Right-click menu
35
+ - Right-click anywhere on a page → **🔍 Analyze with Authrix** (captures tab stream)
36
+ - Right-click a video link → **🔗 Analyze video URL with Authrix**
37
 
38
+ ## Architecture (MV3)
39
+
40
+ ```
41
+ popup.js
42
+ └─ sends START_CAPTURE / ANALYZE_URL → background.js
43
 
44
+ background.js
45
+ ├─ tabCapture.getMediaStreamId() (stream ID for the tab)
46
+ ├─ creates offscreen document
47
+ └─ sends RECORD_OFFSCREEN offscreen.js
48
+
49
+ offscreen.js
50
+ ├─ getUserMedia({ chromeMediaSource: 'tab' })
51
+ ├─ MediaRecorder records for 12s
52
+ └─ sends BLOB_READY → background.js
53
+
54
+ background.js
55
+ ├─ POST /analyze (blob) or POST /analyze-url (URL)
56
+ └─ sends ANALYSIS_RESULT → content.js
57
+
58
+ content.js
59
+ └─ renders overlay with verdict, confidence, details
60
+ ```
61
 
62
  ## Limitations
63
 
64
+ - **DRM-protected content** (Netflix, Disney+, Prime) cannot be captured
65
+ - **YouTube** tab capture works; URL analysis requires `yt-dlp` on the backend
66
  - Backend must be running on `localhost:8000`
67
+ - Videos > 100 MB may time out
extension/background.js CHANGED
@@ -1,40 +1,60 @@
1
  /**
2
- * Authrix Extension — Background Service Worker v2
3
  *
4
- * Strategy: Use chrome.tabCapture to record the tab's live video stream
5
- * for N seconds, then send the recorded blob to the local FastAPI backend.
6
- * No video download needed works on any platform.
 
 
 
7
  */
8
 
9
- const API_BASE = 'http://localhost:8000';
10
- const CAPTURE_SEC = 12; // seconds to record (enough for frame sampling)
 
11
 
12
  // ── Context menu ──────────────────────────────────────────────────────────────
13
  chrome.runtime.onInstalled.addListener(() => {
14
  chrome.contextMenus.create({
15
  id: 'authrix-capture',
16
- title: '🔍 Analyze with Authrix (capture tab)',
17
  contexts: ['page', 'video', 'frame'],
18
  });
 
 
 
 
 
19
  });
20
 
21
  chrome.contextMenus.onClicked.addListener((info, tab) => {
22
  if (info.menuItemId === 'authrix-capture' && tab?.id) {
23
  startCapture(tab.id);
24
  }
 
 
 
25
  });
26
 
27
  // ── Message handler ───────────────────────────────────────────────────────────
28
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
29
 
30
  if (msg.type === 'START_CAPTURE') {
31
- const tabId = sender.tab?.id || msg.tabId;
32
  startCapture(tabId)
33
  .then(() => sendResponse({ ok: true }))
34
  .catch(e => sendResponse({ ok: false, error: e.message }));
35
  return true;
36
  }
37
 
 
 
 
 
 
 
 
 
38
  if (msg.type === 'CHECK_HEALTH') {
39
  fetch(`${API_BASE}/health`)
40
  .then(r => r.json())
@@ -43,29 +63,32 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
43
  return true;
44
  }
45
 
46
- if (msg.type === 'SEND_BLOB_CHUNKS') {
47
- // Receive recorded video chunks from offscreen/content, send to backend
48
- receiveAndAnalyze(msg.chunks, msg.mimeType, sender.tab?.id)
49
- .then(result => {
50
- chrome.storage.local.set({ lastResult: { ...result, file: 'Tab capture' } });
51
- // Send result back to the content script on that tab
52
- if (sender.tab?.id) {
53
- chrome.tabs.sendMessage(sender.tab.id, { type: 'ANALYSIS_RESULT', result });
54
- }
55
- sendResponse({ ok: true, result });
56
- })
57
- .catch(e => {
58
- if (sender.tab?.id) {
59
- chrome.tabs.sendMessage(sender.tab.id, { type: 'ANALYSIS_ERROR', error: e.message });
60
- }
61
- sendResponse({ ok: false, error: e.message });
62
- });
63
- return true;
64
  }
65
 
66
- if (msg.type === 'ANALYSIS_ERROR_FROM_CONTENT') {
67
- // Bubble up errors from content script recording
68
- console.error('[Authrix BG] Content error:', msg.error);
 
 
 
 
 
 
69
  }
70
  });
71
 
@@ -73,37 +96,112 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
73
  async function startCapture(tabId) {
74
  if (!tabId) throw new Error('No tab ID');
75
 
76
- // Tell content script to show loading overlay
77
- await chrome.tabs.sendMessage(tabId, { type: 'SHOW_CAPTURE_OVERLAY' });
 
 
 
 
 
 
 
 
78
 
79
- // Get a MediaStream for the tab using tabCapture
80
- // tabCapture.getMediaStreamId gives us a stream ID usable in content scripts
81
  const streamId = await new Promise((resolve, reject) => {
82
- chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, (id) => {
83
- if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message));
84
- else resolve(id);
 
 
 
85
  });
86
  });
87
 
88
- // Send stream ID to content script — it will record and send back chunks
89
- await chrome.tabs.sendMessage(tabId, {
90
- type: 'RECORD_STREAM',
91
- streamId: streamId,
 
 
 
92
  durationMs: CAPTURE_SEC * 1000,
 
93
  });
94
  }
95
 
96
- // ── Receive chunks and analyze ────────────────────────────────────────────────
97
- async function receiveAndAnalyze(base64Chunks, mimeType, tabId) {
98
- // Reconstruct blob from base64 chunks
99
- const byteArrays = base64Chunks.map(chunk => {
100
- const binary = atob(chunk);
101
- const bytes = new Uint8Array(binary.length);
102
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
103
- return bytes;
104
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
- const blob = new Blob(byteArrays, { type: mimeType || 'video/webm' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  const filename = `capture_${Date.now()}.webm`;
108
 
109
  const fd = new FormData();
@@ -116,3 +214,22 @@ async function receiveAndAnalyze(base64Chunks, mimeType, tabId) {
116
  }
117
  return res.json();
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /**
2
+ * Authrix Extension — Background Service Worker v3
3
  *
4
+ * Architecture (MV3-compliant):
5
+ * 1. tabCapture.getMediaStreamId() → get stream ID for the target tab
6
+ * 2. Create offscreen document → only place getUserMedia(tab) works in MV3
7
+ * 3. offscreen.js records the stream and sends BLOB_READY back here
8
+ * 4. We POST the blob to the local FastAPI backend
9
+ * 5. Result is sent to the content script overlay on the original tab
10
  */
11
 
12
+ const API_BASE = 'https://aarav13-authrix.hf.space';
13
+ const CAPTURE_SEC = 20; // Increased from 12s for more sample frames
14
+ const OFFSCREEN_URL = chrome.runtime.getURL('offscreen.html');
15
 
16
  // ── Context menu ──────────────────────────────────────────────────────────────
17
  chrome.runtime.onInstalled.addListener(() => {
18
  chrome.contextMenus.create({
19
  id: 'authrix-capture',
20
+ title: '🔍 Analyze with Authrix',
21
  contexts: ['page', 'video', 'frame'],
22
  });
23
+ chrome.contextMenus.create({
24
+ id: 'authrix-url',
25
+ title: '🔗 Analyze video URL with Authrix',
26
+ contexts: ['link'],
27
+ });
28
  });
29
 
30
  chrome.contextMenus.onClicked.addListener((info, tab) => {
31
  if (info.menuItemId === 'authrix-capture' && tab?.id) {
32
  startCapture(tab.id);
33
  }
34
+ if (info.menuItemId === 'authrix-url' && tab?.id && info.linkUrl) {
35
+ analyzeUrl(info.linkUrl, tab.id);
36
+ }
37
  });
38
 
39
  // ── Message handler ───────────────────────────────────────────────────────────
40
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
41
 
42
  if (msg.type === 'START_CAPTURE') {
43
+ const tabId = msg.tabId || sender.tab?.id;
44
  startCapture(tabId)
45
  .then(() => sendResponse({ ok: true }))
46
  .catch(e => sendResponse({ ok: false, error: e.message }));
47
  return true;
48
  }
49
 
50
+ if (msg.type === 'ANALYZE_URL') {
51
+ const tabId = msg.tabId || sender.tab?.id;
52
+ analyzeUrl(msg.url, tabId)
53
+ .then(() => sendResponse({ ok: true }))
54
+ .catch(e => sendResponse({ ok: false, error: e.message }));
55
+ return true;
56
+ }
57
+
58
  if (msg.type === 'CHECK_HEALTH') {
59
  fetch(`${API_BASE}/health`)
60
  .then(r => r.json())
 
63
  return true;
64
  }
65
 
66
+ // ── Messages from offscreen document ─────────────────────────────────────
67
+ if (msg.type === 'BLOB_READY') {
68
+ handleBlobReady(msg.chunks, msg.mimeType, msg.tabId, msg.totalSize);
69
+ return false;
70
+ }
71
+
72
+ if (msg.type === 'RECORD_PROGRESS') {
73
+ if (msg.tabId) {
74
+ chrome.tabs.sendMessage(msg.tabId, {
75
+ type: 'CAPTURE_PROGRESS',
76
+ elapsed: msg.elapsed,
77
+ total: msg.total,
78
+ }).catch(() => {});
79
+ }
80
+ return false;
 
 
 
81
  }
82
 
83
+ if (msg.type === 'RECORD_ERROR') {
84
+ if (msg.tabId) {
85
+ chrome.tabs.sendMessage(msg.tabId, {
86
+ type: 'ANALYSIS_ERROR',
87
+ error: msg.error,
88
+ }).catch(() => {});
89
+ }
90
+ closeOffscreen();
91
+ return false;
92
  }
93
  });
94
 
 
96
  async function startCapture(tabId) {
97
  if (!tabId) throw new Error('No tab ID');
98
 
99
+ // Show loading overlay on the tab
100
+ await chrome.tabs.sendMessage(tabId, { type: 'SHOW_CAPTURE_OVERLAY' }).catch(() => {
101
+ // Content script may not be injected yet — inject it
102
+ return chrome.scripting.executeScript({
103
+ target: { tabId },
104
+ files: ['content.js'],
105
+ }).then(() =>
106
+ chrome.tabs.sendMessage(tabId, { type: 'SHOW_CAPTURE_OVERLAY' })
107
+ );
108
+ });
109
 
110
+ // Get stream ID (must be called from background, not offscreen)
 
111
  const streamId = await new Promise((resolve, reject) => {
112
+ chrome.tabCapture.getMediaStreamId({ targetTabId: tabId }, id => {
113
+ if (chrome.runtime.lastError) {
114
+ reject(new Error(chrome.runtime.lastError.message));
115
+ } else {
116
+ resolve(id);
117
+ }
118
  });
119
  });
120
 
121
+ // Ensure offscreen document exists
122
+ await ensureOffscreen();
123
+
124
+ // Tell offscreen to start recording
125
+ await chrome.runtime.sendMessage({
126
+ type: 'RECORD_OFFSCREEN',
127
+ streamId,
128
  durationMs: CAPTURE_SEC * 1000,
129
+ tabId,
130
  });
131
  }
132
 
133
+ // ── Analyze a direct video URL ────────────────────────────────────────────────
134
+ async function analyzeUrl(url, tabId) {
135
+ if (!url) throw new Error('No URL provided');
136
+
137
+ if (tabId) {
138
+ await chrome.tabs.sendMessage(tabId, { type: 'SHOW_URL_ANALYSIS_OVERLAY', url }).catch(() => {});
139
+ }
140
+
141
+ try {
142
+ const res = await fetch(`${API_BASE}/analyze-url`, {
143
+ method: 'POST',
144
+ headers: { 'Content-Type': 'application/json' },
145
+ body: JSON.stringify({ url }),
146
+ });
147
+
148
+ if (!res.ok) {
149
+ const err = await res.json().catch(() => ({}));
150
+ throw new Error(err.detail || `Backend error ${res.status}`);
151
+ }
152
+
153
+ const result = await res.json();
154
+ result.file = url.length > 60 ? url.slice(0, 57) + '…' : url;
155
 
156
+ chrome.storage.local.set({ lastResult: result });
157
+
158
+ if (tabId) {
159
+ chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_RESULT', result }).catch(() => {});
160
+ }
161
+ } catch (err) {
162
+ if (tabId) {
163
+ chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_ERROR', error: err.message }).catch(() => {});
164
+ }
165
+ throw err;
166
+ }
167
+ }
168
+
169
+ // ── Handle blob from offscreen ────────────────────────────────────────────────
170
+ async function handleBlobReady(chunks, mimeType, tabId, totalSize) {
171
+ closeOffscreen();
172
+
173
+ try {
174
+ if (tabId) {
175
+ chrome.tabs.sendMessage(tabId, { type: 'CAPTURE_PROGRESS', elapsed: 12, total: 12 }).catch(() => {});
176
+ }
177
+
178
+ const result = await submitBlob(chunks, mimeType, totalSize);
179
+ result.file = 'Tab capture';
180
+
181
+ chrome.storage.local.set({ lastResult: result });
182
+
183
+ if (tabId) {
184
+ chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_RESULT', result }).catch(() => {});
185
+ }
186
+ } catch (err) {
187
+ if (tabId) {
188
+ chrome.tabs.sendMessage(tabId, { type: 'ANALYSIS_ERROR', error: err.message }).catch(() => {});
189
+ }
190
+ }
191
+ }
192
+
193
+ // ── Submit blob to backend ────────────────────────────────────────────────────
194
+ async function submitBlob(chunks, mimeType, totalSize) {
195
+ // Reassemble chunks from array format back to Uint8Array
196
+ const uint8Array = new Uint8Array(totalSize);
197
+ let offset = 0;
198
+
199
+ for (const chunk of chunks) {
200
+ uint8Array.set(chunk, offset);
201
+ offset += chunk.length;
202
+ }
203
+
204
+ const blob = new Blob([uint8Array], { type: mimeType || 'video/webm' });
205
  const filename = `capture_${Date.now()}.webm`;
206
 
207
  const fd = new FormData();
 
214
  }
215
  return res.json();
216
  }
217
+
218
+ // ── Offscreen document management ────────────────────────────────────────────
219
+ async function ensureOffscreen() {
220
+ const existing = await chrome.offscreen.hasDocument().catch(() => false);
221
+ if (!existing) {
222
+ await chrome.offscreen.createDocument({
223
+ url: OFFSCREEN_URL,
224
+ reasons: ['USER_MEDIA'],
225
+ justification: 'Record tab video stream for deepfake analysis',
226
+ });
227
+ }
228
+ }
229
+
230
+ async function closeOffscreen() {
231
+ const exists = await chrome.offscreen.hasDocument().catch(() => false);
232
+ if (exists) {
233
+ await chrome.offscreen.closeDocument().catch(() => {});
234
+ }
235
+ }
extension/content.js CHANGED
@@ -1,25 +1,37 @@
1
  /**
2
- * Authrix Extension — Content Script v2
3
  *
4
- * Handles:
5
- * 1. Overlay UI (loading, result, error)
6
- * 2. Tab stream recording via MediaRecorder (using streamId from background)
7
- * 3. Sending recorded chunks back to background for analysis
8
  */
9
 
10
- // ── Message listener from background ─────────────────────────────────────────
11
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
 
12
  if (msg.type === 'SHOW_CAPTURE_OVERLAY') {
13
- showOverlay();
14
  sendResponse({ ok: true });
15
  }
16
 
17
- if (msg.type === 'RECORD_STREAM') {
18
- recordStream(msg.streamId, msg.durationMs);
19
  sendResponse({ ok: true });
20
  }
21
 
 
 
 
 
 
 
 
 
 
 
22
  if (msg.type === 'ANALYSIS_RESULT') {
 
23
  renderResult(msg.result);
24
  }
25
 
@@ -28,101 +40,25 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
28
  }
29
  });
30
 
31
- // ── Global trigger (from background context menu) ─────────────────────────────
32
- window.__authrixCapture = function() {
33
- chrome.runtime.sendMessage({ type: 'START_CAPTURE' });
34
- };
35
-
36
- // ── Record stream ─────────────────────────────────────────────────────────────
37
- async function recordStream(streamId, durationMs) {
38
- try {
39
- updateLoadingText('Connecting to video stream...');
40
-
41
- // Get the MediaStream from the stream ID
42
- const stream = await navigator.mediaDevices.getUserMedia({
43
- video: { mandatory: { chromeMediaSource: 'tab', chromeMediaSourceId: streamId } },
44
- audio: { mandatory: { chromeMediaSource: 'tab', chromeMediaSourceId: streamId } },
45
- });
46
-
47
- updateLoadingText('Recording video stream...');
48
- activateStep(0);
49
-
50
- // Pick best supported format
51
- const mimeType = getSupportedMimeType();
52
- const recorder = new MediaRecorder(stream, { mimeType, videoBitsPerSecond: 2_000_000 });
53
- const chunks = [];
54
-
55
- recorder.ondataavailable = e => { if (e.data.size > 0) chunks.push(e.data); };
56
-
57
- recorder.onstop = async () => {
58
- stream.getTracks().forEach(t => t.stop());
59
- activateStep(1);
60
- updateLoadingText('Processing captured frames...');
61
-
62
- try {
63
- // Convert chunks to base64 for message passing
64
- const blob = new Blob(chunks, { type: mimeType });
65
- const base64 = await blobToBase64(blob);
66
-
67
- activateStep(2);
68
- updateLoadingText('Running AI analysis...');
69
-
70
- // Send to background for backend submission
71
- chrome.runtime.sendMessage({
72
- type: 'SEND_BLOB_CHUNKS',
73
- chunks: [base64], // single base64 string of full blob
74
- mimeType: mimeType,
75
- });
76
- } catch (err) {
77
- showError('Failed to process recording: ' + err.message);
78
- chrome.runtime.sendMessage({ type: 'ANALYSIS_ERROR_FROM_CONTENT', error: err.message });
79
- }
80
- };
81
-
82
- recorder.onerror = e => {
83
- showError('Recording error: ' + e.error?.message);
84
- };
85
-
86
- // Record for durationMs then stop
87
- recorder.start(1000); // collect data every 1s
88
- setTimeout(() => {
89
- if (recorder.state === 'recording') recorder.stop();
90
- }, durationMs);
91
-
92
- // Update progress during recording
93
- const startTime = Date.now();
94
- const progressInterval = setInterval(() => {
95
- if (recorder.state !== 'recording') { clearInterval(progressInterval); return; }
96
- const elapsed = (Date.now() - startTime) / 1000;
97
- const total = durationMs / 1000;
98
- const pct = Math.min(99, Math.round((elapsed / total) * 60));
99
- updateLoadingText(`Recording: ${elapsed.toFixed(0)}s / ${total}s (${pct}% captured)`);
100
- }, 500);
101
-
102
- } catch (err) {
103
- showError(err.message.includes('Permission')
104
- ? 'Tab capture permission denied. Try reloading the page.'
105
- : 'Stream capture failed: ' + err.message);
106
- }
107
- }
108
-
109
- // ── Overlay UI ──────────────────────���─────────────────────────────────────────
110
- function showOverlay() {
111
  document.getElementById('authrix-overlay')?.remove();
112
 
113
  const overlay = document.createElement('div');
114
  overlay.id = 'authrix-overlay';
115
  overlay.innerHTML = `
116
  <div id="authrix-panel">
 
 
117
  <div id="authrix-header">
118
  <div id="authrix-logo">
119
  <span id="authrix-logo-icon">◉</span>
120
  <span>AUTHRIX AI</span>
121
  </div>
122
- <button id="authrix-close">✕</button>
123
  </div>
124
 
125
- <!-- Loading -->
126
  <div id="authrix-loading">
127
  <div id="authrix-radar">
128
  <div class="authrix-ring r1"></div>
@@ -130,17 +66,24 @@ function showOverlay() {
130
  <div class="authrix-ring r3"></div>
131
  <div id="authrix-radar-dot"></div>
132
  </div>
133
- <div id="authrix-loading-text">Initializing capture...</div>
 
 
134
  <div id="authrix-steps">
135
- <div class="authrix-step" id="as0">🎬 Recording tab stream</div>
 
 
136
  <div class="authrix-step" id="as1">🖼️ Extracting frames</div>
137
  <div class="authrix-step" id="as2">🧠 Running AI models</div>
138
  <div class="authrix-step" id="as3">📊 Generating report</div>
139
  </div>
140
- <div id="authrix-note">Recording ~12 seconds of video for analysis</div>
 
 
 
141
  </div>
142
 
143
- <!-- Result -->
144
  <div id="authrix-result" style="display:none;">
145
  <div id="authrix-verdict-row">
146
  <div id="authrix-badge"></div>
@@ -155,34 +98,44 @@ function showOverlay() {
155
  <span id="authrix-audio-icon">🎙️</span>
156
  <span id="authrix-audio-label"></span>
157
  </div>
 
158
  <div id="authrix-actions">
159
  <button id="authrix-reanalyze">↺ Capture Again</button>
160
  <button id="authrix-open-app">Open Authrix App ↗</button>
161
  </div>
162
  </div>
163
 
164
- <!-- Error -->
165
  <div id="authrix-error" style="display:none;">
166
  <div id="authrix-error-icon">⚠</div>
167
  <div id="authrix-error-msg"></div>
168
  <div id="authrix-error-hint"></div>
169
  <button id="authrix-retry">↺ Retry</button>
170
  </div>
 
171
  </div>
172
  `;
173
 
174
  document.body.appendChild(overlay);
175
 
 
176
  document.getElementById('authrix-close').onclick = () => overlay.remove();
177
  overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
 
178
  document.getElementById('authrix-open-app').onclick = () =>
179
- window.open('http://localhost:8000', '_blank');
 
180
  document.getElementById('authrix-reanalyze').onclick = () =>
181
  chrome.runtime.sendMessage({ type: 'START_CAPTURE' });
 
182
  document.getElementById('authrix-retry').onclick = () =>
183
  chrome.runtime.sendMessage({ type: 'START_CAPTURE' });
 
 
 
184
  }
185
 
 
186
  function updateLoadingText(text) {
187
  const el = document.getElementById('authrix-loading-text');
188
  if (el) el.textContent = text;
@@ -204,27 +157,30 @@ function showState(state) {
204
  if (error) error.style.display = state === 'error' ? 'flex' : 'none';
205
  }
206
 
 
207
  function renderResult(data) {
208
- activateStep(3);
209
-
210
  const isFake = data.result === 'FAKE';
211
- const conf = data.confidence || 0;
212
  const color = isFake ? '#ff4466' : '#00ff9c';
213
 
 
214
  const badge = document.getElementById('authrix-badge');
215
  if (badge) {
216
- badge.textContent = isFake ? '⚠' : '✓';
217
- badge.style.color = color;
218
  badge.style.borderColor = color;
219
  badge.style.boxShadow = `0 0 16px ${color}44`;
220
  }
221
 
 
222
  const vt = document.getElementById('authrix-verdict-text');
223
  if (vt) { vt.textContent = isFake ? 'DEEPFAKE DETECTED' : 'AUTHENTIC VIDEO'; vt.style.color = color; }
224
 
 
225
  const cv = document.getElementById('authrix-conf');
226
  if (cv) { cv.textContent = conf + '%'; cv.style.color = color; }
227
 
 
228
  const bar = document.getElementById('authrix-conf-bar');
229
  if (bar) {
230
  bar.style.background = isFake
@@ -234,6 +190,7 @@ function renderResult(data) {
234
  setTimeout(() => { bar.style.width = conf + '%'; }, 80);
235
  }
236
 
 
237
  const dl = document.getElementById('authrix-details');
238
  if (dl) {
239
  dl.innerHTML = (data.details || []).slice(0, 3).map(d =>
@@ -241,58 +198,68 @@ function renderResult(data) {
241
  ).join('');
242
  }
243
 
 
244
  const audioRow = document.getElementById('authrix-audio-row');
245
  if (audioRow && data.audio?.available) {
246
- const isAI = data.audio.result === 'AI_VOICE';
247
  const isMismatch = data.audio.result === 'AV_MISMATCH';
248
- const aColor = (isAI || isMismatch) ? '#ff4466' : '#00ff9c';
249
  const audioIcon = document.getElementById('authrix-audio-icon');
250
  const audioLabel = document.getElementById('authrix-audio-label');
251
  if (audioIcon) audioIcon.textContent = (isAI || isMismatch) ? '🤖' : '🎙️';
252
  if (audioLabel) {
253
  audioLabel.textContent = isMismatch
254
  ? 'AV Mismatch — face-swap detected'
255
- : isAI ? `AI Voice (${data.audio.confidence}%)`
256
- : `Human Voice (${data.audio.confidence}%)`;
 
257
  audioLabel.style.color = aColor;
258
  }
259
  audioRow.style.display = 'flex';
260
  }
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  showState('result');
263
  }
264
 
 
265
  function showError(message) {
266
- const isOffline = message?.includes('fetch') || message?.includes('Failed to fetch');
267
  const errMsg = document.getElementById('authrix-error-msg');
268
  const errHint = document.getElementById('authrix-error-hint');
269
- if (errMsg) errMsg.textContent = isOffline ? 'Authrix server is not running' : (message || 'Unknown error');
270
- if (errHint) errHint.textContent = isOffline
271
- ? 'Run: cd backend && python -m uvicorn main:app --port 8000'
272
- : 'Make sure the video is playing before capturing.';
273
- showState('error');
274
- }
275
-
276
- // ── Utilities ─────────────────────────────────────────────────────────────────
277
- function getSupportedMimeType() {
278
- const types = [
279
- 'video/webm;codecs=vp9,opus',
280
- 'video/webm;codecs=vp8,opus',
281
- 'video/webm',
282
- 'video/mp4',
283
- ];
284
- return types.find(t => MediaRecorder.isTypeSupported(t)) || 'video/webm';
285
- }
286
 
287
- function blobToBase64(blob) {
288
- return new Promise((resolve, reject) => {
289
- const reader = new FileReader();
290
- reader.onload = () => resolve(reader.result.split(',')[1]);
291
- reader.onerror = reject;
292
- reader.readAsDataURL(blob);
293
- });
 
 
 
 
294
  }
295
 
 
296
  function escHtml(s) {
297
- return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
 
 
 
 
298
  }
 
1
  /**
2
+ * Authrix Extension — Content Script v3
3
  *
4
+ * Responsibilities:
5
+ * - Render the overlay UI (loading, result, error states)
6
+ * - Relay messages from background to the overlay
7
+ * - Recording is handled by offscreen.js (MV3 requirement)
8
  */
9
 
10
+ // ── Message listener ──────────────────────────────────────────────────────────
11
  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
12
+
13
  if (msg.type === 'SHOW_CAPTURE_OVERLAY') {
14
+ showOverlay('capture');
15
  sendResponse({ ok: true });
16
  }
17
 
18
+ if (msg.type === 'SHOW_URL_ANALYSIS_OVERLAY') {
19
+ showOverlay('url', msg.url);
20
  sendResponse({ ok: true });
21
  }
22
 
23
+ if (msg.type === 'CAPTURE_PROGRESS') {
24
+ const pct = Math.min(95, Math.round((msg.elapsed / msg.total) * 65));
25
+ updateLoadingText(`Recording: ${msg.elapsed}s / ${msg.total}s`);
26
+ if (msg.elapsed === 1) activateStep(0);
27
+ if (msg.elapsed >= msg.total) {
28
+ activateStep(1);
29
+ updateLoadingText('Processing frames — running AI analysis…');
30
+ }
31
+ }
32
+
33
  if (msg.type === 'ANALYSIS_RESULT') {
34
+ activateStep(3);
35
  renderResult(msg.result);
36
  }
37
 
 
40
  }
41
  });
42
 
43
+ // ── Show overlay ──────────────────────────────────────────────────────────────
44
+ function showOverlay(mode = 'capture', url = '') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  document.getElementById('authrix-overlay')?.remove();
46
 
47
  const overlay = document.createElement('div');
48
  overlay.id = 'authrix-overlay';
49
  overlay.innerHTML = `
50
  <div id="authrix-panel">
51
+
52
+ <!-- Header -->
53
  <div id="authrix-header">
54
  <div id="authrix-logo">
55
  <span id="authrix-logo-icon">◉</span>
56
  <span>AUTHRIX AI</span>
57
  </div>
58
+ <button id="authrix-close" title="Close">✕</button>
59
  </div>
60
 
61
+ <!-- Loading state -->
62
  <div id="authrix-loading">
63
  <div id="authrix-radar">
64
  <div class="authrix-ring r1"></div>
 
66
  <div class="authrix-ring r3"></div>
67
  <div id="authrix-radar-dot"></div>
68
  </div>
69
+ <div id="authrix-loading-text">
70
+ ${mode === 'url' ? 'Downloading &amp; analyzing video…' : 'Initializing capture…'}
71
+ </div>
72
  <div id="authrix-steps">
73
+ <div class="authrix-step" id="as0">
74
+ ${mode === 'url' ? '⬇️ Downloading video' : '🎬 Recording tab stream'}
75
+ </div>
76
  <div class="authrix-step" id="as1">🖼️ Extracting frames</div>
77
  <div class="authrix-step" id="as2">🧠 Running AI models</div>
78
  <div class="authrix-step" id="as3">📊 Generating report</div>
79
  </div>
80
+ ${mode === 'url'
81
+ ? `<div id="authrix-note" style="font-family:monospace;font-size:10px;word-break:break-all;">${escHtml(url.slice(0, 80))}${url.length > 80 ? '…' : ''}</div>`
82
+ : `<div id="authrix-note">Recording ~20 seconds of video for analysis</div>`
83
+ }
84
  </div>
85
 
86
+ <!-- Result state -->
87
  <div id="authrix-result" style="display:none;">
88
  <div id="authrix-verdict-row">
89
  <div id="authrix-badge"></div>
 
98
  <span id="authrix-audio-icon">🎙️</span>
99
  <span id="authrix-audio-label"></span>
100
  </div>
101
+ <div id="authrix-meta"></div>
102
  <div id="authrix-actions">
103
  <button id="authrix-reanalyze">↺ Capture Again</button>
104
  <button id="authrix-open-app">Open Authrix App ↗</button>
105
  </div>
106
  </div>
107
 
108
+ <!-- Error state -->
109
  <div id="authrix-error" style="display:none;">
110
  <div id="authrix-error-icon">⚠</div>
111
  <div id="authrix-error-msg"></div>
112
  <div id="authrix-error-hint"></div>
113
  <button id="authrix-retry">↺ Retry</button>
114
  </div>
115
+
116
  </div>
117
  `;
118
 
119
  document.body.appendChild(overlay);
120
 
121
+ // Wire up buttons
122
  document.getElementById('authrix-close').onclick = () => overlay.remove();
123
  overlay.addEventListener('click', e => { if (e.target === overlay) overlay.remove(); });
124
+
125
  document.getElementById('authrix-open-app').onclick = () =>
126
+ window.open('https://aarav13-authrix.hf.space', '_blank');
127
+
128
  document.getElementById('authrix-reanalyze').onclick = () =>
129
  chrome.runtime.sendMessage({ type: 'START_CAPTURE' });
130
+
131
  document.getElementById('authrix-retry').onclick = () =>
132
  chrome.runtime.sendMessage({ type: 'START_CAPTURE' });
133
+
134
+ // Auto-activate first step for URL mode (no recording phase)
135
+ if (mode === 'url') activateStep(1);
136
  }
137
 
138
+ // ── Loading helpers ───────────────────────────────────────────────────────────
139
  function updateLoadingText(text) {
140
  const el = document.getElementById('authrix-loading-text');
141
  if (el) el.textContent = text;
 
157
  if (error) error.style.display = state === 'error' ? 'flex' : 'none';
158
  }
159
 
160
+ // ── Render result ─────────────────────────────────────────────────────────────
161
  function renderResult(data) {
 
 
162
  const isFake = data.result === 'FAKE';
163
+ const conf = data.confidence ?? 0;
164
  const color = isFake ? '#ff4466' : '#00ff9c';
165
 
166
+ // Badge
167
  const badge = document.getElementById('authrix-badge');
168
  if (badge) {
169
+ badge.textContent = isFake ? '⚠' : '✓';
170
+ badge.style.color = color;
171
  badge.style.borderColor = color;
172
  badge.style.boxShadow = `0 0 16px ${color}44`;
173
  }
174
 
175
+ // Verdict text
176
  const vt = document.getElementById('authrix-verdict-text');
177
  if (vt) { vt.textContent = isFake ? 'DEEPFAKE DETECTED' : 'AUTHENTIC VIDEO'; vt.style.color = color; }
178
 
179
+ // Confidence number
180
  const cv = document.getElementById('authrix-conf');
181
  if (cv) { cv.textContent = conf + '%'; cv.style.color = color; }
182
 
183
+ // Confidence bar
184
  const bar = document.getElementById('authrix-conf-bar');
185
  if (bar) {
186
  bar.style.background = isFake
 
190
  setTimeout(() => { bar.style.width = conf + '%'; }, 80);
191
  }
192
 
193
+ // Detail bullets (max 3)
194
  const dl = document.getElementById('authrix-details');
195
  if (dl) {
196
  dl.innerHTML = (data.details || []).slice(0, 3).map(d =>
 
198
  ).join('');
199
  }
200
 
201
+ // Audio row
202
  const audioRow = document.getElementById('authrix-audio-row');
203
  if (audioRow && data.audio?.available) {
204
+ const isAI = data.audio.result === 'AI_VOICE';
205
  const isMismatch = data.audio.result === 'AV_MISMATCH';
206
+ const aColor = (isAI || isMismatch) ? '#ff4466' : '#00ff9c';
207
  const audioIcon = document.getElementById('authrix-audio-icon');
208
  const audioLabel = document.getElementById('authrix-audio-label');
209
  if (audioIcon) audioIcon.textContent = (isAI || isMismatch) ? '🤖' : '🎙️';
210
  if (audioLabel) {
211
  audioLabel.textContent = isMismatch
212
  ? 'AV Mismatch — face-swap detected'
213
+ : isAI
214
+ ? `AI Voice (${data.audio.confidence}%)`
215
+ : `Human Voice (${data.audio.confidence}%)`;
216
  audioLabel.style.color = aColor;
217
  }
218
  audioRow.style.display = 'flex';
219
  }
220
 
221
+ // Metadata row
222
+ const meta = document.getElementById('authrix-meta');
223
+ if (meta && data.metadata) {
224
+ const m = data.metadata;
225
+ const parts = [];
226
+ if (m.video_duration_sec) parts.push(`${m.video_duration_sec}s`);
227
+ if (m.resolution && m.resolution !== '0x0') parts.push(m.resolution);
228
+ if (m.frames_with_faces != null) parts.push(`${m.frames_with_faces} faces`);
229
+ if (data.processing_time_sec) parts.push(`${data.processing_time_sec}s analysis`);
230
+ if (parts.length) {
231
+ meta.textContent = parts.join(' · ');
232
+ meta.style.cssText = 'font-size:10px;color:rgba(255,255,255,0.25);margin-bottom:12px;font-family:monospace;';
233
+ }
234
+ }
235
+
236
  showState('result');
237
  }
238
 
239
+ // ── Show error ────────────────────────────────────────────────────────────────
240
  function showError(message) {
241
+ const isOffline = !message || message.includes('fetch') || message.includes('Failed to fetch') || message.includes('ERR_CONNECTION');
242
  const errMsg = document.getElementById('authrix-error-msg');
243
  const errHint = document.getElementById('authrix-error-hint');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ if (errMsg) {
246
+ errMsg.textContent = isOffline
247
+ ? 'Authrix server is not running'
248
+ : (message || 'Unknown error');
249
+ }
250
+ if (errHint) {
251
+ errHint.textContent = isOffline
252
+ ? 'Visit https://aarav13-authrix.hf.space to check server status'
253
+ : 'Make sure a video is playing before capturing.';
254
+ }
255
+ showState('error');
256
  }
257
 
258
+ // ── Utility ───────────────────────────────────────────────────────────────────
259
  function escHtml(s) {
260
+ return String(s)
261
+ .replace(/&/g, '&amp;')
262
+ .replace(/</g, '&lt;')
263
+ .replace(/>/g, '&gt;')
264
+ .replace(/"/g, '&quot;');
265
  }
extension/manifest.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
  "manifest_version": 3,
3
  "name": "Authrix — Deepfake Detector",
4
- "version": "2.0.0",
5
- "description": "Detect deepfake videos on any webpage. Captures tab stream directly — no download needed. Works on YouTube, Twitter, any platform.",
6
 
7
  "permissions": [
8
  "contextMenus",
@@ -10,11 +10,13 @@
10
  "scripting",
11
  "storage",
12
  "tabCapture",
13
- "tabs"
 
14
  ],
15
 
16
  "host_permissions": [
17
- "http://localhost:8000/*"
 
18
  ],
19
 
20
  "background": {
 
1
  {
2
  "manifest_version": 3,
3
  "name": "Authrix — Deepfake Detector",
4
+ "version": "2.2.0",
5
+ "description": "Detect deepfake videos on any webpage. Captures tab stream directly — works on YouTube, Twitter, TikTok, any platform.",
6
 
7
  "permissions": [
8
  "contextMenus",
 
10
  "scripting",
11
  "storage",
12
  "tabCapture",
13
+ "tabs",
14
+ "offscreen"
15
  ],
16
 
17
  "host_permissions": [
18
+ "http://localhost:8000/*",
19
+ "https://aarav13-authrix.hf.space/*"
20
  ],
21
 
22
  "background": {
extension/offscreen.html ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><meta charset="UTF-8"/></head>
4
+ <body>
5
+ <!-- Offscreen document: handles MediaRecorder since service workers can't -->
6
+ <script src="offscreen.js"></script>
7
+ </body>
8
+ </html>
extension/offscreen.js ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Authrix Extension — Offscreen Document
3
+ *
4
+ * Offscreen documents can use getUserMedia with chromeMediaSource:'tab',
5
+ * which is NOT available in content scripts or service workers in MV3.
6
+ */
7
+
8
+ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
9
+ if (msg.type === 'RECORD_OFFSCREEN') {
10
+ startRecording(msg.streamId, msg.durationMs, msg.tabId)
11
+ .then(() => sendResponse({ ok: true }))
12
+ .catch(e => sendResponse({ ok: false, error: e.message }));
13
+ return true;
14
+ }
15
+ });
16
+
17
+ let activeRecorder = null;
18
+
19
+ async function startRecording(streamId, durationMs, tabId) {
20
+ if (activeRecorder && activeRecorder.state === 'recording') {
21
+ activeRecorder.stop();
22
+ }
23
+
24
+ const stream = await navigator.mediaDevices.getUserMedia({
25
+ video: {
26
+ mandatory: {
27
+ chromeMediaSource: 'tab',
28
+ chromeMediaSourceId: streamId,
29
+ },
30
+ },
31
+ audio: {
32
+ mandatory: {
33
+ chromeMediaSource: 'tab',
34
+ chromeMediaSourceId: streamId,
35
+ },
36
+ },
37
+ });
38
+
39
+ const mimeType = getSupportedMimeType();
40
+ const recorder = new MediaRecorder(stream, {
41
+ mimeType,
42
+ videoBitsPerSecond: 5_000_000, // Increased from 2.5Mbps for better quality
43
+ });
44
+ activeRecorder = recorder;
45
+
46
+ const chunks = [];
47
+ recorder.ondataavailable = e => {
48
+ if (e.data && e.data.size > 0) chunks.push(e.data);
49
+ };
50
+
51
+ recorder.onstop = async () => {
52
+ stream.getTracks().forEach(t => t.stop());
53
+
54
+ try {
55
+ const blob = new Blob(chunks, { type: mimeType });
56
+
57
+ // Convert blob to ArrayBuffer (more reliable than base64 for large files)
58
+ const arrayBuffer = await blob.arrayBuffer();
59
+ const uint8Array = new Uint8Array(arrayBuffer);
60
+
61
+ // Chunk into 1MB pieces to avoid message size limits
62
+ const CHUNK_SIZE = 1024 * 1024; // 1MB
63
+ const totalChunks = Math.ceil(uint8Array.length / CHUNK_SIZE);
64
+ const chunkedData = [];
65
+
66
+ for (let i = 0; i < totalChunks; i++) {
67
+ const start = i * CHUNK_SIZE;
68
+ const end = Math.min(start + CHUNK_SIZE, uint8Array.length);
69
+ const chunk = uint8Array.slice(start, end);
70
+ // Convert to regular array for JSON serialization
71
+ chunkedData.push(Array.from(chunk));
72
+ }
73
+
74
+ chrome.runtime.sendMessage({
75
+ type: 'BLOB_READY',
76
+ chunks: chunkedData,
77
+ mimeType,
78
+ tabId,
79
+ totalSize: uint8Array.length,
80
+ });
81
+ } catch (err) {
82
+ chrome.runtime.sendMessage({
83
+ type: 'RECORD_ERROR',
84
+ error: err.message,
85
+ tabId,
86
+ });
87
+ }
88
+ };
89
+
90
+ recorder.onerror = e => {
91
+ chrome.runtime.sendMessage({
92
+ type: 'RECORD_ERROR',
93
+ error: e.error?.message || 'MediaRecorder error',
94
+ tabId,
95
+ });
96
+ };
97
+
98
+ recorder.start(1000);
99
+
100
+ setTimeout(() => {
101
+ if (recorder.state === 'recording') recorder.stop();
102
+ }, durationMs);
103
+
104
+ // Progress ticks
105
+ const startTime = Date.now();
106
+ const tick = setInterval(() => {
107
+ if (recorder.state !== 'recording') { clearInterval(tick); return; }
108
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
109
+ const total = Math.round(durationMs / 1000);
110
+ chrome.runtime.sendMessage({ type: 'RECORD_PROGRESS', elapsed, total, tabId });
111
+ }, 800);
112
+ }
113
+
114
+ function getSupportedMimeType() {
115
+ const types = [
116
+ 'video/webm;codecs=vp9,opus',
117
+ 'video/webm;codecs=vp8,opus',
118
+ 'video/webm',
119
+ 'video/mp4',
120
+ ];
121
+ return types.find(t => MediaRecorder.isTypeSupported(t)) || 'video/webm';
122
+ }
extension/popup.html CHANGED
@@ -13,6 +13,7 @@
13
  font-size: 13px;
14
  }
15
 
 
16
  .header {
17
  padding: 14px 16px 12px;
18
  border-bottom: 1px solid rgba(0,255,156,0.12);
@@ -43,14 +44,19 @@
43
  border-color: rgba(255,68,102,0.3);
44
  color: #ff4466; background: rgba(255,68,102,0.08);
45
  }
 
 
 
 
46
 
47
- .body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
 
48
 
49
  /* Page info */
50
  .page-card {
51
  background: rgba(255,255,255,0.03);
52
  border: 1px solid rgba(255,255,255,0.07);
53
- border-radius: 8px; padding: 10px 12px;
54
  display: flex; align-items: center; gap: 10px;
55
  }
56
  .page-icon { font-size: 18px; flex-shrink: 0; }
@@ -62,7 +68,7 @@
62
 
63
  /* Main capture button */
64
  .btn-capture {
65
- width: 100%; padding: 14px;
66
  background: linear-gradient(135deg, #00ff9c, #00cc6a);
67
  color: #002110; border: none; border-radius: 10px;
68
  font-size: 13px; font-weight: 700; letter-spacing: 0.08em;
@@ -71,25 +77,61 @@
71
  display: flex; align-items: center; justify-content: center; gap: 8px;
72
  }
73
  .btn-capture:hover {
74
- transform: translateY(-2px);
75
  box-shadow: 0 0 36px rgba(0,255,156,0.5);
76
  }
77
  .btn-capture:active { transform: scale(0.98); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
  /* How it works */
80
  .how-it-works {
81
  background: rgba(255,255,255,0.02);
82
  border: 1px solid rgba(255,255,255,0.05);
83
- border-radius: 8px; padding: 10px 12px;
84
  }
85
  .how-label {
86
  font-size: 9px; font-weight: 700; letter-spacing: 0.15em;
87
- color: rgba(255,255,255,0.25); text-transform: uppercase; margin-bottom: 8px;
88
  }
89
  .how-step {
90
  display: flex; align-items: center; gap: 8px;
91
- font-size: 10px; color: rgba(255,255,255,0.4);
92
- padding: 3px 0;
93
  }
94
  .how-step-num {
95
  width: 16px; height: 16px; border-radius: 50%;
@@ -102,25 +144,27 @@
102
  #last-result {
103
  background: rgba(255,255,255,0.03);
104
  border: 1px solid rgba(255,255,255,0.07);
105
- border-radius: 8px; padding: 10px 12px;
106
  display: none;
107
  }
108
  .result-label {
109
  font-size: 9px; font-weight: 700; letter-spacing: 0.15em;
110
- color: rgba(255,255,255,0.25); text-transform: uppercase; margin-bottom: 6px;
111
  }
112
  .result-row { display: flex; align-items: center; justify-content: space-between; }
113
  .result-verdict { font-size: 13px; font-weight: 700; letter-spacing: 0.06em; }
114
  .result-conf { font-size: 16px; font-weight: 700; font-family: monospace; }
115
  .result-bar-wrap {
116
  height: 3px; background: rgba(255,255,255,0.06);
117
- border-radius: 99px; overflow: hidden; margin: 6px 0;
118
  }
119
  .result-bar { height: 100%; border-radius: 99px; transition: width 0.8s ease; }
120
- .result-file { font-size: 9px; color: rgba(255,255,255,0.2); font-family: monospace; }
 
121
 
 
122
  .footer {
123
- padding: 10px 16px;
124
  border-top: 1px solid rgba(255,255,255,0.05);
125
  display: flex; align-items: center; justify-content: space-between;
126
  }
@@ -130,6 +174,20 @@
130
  }
131
  .footer-link:hover { color: #00ff9c; }
132
  .model-info { font-size: 9px; color: rgba(255,255,255,0.2); letter-spacing: 0.06em; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  </style>
134
  </head>
135
  <body>
@@ -148,22 +206,38 @@
148
  <div class="page-card">
149
  <div class="page-icon">🎬</div>
150
  <div style="flex:1;min-width:0;">
151
- <div class="page-title" id="pageTitle">Loading...</div>
152
  <div class="page-sub" id="pageSub">Detecting page</div>
153
  </div>
154
  </div>
155
 
156
- <!-- Main button -->
 
 
 
 
 
 
157
  <button class="btn-capture" id="btnCapture">
158
  <span>⬤</span> Capture &amp; Analyze Video
159
  </button>
160
 
 
 
 
 
 
 
 
 
 
 
161
  <!-- How it works -->
162
  <div class="how-it-works">
163
  <div class="how-label">How it works</div>
164
- <div class="how-step"><div class="how-step-num">1</div>Records 12s of the playing video</div>
165
  <div class="how-step"><div class="how-step-num">2</div>Sends to local AI for analysis</div>
166
- <div class="how-step"><div class="how-step-num">3</div>Shows FAKE / REAL verdict</div>
167
  </div>
168
 
169
  <!-- Last result -->
 
13
  font-size: 13px;
14
  }
15
 
16
+ /* ── Header ── */
17
  .header {
18
  padding: 14px 16px 12px;
19
  border-bottom: 1px solid rgba(0,255,156,0.12);
 
44
  border-color: rgba(255,68,102,0.3);
45
  color: #ff4466; background: rgba(255,68,102,0.08);
46
  }
47
+ .status-badge.loading {
48
+ border-color: rgba(255,170,0,0.3);
49
+ color: #ffaa00; background: rgba(255,170,0,0.08);
50
+ }
51
 
52
+ /* ── Body ── */
53
+ .body { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; }
54
 
55
  /* Page info */
56
  .page-card {
57
  background: rgba(255,255,255,0.03);
58
  border: 1px solid rgba(255,255,255,0.07);
59
+ border-radius: 8px; padding: 9px 12px;
60
  display: flex; align-items: center; gap: 10px;
61
  }
62
  .page-icon { font-size: 18px; flex-shrink: 0; }
 
68
 
69
  /* Main capture button */
70
  .btn-capture {
71
+ width: 100%; padding: 13px;
72
  background: linear-gradient(135deg, #00ff9c, #00cc6a);
73
  color: #002110; border: none; border-radius: 10px;
74
  font-size: 13px; font-weight: 700; letter-spacing: 0.08em;
 
77
  display: flex; align-items: center; justify-content: center; gap: 8px;
78
  }
79
  .btn-capture:hover {
80
+ transform: translateY(-1px);
81
  box-shadow: 0 0 36px rgba(0,255,156,0.5);
82
  }
83
  .btn-capture:active { transform: scale(0.98); }
84
+ .btn-capture:disabled {
85
+ opacity: 0.4; cursor: not-allowed; transform: none;
86
+ box-shadow: none;
87
+ }
88
+
89
+ /* Divider */
90
+ .divider {
91
+ display: flex; align-items: center; gap: 8px;
92
+ color: rgba(255,255,255,0.2); font-size: 10px; letter-spacing: 0.1em;
93
+ }
94
+ .divider::before, .divider::after {
95
+ content: ''; flex: 1; height: 1px;
96
+ background: rgba(255,255,255,0.07);
97
+ }
98
+
99
+ /* URL input */
100
+ .url-row { display: flex; gap: 6px; }
101
+ .url-input {
102
+ flex: 1; padding: 8px 10px;
103
+ background: rgba(255,255,255,0.04);
104
+ border: 1px solid rgba(255,255,255,0.1);
105
+ border-radius: 7px; color: #dae5da;
106
+ font-size: 11px; font-family: monospace;
107
+ outline: none; transition: border-color 0.2s;
108
+ }
109
+ .url-input::placeholder { color: rgba(255,255,255,0.2); }
110
+ .url-input:focus { border-color: rgba(0,255,156,0.4); }
111
+ .btn-url {
112
+ padding: 8px 12px;
113
+ background: rgba(0,255,156,0.1);
114
+ border: 1px solid rgba(0,255,156,0.25);
115
+ border-radius: 7px; color: #00ff9c;
116
+ font-size: 11px; font-weight: 600; cursor: pointer;
117
+ transition: all 0.2s; white-space: nowrap;
118
+ }
119
+ .btn-url:hover { background: rgba(0,255,156,0.18); }
120
+ .btn-url:disabled { opacity: 0.4; cursor: not-allowed; }
121
 
122
  /* How it works */
123
  .how-it-works {
124
  background: rgba(255,255,255,0.02);
125
  border: 1px solid rgba(255,255,255,0.05);
126
+ border-radius: 8px; padding: 9px 12px;
127
  }
128
  .how-label {
129
  font-size: 9px; font-weight: 700; letter-spacing: 0.15em;
130
+ color: rgba(255,255,255,0.25); text-transform: uppercase; margin-bottom: 7px;
131
  }
132
  .how-step {
133
  display: flex; align-items: center; gap: 8px;
134
+ font-size: 10px; color: rgba(255,255,255,0.4); padding: 2px 0;
 
135
  }
136
  .how-step-num {
137
  width: 16px; height: 16px; border-radius: 50%;
 
144
  #last-result {
145
  background: rgba(255,255,255,0.03);
146
  border: 1px solid rgba(255,255,255,0.07);
147
+ border-radius: 8px; padding: 9px 12px;
148
  display: none;
149
  }
150
  .result-label {
151
  font-size: 9px; font-weight: 700; letter-spacing: 0.15em;
152
+ color: rgba(255,255,255,0.25); text-transform: uppercase; margin-bottom: 5px;
153
  }
154
  .result-row { display: flex; align-items: center; justify-content: space-between; }
155
  .result-verdict { font-size: 13px; font-weight: 700; letter-spacing: 0.06em; }
156
  .result-conf { font-size: 16px; font-weight: 700; font-family: monospace; }
157
  .result-bar-wrap {
158
  height: 3px; background: rgba(255,255,255,0.06);
159
+ border-radius: 99px; overflow: hidden; margin: 5px 0;
160
  }
161
  .result-bar { height: 100%; border-radius: 99px; transition: width 0.8s ease; }
162
+ .result-file { font-size: 9px; color: rgba(255,255,255,0.2); font-family: monospace;
163
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
164
 
165
+ /* Footer */
166
  .footer {
167
+ padding: 9px 16px;
168
  border-top: 1px solid rgba(255,255,255,0.05);
169
  display: flex; align-items: center; justify-content: space-between;
170
  }
 
174
  }
175
  .footer-link:hover { color: #00ff9c; }
176
  .model-info { font-size: 9px; color: rgba(255,255,255,0.2); letter-spacing: 0.06em; }
177
+
178
+ /* Offline warning */
179
+ #offline-warn {
180
+ display: none;
181
+ background: rgba(255,68,102,0.06);
182
+ border: 1px solid rgba(255,68,102,0.2);
183
+ border-radius: 7px; padding: 8px 10px;
184
+ font-size: 10px; color: rgba(255,68,102,0.8);
185
+ line-height: 1.5;
186
+ }
187
+ #offline-warn code {
188
+ display: block; margin-top: 4px;
189
+ font-family: monospace; color: rgba(255,68,102,0.6);
190
+ }
191
  </style>
192
  </head>
193
  <body>
 
206
  <div class="page-card">
207
  <div class="page-icon">🎬</div>
208
  <div style="flex:1;min-width:0;">
209
+ <div class="page-title" id="pageTitle">Loading</div>
210
  <div class="page-sub" id="pageSub">Detecting page</div>
211
  </div>
212
  </div>
213
 
214
+ <!-- Offline warning -->
215
+ <div id="offline-warn">
216
+ Backend not reachable.
217
+ <code>Visit: aarav13-authrix.hf.space</code>
218
+ </div>
219
+
220
+ <!-- Main capture button -->
221
  <button class="btn-capture" id="btnCapture">
222
  <span>⬤</span> Capture &amp; Analyze Video
223
  </button>
224
 
225
+ <!-- Divider -->
226
+ <div class="divider">OR PASTE URL</div>
227
+
228
+ <!-- URL input -->
229
+ <div class="url-row">
230
+ <input class="url-input" id="urlInput" type="url"
231
+ placeholder="https://example.com/video.mp4" />
232
+ <button class="btn-url" id="btnUrl">Analyze</button>
233
+ </div>
234
+
235
  <!-- How it works -->
236
  <div class="how-it-works">
237
  <div class="how-label">How it works</div>
238
+ <div class="how-step"><div class="how-step-num">1</div>Records 20s of the playing video</div>
239
  <div class="how-step"><div class="how-step-num">2</div>Sends to local AI for analysis</div>
240
+ <div class="how-step"><div class="how-step-num">3</div>Shows FAKE / REAL verdict on page</div>
241
  </div>
242
 
243
  <!-- Last result -->
extension/popup.js CHANGED
@@ -1,35 +1,45 @@
1
  /**
2
- * Authrix Extension — Popup Script v2
3
- * Simple popup: check health, show status, trigger capture on click.
4
  */
5
 
6
- const API_BASE = 'http://localhost:8000';
7
 
8
  document.addEventListener('DOMContentLoaded', async () => {
9
- await checkHealth();
10
- loadLastResult();
11
- wireButtons();
12
  updatePageInfo();
 
 
13
  });
14
 
15
  // ── Health check ──────────────────────────────────────────────────────────────
16
  async function checkHealth() {
17
  const badge = document.getElementById('statusBadge');
18
  const modelInfo = document.getElementById('modelInfo');
 
 
19
  try {
20
- const res = await fetch(`${API_BASE}/health`);
21
  const d = await res.json();
 
22
  if (d.ready) {
23
  badge.textContent = 'ONLINE';
24
- badge.classList.remove('offline');
25
  modelInfo.textContent = (d.model || 'VIT ENSEMBLE').toUpperCase().slice(0, 22);
 
26
  return true;
27
  }
28
- badge.textContent = 'LOADING...';
 
 
 
 
29
  return false;
 
30
  } catch {
31
  badge.textContent = 'OFFLINE';
32
  badge.classList.add('offline');
 
 
33
  return false;
34
  }
35
  }
@@ -41,8 +51,11 @@ async function updatePageInfo() {
41
  try {
42
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
43
  if (tab?.title) {
44
- title.textContent = tab.title.slice(0, 45);
45
- sub.textContent = new URL(tab.url).hostname;
 
 
 
46
  }
47
  } catch {
48
  title.textContent = 'Current page';
@@ -51,14 +64,32 @@ async function updatePageInfo() {
51
  }
52
 
53
  // ── Wire buttons ──────────────────────────────────────────────────────────────
54
- function wireButtons() {
55
- document.getElementById('btnCapture').addEventListener('click', async () => {
 
 
 
 
 
56
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
57
  if (!tab?.id) return;
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- // Trigger capture from background (needs to be initiated from background for tabCapture)
60
- await chrome.runtime.sendMessage({ type: 'START_CAPTURE', tabId: tab.id });
61
- window.close(); // close popup so overlay is visible
62
  });
63
  }
64
 
@@ -66,22 +97,30 @@ function wireButtons() {
66
  function loadLastResult() {
67
  chrome.storage.local.get('lastResult', ({ lastResult }) => {
68
  if (!lastResult) return;
 
69
  const el = document.getElementById('last-result');
70
  el.style.display = 'block';
71
 
72
  const isFake = lastResult.result === 'FAKE';
73
  const color = isFake ? '#ff4466' : '#00ff9c';
 
 
 
 
 
 
 
 
 
74
 
75
- document.getElementById('lastVerdict').textContent = lastResult.result;
76
- document.getElementById('lastVerdict').style.color = color;
77
- document.getElementById('lastConf').textContent = lastResult.confidence + '%';
78
- document.getElementById('lastConf').style.color = color;
79
- document.getElementById('lastFile').textContent = lastResult.file || 'Tab capture';
80
 
81
  const bar = document.getElementById('lastBar');
82
  bar.style.background = isFake
83
  ? 'linear-gradient(90deg,#880022,#ff4466)'
84
  : 'linear-gradient(90deg,#006633,#00ff9c)';
85
- setTimeout(() => { bar.style.width = lastResult.confidence + '%'; }, 80);
86
  });
87
  }
 
1
  /**
2
+ * Authrix Extension — Popup Script v3
 
3
  */
4
 
5
+ const API_BASE = 'https://aarav13-authrix.hf.space';
6
 
7
  document.addEventListener('DOMContentLoaded', async () => {
8
+ const online = await checkHealth();
 
 
9
  updatePageInfo();
10
+ loadLastResult();
11
+ wireButtons(online);
12
  });
13
 
14
  // ── Health check ──────────────────────────────────────────────────────────────
15
  async function checkHealth() {
16
  const badge = document.getElementById('statusBadge');
17
  const modelInfo = document.getElementById('modelInfo');
18
+ const warn = document.getElementById('offline-warn');
19
+
20
  try {
21
+ const res = await fetch(`${API_BASE}/health`, { signal: AbortSignal.timeout(3000) });
22
  const d = await res.json();
23
+
24
  if (d.ready) {
25
  badge.textContent = 'ONLINE';
26
+ badge.classList.remove('offline', 'loading');
27
  modelInfo.textContent = (d.model || 'VIT ENSEMBLE').toUpperCase().slice(0, 22);
28
+ if (warn) warn.style.display = 'none';
29
  return true;
30
  }
31
+
32
+ badge.textContent = 'LOADING…';
33
+ badge.classList.remove('offline');
34
+ badge.classList.add('loading');
35
+ if (warn) warn.style.display = 'none';
36
  return false;
37
+
38
  } catch {
39
  badge.textContent = 'OFFLINE';
40
  badge.classList.add('offline');
41
+ badge.classList.remove('loading');
42
+ if (warn) warn.style.display = 'block';
43
  return false;
44
  }
45
  }
 
51
  try {
52
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
53
  if (tab?.title) {
54
+ title.textContent = tab.title.length > 44 ? tab.title.slice(0, 41) + '…' : tab.title;
55
+ }
56
+ if (tab?.url) {
57
+ try { sub.textContent = new URL(tab.url).hostname; }
58
+ catch { sub.textContent = 'Ready to capture'; }
59
  }
60
  } catch {
61
  title.textContent = 'Current page';
 
64
  }
65
 
66
  // ── Wire buttons ──────────────────────────────────────────────────────────────
67
+ function wireButtons(online) {
68
+ const btnCapture = document.getElementById('btnCapture');
69
+ const btnUrl = document.getElementById('btnUrl');
70
+ const urlInput = document.getElementById('urlInput');
71
+
72
+ // Capture button
73
+ btnCapture.addEventListener('click', async () => {
74
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
75
  if (!tab?.id) return;
76
+ chrome.runtime.sendMessage({ type: 'START_CAPTURE', tabId: tab.id });
77
+ window.close();
78
+ });
79
+
80
+ // URL analyze button
81
+ btnUrl.addEventListener('click', async () => {
82
+ const url = urlInput.value.trim();
83
+ if (!url) { urlInput.focus(); return; }
84
+
85
+ const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
86
+ chrome.runtime.sendMessage({ type: 'ANALYZE_URL', url, tabId: tab?.id });
87
+ window.close();
88
+ });
89
 
90
+ // Allow Enter key in URL input
91
+ urlInput.addEventListener('keydown', e => {
92
+ if (e.key === 'Enter') btnUrl.click();
93
  });
94
  }
95
 
 
97
  function loadLastResult() {
98
  chrome.storage.local.get('lastResult', ({ lastResult }) => {
99
  if (!lastResult) return;
100
+
101
  const el = document.getElementById('last-result');
102
  el.style.display = 'block';
103
 
104
  const isFake = lastResult.result === 'FAKE';
105
  const color = isFake ? '#ff4466' : '#00ff9c';
106
+ const conf = lastResult.confidence ?? 0;
107
+
108
+ const verdict = document.getElementById('lastVerdict');
109
+ verdict.textContent = lastResult.result;
110
+ verdict.style.color = color;
111
+
112
+ const confEl = document.getElementById('lastConf');
113
+ confEl.textContent = conf + '%';
114
+ confEl.style.color = color;
115
 
116
+ const fileEl = document.getElementById('lastFile');
117
+ const fileStr = lastResult.file || 'Tab capture';
118
+ fileEl.textContent = fileStr.length > 50 ? fileStr.slice(0, 47) + '' : fileStr;
 
 
119
 
120
  const bar = document.getElementById('lastBar');
121
  bar.style.background = isFake
122
  ? 'linear-gradient(90deg,#880022,#ff4466)'
123
  : 'linear-gradient(90deg,#006633,#00ff9c)';
124
+ setTimeout(() => { bar.style.width = conf + '%'; }, 80);
125
  });
126
  }
frontend-vanilla/index.html CHANGED
@@ -276,6 +276,7 @@
276
  <span onclick="showState('heroSection')" class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)] cursor-pointer">AUTHRIX AI</span>
277
  <div class="hidden md:flex items-center gap-6">
278
  <a onclick="showState('heroSection')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Dashboard</a>
 
279
  <a onclick="openModal('agentsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Agents</a>
280
  <a onclick="openModal('logsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Logs</a>
281
  <a onclick="openModal('networkModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Network</a>
 
276
  <span onclick="showState('heroSection')" class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)] cursor-pointer">AUTHRIX AI</span>
277
  <div class="hidden md:flex items-center gap-6">
278
  <a onclick="showState('heroSection')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Dashboard</a>
279
+ <a href="/pricing" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Pricing</a>
280
  <a onclick="openModal('agentsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Agents</a>
281
  <a onclick="openModal('logsModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Logs</a>
282
  <a onclick="openModal('networkModal')" class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 py-1 px-2 rounded-sm cursor-pointer">Network</a>
frontend-vanilla/pricing.html ADDED
@@ -0,0 +1,470 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Authrix Pricing - AI-Powered Deepfake Detection</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+
10
+ body {
11
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
12
+ background: linear-gradient(135deg, #0a1f0f 0%, #0c150f 100%);
13
+ color: #dae5da;
14
+ min-height: 100vh;
15
+ padding: 40px 20px;
16
+ }
17
+
18
+ .container {
19
+ max-width: 1200px;
20
+ margin: 0 auto;
21
+ }
22
+
23
+ /* Header */
24
+ .header {
25
+ text-align: center;
26
+ margin-bottom: 60px;
27
+ }
28
+
29
+ .logo {
30
+ display: inline-flex;
31
+ align-items: center;
32
+ gap: 12px;
33
+ font-size: 28px;
34
+ font-weight: 700;
35
+ letter-spacing: 0.12em;
36
+ color: #00ff9c;
37
+ text-shadow: 0 0 20px rgba(0,255,156,0.4);
38
+ margin-bottom: 20px;
39
+ }
40
+
41
+ .logo-dot {
42
+ width: 12px;
43
+ height: 12px;
44
+ border-radius: 50%;
45
+ background: #00ff9c;
46
+ box-shadow: 0 0 16px #00ff9c;
47
+ animation: pulse 2s ease-in-out infinite;
48
+ }
49
+
50
+ @keyframes pulse {
51
+ 0%, 100% { opacity: 1; transform: scale(1); }
52
+ 50% { opacity: 0.6; transform: scale(0.9); }
53
+ }
54
+
55
+ h1 {
56
+ font-size: 48px;
57
+ margin-bottom: 16px;
58
+ background: linear-gradient(135deg, #00ff9c, #00cc6a);
59
+ -webkit-background-clip: text;
60
+ -webkit-text-fill-color: transparent;
61
+ background-clip: text;
62
+ }
63
+
64
+ .subtitle {
65
+ font-size: 20px;
66
+ color: rgba(255,255,255,0.6);
67
+ max-width: 600px;
68
+ margin: 0 auto;
69
+ }
70
+
71
+ /* Pricing Cards */
72
+ .pricing-grid {
73
+ display: grid;
74
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
75
+ gap: 30px;
76
+ margin-bottom: 60px;
77
+ }
78
+
79
+ .pricing-card {
80
+ background: rgba(255,255,255,0.03);
81
+ border: 1px solid rgba(255,255,255,0.1);
82
+ border-radius: 16px;
83
+ padding: 40px 30px;
84
+ position: relative;
85
+ transition: all 0.3s ease;
86
+ }
87
+
88
+ .pricing-card:hover {
89
+ transform: translateY(-8px);
90
+ border-color: rgba(0,255,156,0.3);
91
+ box-shadow: 0 20px 60px rgba(0,255,156,0.1);
92
+ }
93
+
94
+ .pricing-card.popular {
95
+ border-color: #00ff9c;
96
+ box-shadow: 0 0 40px rgba(0,255,156,0.2);
97
+ }
98
+
99
+ .popular-badge {
100
+ position: absolute;
101
+ top: -12px;
102
+ right: 30px;
103
+ background: linear-gradient(135deg, #00ff9c, #00cc6a);
104
+ color: #002110;
105
+ padding: 6px 16px;
106
+ border-radius: 20px;
107
+ font-size: 12px;
108
+ font-weight: 700;
109
+ letter-spacing: 0.08em;
110
+ }
111
+
112
+ .plan-name {
113
+ font-size: 24px;
114
+ font-weight: 700;
115
+ margin-bottom: 12px;
116
+ color: #00ff9c;
117
+ }
118
+
119
+ .plan-price {
120
+ font-size: 48px;
121
+ font-weight: 700;
122
+ margin-bottom: 8px;
123
+ font-family: monospace;
124
+ }
125
+
126
+ .plan-price span {
127
+ font-size: 18px;
128
+ color: rgba(255,255,255,0.5);
129
+ }
130
+
131
+ .plan-interval {
132
+ font-size: 14px;
133
+ color: rgba(255,255,255,0.4);
134
+ margin-bottom: 24px;
135
+ }
136
+
137
+ .plan-features {
138
+ list-style: none;
139
+ margin-bottom: 30px;
140
+ }
141
+
142
+ .plan-features li {
143
+ padding: 12px 0;
144
+ border-bottom: 1px solid rgba(255,255,255,0.05);
145
+ display: flex;
146
+ align-items: center;
147
+ gap: 12px;
148
+ font-size: 14px;
149
+ }
150
+
151
+ .plan-features li:last-child {
152
+ border-bottom: none;
153
+ }
154
+
155
+ .check {
156
+ color: #00ff9c;
157
+ font-size: 18px;
158
+ }
159
+
160
+ .cta-button {
161
+ width: 100%;
162
+ padding: 16px;
163
+ border: none;
164
+ border-radius: 10px;
165
+ font-size: 16px;
166
+ font-weight: 700;
167
+ cursor: pointer;
168
+ transition: all 0.3s ease;
169
+ letter-spacing: 0.06em;
170
+ }
171
+
172
+ .cta-button.primary {
173
+ background: linear-gradient(135deg, #00ff9c, #00cc6a);
174
+ color: #002110;
175
+ box-shadow: 0 8px 24px rgba(0,255,156,0.3);
176
+ }
177
+
178
+ .cta-button.primary:hover {
179
+ transform: translateY(-2px);
180
+ box-shadow: 0 12px 32px rgba(0,255,156,0.4);
181
+ }
182
+
183
+ .cta-button.secondary {
184
+ background: rgba(255,255,255,0.05);
185
+ color: #00ff9c;
186
+ border: 1px solid rgba(0,255,156,0.3);
187
+ }
188
+
189
+ .cta-button.secondary:hover {
190
+ background: rgba(0,255,156,0.1);
191
+ }
192
+
193
+ /* Enterprise Section */
194
+ .enterprise-section {
195
+ background: rgba(0,255,156,0.05);
196
+ border: 1px solid rgba(0,255,156,0.2);
197
+ border-radius: 16px;
198
+ padding: 50px;
199
+ text-align: center;
200
+ margin-bottom: 60px;
201
+ }
202
+
203
+ .enterprise-section h2 {
204
+ font-size: 36px;
205
+ margin-bottom: 16px;
206
+ color: #00ff9c;
207
+ }
208
+
209
+ .enterprise-section p {
210
+ font-size: 18px;
211
+ color: rgba(255,255,255,0.6);
212
+ margin-bottom: 30px;
213
+ max-width: 700px;
214
+ margin-left: auto;
215
+ margin-right: auto;
216
+ }
217
+
218
+ .enterprise-features {
219
+ display: grid;
220
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
221
+ gap: 20px;
222
+ margin-bottom: 40px;
223
+ }
224
+
225
+ .enterprise-feature {
226
+ background: rgba(255,255,255,0.03);
227
+ padding: 20px;
228
+ border-radius: 10px;
229
+ border: 1px solid rgba(255,255,255,0.1);
230
+ }
231
+
232
+ .enterprise-feature h3 {
233
+ font-size: 18px;
234
+ margin-bottom: 8px;
235
+ color: #00ff9c;
236
+ }
237
+
238
+ .enterprise-feature p {
239
+ font-size: 14px;
240
+ color: rgba(255,255,255,0.5);
241
+ margin: 0;
242
+ }
243
+
244
+ /* FAQ */
245
+ .faq-section {
246
+ max-width: 800px;
247
+ margin: 0 auto;
248
+ }
249
+
250
+ .faq-section h2 {
251
+ text-align: center;
252
+ font-size: 36px;
253
+ margin-bottom: 40px;
254
+ color: #00ff9c;
255
+ }
256
+
257
+ .faq-item {
258
+ background: rgba(255,255,255,0.03);
259
+ border: 1px solid rgba(255,255,255,0.1);
260
+ border-radius: 10px;
261
+ padding: 24px;
262
+ margin-bottom: 16px;
263
+ }
264
+
265
+ .faq-question {
266
+ font-size: 18px;
267
+ font-weight: 600;
268
+ margin-bottom: 12px;
269
+ color: #00ff9c;
270
+ }
271
+
272
+ .faq-answer {
273
+ font-size: 15px;
274
+ line-height: 1.6;
275
+ color: rgba(255,255,255,0.6);
276
+ }
277
+
278
+ /* Footer */
279
+ .footer {
280
+ text-align: center;
281
+ margin-top: 80px;
282
+ padding-top: 40px;
283
+ border-top: 1px solid rgba(255,255,255,0.1);
284
+ }
285
+
286
+ .footer-links {
287
+ display: flex;
288
+ justify-content: center;
289
+ gap: 30px;
290
+ margin-bottom: 20px;
291
+ }
292
+
293
+ .footer-links a {
294
+ color: rgba(0,255,156,0.6);
295
+ text-decoration: none;
296
+ font-size: 14px;
297
+ transition: color 0.3s;
298
+ }
299
+
300
+ .footer-links a:hover {
301
+ color: #00ff9c;
302
+ }
303
+
304
+ .demo-badge {
305
+ display: inline-block;
306
+ background: rgba(255,170,0,0.1);
307
+ border: 1px solid rgba(255,170,0,0.3);
308
+ color: #ffaa00;
309
+ padding: 8px 16px;
310
+ border-radius: 20px;
311
+ font-size: 12px;
312
+ font-weight: 700;
313
+ letter-spacing: 0.08em;
314
+ margin-top: 20px;
315
+ }
316
+ </style>
317
+ </head>
318
+ <body>
319
+ <div class="container">
320
+ <!-- Header -->
321
+ <div class="header">
322
+ <div class="logo">
323
+ <div class="logo-dot"></div>
324
+ <a href="/" style="text-decoration: none; color: inherit;">AUTHRIX AI</a>
325
+ </div>
326
+ <h1>Choose Your Plan</h1>
327
+ <p class="subtitle">Protect yourself from deepfakes with AI-powered detection. Start free, upgrade anytime.</p>
328
+ </div>
329
+
330
+ <!-- Pricing Cards -->
331
+ <div class="pricing-grid">
332
+ <!-- Free -->
333
+ <div class="pricing-card">
334
+ <div class="plan-name">Free</div>
335
+ <div class="plan-price">$0<span>/mo</span></div>
336
+ <div class="plan-interval">Perfect for trying out</div>
337
+ <ul class="plan-features">
338
+ <li><span class="check">✓</span> 10 video analyses/month</li>
339
+ <li><span class="check">✓</span> Browser extension</li>
340
+ <li><span class="check">✓</span> Max 2-minute videos</li>
341
+ <li><span class="check">✓</span> Community support</li>
342
+ </ul>
343
+ <button class="cta-button secondary" onclick="showDemo('free')">Get Started Free</button>
344
+ </div>
345
+
346
+ <!-- Pro -->
347
+ <div class="pricing-card popular">
348
+ <div class="popular-badge">MOST POPULAR</div>
349
+ <div class="plan-name">Pro</div>
350
+ <div class="plan-price">$9.99<span>/mo</span></div>
351
+ <div class="plan-interval">For individuals & creators</div>
352
+ <ul class="plan-features">
353
+ <li><span class="check">✓</span> 100 analyses/month</li>
354
+ <li><span class="check">✓</span> Up to 10-minute videos</li>
355
+ <li><span class="check">✓</span> API access (100 calls/mo)</li>
356
+ <li><span class="check">✓</span> Priority processing</li>
357
+ <li><span class="check">✓</span> Email support</li>
358
+ <li><span class="check">✓</span> Batch upload</li>
359
+ </ul>
360
+ <button class="cta-button primary" onclick="showDemo('pro')">Subscribe to Pro</button>
361
+ </div>
362
+
363
+ <!-- Business -->
364
+ <div class="pricing-card">
365
+ <div class="plan-name">Business</div>
366
+ <div class="plan-price">$49<span>/mo</span></div>
367
+ <div class="plan-interval">For teams & organizations</div>
368
+ <ul class="plan-features">
369
+ <li><span class="check">✓</span> 1,000 analyses/month</li>
370
+ <li><span class="check">✓</span> Unlimited video length</li>
371
+ <li><span class="check">✓</span> API access (5K calls/mo)</li>
372
+ <li><span class="check">✓</span> White-label reports</li>
373
+ <li><span class="check">✓</span> Slack/Teams integration</li>
374
+ <li><span class="check">✓</span> Priority support</li>
375
+ <li><span class="check">✓</span> Custom branding</li>
376
+ </ul>
377
+ <button class="cta-button primary" onclick="showDemo('business')">Subscribe to Business</button>
378
+ </div>
379
+ </div>
380
+
381
+ <!-- Enterprise Section -->
382
+ <div class="enterprise-section">
383
+ <h2>Enterprise Solutions</h2>
384
+ <p>Custom solutions for large organizations, social media platforms, and government agencies</p>
385
+
386
+ <div class="enterprise-features">
387
+ <div class="enterprise-feature">
388
+ <h3>🏢 On-Premise Deployment</h3>
389
+ <p>Deploy Authrix on your own infrastructure for maximum security</p>
390
+ </div>
391
+ <div class="enterprise-feature">
392
+ <h3>🎯 Custom Model Training</h3>
393
+ <p>Train models on your specific content and use cases</p>
394
+ </div>
395
+ <div class="enterprise-feature">
396
+ <h3>⚡ Unlimited Analyses</h3>
397
+ <p>No limits on video analyses or API calls</p>
398
+ </div>
399
+ <div class="enterprise-feature">
400
+ <h3>🛡️ SLA Guarantees</h3>
401
+ <p>99.9% uptime with dedicated support team</p>
402
+ </div>
403
+ </div>
404
+
405
+ <button class="cta-button primary" onclick="showDemo('enterprise')" style="max-width: 300px; margin: 0 auto;">Contact Sales</button>
406
+ </div>
407
+
408
+ <!-- FAQ -->
409
+ <div class="faq-section">
410
+ <h2>Frequently Asked Questions</h2>
411
+
412
+ <div class="faq-item">
413
+ <div class="faq-question">How accurate is Authrix?</div>
414
+ <div class="faq-answer">Authrix uses an ensemble of state-of-the-art ViT models achieving 99%+ accuracy on benchmark datasets. We combine visual analysis with audio detection for comprehensive deepfake identification.</div>
415
+ </div>
416
+
417
+ <div class="faq-item">
418
+ <div class="faq-question">Can I cancel anytime?</div>
419
+ <div class="faq-answer">Yes! All subscriptions are month-to-month with no long-term commitment. Cancel anytime from your account dashboard.</div>
420
+ </div>
421
+
422
+ <div class="faq-item">
423
+ <div class="faq-question">What video formats are supported?</div>
424
+ <div class="faq-answer">We support MP4, AVI, MOV, MKV, WebM, and WMV formats. Maximum file size is 100MB for Pro tier, unlimited for Business and Enterprise.</div>
425
+ </div>
426
+
427
+ <div class="faq-item">
428
+ <div class="faq-question">Is my data secure?</div>
429
+ <div class="faq-answer">Absolutely. All videos are analyzed locally and deleted immediately after processing. We never store your content or share it with third parties.</div>
430
+ </div>
431
+
432
+ <div class="faq-item">
433
+ <div class="faq-question">Do you offer refunds?</div>
434
+ <div class="faq-answer">Yes, we offer a 30-day money-back guarantee. If you're not satisfied, contact support for a full refund.</div>
435
+ </div>
436
+ </div>
437
+
438
+ <!-- Footer -->
439
+ <div class="footer">
440
+ <div class="footer-links">
441
+ <a href="/">Home</a>
442
+ <a href="/pricing.html">Pricing</a>
443
+ <a href="https://docs.authrix.ai">API Docs</a>
444
+ <a href="mailto:support@authrix.ai">Support</a>
445
+ <a href="/terms.html">Terms</a>
446
+ <a href="/privacy.html">Privacy</a>
447
+ </div>
448
+ <p style="color: rgba(255,255,255,0.3); font-size: 14px;">
449
+ © 2026 Authrix AI. All rights reserved.
450
+ </p>
451
+ </div>
452
+ </div>
453
+
454
+ <script>
455
+ function showDemo(plan) {
456
+ const messages = {
457
+ free: 'Free tier selected! In production, this would redirect to signup.',
458
+ pro: 'Pro plan selected ($9.99/mo)! In production, this would open Stripe checkout.',
459
+ business: 'Business plan selected ($49/mo)! In production, this would open Stripe checkout.',
460
+ enterprise: 'Enterprise inquiry! In production, this would open a contact form or schedule a demo call.'
461
+ };
462
+
463
+ alert(messages[plan] + '\n\n💡 This is a demo. Payment integration will be added in production.');
464
+
465
+ // In production, this would be:
466
+ // window.location.href = `/checkout?plan=${plan}`;
467
+ }
468
+ </script>
469
+ </body>
470
+ </html>
render.yaml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ - type: web
3
+ name: authrix-api
4
+ runtime: docker
5
+ dockerfilePath: ./Dockerfile
6
+ plan: standard
7
+ healthCheckPath: /health
8
+ envVars:
9
+ - key: PORT
10
+ value: 8000
11
+ - key: PYTHONUNBUFFERED
12
+ value: "1"
stitch_authrix_deepfake_detection_engine/authrix_analysis_results/code.html ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>AUTHRIX AI - Results Dashboard</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;900&amp;display=swap" rel="stylesheet"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ "colors": {
17
+ "inverse-surface": "#dae5da",
18
+ "on-error": "#690005",
19
+ "surface-tint": "#00e38a",
20
+ "error": "#ffb4ab",
21
+ "outline-variant": "#3b4b3f",
22
+ "outline": "#849587",
23
+ "background": "#0c150f",
24
+ "surface-container-high": "#232c25",
25
+ "secondary-container": "#00e3fd",
26
+ "on-surface-variant": "#b9cbbc",
27
+ "on-secondary-container": "#00616d",
28
+ "on-error-container": "#ffdad6",
29
+ "primary": "#f3fff3",
30
+ "primary-container": "#00ff9c",
31
+ "on-primary": "#00391f",
32
+ "surface-bright": "#323c34",
33
+ "primary-fixed-dim": "#00e38a",
34
+ "tertiary-fixed-dim": "#e4c44f",
35
+ "on-secondary-fixed": "#001f24",
36
+ "tertiary-fixed": "#ffe17a",
37
+ "on-tertiary-container": "#766000",
38
+ "on-secondary": "#00363d",
39
+ "on-tertiary-fixed": "#231b00",
40
+ "on-tertiary": "#3b2f00",
41
+ "surface-container-low": "#141e17",
42
+ "surface": "#0c150f",
43
+ "primary-fixed": "#56ffa7",
44
+ "secondary-fixed-dim": "#00daf3",
45
+ "on-secondary-fixed-variant": "#004f58",
46
+ "inverse-on-surface": "#29332b",
47
+ "surface-container": "#18221b",
48
+ "on-surface": "#dae5da",
49
+ "on-tertiary-fixed-variant": "#554500",
50
+ "error-container": "#93000a",
51
+ "surface-variant": "#2d3730",
52
+ "surface-container-highest": "#2d3730",
53
+ "secondary-fixed": "#9cf0ff",
54
+ "on-background": "#dae5da",
55
+ "on-primary-container": "#007142",
56
+ "inverse-primary": "#006d40",
57
+ "on-primary-fixed-variant": "#00522f",
58
+ "secondary": "#bdf4ff",
59
+ "tertiary": "#fffaff",
60
+ "surface-dim": "#0c150f",
61
+ "on-primary-fixed": "#002110",
62
+ "surface-container-lowest": "#07100a",
63
+ "tertiary-container": "#ffdd65"
64
+ },
65
+ "borderRadius": {
66
+ "DEFAULT": "0.25rem",
67
+ "lg": "0.5rem",
68
+ "xl": "0.75rem",
69
+ "full": "9999px"
70
+ },
71
+ "spacing": {
72
+ "xs": "4px",
73
+ "md": "16px",
74
+ "base": "4px",
75
+ "sm": "8px",
76
+ "xl": "48px",
77
+ "lg": "24px",
78
+ "gutter": "20px",
79
+ "margin": "32px"
80
+ },
81
+ "fontFamily": {
82
+ "h2": ["Space Grotesk"],
83
+ "h1": ["Space Grotesk"],
84
+ "h3": ["Space Grotesk"],
85
+ "label-xs": ["Space Grotesk"],
86
+ "data-mono": ["Space Grotesk"],
87
+ "body-base": ["Space Grotesk"]
88
+ },
89
+ "fontSize": {
90
+ "h2": ["32px", { "lineHeight": "1.2", "letterSpacing": "-0.01em", "fontWeight": "600" }],
91
+ "h1": ["48px", { "lineHeight": "1.1", "letterSpacing": "-0.02em", "fontWeight": "700" }],
92
+ "h3": ["24px", { "lineHeight": "1.3", "letterSpacing": "0em", "fontWeight": "600" }],
93
+ "label-xs": ["12px", { "lineHeight": "1", "letterSpacing": "0.1em", "fontWeight": "700" }],
94
+ "data-mono": ["14px", { "lineHeight": "1.5", "letterSpacing": "0.05em", "fontWeight": "500" }],
95
+ "body-base": ["16px", { "lineHeight": "1.6", "letterSpacing": "0.01em", "fontWeight": "400" }]
96
+ }
97
+ }
98
+ }
99
+ }
100
+ </script>
101
+ <style>
102
+ .glass-panel {
103
+ background-color: rgba(35, 44, 37, 0.2);
104
+ backdrop-filter: blur(20px);
105
+ box-shadow: inset 1px 1px 0 0 rgba(255, 255, 255, 0.05);
106
+ }
107
+ .glow-border-primary {
108
+ border: 1px solid rgba(0, 255, 156, 0.3);
109
+ box-shadow: 0 0 15px rgba(0, 255, 156, 0.1);
110
+ }
111
+ .glow-border-error {
112
+ border: 1px solid rgba(255, 180, 171, 0.3);
113
+ box-shadow: 0 0 15px rgba(255, 180, 171, 0.1);
114
+ }
115
+ .scanline {
116
+ position: absolute;
117
+ top: 0;
118
+ left: 0;
119
+ width: 100%;
120
+ height: 1px;
121
+ background: rgba(0, 255, 156, 0.1);
122
+ animation: scan 4s linear infinite;
123
+ }
124
+ @keyframes scan {
125
+ 0% { top: 0; }
126
+ 100% { top: 100%; }
127
+ }
128
+ .blinking-cursor::after {
129
+ content: '_';
130
+ animation: blink 1s step-end infinite;
131
+ }
132
+ @keyframes blink {
133
+ 0%, 100% { opacity: 1; }
134
+ 50% { opacity: 0; }
135
+ }
136
+ </style>
137
+ </head>
138
+ <body class="bg-background text-on-surface font-body-base min-h-screen flex flex-col relative overflow-x-hidden selection:bg-primary-container selection:text-on-primary-container">
139
+ <!-- Ambient Particles/Glows -->
140
+ <div class="fixed inset-0 pointer-events-none z-0">
141
+ <div class="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary-container/5 rounded-full blur-[120px]"></div>
142
+ <div class="absolute bottom-[-20%] right-[-10%] w-[40%] h-[40%] bg-error/5 rounded-full blur-[100px]"></div>
143
+ </div>
144
+ <!-- TopNavBar -->
145
+ <header class="fixed top-0 left-0 w-full z-50 flex justify-between items-center px-6 h-16 bg-emerald-950/20 backdrop-blur-xl border-b border-emerald-500/20 shadow-[0_4_20px_rgba(0,0,0,0.5)]">
146
+ <div class="flex items-center gap-md">
147
+ <span class="font-h3 text-h3 font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)]">AUTHRIX AI</span>
148
+ </div>
149
+ <nav class="hidden md:flex gap-lg">
150
+ <a class="font-label-xs text-label-xs uppercase text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] px-3 py-1 rounded" href="#">Dashboard</a>
151
+ <a class="font-label-xs text-label-xs uppercase text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] px-3 py-1 rounded" href="#">Agents</a>
152
+ <a class="font-label-xs text-label-xs uppercase text-emerald-400 border-b-2 border-emerald-400 pb-1 px-3 py-1" href="#">Logs</a>
153
+ <a class="font-label-xs text-label-xs uppercase text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] px-3 py-1 rounded" href="#">Network</a>
154
+ </nav>
155
+ <div class="flex items-center gap-sm">
156
+ <button class="text-emerald-400 hover:text-emerald-300 p-2 rounded-full hover:bg-emerald-400/10 transition-colors">
157
+ <span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">sensors</span>
158
+ </button>
159
+ <button class="text-emerald-400 hover:text-emerald-300 p-2 rounded-full hover:bg-emerald-400/10 transition-colors">
160
+ <span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">memory</span>
161
+ </button>
162
+ <button class="text-emerald-400 hover:text-emerald-300 p-2 rounded-full hover:bg-emerald-400/10 transition-colors">
163
+ <span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">speed</span>
164
+ </button>
165
+ <button class="bg-primary-container text-on-primary-container font-label-xs text-label-xs uppercase px-4 py-2 rounded-DEFAULT ml-md shadow-[0_0_15px_rgba(0,255,156,0.3)] hover:bg-primary-fixed transition-colors">
166
+ GET STARTED
167
+ </button>
168
+ </div>
169
+ </header>
170
+ <main class="flex-grow pt-[80px] pb-xl px-gutter max-w-[1600px] mx-auto w-full z-10 flex flex-col gap-lg">
171
+ <!-- Cinematic Banner -->
172
+ <section class="glass-panel glow-border-primary rounded-xl relative overflow-hidden flex flex-col md:flex-row items-center justify-between p-lg md:p-margin min-h-[300px]">
173
+ <div class="scanline"></div>
174
+ <!-- Background Image -->
175
+ <div class="absolute inset-0 opacity-20 mix-blend-overlay pointer-events-none" data-alt="Abstract digital matrix code falling in a futuristic high-tech environment with green hues" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuCm6V5yQ3vKXnTfGWeqxvXPbNe2CvDhwLr4NreMaQM6OFtThX8-JDMNwORYSjTMxIXoCBzt8MticyWVNk4DNbUyoBJIlb4YsCoXkDzWVT3TThqqe3qzdYl-Jui2WDP5m7Oz3o0WWKG7NnnUmBUtZsaD8xv6cx0LodaRq6UhOxWBNDs1jbiBuzpNZ7n9tfrz4HYvK-nCymTLcymqGCsvfntziWmX7JJpsLc30o5fYaQrhz-l3htAV2sSSgwetQmTdz3-IlSPRaiYNGY'); background-size: cover; background-position: center;"></div>
176
+ <div class="relative z-10 flex flex-col md:flex-row items-center gap-margin w-full">
177
+ <!-- Circular Badge -->
178
+ <div class="relative w-48 h-48 rounded-full border-4 border-primary-container flex items-center justify-center shadow-[0_0_30px_rgba(0,255,156,0.4)] flex-shrink-0">
179
+ <div class="absolute inset-0 rounded-full border border-primary-container animate-[spin_10s_linear_infinite] opacity-50"></div>
180
+ <div class="absolute inset-2 rounded-full border border-dashed border-primary-container animate-[spin_15s_linear_infinite_reverse] opacity-30"></div>
181
+ <div class="text-center">
182
+ <span class="material-symbols-outlined text-primary-container text-5xl mb-2" style="font-variation-settings: 'FILL' 1;">verified_user</span>
183
+ <h2 class="font-h3 text-h3 text-primary-container uppercase tracking-widest drop-shadow-[0_0_5px_rgba(0,255,156,0.8)]">AUTHENTIC</h2>
184
+ </div>
185
+ </div>
186
+ <div class="flex-grow text-center md:text-left flex flex-col gap-sm">
187
+ <p class="font-data-mono text-data-mono text-primary-container/80 uppercase tracking-widest">Analysis Complete // ID: 8992-AX</p>
188
+ <h1 class="font-h1 text-h1 text-on-surface">Confidence Score</h1>
189
+ <div class="font-h1 text-[80px] leading-none text-primary-container drop-shadow-[0_0_15px_rgba(0,255,156,0.6)] font-black">98.7%</div>
190
+ </div>
191
+ </div>
192
+ </section>
193
+ <!-- Main Dashboard Grid -->
194
+ <div class="grid grid-cols-1 lg:grid-cols-12 gap-lg flex-grow">
195
+ <!-- Left: Insights Panel -->
196
+ <div class="lg:col-span-4 flex flex-col gap-md">
197
+ <h3 class="font-label-xs text-label-xs text-outline uppercase tracking-widest border-b border-surface-variant pb-2">Primary Vectors</h3>
198
+ <div class="glass-panel rounded-lg p-md flex items-start gap-md hover:glow-border-primary transition-all duration-300 transform hover:-translate-y-1">
199
+ <div class="w-10 h-10 rounded-full bg-surface-container-highest flex items-center justify-center flex-shrink-0 border border-surface-variant">
200
+ <span class="material-symbols-outlined text-primary-container" style="font-variation-settings: 'FILL' 0;">face</span>
201
+ </div>
202
+ <div>
203
+ <h4 class="font-body-base text-body-base font-semibold text-on-surface">Facial Morphology</h4>
204
+ <p class="font-data-mono text-data-mono text-on-surface-variant mt-1 text-sm">Consistent geometry. No micro-warping detected in periocular region.</p>
205
+ <div class="mt-3 flex items-center gap-2">
206
+ <div class="h-1 flex-grow bg-surface-container-highest rounded-full overflow-hidden">
207
+ <div class="h-full bg-primary-container w-[95%]"></div>
208
+ </div>
209
+ <span class="font-data-mono text-data-mono text-primary-container text-xs">95%</span>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ <div class="glass-panel rounded-lg p-md flex items-start gap-md hover:glow-border-primary transition-all duration-300 transform hover:-translate-y-1">
214
+ <div class="w-10 h-10 rounded-full bg-surface-container-highest flex items-center justify-center flex-shrink-0 border border-surface-variant">
215
+ <span class="material-symbols-outlined text-primary-container" style="font-variation-settings: 'FILL' 0;">graphic_eq</span>
216
+ </div>
217
+ <div>
218
+ <h4 class="font-body-base text-body-base font-semibold text-on-surface">Audio Spectral Analysis</h4>
219
+ <p class="font-data-mono text-data-mono text-on-surface-variant mt-1 text-sm">Natural harmonic resonance. No synthetic vocal fry identified.</p>
220
+ <div class="mt-3 flex items-center gap-2">
221
+ <div class="h-1 flex-grow bg-surface-container-highest rounded-full overflow-hidden">
222
+ <div class="h-full bg-primary-container w-[99%]"></div>
223
+ </div>
224
+ <span class="font-data-mono text-data-mono text-primary-container text-xs">99%</span>
225
+ </div>
226
+ </div>
227
+ </div>
228
+ <div class="glass-panel rounded-lg p-md flex items-start gap-md hover:glow-border-error transition-all duration-300 transform hover:-translate-y-1">
229
+ <div class="w-10 h-10 rounded-full bg-surface-container-highest flex items-center justify-center flex-shrink-0 border border-surface-variant">
230
+ <span class="material-symbols-outlined text-error" style="font-variation-settings: 'FILL' 0;">visibility</span>
231
+ </div>
232
+ <div>
233
+ <h4 class="font-body-base text-body-base font-semibold text-on-surface">Lighting/Shadow Consistency</h4>
234
+ <p class="font-data-mono text-data-mono text-on-surface-variant mt-1 text-sm">Minor specular highlight anomaly detected on frame 412. Flagged for review.</p>
235
+ <div class="mt-3 flex items-center gap-2">
236
+ <div class="h-1 flex-grow bg-surface-container-highest rounded-full overflow-hidden">
237
+ <div class="h-full bg-error w-[42%]"></div>
238
+ </div>
239
+ <span class="font-data-mono text-data-mono text-error text-xs">42%</span>
240
+ </div>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ <!-- Right: Diagnostics/Metadata -->
245
+ <div class="lg:col-span-8 flex flex-col gap-md">
246
+ <h3 class="font-label-xs text-label-xs text-outline uppercase tracking-widest border-b border-surface-variant pb-2">System Diagnostics</h3>
247
+ <div class="glass-panel rounded-lg flex-grow p-md font-data-mono text-data-mono text-sm relative overflow-hidden flex flex-col">
248
+ <div class="scanline"></div>
249
+ <div class="flex justify-between items-center border-b border-surface-variant/50 pb-sm mb-sm text-on-surface-variant">
250
+ <span>METADATA_STREAM</span>
251
+ <span class="text-primary-container animate-pulse">ACTIVE</span>
252
+ </div>
253
+ <div class="flex-grow space-y-2 text-on-surface-variant overflow-y-auto pr-2">
254
+ <div class="flex">
255
+ <span class="text-outline w-32 shrink-0">SOURCE_HASH:</span>
256
+ <span class="text-primary-container break-all">a8f9c2e4b5d6...7f8e9a0b1c2d3</span>
257
+ </div>
258
+ <div class="flex">
259
+ <span class="text-outline w-32 shrink-0">FRAME_COUNT:</span>
260
+ <span>1440 @ 60FPS</span>
261
+ </div>
262
+ <div class="flex">
263
+ <span class="text-outline w-32 shrink-0">ENCODING:</span>
264
+ <span>H.265/HEVC Main 10 Profile</span>
265
+ </div>
266
+ <div class="flex">
267
+ <span class="text-outline w-32 shrink-0">RESOLUTION:</span>
268
+ <span>3840x2160 (4K UHD)</span>
269
+ </div>
270
+ <div class="my-4 border-t border-surface-variant/30 border-dashed"></div>
271
+ <div class="text-primary-container opacity-80">&gt; INIT NEURAL_NET_V4.2</div>
272
+ <div class="text-primary-container opacity-80">&gt; LOADING WEIGHTS... DONE</div>
273
+ <div class="text-primary-container opacity-80">&gt; ANALYZING SPATIAL_TEMPORAL_NOISE</div>
274
+ <div class="text-primary-container opacity-80">&gt; EXTRACTING BIOMETRIC_FEATURES</div>
275
+ <div class="text-primary-container opacity-80">&gt; CROSS_REFERENCING KNOWN_GAN_MODELS</div>
276
+ <div class="text-on-surface mt-4 blinking-cursor">&gt; SYSTEM_READY</div>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ <!-- Bottom: Timeline Graph (Stylized Representation) -->
282
+ <section class="glass-panel rounded-xl p-lg flex flex-col gap-md">
283
+ <h3 class="font-label-xs text-label-xs text-outline uppercase tracking-widest border-b border-surface-variant pb-2">Probability Timeline</h3>
284
+ <div class="h-48 w-full relative flex items-end pt-4">
285
+ <!-- Grid Lines -->
286
+ <div class="absolute inset-0 flex flex-col justify-between pointer-events-none opacity-20">
287
+ <div class="w-full h-px bg-outline"></div>
288
+ <div class="w-full h-px bg-outline"></div>
289
+ <div class="w-full h-px bg-outline"></div>
290
+ <div class="w-full h-px bg-outline"></div>
291
+ </div>
292
+ <!-- Fake Graph Line (CSS Stylized) -->
293
+ <div class="w-full h-full relative z-10 flex items-end border-b border-l border-surface-variant">
294
+ <!-- Data points / segments -->
295
+ <div class="h-[20%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)]"></div>
296
+ <div class="h-[15%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)] transform -translate-y-2"></div>
297
+ <div class="h-[25%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)] transform -translate-y-4"></div>
298
+ <div class="h-[10%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)]"></div>
299
+ <div class="h-[30%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)] transform -translate-y-6"></div>
300
+ <!-- Anomaly Spike -->
301
+ <div class="h-[80%] w-[5%] bg-error/20 border-t-2 border-l border-r border-error shadow-[0_0_20px_rgba(255,180,171,0.4)] relative group">
302
+ <div class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 bg-surface-container-highest border border-error text-error font-data-mono text-xs px-2 py-1 rounded hidden group-hover:block whitespace-nowrap z-20">
303
+ Spike: Frame 412
304
+ </div>
305
+ </div>
306
+ <div class="h-[35%] w-[15%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)] transform -translate-y-8"></div>
307
+ <div class="h-[20%] w-[10%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)] transform -translate-y-2"></div>
308
+ <div class="h-[15%] w-[20%] border-t-2 border-primary-container shadow-[0_-5px_10px_rgba(0,255,156,0.2)]"></div>
309
+ </div>
310
+ </div>
311
+ <div class="flex justify-between font-data-mono text-data-mono text-xs text-outline mt-2">
312
+ <span>00:00:00</span>
313
+ <span>00:01:12</span>
314
+ <span>00:02:24</span>
315
+ </div>
316
+ </section>
317
+ </main>
318
+ <!-- Footer -->
319
+ <footer class="w-full py-8 px-10 flex flex-col md:flex-row justify-between items-center opacity-80 bg-black border-t border-emerald-900/30 font-data-mono text-[10px] uppercase tracking-[0.2em] cursor-crosshair z-10 relative">
320
+ <span class="text-emerald-500 mb-4 md:mb-0">© 2024 AUTHRIX COMMAND. SYSTEM_STATE: VIGILANT</span>
321
+ <div class="flex gap-md">
322
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all" href="#">Kernel</a>
323
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all" href="#">Protocol</a>
324
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all" href="#">Override</a>
325
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all" href="#">Diagnostics</a>
326
+ </div>
327
+ </footer>
328
+ </body></html>
stitch_authrix_deepfake_detection_engine/authrix_hero_section/code.html ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>Authrix - Deepfake Detection Platform</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&amp;family=Space+Grotesk:wght@600;700;900&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ "colors": {
17
+ "inverse-surface": "#e5e2e1",
18
+ "primary-container": "#00ff9c",
19
+ "on-secondary-fixed": "#002022",
20
+ "on-tertiary-fixed-variant": "#930010",
21
+ "primary-fixed-dim": "#00e38a",
22
+ "surface-dim": "#131313",
23
+ "on-background": "#e5e2e1",
24
+ "primary-fixed": "#56ffa7",
25
+ "surface-variant": "#353534",
26
+ "surface-container": "#201f1f",
27
+ "surface-tint": "#00e38a",
28
+ "on-secondary-fixed-variant": "#004f54",
29
+ "secondary-container": "#00eefc",
30
+ "error": "#ffb4ab",
31
+ "surface": "#131313",
32
+ "tertiary": "#fffbff",
33
+ "tertiary-fixed": "#ffdad6",
34
+ "outline": "#849587",
35
+ "on-primary-fixed": "#002110",
36
+ "on-tertiary-fixed": "#410003",
37
+ "outline-variant": "#3b4b3f",
38
+ "tertiary-container": "#ffd6d2",
39
+ "surface-container-lowest": "#0e0e0e",
40
+ "on-surface": "#e5e2e1",
41
+ "tertiary-fixed-dim": "#ffb3ac",
42
+ "surface-container-low": "#1c1b1b",
43
+ "surface-container-highest": "#353534",
44
+ "on-tertiary-container": "#c7041a",
45
+ "background": "#131313",
46
+ "on-secondary-container": "#00686f",
47
+ "surface-container-high": "#2a2a2a",
48
+ "error-container": "#93000a",
49
+ "on-secondary": "#00363a",
50
+ "primary": "#f3fff3",
51
+ "on-primary-fixed-variant": "#00522f",
52
+ "on-primary": "#00391f",
53
+ "surface-bright": "#3a3939",
54
+ "inverse-primary": "#006d40",
55
+ "secondary": "#d3fbff",
56
+ "inverse-on-surface": "#313030",
57
+ "secondary-fixed": "#7df4ff",
58
+ "on-tertiary": "#680008",
59
+ "on-error-container": "#ffdad6",
60
+ "secondary-fixed-dim": "#00dbe9",
61
+ "on-surface-variant": "#b9cbbc",
62
+ "on-error": "#690005",
63
+ "on-primary-container": "#007142"
64
+ },
65
+ "borderRadius": {
66
+ "DEFAULT": "0.125rem",
67
+ "lg": "0.25rem",
68
+ "xl": "0.5rem",
69
+ "full": "0.75rem"
70
+ },
71
+ "spacing": {
72
+ "margin": "48px",
73
+ "unit": "4px",
74
+ "container-max": "1440px",
75
+ "gutter": "24px",
76
+ "grid-overlay": "32px"
77
+ },
78
+ "fontFamily": {
79
+ "h1-display": ["Space Grotesk"],
80
+ "h2-technical": ["Space Grotesk"],
81
+ "label-caps": ["Space Grotesk"],
82
+ "body-main": ["Inter"],
83
+ "body-light": ["Inter"]
84
+ },
85
+ "fontSize": {
86
+ "h1-display": ["48px", { "lineHeight": "1.1", "letterSpacing": "-0.02em", "fontWeight": "700" }],
87
+ "h2-technical": ["32px", { "lineHeight": "1.2", "letterSpacing": "0.01em", "fontWeight": "600" }],
88
+ "label-caps": ["11px", { "lineHeight": "1", "letterSpacing": "0.15em", "fontWeight": "600" }],
89
+ "body-main": ["16px", { "lineHeight": "1.6", "letterSpacing": "0em", "fontWeight": "400" }],
90
+ "body-light": ["14px", { "lineHeight": "1.5", "letterSpacing": "0.03em", "fontWeight": "300" }]
91
+ }
92
+ }
93
+ }
94
+ }
95
+ </script>
96
+ <style>
97
+ body {
98
+ background-color: #050505;
99
+ background-image:
100
+ linear-gradient(rgba(0, 255, 156, 0.02) 1px, transparent 1px),
101
+ linear-gradient(90deg, rgba(0, 255, 156, 0.02) 1px, transparent 1px);
102
+ background-size: 32px 32px;
103
+ color: #e5e2e1;
104
+ position: relative;
105
+ overflow-x: hidden;
106
+ }
107
+
108
+ .scan-line {
109
+ position: absolute;
110
+ top: 0;
111
+ left: 0;
112
+ width: 100%;
113
+ height: 2px;
114
+ background: rgba(0, 255, 156, 0.3);
115
+ box-shadow: 0 0 10px rgba(0, 255, 156, 0.5), 0 0 20px rgba(0, 255, 156, 0.2);
116
+ animation: scan 4s linear infinite;
117
+ z-index: 10;
118
+ pointer-events: none;
119
+ }
120
+
121
+ @keyframes scan {
122
+ 0% { top: 0; opacity: 0; }
123
+ 10% { opacity: 1; }
124
+ 90% { opacity: 1; }
125
+ 100% { top: 100%; opacity: 0; }
126
+ }
127
+
128
+ .ambient-glow {
129
+ position: absolute;
130
+ width: 60vw;
131
+ height: 60vw;
132
+ border-radius: 50%;
133
+ background: radial-gradient(circle, rgba(0, 238, 252, 0.05) 0%, rgba(0, 0, 0, 0) 70%);
134
+ top: 50%;
135
+ left: 50%;
136
+ transform: translate(-50%, -50%);
137
+ z-index: -1;
138
+ pointer-events: none;
139
+ }
140
+
141
+ .glass-panel {
142
+ background: rgba(19, 19, 19, 0.6);
143
+ backdrop-filter: blur(12px);
144
+ -webkit-backdrop-filter: blur(12px);
145
+ border: 1px solid rgba(255, 255, 255, 0.05);
146
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
147
+ }
148
+
149
+ .orb-container {
150
+ position: relative;
151
+ width: 400px;
152
+ height: 400px;
153
+ perspective: 1000px;
154
+ }
155
+
156
+ .orb-core {
157
+ position: absolute;
158
+ top: 50%;
159
+ left: 50%;
160
+ transform: translate(-50%, -50%);
161
+ width: 120px;
162
+ height: 120px;
163
+ border-radius: 50%;
164
+ background: radial-gradient(circle, rgba(0, 255, 156, 0.8) 0%, rgba(0, 255, 156, 0.2) 60%, transparent 100%);
165
+ box-shadow: 0 0 60px rgba(0, 255, 156, 0.4);
166
+ z-index: 2;
167
+ }
168
+
169
+ .ring {
170
+ position: absolute;
171
+ top: 50%;
172
+ left: 50%;
173
+ border-radius: 50%;
174
+ border: 1px solid rgba(0, 238, 252, 0.3);
175
+ transform-style: preserve-3d;
176
+ }
177
+
178
+ .ring-1 { width: 200px; height: 200px; margin-top: -100px; margin-left: -100px; transform: rotateX(60deg) rotateY(20deg); border-top-color: rgba(0, 255, 156, 0.8); }
179
+ .ring-2 { width: 280px; height: 280px; margin-top: -140px; margin-left: -140px; transform: rotateX(40deg) rotateY(-30deg); border-right-color: rgba(0, 238, 252, 0.6); }
180
+ .ring-3 { width: 360px; height: 360px; margin-top: -180px; margin-left: -180px; transform: rotateX(80deg) rotateY(10deg); border-bottom-color: rgba(255, 180, 171, 0.4); }
181
+
182
+ .btn-primary {
183
+ background-color: #00ff9c;
184
+ color: #002110;
185
+ transition: all 0.3s ease;
186
+ box-shadow: 0 0 15px rgba(0, 255, 156, 0.3);
187
+ }
188
+ .btn-primary:hover {
189
+ background-color: #56ffa7;
190
+ box-shadow: 0 0 25px rgba(0, 255, 156, 0.5);
191
+ transform: translateY(-2px);
192
+ }
193
+
194
+ .btn-secondary {
195
+ background: transparent;
196
+ color: #e5e2e1;
197
+ border: 1px solid rgba(0, 255, 156, 0.5);
198
+ transition: all 0.3s ease;
199
+ }
200
+ .btn-secondary:hover {
201
+ border-color: #00ff9c;
202
+ box-shadow: 0 0 15px rgba(0, 255, 156, 0.2);
203
+ background: rgba(0, 255, 156, 0.05);
204
+ }
205
+ </style>
206
+ </head>
207
+ <body class="min-h-screen flex flex-col font-body-main text-body-main antialiased selection:bg-primary-container selection:text-on-primary-fixed">
208
+ <div class="scan-line"></div>
209
+ <div class="ambient-glow"></div>
210
+ <!-- TopNavBar -->
211
+ <nav class="bg-zinc-950/30 backdrop-blur-xl fixed top-0 w-full z-50 border-b border-white/5 shadow-[0_8px_32px_0_rgba(0,0,0,0.8)]">
212
+ <div class="flex justify-between items-center px-8 py-4 w-full max-w-screen-2xl mx-auto">
213
+ <div class="text-2xl font-black text-white tracking-tighter italic">Authrix</div>
214
+ <div class="hidden md:flex gap-8 items-center">
215
+ <a class="font-['Space_Grotesk'] font-medium tracking-tight text-zinc-400 font-medium hover:text-emerald-300 hover:bg-white/5 transition-all duration-300 ease-out px-3 py-2 rounded-md active:scale-95 transition-transform" href="#">Technology</a>
216
+ <a class="font-['Space_Grotesk'] font-medium tracking-tight text-emerald-400 font-bold border-b-2 border-emerald-400 pb-1 hover:text-emerald-300 hover:bg-white/5 transition-all duration-300 ease-out px-3 py-2 rounded-md active:scale-95 transition-transform" href="#">Solutions</a>
217
+ <a class="font-['Space_Grotesk'] font-medium tracking-tight text-zinc-400 font-medium hover:text-emerald-300 hover:bg-white/5 transition-all duration-300 ease-out px-3 py-2 rounded-md active:scale-95 transition-transform" href="#">Intelligence</a>
218
+ <a class="font-['Space_Grotesk'] font-medium tracking-tight text-zinc-400 font-medium hover:text-emerald-300 hover:bg-white/5 transition-all duration-300 ease-out px-3 py-2 rounded-md active:scale-95 transition-transform" href="#">Security</a>
219
+ </div>
220
+ <div class="flex items-center gap-4">
221
+ <button class="hidden lg:flex items-center justify-center w-10 h-10 rounded-full hover:bg-white/10 transition-colors text-zinc-400">
222
+ <span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">language</span>
223
+ </button>
224
+ <button class="hidden lg:flex items-center justify-center w-10 h-10 rounded-full hover:bg-white/10 transition-colors text-zinc-400">
225
+ <span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">account_circle</span>
226
+ </button>
227
+ <button class="font-['Space_Grotesk'] font-medium tracking-tight text-emerald-400 border border-emerald-400/30 px-5 py-2 rounded-md hover:bg-emerald-400/10 transition-colors">
228
+ Get Started
229
+ </button>
230
+ </div>
231
+ </div>
232
+ </nav>
233
+ <!-- Main Hero Section -->
234
+ <main class="flex-grow flex items-center justify-center pt-24 pb-12 px-6 lg:px-8 relative z-10 w-full max-w-container-max mx-auto">
235
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-12 lg:gap-8 items-center w-full">
236
+ <!-- Hero Left: Content -->
237
+ <div class="flex flex-col items-start max-w-2xl">
238
+ <div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full border border-primary-container/30 bg-primary-container/5 mb-8">
239
+ <span class="w-2 h-2 rounded-full bg-primary-container animate-pulse"></span>
240
+ <span class="font-label-caps text-label-caps text-primary-container uppercase tracking-widest">AI-Powered Authenticity Engine</span>
241
+ </div>
242
+ <h1 class="font-h1-display text-h1-display text-on-surface mb-6 leading-tight">
243
+ Verify Reality in <br/>
244
+ <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-container to-secondary-container">Real-Time</span>
245
+ </h1>
246
+ <p class="font-body-main text-body-main text-on-surface-variant mb-10 max-w-lg leading-relaxed">
247
+ Detect deepfake videos and synthetic voices using multi-agent AI intelligence with explainable insights. Protect your digital perimeter with military-grade precision.
248
+ </p>
249
+ <div class="flex flex-col sm:flex-row gap-4 w-full sm:w-auto">
250
+ <button class="btn-primary font-h2-technical text-[16px] font-semibold px-8 py-4 rounded-md flex items-center justify-center gap-2">
251
+ <span class="material-symbols-outlined text-[20px]" style="font-variation-settings: 'FILL' 1;">troubleshoot</span>
252
+ Analyze Video
253
+ </button>
254
+ <button class="btn-secondary font-h2-technical text-[16px] font-medium px-8 py-4 rounded-md flex items-center justify-center gap-2">
255
+ View Demo
256
+ <span class="material-symbols-outlined text-[20px]">play_circle</span>
257
+ </button>
258
+ </div>
259
+ <!-- Trust Indicators Glass Card -->
260
+ <div class="mt-16 glass-panel rounded-lg p-6 w-full max-w-md">
261
+ <div class="flex justify-between items-center mb-4">
262
+ <span class="font-label-caps text-label-caps text-on-surface-variant">System Status</span>
263
+ <div class="flex items-center gap-2 text-primary-container">
264
+ <span class="material-symbols-outlined text-sm">check_circle</span>
265
+ <span class="font-body-light text-body-light text-xs">Operational</span>
266
+ </div>
267
+ </div>
268
+ <div class="h-1 w-full bg-surface-container-high rounded-full overflow-hidden">
269
+ <div class="h-full bg-primary-container w-full relative">
270
+ <div class="absolute top-0 right-0 h-full w-1/2 bg-gradient-to-l from-white/40 to-transparent animate-pulse"></div>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ <!-- Hero Right: 3D Visual -->
276
+ <div class="flex justify-center items-center relative h-[500px] w-full">
277
+ <!-- Background Mesh/Particles abstraction -->
278
+ <div class="absolute inset-0 z-0 opacity-20 bg-[url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCI+PHBhdGggZD0iTTIwIDBMMCAyMEwyMCA0MEw0MCAyMEwyMCAweiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDBmZjljIiBzdHJva2Utb3BhY2l0eT0iMC4xIi8+PC9zdmc+')]"></div>
279
+ <div class="orb-container">
280
+ <div class="orb-core"></div>
281
+ <div class="ring ring-1"></div>
282
+ <div class="ring ring-2"></div>
283
+ <div class="ring ring-3"></div>
284
+ <!-- Decorative Data Points -->
285
+ <div class="absolute top-1/4 left-1/4 glass-panel px-3 py-1.5 rounded text-xs text-primary-container font-h2-technical z-10 shadow-[0_0_10px_rgba(0,255,156,0.2)]">
286
+ 99.8% Match
287
+ </div>
288
+ <div class="absolute bottom-1/4 right-1/4 glass-panel px-3 py-1.5 rounded text-xs text-secondary-container font-h2-technical z-10 shadow-[0_0_10px_rgba(0,238,252,0.2)]">
289
+ Audio Sync OK
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </main>
295
+ <!-- Footer -->
296
+ <footer class="bg-zinc-950 w-full py-12 px-8 border-t border-white/5 mt-auto relative z-20">
297
+ <div class="flex flex-col md:flex-row justify-between items-center w-full max-w-screen-2xl mx-auto">
298
+ <div class="text-lg font-bold text-emerald-400 mb-6 md:mb-0">
299
+ Authrix
300
+ </div>
301
+ <div class="flex flex-wrap justify-center gap-6 md:gap-8 mb-6 md:mb-0">
302
+ <a class="font-['Space_Grotesk'] text-xs uppercase tracking-widest text-zinc-500 hover:text-emerald-300 transition-colors opacity-80 hover:opacity-100" href="#">Privacy Protocol</a>
303
+ <a class="font-['Space_Grotesk'] text-xs uppercase tracking-widest text-zinc-500 hover:text-emerald-300 transition-colors opacity-80 hover:opacity-100" href="#">Terms of Engagement</a>
304
+ <a class="font-['Space_Grotesk'] text-xs uppercase tracking-widest text-zinc-500 hover:text-emerald-300 transition-colors opacity-80 hover:opacity-100" href="#">API Documentation</a>
305
+ <a class="font-['Space_Grotesk'] text-xs uppercase tracking-widest text-zinc-500 hover:text-emerald-300 transition-colors opacity-80 hover:opacity-100" href="#">Support</a>
306
+ </div>
307
+ <div class="font-['Space_Grotesk'] text-xs uppercase tracking-widest text-zinc-500">
308
+ © 2024 Authrix Intelligence Systems. All rights reserved.
309
+ </div>
310
+ </div>
311
+ </footer>
312
+ </body></html>
stitch_authrix_deepfake_detection_engine/authrix_hero_section_v2/code.html ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>Authrix - AI Command Center</title>
7
+ <!-- Google Fonts -->
8
+ <link href="https://fonts.googleapis.com" rel="preconnect"/>
9
+ <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&amp;display=swap" rel="stylesheet"/>
11
+ <!-- Material Symbols -->
12
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
13
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
14
+ <!-- Tailwind CSS -->
15
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
16
+ <!-- Tailwind Config -->
17
+ <script id="tailwind-config">
18
+ tailwind.config = {
19
+ darkMode: "class",
20
+ theme: {
21
+ extend: {
22
+ "colors": {
23
+ "inverse-surface": "#dae5da",
24
+ "on-error": "#690005",
25
+ "surface-tint": "#00e38a",
26
+ "error": "#ffb4ab",
27
+ "outline-variant": "#3b4b3f",
28
+ "outline": "#849587",
29
+ "background": "#0c150f",
30
+ "surface-container-high": "#232c25",
31
+ "secondary-container": "#00e3fd",
32
+ "on-surface-variant": "#b9cbbc",
33
+ "on-secondary-container": "#00616d",
34
+ "on-error-container": "#ffdad6",
35
+ "primary": "#f3fff3",
36
+ "primary-container": "#00ff9c",
37
+ "on-primary": "#00391f",
38
+ "surface-bright": "#323c34",
39
+ "primary-fixed-dim": "#00e38a",
40
+ "tertiary-fixed-dim": "#e4c44f",
41
+ "on-secondary-fixed": "#001f24",
42
+ "tertiary-fixed": "#ffe17a",
43
+ "on-tertiary-container": "#766000",
44
+ "on-secondary": "#00363d",
45
+ "on-tertiary-fixed": "#231b00",
46
+ "on-tertiary": "#3b2f00",
47
+ "surface-container-low": "#141e17",
48
+ "surface": "#0c150f",
49
+ "primary-fixed": "#56ffa7",
50
+ "secondary-fixed-dim": "#00daf3",
51
+ "on-secondary-fixed-variant": "#004f58",
52
+ "inverse-on-surface": "#29332b",
53
+ "surface-container": "#18221b",
54
+ "on-surface": "#dae5da",
55
+ "on-tertiary-fixed-variant": "#554500",
56
+ "error-container": "#93000a",
57
+ "surface-variant": "#2d3730",
58
+ "surface-container-highest": "#2d3730",
59
+ "secondary-fixed": "#9cf0ff",
60
+ "on-background": "#dae5da",
61
+ "on-primary-container": "#007142",
62
+ "inverse-primary": "#006d40",
63
+ "on-primary-fixed-variant": "#00522f",
64
+ "secondary": "#bdf4ff",
65
+ "tertiary": "#fffaff",
66
+ "surface-dim": "#0c150f",
67
+ "on-primary-fixed": "#002110",
68
+ "surface-container-lowest": "#07100a",
69
+ "tertiary-container": "#ffdd65"
70
+ },
71
+ "borderRadius": {
72
+ "DEFAULT": "0.25rem",
73
+ "lg": "0.5rem",
74
+ "xl": "0.75rem",
75
+ "full": "9999px"
76
+ },
77
+ "spacing": {
78
+ "xs": "4px",
79
+ "md": "16px",
80
+ "base": "4px",
81
+ "sm": "8px",
82
+ "xl": "48px",
83
+ "lg": "24px",
84
+ "gutter": "20px",
85
+ "margin": "32px"
86
+ },
87
+ "fontFamily": {
88
+ "h2": ["Space Grotesk"],
89
+ "h1": ["Space Grotesk"],
90
+ "h3": ["Space Grotesk"],
91
+ "label-xs": ["Space Grotesk"],
92
+ "data-mono": ["Space Grotesk"],
93
+ "body-base": ["Space Grotesk"]
94
+ },
95
+ "fontSize": {
96
+ "h2": ["32px", {"lineHeight": "1.2", "letterSpacing": "-0.01em", "fontWeight": "600"}],
97
+ "h1": ["48px", {"lineHeight": "1.1", "letterSpacing": "-0.02em", "fontWeight": "700"}],
98
+ "h3": ["24px", {"lineHeight": "1.3", "letterSpacing": "0em", "fontWeight": "600"}],
99
+ "label-xs": ["12px", {"lineHeight": "1", "letterSpacing": "0.1em", "fontWeight": "700"}],
100
+ "data-mono": ["14px", {"lineHeight": "1.5", "letterSpacing": "0.05em", "fontWeight": "500"}],
101
+ "body-base": ["16px", {"lineHeight": "1.6", "letterSpacing": "0.01em", "fontWeight": "400"}]
102
+ }
103
+ }
104
+ }
105
+ }
106
+ </script>
107
+ <style>
108
+ .material-symbols-outlined {
109
+ font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
110
+ }
111
+ </style>
112
+ </head>
113
+ <body class="bg-background text-on-surface min-h-screen flex flex-col relative overflow-x-hidden font-body-base">
114
+ <!-- Atmospheric Background Layers -->
115
+ <div class="fixed inset-0 z-0 pointer-events-none">
116
+ <!-- Scan Lines Simulation -->
117
+ <div class="absolute inset-0 bg-[linear-gradient(rgba(0,0,0,0)_50%,rgba(0,0,0,0.25)_50%),linear-gradient(90deg,rgba(255,0,0,0.06),rgba(0,255,0,0.02),rgba(0,0,255,0.06))] bg-[size:100%_4px,3px_100%] opacity-20"></div>
118
+ <!-- Technical Grid -->
119
+ <div class="absolute inset-0 bg-[linear-gradient(to_right,#3b4b3f_1px,transparent_1px),linear-gradient(to_bottom,#3b4b3f_1px,transparent_1px)] bg-[size:48px_48px] opacity-10"></div>
120
+ <!-- Ambient Radial Glow -->
121
+ <div class="absolute top-1/4 -right-1/4 w-[800px] h-[800px] bg-primary-container/5 rounded-full blur-[120px]"></div>
122
+ </div>
123
+ <!-- TopNavBar Shared Component -->
124
+ <nav class="fixed top-0 left-0 w-full z-50 flex justify-between items-center px-6 h-16 bg-emerald-950/20 backdrop-blur-xl border-b border-emerald-500/20 shadow-[0_4_20px_rgba(0,0,0,0.5)] font-['Space_Grotesk'] tracking-wider uppercase text-xs">
125
+ <!-- Brand Logo -->
126
+ <div class="flex items-center">
127
+ <span class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)]">AUTHRIX AI</span>
128
+ </div>
129
+ <!-- Navigation Links (Hidden on small screens) -->
130
+ <div class="hidden md:flex items-center space-x-lg">
131
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-1 px-2 rounded-sm active:scale-95 transition-transform duration-150" href="#">Dashboard</a>
132
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-1 px-2 rounded-sm active:scale-95 transition-transform duration-150" href="#">Agents</a>
133
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-1 px-2 rounded-sm active:scale-95 transition-transform duration-150" href="#">Logs</a>
134
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-1 px-2 rounded-sm active:scale-95 transition-transform duration-150" href="#">Network</a>
135
+ </div>
136
+ <!-- Trailing Actions -->
137
+ <div class="flex items-center space-x-md">
138
+ <!-- Search Icon (Search bar on right logic) -->
139
+ <button class="text-emerald-800/60 hover:text-emerald-400 transition-colors flex items-center justify-center p-1 rounded-full hover:bg-emerald-400/10">
140
+ <span class="material-symbols-outlined text-lg">search</span>
141
+ </button>
142
+ <!-- Icon Actions -->
143
+ <div class="hidden lg:flex items-center space-x-sm border-r border-emerald-500/20 pr-md mr-sm">
144
+ <button class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-sm active:scale-95 transition-transform duration-150"><span class="material-symbols-outlined text-lg">sensors</span></button>
145
+ <button class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-sm active:scale-95 transition-transform duration-150"><span class="material-symbols-outlined text-lg">memory</span></button>
146
+ <button class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-sm active:scale-95 transition-transform duration-150"><span class="material-symbols-outlined text-lg">speed</span></button>
147
+ </div>
148
+ <!-- Primary Action -->
149
+ <button class="text-emerald-400 border border-emerald-400/50 hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] px-sm py-1 rounded-DEFAULT active:scale-95 transition-transform duration-150 font-bold">
150
+ GET STARTED
151
+ </button>
152
+ </div>
153
+ </nav>
154
+ <!-- Main Content Canvas -->
155
+ <main class="flex-grow relative z-10 flex flex-col justify-center pt-[100px] pb-xl px-6 md:px-gutter max-w-7xl mx-auto w-full">
156
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-xl items-center min-h-[716px]">
157
+ <!-- Left Column: Command Console Content -->
158
+ <div class="flex flex-col items-start space-y-md">
159
+ <!-- Status Badge -->
160
+ <div class="inline-flex items-center gap-2 bg-surface-container/40 border border-outline/30 backdrop-blur-md px-sm py-xs rounded-none shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]">
161
+ <span class="w-2 h-2 rounded-full bg-primary-container shadow-[0_0_8px_#00ff9c] animate-pulse"></span>
162
+ <span class="font-label-xs text-label-xs text-on-surface-variant uppercase tracking-widest">AI-Powered Authenticity Engine</span>
163
+ </div>
164
+ <!-- Main Headline -->
165
+ <h1 class="font-h1 text-h1 text-on-surface drop-shadow-md">
166
+ Verify Reality <br class="hidden md:block"/>
167
+ in <span class="text-transparent bg-clip-text bg-gradient-to-r from-primary-container to-secondary-container">Real-Time</span>
168
+ </h1>
169
+ <!-- Subtext -->
170
+ <p class="font-body-base text-body-base text-on-surface-variant max-w-lg mt-sm mb-lg">
171
+ Deploy advanced neural networks to detect deepfakes, synthetic media, and manipulated data streams with military-grade precision. Establish an unbreakable perimeter of truth.
172
+ </p>
173
+ <!-- Action Group -->
174
+ <div class="flex flex-wrap items-center gap-md mt-sm">
175
+ <!-- Primary CTA -->
176
+ <button class="group relative px-lg py-sm bg-primary-container text-on-primary font-label-xs text-label-xs uppercase tracking-wider rounded-DEFAULT overflow-hidden transition-all duration-300 hover:shadow-[0_0_30px_rgba(0,255,156,0.4)] active:scale-95">
177
+ <span class="relative z-10 flex items-center gap-2">
178
+ Analyze Video
179
+ <span class="material-symbols-outlined text-[16px]">radar</span>
180
+ </span>
181
+ <!-- Neon sweep effect -->
182
+ <div class="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/40 to-transparent group-hover:animate-[shimmer_1s_infinite]"></div>
183
+ </button>
184
+ <!-- Secondary CTA -->
185
+ <button class="px-lg py-sm bg-transparent border border-surface-tint/50 text-surface-tint font-label-xs text-label-xs uppercase tracking-wider rounded-DEFAULT hover:bg-surface-tint/10 hover:border-surface-tint transition-all duration-300 active:scale-95 shadow-[inset_0_0_10px_rgba(0,227,138,0)] hover:shadow-[inset_0_0_15px_rgba(0,227,138,0.2)]">
186
+ View Demo
187
+ </button>
188
+ </div>
189
+ <!-- Micro-data readouts -->
190
+ <div class="flex items-center gap-lg mt-xl pt-lg border-t border-outline/20 w-full max-w-md">
191
+ <div>
192
+ <div class="font-data-mono text-data-mono text-primary-container">99.9%</div>
193
+ <div class="font-label-xs text-[10px] text-outline mt-1 uppercase">Accuracy Rate</div>
194
+ </div>
195
+ <div class="w-px h-8 bg-outline/30"></div>
196
+ <div>
197
+ <div class="font-data-mono text-data-mono text-secondary-container">&lt;15ms</div>
198
+ <div class="font-label-xs text-[10px] text-outline mt-1 uppercase">Latency Ping</div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ <!-- Right Column: Visualizer -->
203
+ <div class="relative w-full aspect-square max-w-lg mx-auto flex items-center justify-center lg:justify-end">
204
+ <!-- Tech Frame -->
205
+ <div class="absolute inset-0 bg-surface-container-low/20 backdrop-blur-3xl rounded-full border border-outline/10 shadow-[inset_0_0_40px_rgba(0,0,0,0.8)] flex items-center justify-center overflow-hidden">
206
+ <!-- Decorative Scanning Rings -->
207
+ <div class="absolute w-[120%] h-[120%] border border-primary-container/10 rounded-full animate-[spin_20s_linear_infinite]"></div>
208
+ <div class="absolute w-[100%] h-[100%] border border-secondary-container/20 rounded-full border-dashed animate-[spin_15s_linear_infinite_reverse]"></div>
209
+ <div class="absolute w-[80%] h-[80%] border-2 border-primary-container/5 rounded-full"></div>
210
+ <!-- Core Visual -->
211
+ <div class="relative w-3/4 h-3/4 rounded-full overflow-hidden mix-blend-screen shadow-[0_0_60px_rgba(0,255,156,0.2)]">
212
+ <img alt="3D glowing cybernetic orb visualization" class="w-full h-full object-cover object-center opacity-80" data-alt="Abstract 3D glowing cybernetic orb with concentric energy waves, neon emerald green and cyan light emitting from a dark core, futuristic high-tech data visualization aesthetic, dark void background" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAfjdr7a8Q86RDUDxW_XWXXmcIjP7JOyIcxR4xcrlyRQZmNeR_EenBL8QBXKm6GcHvwWsudOrefOpJ8iocKhqnG1eqqCIBpbIqEbbtgJLZlHjST1TxCI-TBoazDTYTnr-Lf6jMuMrrGKFRgWai675Vl0mgUwTbVz2lfL1OG_DrEnls6BoPqHRRX_RkxpE1mSNEqDTqkA0AqzoMCdoikLZbabr5YYlZfdq7lO9fh37RM5ICH1o9BBOqolMlclMSDtd04scZIRgjvhMQ"/>
213
+ <!-- Overlay gradient to mesh with background -->
214
+ <div class="absolute inset-0 bg-gradient-to-t from-background via-transparent to-transparent"></div>
215
+ </div>
216
+ </div>
217
+ <!-- Floating HUD elements -->
218
+ <div class="absolute top-10 -left-4 bg-surface/80 backdrop-blur-md border border-outline/30 px-sm py-1 font-data-mono text-[10px] text-primary-container">SYS.OPT.OK</div>
219
+ <div class="absolute bottom-20 -right-4 bg-surface/80 backdrop-blur-md border border-outline/30 px-sm py-1 font-data-mono text-[10px] text-secondary-container flex items-center gap-1">
220
+ <span class="material-symbols-outlined text-[12px]">sync</span> LIVE
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </main>
225
+ <!-- Footer Shared Component -->
226
+ <footer class="w-full py-8 px-10 flex flex-col md:flex-row justify-between items-center opacity-80 bg-black border-t border-emerald-900/30 relative z-10 font-mono text-[10px] uppercase tracking-[0.2em]">
227
+ <!-- Copyright -->
228
+ <div class="text-emerald-500 mb-4 md:mb-0">
229
+ © 2024 AUTHRIX COMMAND. SYSTEM_STATE: VIGILANT
230
+ </div>
231
+ <!-- Links -->
232
+ <div class="flex items-center space-x-md">
233
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Kernel</a>
234
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Protocol</a>
235
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Override</a>
236
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Diagnostics</a>
237
+ </div>
238
+ </footer>
239
+ <!-- Inline styles for custom animations that Tailwind standard config doesn't cover natively without arbitrary values,
240
+ using minimal standard tailwind approach where possible, falling back to basic CSS for the shimmer -->
241
+ <style>
242
+ @keyframes shimmer {
243
+ 100% { transform: translateX(100%); }
244
+ }
245
+ </style>
246
+ </body></html>
stitch_authrix_deepfake_detection_engine/authrix_kinetic_1/DESIGN.md ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Authrix Kinetic
3
+ colors:
4
+ surface: '#0c150f'
5
+ surface-dim: '#0c150f'
6
+ surface-bright: '#323c34'
7
+ surface-container-lowest: '#07100a'
8
+ surface-container-low: '#141e17'
9
+ surface-container: '#18221b'
10
+ surface-container-high: '#232c25'
11
+ surface-container-highest: '#2d3730'
12
+ on-surface: '#dae5da'
13
+ on-surface-variant: '#b9cbbc'
14
+ inverse-surface: '#dae5da'
15
+ inverse-on-surface: '#29332b'
16
+ outline: '#849587'
17
+ outline-variant: '#3b4b3f'
18
+ surface-tint: '#00e38a'
19
+ primary: '#f3fff3'
20
+ on-primary: '#00391f'
21
+ primary-container: '#00ff9c'
22
+ on-primary-container: '#007142'
23
+ inverse-primary: '#006d40'
24
+ secondary: '#bdf4ff'
25
+ on-secondary: '#00363d'
26
+ secondary-container: '#00e3fd'
27
+ on-secondary-container: '#00616d'
28
+ tertiary: '#fffaff'
29
+ on-tertiary: '#3b2f00'
30
+ tertiary-container: '#ffdd65'
31
+ on-tertiary-container: '#766000'
32
+ error: '#ffb4ab'
33
+ on-error: '#690005'
34
+ error-container: '#93000a'
35
+ on-error-container: '#ffdad6'
36
+ primary-fixed: '#56ffa7'
37
+ primary-fixed-dim: '#00e38a'
38
+ on-primary-fixed: '#002110'
39
+ on-primary-fixed-variant: '#00522f'
40
+ secondary-fixed: '#9cf0ff'
41
+ secondary-fixed-dim: '#00daf3'
42
+ on-secondary-fixed: '#001f24'
43
+ on-secondary-fixed-variant: '#004f58'
44
+ tertiary-fixed: '#ffe17a'
45
+ tertiary-fixed-dim: '#e4c44f'
46
+ on-tertiary-fixed: '#231b00'
47
+ on-tertiary-fixed-variant: '#554500'
48
+ background: '#0c150f'
49
+ on-background: '#dae5da'
50
+ surface-variant: '#2d3730'
51
+ typography:
52
+ h1:
53
+ fontFamily: Space Grotesk
54
+ fontSize: 48px
55
+ fontWeight: '700'
56
+ lineHeight: '1.1'
57
+ letterSpacing: -0.02em
58
+ h2:
59
+ fontFamily: Space Grotesk
60
+ fontSize: 32px
61
+ fontWeight: '600'
62
+ lineHeight: '1.2'
63
+ letterSpacing: -0.01em
64
+ h3:
65
+ fontFamily: Space Grotesk
66
+ fontSize: 24px
67
+ fontWeight: '600'
68
+ lineHeight: '1.3'
69
+ letterSpacing: 0em
70
+ body-base:
71
+ fontFamily: Space Grotesk
72
+ fontSize: 16px
73
+ fontWeight: '400'
74
+ lineHeight: '1.6'
75
+ letterSpacing: 0.01em
76
+ data-mono:
77
+ fontFamily: Space Grotesk
78
+ fontSize: 14px
79
+ fontWeight: '500'
80
+ lineHeight: '1.5'
81
+ letterSpacing: 0.05em
82
+ label-xs:
83
+ fontFamily: Space Grotesk
84
+ fontSize: 12px
85
+ fontWeight: '700'
86
+ lineHeight: '1'
87
+ letterSpacing: 0.1em
88
+ spacing:
89
+ base: 4px
90
+ xs: 4px
91
+ sm: 8px
92
+ md: 16px
93
+ lg: 24px
94
+ xl: 48px
95
+ gutter: 20px
96
+ margin: 32px
97
+ ---
98
+
99
+ ## Brand & Style
100
+
101
+ The design system is engineered to evoke the high-stakes environment of a state-of-the-art cybersecurity lab and a futuristic AI command center. It targets elite technical operators who require high-precision data visualization and a sense of absolute control. The aesthetic is "Industrial-Futurism"—combining the raw, structural feel of hardware with the ethereal, cinematic quality of advanced software interfaces.
102
+
103
+ The brand personality is authoritative, vigilant, and cutting-edge. Every interface element should feel like a piece of high-end equipment rather than a standard web component. The user experience is built on the tension between deep obsidian voids and vibrant, hyper-focused energy emissions (neon glows), creating a sense of immense power being harnessed through a precise digital lens.
104
+
105
+ ## Colors
106
+
107
+ The palette is anchored in a "Deep Space" foundation, utilizing Zinc-950 and pure blacks to maximize the luminous impact of the accent colors.
108
+
109
+ - **Primary Neon Green (#00ff9c):** Used for active states, successful validations, and primary calls to action. It represents "System GO" and operational health.
110
+ - **Cyan Secondary (#00e5ff):** Dedicated to data visualization, information overlays, and secondary interactive elements. It provides a "scanning" or "holographic" feel.
111
+ - **Alert Red (#ff3e3e):** Reserved strictly for critical system breaches, errors, and destructive actions.
112
+ - **Neutrals:** A range of Zinc grays (900 to 400) provide structural hierarchy.
113
+
114
+ The color application relies heavily on luminescence; colors are rarely flat but are instead accompanied by subtle radial glows and inner shadows to simulate light emitting from within the screen.
115
+
116
+ ## Typography
117
+
118
+ This design system utilizes **Space Grotesk** across all levels to maintain a cohesive, technical identity.
119
+
120
+ - **Headlines:** Use Bold or Semi-Bold weights. High-level headers should feel architectural and commanding.
121
+ - **Body Text:** Standard reading weights prioritize legibility against the dark background.
122
+ - **Metadata & Technical Readouts:** Use the uppercase "data-mono" styling to simulate terminal outputs and sensor readings.
123
+ - **Emphasis:** Rather than italics, use color shifts (to Primary Neon) or weight increases to highlight critical information.
124
+
125
+ ## Layout & Spacing
126
+
127
+ The design system employs a **Modular Grid System** with a high-density rhythm. The spacing is tight and deliberate, reflecting the efficiency of a command console.
128
+
129
+ - **Grid:** A 12-column fluid grid for main layouts, with a 20px gutter to ensure data density without crowding.
130
+ - **Rhythm:** All margins and padding follow a 4px baseline.
131
+ - **Density:** Use "Compact" spacing for data tables and "Spacious" spacing for dashboard overviews.
132
+ - **Borders as Spacing:** In many instances, thin 1px borders with 10% opacity are used in place of whitespace to define structural sections, reinforcing the industrial aesthetic.
133
+
134
+ ## Elevation & Depth
135
+
136
+ Depth in this design system is achieved through **Luminous Layering** rather than traditional shadows.
137
+
138
+ 1. **Glassmorphism:** Surfaces use a 10-20% opacity fill with a heavy (20px-40px) backdrop blur. This creates a "frosted laboratory glass" effect.
139
+ 2. **Inner Shadows:** Use subtle, dark inner shadows on the top and left edges to create an "etched" or "recessed" look for input fields and containers.
140
+ 3. **Border Glows:** Elements at higher elevation levels (e.g., modals) feature a 1px solid border with an outer "bloom" or neon glow effect using the Primary or Secondary color.
141
+ 4. **Atmospheric Particles:** Background layers should feature very low-opacity animated particles or scan lines to give the UI a sense of "living" energy.
142
+
143
+ ## Shapes
144
+
145
+ The design system adopts a **Sharp (0px)** philosophy for its core structural elements. This reinforces the industrial, high-precision nature of the brand.
146
+
147
+ - **Containers & Buttons:** Square corners suggest rigidity and technical accuracy.
148
+ - **Active Indicators:** Small 45-degree clipped corners can be used on decorative elements or active "tabs" to add a futuristic, military-spec feel.
149
+ - **Icons:** Must be stroke-based, using a consistent 1.5px or 2px weight, echoing the geometric precision of the typography.
150
+
151
+ ## Components
152
+
153
+ - **Buttons:** Primary buttons feature a solid #00ff9c background with black text. On hover, they emit a strong radial neon glow. Secondary buttons are "Ghost" style with a 1px glowing border.
154
+ - **Input Fields:** Recessed into the background using inner shadows. The bottom border "lights up" with Cyan when focused. Use monospace font for input values.
155
+ - **Cards:** These are the primary glassmorphic containers. They feature a subtle "Scan Line" animation overlay (a 1px line moving vertically at 2% opacity).
156
+ - **Status Chips:** Small, rectangular tags with no border-radius. They use a "pulse" animation for active states (a soft glow that expands and contracts).
157
+ - **Data Grids:** High-density tables with subtle horizontal separators. Row hovers should trigger a full-width Cyan highlight at 5% opacity.
158
+ - **Command Line Interface (CLI) Modules:** Specialized components for raw data input, featuring a blinking underscore cursor and Primary color text.
159
+ - **Progress Gauges:** Circular or linear indicators using "segmented" bars rather than solid fills to emphasize the mechanical, digital readout style.
stitch_authrix_deepfake_detection_engine/authrix_kinetic_2/DESIGN.md ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: Authrix Kinetic
3
+ colors:
4
+ surface: '#131313'
5
+ surface-dim: '#131313'
6
+ surface-bright: '#3a3939'
7
+ surface-container-lowest: '#0e0e0e'
8
+ surface-container-low: '#1c1b1b'
9
+ surface-container: '#201f1f'
10
+ surface-container-high: '#2a2a2a'
11
+ surface-container-highest: '#353534'
12
+ on-surface: '#e5e2e1'
13
+ on-surface-variant: '#b9cbbc'
14
+ inverse-surface: '#e5e2e1'
15
+ inverse-on-surface: '#313030'
16
+ outline: '#849587'
17
+ outline-variant: '#3b4b3f'
18
+ surface-tint: '#00e38a'
19
+ primary: '#f3fff3'
20
+ on-primary: '#00391f'
21
+ primary-container: '#00ff9c'
22
+ on-primary-container: '#007142'
23
+ inverse-primary: '#006d40'
24
+ secondary: '#d3fbff'
25
+ on-secondary: '#00363a'
26
+ secondary-container: '#00eefc'
27
+ on-secondary-container: '#00686f'
28
+ tertiary: '#fffbff'
29
+ on-tertiary: '#680008'
30
+ tertiary-container: '#ffd6d2'
31
+ on-tertiary-container: '#c7041a'
32
+ error: '#ffb4ab'
33
+ on-error: '#690005'
34
+ error-container: '#93000a'
35
+ on-error-container: '#ffdad6'
36
+ primary-fixed: '#56ffa7'
37
+ primary-fixed-dim: '#00e38a'
38
+ on-primary-fixed: '#002110'
39
+ on-primary-fixed-variant: '#00522f'
40
+ secondary-fixed: '#7df4ff'
41
+ secondary-fixed-dim: '#00dbe9'
42
+ on-secondary-fixed: '#002022'
43
+ on-secondary-fixed-variant: '#004f54'
44
+ tertiary-fixed: '#ffdad6'
45
+ tertiary-fixed-dim: '#ffb3ac'
46
+ on-tertiary-fixed: '#410003'
47
+ on-tertiary-fixed-variant: '#930010'
48
+ background: '#131313'
49
+ on-background: '#e5e2e1'
50
+ surface-variant: '#353534'
51
+ typography:
52
+ h1-display:
53
+ fontFamily: Space Grotesk
54
+ fontSize: 48px
55
+ fontWeight: '700'
56
+ lineHeight: '1.1'
57
+ letterSpacing: -0.02em
58
+ h2-technical:
59
+ fontFamily: Space Grotesk
60
+ fontSize: 32px
61
+ fontWeight: '600'
62
+ lineHeight: '1.2'
63
+ letterSpacing: 0.01em
64
+ body-main:
65
+ fontFamily: Inter
66
+ fontSize: 16px
67
+ fontWeight: '400'
68
+ lineHeight: '1.6'
69
+ letterSpacing: 0em
70
+ body-light:
71
+ fontFamily: Inter
72
+ fontSize: 14px
73
+ fontWeight: '300'
74
+ lineHeight: '1.5'
75
+ letterSpacing: 0.03em
76
+ label-caps:
77
+ fontFamily: Space Grotesk
78
+ fontSize: 11px
79
+ fontWeight: '600'
80
+ lineHeight: '1'
81
+ letterSpacing: 0.15em
82
+ rounded:
83
+ sm: 0.125rem
84
+ DEFAULT: 0.25rem
85
+ md: 0.375rem
86
+ lg: 0.5rem
87
+ xl: 0.75rem
88
+ full: 9999px
89
+ spacing:
90
+ unit: 4px
91
+ gutter: 24px
92
+ margin: 48px
93
+ container-max: 1440px
94
+ grid-overlay: 32px
95
+ ---
96
+
97
+ ## Brand & Style
98
+
99
+ The design system is engineered to evoke the atmosphere of a high-security AI research facility—clandestine, powerful, and hyper-intelligent. The visual narrative centers on "The Guardian in the Machine," balancing cold, technical precision with the ethereal glow of advanced neural networks.
100
+
101
+ The style is a sophisticated fusion of **Cinematic Glassmorphism** and **Technical Minimalism**. It utilizes deep layers of transparency and light to create a sense of infinite digital depth. Every interaction should feel like a high-stakes command within a tactical operations center, blending the sleek automotive precision of Tesla’s interface with the information-dense, holographic aesthetics of futuristic head-up displays (HUDs).
102
+
103
+ Targeting high-level security architects and C-suite executives, the UI prioritizes "Perceived Intelligence"—where the interface feels like it is thinking alongside the user through subtle animations, particle transitions, and reactive luminescence.
104
+
105
+ ## Colors
106
+
107
+ The palette is anchored by a "True Deep" black (#050505) to provide a canvas for light-based hierarchy.
108
+ - **Neon Green (#00ff9c):** The primary kinetic color, used for active states, successful encryptions, and "Safe" status indicators.
109
+ - **Cyan Glow (#00f0ff):** Used for secondary accents, data visualization, and soft atmospheric backlighting.
110
+ - **Subtle Red (#ff3b3b):** Reserved strictly for critical breaches and system warnings; it should feel alarming but polished, never vibrating against the black background.
111
+ - **Grayscale:** Uses high-opacity whites for headings and low-opacity "Oxygen" blues for subtext to maintain the cinematic atmosphere.
112
+
113
+ ## Typography
114
+
115
+ This design system utilizes a dual-font strategy. **Space Grotesk** provides a technical, geometric edge for headings and data labels, reinforcing the "AI Lab" aesthetic. **Inter** is utilized for body copy and dense technical logs to ensure maximum legibility at smaller scales.
116
+
117
+ Headings should be treated as structural elements—bold and authoritative. Subtext and secondary labels use lighter weights with increased letter spacing to mimic the look of a digital readout or tactical terminal. All capitalized labels should have a minimum of 0.1em tracking.
118
+
119
+ ## Layout & Spacing
120
+
121
+ The layout philosophy follows a **Subtle Grid** model. All layouts are based on a 4px baseline unit, ensuring mathematical rhythm. A 12-column fluid grid is used for primary dashboard layouts, but it is visually reinforced by a background "grid mesh"—a very faint 32px x 32px grid pattern (opacity 2-4%) that anchors the UI elements.
122
+
123
+ Padding is generous to convey a "premium" feel. Layouts should utilize "Safe Zones" around critical data visualizations, allowing the UI to breathe despite high information density. Elements are often grouped in modular clusters to mimic a HUD-style modularity.
124
+
125
+ ## Elevation & Depth
126
+
127
+ Depth in this design system is achieved through **Luminous Layering** rather than traditional shadows.
128
+ - **Glassmorphism:** Primary containers use a 10-20px backdrop blur with a 1px "inner-glow" border (white at 5-10% opacity).
129
+ - **Z-Axis Tiering:** Elements closer to the user have a brighter, more defined border and a soft Cyan or Green "under-glow" (box-shadow with 40px spread, 10% opacity).
130
+ - **Parallax Particles:** Use a subtle particle background on a separate Z-layer to create a sense of environmental volume.
131
+ - **Tonal Stepping:** The background is #050505, cards are "glass" at 3% white, and hovered elements increase to 8% white.
132
+
133
+ ## Shapes
134
+
135
+ The shape language is "Soft-Technical." We use a **Soft (0.25rem)** base radius to maintain a modern, engineered feel without the aggression of sharp 90-degree corners.
136
+
137
+ - **Primary Radius:** 4px for inputs, buttons, and small widgets.
138
+ - **Container Radius:** 8px for large dashboard cards.
139
+ - **Specialty Shapes:** Use 45-degree chamfered corners (clipped corners) on decorative elements or "Scan" buttons to heighten the futuristic military-grade aesthetic.
140
+
141
+ ## Components
142
+
143
+ - **Buttons:** Primary buttons feature a solid #00ff9c fill with black text. Secondary buttons are "Ghost" style with a 1px primary border and a soft neon outer glow on hover.
144
+ - **Input Fields:** Semi-transparent dark backgrounds with an animated "bottom-line" that expands from the center when focused, glowing in Secondary Cyan.
145
+ - **Data Chips:** Small, pill-shaped with high letter-spacing labels. Status chips for "Active" should have a pulsing 4px dot icon.
146
+ - **Cards:** Defined by "Glass" surfaces. Use a subtle top-left to bottom-right linear gradient (white at 5% to transparent).
147
+ - **Security Scanners:** Custom components including circular progress rings with "rotating data" segments and particle-line scans moving vertically across the component area.
148
+ - **Alerts:** Red borders with a low-opacity red background tint. The text should remain white for readability, while the icon carries the red warning color.
stitch_authrix_deepfake_detection_engine/authrix_system_processing/code.html ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>AUTHRIX AI - Analysis State</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&amp;display=swap" rel="stylesheet"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "inverse-surface": "#dae5da",
18
+ "on-error": "#690005",
19
+ "surface-tint": "#00e38a",
20
+ "error": "#ffb4ab",
21
+ "outline-variant": "#3b4b3f",
22
+ "outline": "#849587",
23
+ "background": "#0c150f",
24
+ "surface-container-high": "#232c25",
25
+ "secondary-container": "#00e3fd",
26
+ "on-surface-variant": "#b9cbbc",
27
+ "on-secondary-container": "#00616d",
28
+ "on-error-container": "#ffdad6",
29
+ "primary": "#f3fff3",
30
+ "primary-container": "#00ff9c",
31
+ "on-primary": "#00391f",
32
+ "surface-bright": "#323c34",
33
+ "primary-fixed-dim": "#00e38a",
34
+ "tertiary-fixed-dim": "#e4c44f",
35
+ "on-secondary-fixed": "#001f24",
36
+ "tertiary-fixed": "#ffe17a",
37
+ "on-tertiary-container": "#766000",
38
+ "on-secondary": "#00363d",
39
+ "on-tertiary-fixed": "#231b00",
40
+ "on-tertiary": "#3b2f00",
41
+ "surface-container-low": "#141e17",
42
+ "surface": "#0c150f",
43
+ "primary-fixed": "#56ffa7",
44
+ "secondary-fixed-dim": "#00daf3",
45
+ "on-secondary-fixed-variant": "#004f58",
46
+ "inverse-on-surface": "#29332b",
47
+ "surface-container": "#18221b",
48
+ "on-surface": "#dae5da",
49
+ "on-tertiary-fixed-variant": "#554500",
50
+ "error-container": "#93000a",
51
+ "surface-variant": "#2d3730",
52
+ "surface-container-highest": "#2d3730",
53
+ "secondary-fixed": "#9cf0ff",
54
+ "on-background": "#dae5da",
55
+ "on-primary-container": "#007142",
56
+ "inverse-primary": "#006d40",
57
+ "on-primary-fixed-variant": "#00522f",
58
+ "secondary": "#bdf4ff",
59
+ "tertiary": "#fffaff",
60
+ "surface-dim": "#0c150f",
61
+ "on-primary-fixed": "#002110",
62
+ "surface-container-lowest": "#07100a",
63
+ "tertiary-container": "#ffdd65"
64
+ },
65
+ borderRadius: {
66
+ "DEFAULT": "0.25rem",
67
+ "lg": "0.5rem",
68
+ "xl": "0.75rem",
69
+ "full": "9999px"
70
+ },
71
+ spacing: {
72
+ "xs": "4px",
73
+ "md": "16px",
74
+ "base": "4px",
75
+ "sm": "8px",
76
+ "xl": "48px",
77
+ "lg": "24px",
78
+ "gutter": "20px",
79
+ "margin": "32px"
80
+ },
81
+ fontFamily: {
82
+ "h2": ["Space Grotesk"],
83
+ "h1": ["Space Grotesk"],
84
+ "h3": ["Space Grotesk"],
85
+ "label-xs": ["Space Grotesk"],
86
+ "data-mono": ["Space Grotesk"],
87
+ "body-base": ["Space Grotesk"]
88
+ },
89
+ fontSize: {
90
+ "h2": ["32px", { lineHeight: "1.2", letterSpacing: "-0.01em", fontWeight: "600" }],
91
+ "h1": ["48px", { lineHeight: "1.1", letterSpacing: "-0.02em", fontWeight: "700" }],
92
+ "h3": ["24px", { lineHeight: "1.3", letterSpacing: "0em", fontWeight: "600" }],
93
+ "label-xs": ["12px", { lineHeight: "1", letterSpacing: "0.1em", fontWeight: "700" }],
94
+ "data-mono": ["14px", { lineHeight: "1.5", letterSpacing: "0.05em", fontWeight: "500" }],
95
+ "body-base": ["16px", { lineHeight: "1.6", letterSpacing: "0.01em", fontWeight: "400" }]
96
+ }
97
+ }
98
+ }
99
+ }
100
+ </script>
101
+ <style>
102
+ .scan-line {
103
+ background: linear-gradient(to bottom, transparent, rgba(0, 255, 156, 0.2), transparent);
104
+ height: 2px;
105
+ width: 100%;
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ opacity: 0.5;
110
+ }
111
+ .noise-bg {
112
+ background-image: 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.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
113
+ opacity: 0.05;
114
+ mix-blend-mode: overlay;
115
+ pointer-events: none;
116
+ }
117
+ .glass-panel {
118
+ background: rgba(35, 44, 37, 0.2);
119
+ backdrop-filter: blur(20px);
120
+ border: 1px solid rgba(0, 255, 156, 0.1);
121
+ box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.05);
122
+ }
123
+ .glow-border-active {
124
+ border: 1px solid rgba(0, 255, 156, 0.5);
125
+ box-shadow: 0 0 15px rgba(0, 255, 156, 0.2);
126
+ }
127
+ </style>
128
+ </head>
129
+ <body class="bg-background text-on-background min-h-screen font-body-base overflow-hidden relative">
130
+ <!-- Ambient Background Elements -->
131
+ <div class="absolute inset-0 noise-bg z-0"></div>
132
+ <div class="absolute top-0 left-0 w-full h-full z-0 overflow-hidden">
133
+ <div class="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-surface-container-high/30 via-background to-background"></div>
134
+ <div class="absolute top-1/4 left-1/4 w-96 h-96 bg-primary-container/5 rounded-full blur-[100px] pointer-events-none"></div>
135
+ </div>
136
+ <div class="scan-line z-0" style="top: 30%"></div>
137
+ <!-- Central Content Area -->
138
+ <div class="relative z-10 container mx-auto px-6 h-screen flex flex-col items-center justify-center">
139
+ <!-- Top Header Info -->
140
+ <div class="w-full max-w-3xl mb-xl text-center">
141
+ <h1 class="font-h1 text-h1 text-primary-container drop-shadow-[0_0_8px_rgba(0,255,156,0.3)] mb-sm">SYSTEM_STATE: ANALYSIS</h1>
142
+ <p class="font-data-mono text-data-mono text-primary-fixed-dim/70 tracking-[0.2em] uppercase">Initiating Deep Inspection Protocol</p>
143
+ </div>
144
+ <!-- Main Bento Grid Layout for Analysis Console -->
145
+ <div class="w-full max-w-4xl grid grid-cols-1 md:grid-cols-12 gap-gutter">
146
+ <!-- Visual / Orb Section (Left/Top) -->
147
+ <div class="md:col-span-5 glass-panel rounded-xl p-lg flex flex-col items-center justify-center relative min-h-[300px] border-outline/20">
148
+ <div class="absolute top-sm left-sm font-label-xs text-label-xs text-outline tracking-widest uppercase">Target Vector</div>
149
+ <!-- Central Rotating Orb Visual -->
150
+ <div class="relative w-48 h-48 rounded-full border border-primary-container/30 shadow-[0_0_30px_rgba(0,255,156,0.1)] flex items-center justify-center">
151
+ <div class="absolute inset-2 rounded-full border border-dashed border-primary-container/40"></div>
152
+ <div class="absolute inset-6 rounded-full bg-surface-container-high shadow-inner flex items-center justify-center overflow-hidden">
153
+ <img alt="Abstract glowing grid structure representing data analysis" class="w-full h-full object-cover opacity-60 mix-blend-screen" data-alt="Abstract glowing geometric grid structure with vibrant cyan and emerald neon lights against dark background, technological feel" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDJdb_uls8CdiY4F9nfTubj-C8Q_ONhUf9a91ObNEcp65p0OrntMRuJI0C38IGnMHouwLDXGGbfpcWSmv6_t0RoIQeoLXUtfGy6tuTwxIiSMVBXHrKIT08HzkIeO5xTy_BpvPKs9thCqsvR5RCtdcWERQuAN62JbBXAJLgRBam6AqZTLX4Oj4W4nhE02AfHC__Ef2UmE41kdBX9auPEFAlMFjX3YJoY7MBXyjN3PK5EufCBhz4A2AIvR0fJlYbAN-JPfn57NzQba80"/>
154
+ </div>
155
+ <div class="absolute inset-0 rounded-full border-t-2 border-primary-container/80 blur-[2px]"></div>
156
+ <span class="material-symbols-outlined absolute text-primary-container text-4xl drop-shadow-[0_0_10px_rgba(0,255,156,0.8)]">troubleshoot</span>
157
+ </div>
158
+ <div class="mt-lg text-center w-full">
159
+ <div class="font-data-mono text-data-mono text-on-surface-variant flex items-center justify-between w-full border-b border-outline/10 pb-xs mb-xs">
160
+ <span>DATA_STREAM</span>
161
+ <span class="text-secondary-fixed">ACTIVE</span>
162
+ </div>
163
+ <div class="font-data-mono text-data-mono text-on-surface-variant flex items-center justify-between w-full border-b border-outline/10 pb-xs">
164
+ <span>THROUGHPUT</span>
165
+ <span class="text-tertiary-container">2.4 TB/s</span>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ <!-- Dynamic Status List Section (Right/Bottom) -->
170
+ <div class="md:col-span-7 glass-panel rounded-xl p-lg flex flex-col justify-center gap-md border-outline/20">
171
+ <div class="flex justify-between items-center mb-sm">
172
+ <div class="font-label-xs text-label-xs text-outline tracking-widest uppercase">Processing Pipeline</div>
173
+ <div class="font-data-mono text-data-mono text-primary-container bg-primary-container/10 px-2 py-1 rounded">68% COMPLETE</div>
174
+ </div>
175
+ <!-- Step 1: Completed -->
176
+ <div class="flex flex-col gap-xs opacity-60">
177
+ <div class="flex justify-between items-center">
178
+ <div class="flex items-center gap-sm">
179
+ <span class="material-symbols-outlined text-outline text-sm">check_circle</span>
180
+ <span class="font-data-mono text-data-mono text-on-surface">Extracting frames...</span>
181
+ </div>
182
+ <span class="font-data-mono text-data-mono text-outline">100%</span>
183
+ </div>
184
+ <div class="w-full h-1 bg-surface-container-highest overflow-hidden">
185
+ <div class="h-full bg-outline w-full"></div>
186
+ </div>
187
+ </div>
188
+ <!-- Step 2: Completed -->
189
+ <div class="flex flex-col gap-xs opacity-60">
190
+ <div class="flex justify-between items-center">
191
+ <div class="flex items-center gap-sm">
192
+ <span class="material-symbols-outlined text-outline text-sm">check_circle</span>
193
+ <span class="font-data-mono text-data-mono text-on-surface">Detecting faces...</span>
194
+ </div>
195
+ <span class="font-data-mono text-data-mono text-outline">100%</span>
196
+ </div>
197
+ <div class="w-full h-1 bg-surface-container-highest overflow-hidden">
198
+ <div class="h-full bg-outline w-full"></div>
199
+ </div>
200
+ </div>
201
+ <!-- Step 3: Active Processing -->
202
+ <div class="flex flex-col gap-xs glow-border-active bg-primary-container/5 p-sm -mx-sm rounded-lg">
203
+ <div class="flex justify-between items-center">
204
+ <div class="flex items-center gap-sm">
205
+ <span class="material-symbols-outlined text-primary-container text-sm animate-pulse">memory</span>
206
+ <span class="font-data-mono text-data-mono text-primary-container font-bold">Running model inference...</span>
207
+ </div>
208
+ <span class="font-data-mono text-data-mono text-primary-container">42%</span>
209
+ </div>
210
+ <!-- Segmented Progress Bar for Active State -->
211
+ <div class="w-full h-2 bg-surface-container-highest flex gap-[2px]">
212
+ <div class="h-full bg-primary-container/80 flex-1 shadow-[0_0_8px_rgba(0,255,156,0.5)]"></div>
213
+ <div class="h-full bg-primary-container/80 flex-1 shadow-[0_0_8px_rgba(0,255,156,0.5)]"></div>
214
+ <div class="h-full bg-primary-container/80 flex-1 shadow-[0_0_8px_rgba(0,255,156,0.5)]"></div>
215
+ <div class="h-full bg-primary-container/80 flex-1 shadow-[0_0_8px_rgba(0,255,156,0.5)]"></div>
216
+ <div class="h-full bg-primary-container/20 flex-1"></div>
217
+ <div class="h-full bg-primary-container/20 flex-1"></div>
218
+ <div class="h-full bg-primary-container/20 flex-1"></div>
219
+ <div class="h-full bg-primary-container/20 flex-1"></div>
220
+ <div class="h-full bg-primary-container/20 flex-1"></div>
221
+ <div class="h-full bg-primary-container/20 flex-1"></div>
222
+ </div>
223
+ </div>
224
+ <!-- Step 4: Pending -->
225
+ <div class="flex flex-col gap-xs opacity-30 mt-sm">
226
+ <div class="flex justify-between items-center">
227
+ <div class="flex items-center gap-sm">
228
+ <span class="material-symbols-outlined text-outline text-sm">hourglass_empty</span>
229
+ <span class="font-data-mono text-data-mono text-on-surface">Analyzing audio patterns...</span>
230
+ </div>
231
+ <span class="font-data-mono text-data-mono text-outline">0%</span>
232
+ </div>
233
+ <div class="w-full h-1 bg-surface-container-highest overflow-hidden">
234
+ <div class="h-full bg-outline w-0"></div>
235
+ </div>
236
+ </div>
237
+ <!-- CLI Feedback Area -->
238
+ <div class="mt-auto bg-surface-container-highest/50 border border-outline/10 p-sm rounded font-data-mono text-data-mono text-xs text-primary-fixed-dim/60 shadow-inner">
239
+ &gt; [SYS] Fetching neural weights... OK<br/>
240
+ &gt; [SYS] Allocating tensor buffers... OK<br/>
241
+ &gt; [INF] Batch 0x7F processing... <span class="text-primary-container animate-pulse">_</span>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ <!-- Cancel Action (Ghost Button) -->
246
+ <div class="mt-xl">
247
+ <button class="font-data-mono text-data-mono text-error border border-error/50 px-lg py-sm rounded bg-error/5 hover:bg-error/10 hover:shadow-[0_0_15px_rgba(255,180,171,0.2)] transition-all flex items-center gap-xs">
248
+ <span class="material-symbols-outlined text-sm">cancel</span>
249
+ ABORT SEQUENCE
250
+ </button>
251
+ </div>
252
+ </div>
253
+ </body></html>
stitch_authrix_deepfake_detection_engine/authrix_upload_center/code.html ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+
3
+ <html class="dark" lang="en"><head>
4
+ <meta charset="utf-8"/>
5
+ <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
6
+ <title>Authrix - Secure Data Ingest</title>
7
+ <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
8
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&amp;display=swap" rel="stylesheet"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
10
+ <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
11
+ <script id="tailwind-config">
12
+ tailwind.config = {
13
+ darkMode: "class",
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ "inverse-surface": "#dae5da",
18
+ "on-error": "#690005",
19
+ "surface-tint": "#00e38a",
20
+ "error": "#ffb4ab",
21
+ "outline-variant": "#3b4b3f",
22
+ "outline": "#849587",
23
+ "background": "#0c150f",
24
+ "surface-container-high": "#232c25",
25
+ "secondary-container": "#00e3fd",
26
+ "on-surface-variant": "#b9cbbc",
27
+ "on-secondary-container": "#00616d",
28
+ "on-error-container": "#ffdad6",
29
+ "primary": "#f3fff3",
30
+ "primary-container": "#00ff9c",
31
+ "on-primary": "#00391f",
32
+ "surface-bright": "#323c34",
33
+ "primary-fixed-dim": "#00e38a",
34
+ "tertiary-fixed-dim": "#e4c44f",
35
+ "on-secondary-fixed": "#001f24",
36
+ "tertiary-fixed": "#ffe17a",
37
+ "on-tertiary-container": "#766000",
38
+ "on-secondary": "#00363d",
39
+ "on-tertiary-fixed": "#231b00",
40
+ "on-tertiary": "#3b2f00",
41
+ "surface-container-low": "#141e17",
42
+ "surface": "#0c150f",
43
+ "primary-fixed": "#56ffa7",
44
+ "secondary-fixed-dim": "#00daf3",
45
+ "on-secondary-fixed-variant": "#004f58",
46
+ "inverse-on-surface": "#29332b",
47
+ "surface-container": "#18221b",
48
+ "on-surface": "#dae5da",
49
+ "on-tertiary-fixed-variant": "#554500",
50
+ "error-container": "#93000a",
51
+ "surface-variant": "#2d3730",
52
+ "surface-container-highest": "#2d3730",
53
+ "secondary-fixed": "#9cf0ff",
54
+ "on-background": "#dae5da",
55
+ "on-primary-container": "#007142",
56
+ "inverse-primary": "#006d40",
57
+ "on-primary-fixed-variant": "#00522f",
58
+ "secondary": "#bdf4ff",
59
+ "tertiary": "#fffaff",
60
+ "surface-dim": "#0c150f",
61
+ "on-primary-fixed": "#002110",
62
+ "surface-container-lowest": "#07100a",
63
+ "tertiary-container": "#ffdd65"
64
+ },
65
+ borderRadius: {
66
+ "DEFAULT": "0.25rem",
67
+ "lg": "0.5rem",
68
+ "xl": "0.75rem",
69
+ "full": "9999px"
70
+ },
71
+ spacing: {
72
+ "xs": "4px",
73
+ "md": "16px",
74
+ "base": "4px",
75
+ "sm": "8px",
76
+ "xl": "48px",
77
+ "lg": "24px",
78
+ "gutter": "20px",
79
+ "margin": "32px"
80
+ },
81
+ fontFamily: {
82
+ "h2": ["Space Grotesk"],
83
+ "h1": ["Space Grotesk"],
84
+ "h3": ["Space Grotesk"],
85
+ "label-xs": ["Space Grotesk"],
86
+ "data-mono": ["Space Grotesk"],
87
+ "body-base": ["Space Grotesk"]
88
+ },
89
+ fontSize: {
90
+ "h2": ["32px", { lineHeight: "1.2", letterSpacing: "-0.01em", fontWeight: "600" }],
91
+ "h1": ["48px", { lineHeight: "1.1", letterSpacing: "-0.02em", fontWeight: "700" }],
92
+ "h3": ["24px", { lineHeight: "1.3", letterSpacing: "0em", fontWeight: "600" }],
93
+ "label-xs": ["12px", { lineHeight: "1", letterSpacing: "0.1em", fontWeight: "700" }],
94
+ "data-mono": ["14px", { lineHeight: "1.5", letterSpacing: "0.05em", fontWeight: "500" }],
95
+ "body-base": ["16px", { lineHeight: "1.6", letterSpacing: "0.01em", fontWeight: "400" }]
96
+ }
97
+ }
98
+ }
99
+ }
100
+ </script>
101
+ <style>
102
+ .scan-line {
103
+ background: linear-gradient(to bottom, transparent 0%, rgba(0, 255, 156, 0.1) 50%, transparent 100%);
104
+ animation: scan 4s linear infinite;
105
+ }
106
+ @keyframes scan {
107
+ 0% { transform: translateY(-100%); }
108
+ 100% { transform: translateY(200%); }
109
+ }
110
+ </style>
111
+ </head>
112
+ <body class="bg-background text-on-background min-h-screen flex flex-col font-body-base overflow-x-hidden selection:bg-primary-container selection:text-on-primary">
113
+ <!-- Atmospheric Layering -->
114
+ <div class="fixed inset-0 pointer-events-none z-0">
115
+ <div class="absolute inset-0 bg-[linear-gradient(to_right,#8495870a_1px,transparent_1px),linear-gradient(to_bottom,#8495870a_1px,transparent_1px)] bg-[size:3rem_3rem]"></div>
116
+ <div class="absolute top-[20%] left-[30%] w-[40vw] h-[40vw] bg-surface-tint/5 rounded-full blur-[120px] mix-blend-screen"></div>
117
+ <div class="absolute bottom-[10%] right-[20%] w-[35vw] h-[35vw] bg-secondary-container/5 rounded-full blur-[100px] mix-blend-screen"></div>
118
+ </div>
119
+ <!-- TopNavBar (Shared Component) -->
120
+ <header class="bg-emerald-950/20 backdrop-blur-xl border-emerald-500/20 fixed top-0 left-0 w-full z-50 flex justify-between items-center px-6 h-16 w-full border-b shadow-[0_4_20px_rgba(0,0,0,0.5)]">
121
+ <div class="flex items-center gap-8">
122
+ <span class="text-2xl font-black tracking-tighter text-emerald-400 drop-shadow-[0_0_8px_rgba(0,255,156,0.5)]">AUTHRIX AI</span>
123
+ <nav class="hidden md:flex gap-6 font-['Space_Grotesk'] tracking-wider uppercase text-xs">
124
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-2 px-3 rounded-DEFAULT" href="#">Dashboard</a>
125
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-2 px-3 rounded-DEFAULT" href="#">Agents</a>
126
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-2 px-3 rounded-DEFAULT" href="#">Logs</a>
127
+ <a class="text-emerald-800/60 hover:text-emerald-400 transition-colors hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] py-2 px-3 rounded-DEFAULT" href="#">Network</a>
128
+ </nav>
129
+ </div>
130
+ <div class="flex items-center gap-4">
131
+ <div class="flex gap-3 text-emerald-400">
132
+ <span class="material-symbols-outlined hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-DEFAULT cursor-pointer">sensors</span>
133
+ <span class="material-symbols-outlined hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-DEFAULT cursor-pointer">memory</span>
134
+ <span class="material-symbols-outlined hover:bg-emerald-400/10 hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] p-1 rounded-DEFAULT cursor-pointer">speed</span>
135
+ </div>
136
+ <button class="bg-primary-container text-on-primary font-['Space_Grotesk'] tracking-wider uppercase text-xs px-4 py-2 font-bold hover:shadow-[0_0_15px_rgba(0,255,156,0.3)] transition-all">GET STARTED</button>
137
+ </div>
138
+ </header>
139
+ <!-- Main Content Canvas -->
140
+ <main class="flex-grow relative z-10 flex flex-col items-center justify-center pt-32 pb-24 px-6">
141
+ <!-- Upload Panel Container -->
142
+ <div class="w-full max-w-3xl flex flex-col gap-8">
143
+ <!-- Header Section -->
144
+ <div class="text-center space-y-2">
145
+ <h1 class="font-h2 text-h2 text-primary-container drop-shadow-[0_0_12px_rgba(0,255,156,0.4)]">SECURE INGEST PORTAL</h1>
146
+ <p class="font-data-mono text-data-mono text-on-surface-variant">Awaiting encrypted payload via protocol AX-9.</p>
147
+ </div>
148
+ <!-- Primary Upload Zone (Glassmorphic) -->
149
+ <div class="relative bg-surface-container/40 backdrop-blur-2xl border border-outline/20 rounded-xl overflow-hidden group hover:border-primary-container/50 transition-colors duration-500 shadow-[inset_0_1px_1px_rgba(255,255,255,0.05),0_0_30px_rgba(0,0,0,0.5)]">
150
+ <!-- Inner scan line effect -->
151
+ <div class="absolute inset-0 w-full h-[2px] scan-line pointer-events-none opacity-50"></div>
152
+ <div class="p-10 flex flex-col items-center justify-center border-2 border-dashed border-outline-variant/50 m-4 rounded-lg bg-surface-container-low/30 hover:bg-primary-container/5 hover:border-primary-container hover:shadow-[0_0_40px_rgba(0,255,156,0.1)_inset] transition-all duration-300 cursor-pointer min-h-[300px]">
153
+ <div class="w-24 h-24 rounded-full bg-surface-variant flex items-center justify-center mb-6 shadow-[inset_0_2px_4px_rgba(0,0,0,0.4)] group-hover:bg-surface-container-highest transition-colors">
154
+ <span class="material-symbols-outlined text-[48px] text-on-surface-variant group-hover:text-primary-container drop-shadow-[0_0_8px_transparent] group-hover:drop-shadow-[0_0_12px_rgba(0,255,156,0.8)] transition-all duration-300" style="font-variation-settings: 'FILL' 1;">
155
+ cloud_upload
156
+ </span>
157
+ </div>
158
+ <h3 class="font-h3 text-h3 text-on-surface mb-2">INITIALIZE UPLOAD</h3>
159
+ <p class="font-body-base text-body-base text-on-surface-variant mb-6 text-center max-w-sm">Drag and drop verified .DAT, .BIN, or .LOG packets here, or click to browse terminal files.</p>
160
+ <div class="flex items-center gap-2 font-label-xs text-label-xs text-outline tracking-widest">
161
+ <span class="w-1.5 h-1.5 rounded-full bg-outline"></span>
162
+ MAXIMUM PAYLOAD: 5GB
163
+ <span class="w-1.5 h-1.5 rounded-full bg-outline"></span>
164
+ </div>
165
+ </div>
166
+ </div>
167
+ <!-- Upload Queue / Recent Activity (Minimalist Data Grid style) -->
168
+ <div class="mt-8 flex flex-col gap-4">
169
+ <div class="flex items-center justify-between border-b border-outline/20 pb-2">
170
+ <h4 class="font-label-xs text-label-xs text-primary-container uppercase tracking-widest flex items-center gap-2">
171
+ <span class="w-2 h-2 bg-primary-container shadow-[0_0_8px_rgba(0,255,156,0.8)]"></span>
172
+ ACTIVE QUEUE
173
+ </h4>
174
+ <span class="font-data-mono text-[10px] text-on-surface-variant">SYS_TIME: 14:02:09</span>
175
+ </div>
176
+ <!-- Queue Items -->
177
+ <div class="grid grid-cols-1 gap-2">
178
+ <!-- Item 1: Uploading -->
179
+ <div class="bg-surface-container-low border border-outline-variant/30 rounded-DEFAULT p-4 flex flex-col gap-3 relative overflow-hidden">
180
+ <div class="absolute bottom-0 left-0 h-[1px] bg-primary-container w-[45%] shadow-[0_0_5px_rgba(0,255,156,0.8)]"></div>
181
+ <div class="flex justify-between items-start">
182
+ <div class="flex items-center gap-3">
183
+ <span class="material-symbols-outlined text-primary-container text-[20px]">description</span>
184
+ <div>
185
+ <div class="font-data-mono text-data-mono text-on-surface">nexus_core_dump_v4.bin</div>
186
+ <div class="font-label-xs text-[10px] text-on-surface-variant mt-1">1.2 GB / 2.8 GB</div>
187
+ </div>
188
+ </div>
189
+ <span class="font-label-xs text-[10px] border border-primary-container/50 text-primary-container px-2 py-1 bg-primary-container/10">UPLOADING 45%</span>
190
+ </div>
191
+ </div>
192
+ <!-- Item 2: Scanning -->
193
+ <div class="bg-surface-container-low border border-outline-variant/30 rounded-DEFAULT p-4 flex flex-col gap-3">
194
+ <div class="flex justify-between items-start">
195
+ <div class="flex items-center gap-3">
196
+ <span class="material-symbols-outlined text-secondary-container text-[20px]">radar</span>
197
+ <div>
198
+ <div class="font-data-mono text-data-mono text-on-surface">auth_log_sector7.dat</div>
199
+ <div class="font-label-xs text-[10px] text-on-surface-variant mt-1">450 MB</div>
200
+ </div>
201
+ </div>
202
+ <span class="font-label-xs text-[10px] border border-secondary-container/50 text-secondary-container px-2 py-1 bg-secondary-container/10 shadow-[0_0_10px_rgba(0,227,253,0.2)_inset]">SCANNING</span>
203
+ </div>
204
+ </div>
205
+ <!-- Item 3: Verified -->
206
+ <div class="bg-surface-container-low border border-outline-variant/30 rounded-DEFAULT p-4 flex flex-col gap-3 opacity-60">
207
+ <div class="flex justify-between items-start">
208
+ <div class="flex items-center gap-3">
209
+ <span class="material-symbols-outlined text-outline text-[20px]">check_circle</span>
210
+ <div>
211
+ <div class="font-data-mono text-data-mono text-on-surface line-through decoration-outline/50">legacy_key_archive.log</div>
212
+ <div class="font-label-xs text-[10px] text-outline mt-1">12 MB</div>
213
+ </div>
214
+ </div>
215
+ <span class="font-label-xs text-[10px] border border-outline/50 text-outline px-2 py-1">VERIFIED</span>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </main>
222
+ <!-- Footer (Shared Component) -->
223
+ <footer class="bg-black border-emerald-900/30 full-width border-t w-full py-8 px-10 flex flex-col md:flex-row justify-between items-center opacity-80 z-50 relative mt-auto">
224
+ <div class="text-emerald-500 font-mono text-[10px] uppercase tracking-[0.2em] mb-4 md:mb-0">
225
+ © 2024 AUTHRIX COMMAND. SYSTEM_STATE: VIGILANT
226
+ </div>
227
+ <nav class="flex gap-6 font-mono text-[10px] uppercase tracking-[0.2em]">
228
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Kernel</a>
229
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Protocol</a>
230
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Override</a>
231
+ <a class="text-emerald-900 hover:text-emerald-300 transition-all cursor-crosshair" href="#">Diagnostics</a>
232
+ </nav>
233
+ </footer>
234
+ </body></html>