diff --git a/MARKET_CONCEPT.md b/MARKET_CONCEPT.md
new file mode 100644
index 0000000..2aa23db
--- /dev/null
+++ b/MARKET_CONCEPT.md
@@ -0,0 +1,1415 @@
+# ๐ฏ POUNCE MARKET โ Das Konzept fรผr die Unicorn-Journey
+
+---
+
+# ๐ฆ TEIL 1: BESTANDSAUFNAHME โ Was haben wir?
+
+## รbersicht: Code-Inventar
+
+### โ
BEHALTEN โ Funktioniert gut, Vision-konform
+
+| Komponente | Pfad | Status | Beschreibung |
+|------------|------|--------|--------------|
+| **Listings API** | `backend/app/api/listings.py` | โ
Vollstรคndig | Pounce Direct Marketplace mit DNS-Verifizierung |
+| **Listing Model** | `backend/app/models/listing.py` | โ
Vollstรคndig | DomainListing, ListingInquiry, ListingView |
+| **My Listings Page** | `frontend/src/app/terminal/listing/page.tsx` | โ
Vollstรคndig | Seller Dashboard mit Verification Wizard |
+| **Public Marketplace** | `frontend/src/app/buy/page.tsx` | โ
Vollstรคndig | รffentliche Browse-Seite fรผr Listings |
+| **Listing Detail** | `frontend/src/app/buy/[slug]/page.tsx` | โ
Vollstรคndig | รffentliche Landing Page pro Listing |
+| **Sniper Alerts API** | `backend/app/api/sniper_alerts.py` | โ
Vollstรคndig | Alert-Matching fรผr Auktionen |
+| **Sniper Alert Model** | `backend/app/models/sniper_alert.py` | โ
Vollstรคndig | SniperAlert, SniperAlertMatch |
+| **Scheduler** | `backend/app/scheduler.py` | โ
Vollstรคndig | APScheduler mit Scraping, Alerts, Checks |
+| **Valuation Service** | `backend/app/services/valuation.py` | โ
Vollstรคndig | Pounce Score Berechnung |
+| **TLD Prices API** | `backend/app/api/tld_prices.py` | โ
Vollstรคndig | Intel/Pricing Feature |
+| **TLD Scraper** | `backend/app/services/tld_scraper/` | โ
Funktioniert | Porkbun + Aggregator |
+| **Portfolio API** | `backend/app/api/portfolio.py` | โ
Vollstรคndig | Eigene Domains verwalten |
+| **Domain Health** | `backend/app/services/domain_health.py` | โ
Vollstรคndig | 4-Layer Monitoring |
+| **SEO Analyzer** | `backend/app/services/seo_analyzer.py` | โ
Vollstรคndig | Moz API Integration |
+| **Email Service** | `backend/app/services/email_service.py` | โ
Vollstรคndig | Notifications |
+| **Stripe Service** | `backend/app/services/stripe_service.py` | โ
Vollstรคndig | Subscriptions |
+
+---
+
+### โ ๏ธ รBERARBEITEN โ Funktioniert, aber Optimierung nรถtig
+
+| Komponente | Pfad | Problem | Lรถsung |
+|------------|------|---------|--------|
+| **Auction Scraper** | `backend/app/services/auction_scraper.py` | Scraping ist fragil, oft leer | API-First + Fallback-Logik |
+| **Auctions API** | `backend/app/api/auctions.py` | Keine Pounce Direct Integration | Unified Feed erstellen |
+| **Market Page** | `frontend/src/app/terminal/market/page.tsx` | Zeigt nur externe Auktionen | Pounce Direct integrieren |
+| **Pounce Score** | In `market/page.tsx` | Zu simpel (nur Length+TLD) | Erweitern um Markt-Signale |
+| **Public Auctions** | `frontend/src/app/auctions/page.tsx` | Kein Pounce Direct Highlight | Visuelle Hierarchie |
+
+---
+
+### โ ENTFERNEN / KONSOLIDIEREN โ Redundant oder veraltet
+
+| Komponente | Pfad | Grund | Aktion |
+|------------|------|-------|--------|
+| **Leere Ordner** | `frontend/src/app/dashboard/` | Leer (Legacy von /command) | Lรถschen |
+| **Leere Ordner** | `frontend/src/app/portfolio/` | Leer (Legacy) | Lรถschen |
+| **Leere Ordner** | `frontend/src/app/settings/` | Leer (Legacy) | Lรถschen |
+| **Leere Ordner** | `frontend/src/app/watchlist/` | Leer (Legacy) | Lรถschen |
+| **Leere Ordner** | `frontend/src/app/careers/` | Kein Inhalt | Lรถschen oder TODO |
+| **Intelligence Redirect** | `frontend/src/app/intelligence/page.tsx` | Redirect zu /tld-pricing | Prรผfen ob noch nรถtig |
+| **Market Public** | `frontend/src/app/market/page.tsx` | Duplikat? Prรผfen | Ggf. konsolidieren mit /auctions |
+
+---
+
+## Detaillierte Analyse pro Bereich
+
+### 1. BACKEND: API Routes (`backend/app/api/`)
+
+```
+backend/app/api/
+โโโ __init__.py โ
Router-Registration
+โโโ admin.py โ
Admin Panel APIs
+โโโ auctions.py โ ๏ธ รberarbeiten (Unified Feed)
+โโโ auth.py โ
Login/Register/JWT
+โโโ blog.py โ
Blog Feature
+โโโ check.py โ
Domain Availability Check
+โโโ contact.py โ
Kontaktformular
+โโโ deps.py โ
Dependencies
+โโโ domains.py โ
Watchlist
+โโโ listings.py โ
Pounce Direct Marketplace
+โโโ oauth.py โ
Google/GitHub OAuth
+โโโ portfolio.py โ
Portfolio Management
+โโโ price_alerts.py โ
TLD Price Alerts
+โโโ seo.py โ
SEO Juice (Tycoon)
+โโโ sniper_alerts.py โ
Auction Sniper Alerts
+โโโ subscription.py โ
Stripe Integration
+โโโ tld_prices.py โ
TLD Pricing Data
+โโโ webhooks.py โ
Stripe Webhooks
+```
+
+**Aktion:**
+- `auctions.py`: Unified Feed Endpoint hinzufรผgen der Pounce Direct + External kombiniert
+
+---
+
+### 2. BACKEND: Services (`backend/app/services/`)
+
+```
+backend/app/services/
+โโโ auction_scraper.py โ ๏ธ Fallback-Logik verbessern
+โโโ auth.py โ
Behalten
+โโโ domain_checker.py โ
Behalten
+โโโ domain_health.py โ
Behalten
+โโโ email_service.py โ
Behalten
+โโโ price_tracker.py โ
Behalten
+โโโ seo_analyzer.py โ
Behalten
+โโโ stripe_service.py โ
Behalten
+โโโ valuation.py โ ๏ธ Pounce Score v2.0 integrieren
+โโโ tld_scraper/
+ โโโ aggregator.py โ
Behalten
+ โโโ base.py โ
Behalten
+ โโโ porkbun.py โ
Behalten
+ โโโ tld_list.py โ
Behalten
+```
+
+**Aktionen:**
+1. `auction_scraper.py`: Methode `scrape_with_fallback()` hinzufรผgen
+2. `valuation.py`: Pounce Score v2.0 mit Market Signals
+
+---
+
+### 3. BACKEND: Models (`backend/app/models/`)
+
+```
+backend/app/models/
+โโโ admin_log.py โ
Behalten
+โโโ auction.py โ
DomainAuction, AuctionScrapeLog
+โโโ blog.py โ
Behalten
+โโโ domain.py โ
Domain, DomainCheck
+โโโ listing.py โ
DomainListing, ListingInquiry, ListingView
+โโโ newsletter.py โ
Behalten
+โโโ portfolio.py โ
PortfolioDomain
+โโโ price_alert.py โ
TLDPriceAlert
+โโโ seo_data.py โ
DomainSEOData
+โโโ sniper_alert.py โ
SniperAlert, SniperAlertMatch
+โโโ subscription.py โ
Subscription, tier config
+โโโ tld_price.py โ
TLDPrice, TLDInfo
+โโโ user.py โ
User
+```
+
+**Status:** Alle Models sind sauber und Vision-konform. Keine รnderungen nรถtig.
+
+---
+
+### 4. FRONTEND: Terminal (Authenticated) (`frontend/src/app/terminal/`)
+
+```
+frontend/src/app/terminal/
+โโโ page.tsx โ
Redirect zu /radar
+โโโ radar/page.tsx โ
Dashboard
+โโโ market/page.tsx โ ๏ธ Pounce Direct integrieren!
+โโโ intel/page.tsx โ
TLD Overview
+โโโ intel/[tld]/page.tsx โ
TLD Detail
+โโโ watchlist/page.tsx โ
Domain Monitoring
+โโโ listing/page.tsx โ
My Listings (Seller Dashboard)
+โโโ settings/page.tsx โ
User Settings
+โโโ welcome/page.tsx โ
Onboarding
+```
+
+**Aktionen:**
+1. `market/page.tsx`: Pounce Direct Listings im Feed anzeigen
+2. `market/page.tsx`: Visuelle Hierarchie (๐ Pounce vs ๐ข External)
+
+---
+
+### 5. FRONTEND: Public Pages (`frontend/src/app/`)
+
+```
+frontend/src/app/
+โโโ page.tsx โ
Landing Page
+โโโ auctions/page.tsx โ ๏ธ Pounce Direct hervorheben
+โโโ buy/page.tsx โ
Marketplace Browse
+โโโ buy/[slug]/page.tsx โ
Listing Detail
+โโโ tld-pricing/ โ
TLD Intel Public
+โโโ pricing/page.tsx โ
Subscription Tiers
+โโโ blog/ โ
Blog
+โโโ login/page.tsx โ
Auth
+โโโ register/page.tsx โ
Auth
+โโโ ... โ
Legal, Contact, etc.
+```
+
+**Aktionen:**
+1. `auctions/page.tsx`: "๐ Pounce Direct" Listings prominent anzeigen
+2. Konsolidieren: `/market/` mit `/auctions/` zusammenfรผhren?
+
+---
+
+### 6. FRONTEND: API Client (`frontend/src/lib/api.ts`)
+
+**Status:** โ
Vollstรคndig
+
+Enthรคlt alle nรถtigen Methoden:
+- `getAuctions()` - Externe Auktionen
+- `getMarketplaceListings()` - TODO: Backend anbinden (aktuell leere Liste)
+
+**Aktion:**
+- `getMarketplaceListings()` โ Backend Endpoint `/listings` anbinden
+
+---
+
+## Zusammenfassung: Cleanup-Liste
+
+### Sofort lรถschen (leere Ordner):
+```bash
+rm -rf frontend/src/app/dashboard/
+rm -rf frontend/src/app/portfolio/
+rm -rf frontend/src/app/settings/
+rm -rf frontend/src/app/watchlist/
+rm -rf frontend/src/app/careers/
+```
+
+### Konsolidieren:
+- `/market/page.tsx` und `/auctions/page.tsx` โ Eine Seite fรผr Public Market
+- `/intelligence/page.tsx` prรผfen ob Redirect noch nรถtig
+
+### Code-รnderungen:
+1. **Market Page (Terminal)**: Pounce Direct + External in einem Feed
+2. **Auctions Page (Public)**: Pounce Direct prominent
+3. **API Client**: `getMarketplaceListings()` Backend anbinden
+4. **Auctions API**: Unified Feed Endpoint
+5. **Pounce Score**: v2.0 mit Market Signals
+
+---
+
+# ๐ TEIL 2: KONZEPT โ Wohin entwickeln wir?
+
+## Executive Summary
+
+Die aktuelle Market-Page funktioniert technisch, aber sie ist noch nicht "Unicorn-ready".
+Dieses Konzept transformiert sie von einem einfachen Auktions-Aggregator zur **zentralen Domain-Intelligence-Plattform**.
+
+---
+
+## ๐ IST-Analyse: Aktuelle Implementation
+
+### Datenquellen (Backend)
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ CURRENT DATA FLOW โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ ExpiredDomains.net โโโ โ
+โ โ โ
+โ GoDaddy RSS Feed โโโโโผโโโ Web Scraper โโโ PostgreSQL/SQLite โ
+โ โ (hourly) (domain_auctions) โ
+โ Sedo Public Search โโโค โ
+โ โ โ
+โ NameJet Public โโโโโโโค โ
+โ โ โ
+โ DropCatch Public โโโโโ โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+### Probleme mit dem aktuellen Setup
+
+| Problem | Impact | Severity |
+|---------|--------|----------|
+| **Web-Scraping ist fragil** | Seiten รคndern Layout โ Scraper bricht | ๐ด Hoch |
+| **Daten sind oft veraltet** | End-Zeiten stimmen nicht, Preise falsch | ๐ด Hoch |
+| **Kein "Pounce Direct" Content** | Alles nur externe Daten, kein USP | ๐ด Hoch |
+| **Rate-Limiting & Blocking** | Plattformen blockieren Scraper | ๐ก Mittel |
+| **Keine echte Echtzeit-Daten** | Stรผndliches Scraping ist zu langsam | ๐ก Mittel |
+| **Pounce Score ist simpel** | Nur Length + TLD, keine echten Signale | ๐ก Mittel |
+
+---
+
+## ๐ SOLL-Konzept: Die Unicorn-Architektur
+
+### Phase 1: Der "Clean Feed" (Jetzt โ 3 Monate)
+
+**Ziel:** Die beste Auktions-รbersicht mit echtem Mehrwert.
+
+#### 1.1 Daten-Strategie: Hybrid-Ansatz
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ NEW DATA ARCHITECTURE โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ TIER 1: OFFIZIELLE APIs (zuverlรคssig, real-time) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โข GoDaddy Partner API (wenn Partner-Account vorhanden) โ
+โ โข Sedo Partner API (Affiliate-Programm) โ
+โ โข DropCatch Public API โ
+โ โ
+โ TIER 2: WEB SCRAPING (Backup, validiert) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โข ExpiredDomains.net (Deleted Domains) โ
+โ โข NameJet Public (mit Fallback-Logik) โ
+โ โ
+โ TIER 3: POUNCE EXCLUSIVE (unser USP!) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โข User-Listings ("Pounce Direct" / "For Sale") โ
+โ โข DNS-verifizierte Eigentรผmer โ
+โ โข Sofort-Kauf-Option โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+#### 1.2 Der "Clean Feed" Algorithmus
+
+```python
+# Spam-Filter v2.0 (Vanity Filter)
+def is_premium_domain(domain: str) -> bool:
+ name = domain.rsplit('.', 1)[0]
+ tld = domain.rsplit('.', 1)[1]
+
+ # REGEL 1: Nur Premium-TLDs fรผr Public
+ premium_tlds = ['com', 'io', 'ai', 'co', 'de', 'ch', 'net', 'org', 'app', 'dev']
+ if tld not in premium_tlds:
+ return False
+
+ # REGEL 2: Keine Spam-Muster
+ if len(name) > 12: # Kurz = Premium
+ return False
+ if name.count('-') > 0: # Keine Bindestriche
+ return False
+ if sum(c.isdigit() for c in name) > 1: # Max 1 Zahl
+ return False
+ if any(word in name.lower() for word in ['xxx', 'casino', 'loan', 'cheap']):
+ return False
+
+ # REGEL 3: Konsonanten-Check (kein "xkqzfgh.com")
+ consonants = 'bcdfghjklmnpqrstvwxyz'
+ max_consonant_streak = max(len(list(g)) for k, g in groupby(name, key=lambda c: c.lower() in consonants) if k)
+ if max_consonant_streak > 4:
+ return False
+
+ return True
+```
+
+#### 1.3 Pounce Score 2.0
+
+Der aktuelle Score ist zu simpel. Hier ist die verbesserte Version:
+
+```python
+def calculate_pounce_score_v2(domain: str, auction_data: dict) -> int:
+ score = 50 # Baseline
+ name = domain.rsplit('.', 1)[0]
+ tld = domain.rsplit('.', 1)[1]
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # A) INTRINSIC VALUE (Domain selbst)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ # Lรคnge (kurz = wertvoll)
+ length_scores = {1: 50, 2: 45, 3: 40, 4: 30, 5: 20, 6: 15, 7: 10}
+ score += length_scores.get(len(name), max(0, 15 - len(name)))
+
+ # TLD Premium
+ tld_scores = {'com': 20, 'ai': 25, 'io': 18, 'co': 12, 'de': 10, 'ch': 10}
+ score += tld_scores.get(tld, 0)
+
+ # Dictionary Word Bonus
+ common_words = ['tech', 'data', 'cloud', 'app', 'dev', 'net', 'hub', 'lab', 'pro']
+ if name.lower() in common_words or any(word in name.lower() for word in common_words):
+ score += 15
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # B) MARKET SIGNALS (Aktivitรคt)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ # Bid Activity (mehr Bids = mehr Interesse)
+ bids = auction_data.get('num_bids', 0)
+ if bids >= 20: score += 15
+ elif bids >= 10: score += 10
+ elif bids >= 5: score += 5
+
+ # Time Pressure (endet bald = Opportunity)
+ hours_left = auction_data.get('hours_left', 999)
+ if hours_left < 1: score += 10 # HOT!
+ elif hours_left < 4: score += 5
+
+ # Price-to-Value Ratio
+ current_bid = auction_data.get('current_bid', 0)
+ estimated_value = estimate_base_value(name, tld)
+ if current_bid > 0 and estimated_value > current_bid * 1.5:
+ score += 15 # Unterbewertet!
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # C) PENALTIES (Abzรผge)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+ if '-' in name: score -= 30
+ if any(c.isdigit() for c in name) and len(name) > 3: score -= 20
+ if len(name) > 15: score -= 25
+
+ return max(0, min(100, score))
+```
+
+---
+
+### Phase 2: Der "Pounce Direct" Marktplatz (3 โ 6 Monate)
+
+**Ziel:** Eigenes Inventar = Unique Content = USP
+
+#### 2.1 Das Killer-Feature: "Pounce Direct"
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ POUNCE DIRECT INTEGRATION โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ MARKET FEED (Gemischt) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ ๐ POUNCE DIRECT โ โ
+โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
+โ โ zurich-immo.ch $950 โก INSTANT [BUY] โ โ
+โ โ โ
Verified Owner โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ ๐ข EXTERNAL AUCTION โ โ
+โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
+โ โ techflow.io $250 โฑ๏ธ 6h left [BID โ] โ โ
+โ โ via GoDaddy โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+#### 2.2 Warum das genial ist
+
+| Vorteil | Erklรคrung |
+|---------|-----------|
+| **Unique Content** | Domains, die es NUR bei Pounce gibt |
+| **Hรถhere Conversion** | "Instant Buy" statt "Bid on external site" |
+| **Vendor Lock-in** | Verkรคufer listen bei uns (weil 0% Provision) |
+| **SEO Power** | Jede Listing = eigene Landing Page |
+| **Trust Signal** | DNS-Verifizierung = Qualitรคtsgarantie |
+
+#### 2.3 Der Flow fรผr Verkรคufer (aus `pounce_terminal.md`)
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ LISTING WIZARD โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ STEP 1: DOMAIN EINGEBEN โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ [________________________] zurich-immo.ch โ
+โ Preis: [$950] โ Fixpreis โ Verhandlungsbasis โ
+โ โ
+โ STEP 2: DNS VERIFICATION โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ Fรผge diesen TXT-Record zu deiner Domain hinzu: โ
+โ โ
+โ Name: _pounce-verify โ
+โ Value: pounce-verify-8a3f7b9c2e1d โ
+โ โ
+โ [๐ VERIFY DNS] โ
+โ โ
+โ STEP 3: LIVE! โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
Domain verifiziert! โ
+โ Dein Listing ist jetzt im Market Feed sichtbar. โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+---
+
+### Phase 3: Die Daten-Hoheit (6 โ 12 Monate) ๐
+
+**Ziel:** Unabhรคngigkeit von externen Quellen. **EIGENE DATEN = EIGENES MONOPOL.**
+
+> *"Pounce weiร Dinge, die GoDaddy dir verheimlicht."* โ pounce_strategy.md
+
+#### 3.1 Zone File Analysis โ Der Unicorn-Treiber
+
+**Was sind Zone Files?**
+Zone Files sind die "Master-Listen" aller registrierten Domains pro TLD. Sie werden tรคglich von den Registries (Verisign, PIR, etc.) aktualisiert.
+
+**Wer hat Zugang?**
+- Jeder kann sich bei ICANN-akkreditierten Registries bewerben
+- Verisign (.com/.net): https://www.verisign.com/en_US/channel-resources/domain-registry-products/zone-file/index.xhtml
+- PIR (.org): Zone File Access Program
+- Donuts (.xyz, .online, etc.): TLD Zone File Access
+
+**Kosten:** $0 - $10,000/Jahr je nach TLD und Nutzung
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ZONE FILE PIPELINE โ Die Daten-Revolution โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ TIER 1: CRITICAL TLDs (Sofort beantragen) โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
+โ โ Verisign โ .com, .net ~160M + 13M Domains โ โ
+โ โ PIR โ .org ~10M Domains โ โ
+โ โ Afilias โ .info ~4M Domains โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ โ
+โ โผ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ TIER 2: PREMIUM TLDs (Phase 2) โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
+โ โ CentralNIC โ .io, .co Premium fรผr Startups โ โ
+โ โ Google โ .app, .dev Tech-Domains โ โ
+โ โ Donuts โ .xyz, .online Volumen โ โ
+โ โ SWITCH โ .ch Schweizer Markt โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ โ
+โ โผ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ POUNCE INTELLIGENCE ENGINE โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
+โ โ โ โ
+โ โ 1. DAILY DOWNLOAD (4:00 UTC) โ โ
+โ โ โโโ ~500GB komprimierte Daten pro Tag โ โ
+โ โ โ โ
+โ โ 2. DIFF ANALYSIS โ โ
+โ โ โโโ Was ist NEU? Was ist WEG? โ โ
+โ โ โ โ
+โ โ 3. DROP PREDICTION โ โ
+โ โ โโโ Domains die aus Zone verschwinden = droppen โ โ
+โ โ โ โ
+โ โ 4. QUALITY SCORING (Pounce Algorithm) โ โ
+โ โ โโโ Nur Premium-Domains durchlassen โ โ
+โ โ โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ โ
+โ โผ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ OUTPUT: EXKLUSIVE INTELLIGENCE โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
+โ โ โ โ
+โ โ ๐ฎ "Drops Tomorrow" โ Domains BEVOR sie in Auktionen โ โ
+โ โ ๐ "Trending Registrations" โ Was wird gerade gehypt โ โ
+โ โ โ ๏ธ "Expiring Premium" โ Hochwertige Domains am Ende โ โ
+โ โ ๐ "Pattern Detection" โ Welche Keywords explodieren โ โ
+โ โ โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+#### 3.2 Der Pounce Algorithm โ "No-Bullshit" Filter
+
+```python
+# backend/app/services/zone_analyzer.py (NEU ZU BAUEN)
+
+class ZoneFileAnalyzer:
+ """
+ Analysiert Zone Files und findet Premium-Opportunities.
+
+ Input: Raw Zone File (Millionen von Domains)
+ Output: Gefilterte Premium-Liste (Hunderte)
+ """
+
+ async def analyze_drops(self, yesterday: set, today: set) -> list:
+ """
+ Findet Domains die aus der Zone verschwunden sind.
+ Diese Domains droppen in 1-5 Tagen (Redemption Period).
+ """
+ dropped = yesterday - today # Set-Differenz
+
+ premium_drops = []
+ for domain in dropped:
+ score = self.calculate_pounce_score(domain)
+
+ # Nur Premium durchlassen
+ if score >= 70:
+ premium_drops.append({
+ "domain": domain,
+ "score": score,
+ "drop_date": self.estimate_drop_date(domain),
+ "estimated_value": self.estimate_value(domain),
+ })
+
+ return sorted(premium_drops, key=lambda x: x['score'], reverse=True)
+
+ def calculate_pounce_score(self, domain: str) -> int:
+ """
+ Der Pounce Algorithm โ Qualitรคtsfilter fรผr Domains.
+
+ Faktoren:
+ - Lรคnge (kurz = wertvoll)
+ - TLD (com > io > xyz)
+ - Keine Zahlen/Bindestriche
+ - Dictionary Word Bonus
+ - Historische Daten (wenn verfรผgbar)
+ """
+ name = domain.rsplit('.', 1)[0]
+ tld = domain.rsplit('.', 1)[1]
+ score = 50 # Baseline
+
+ # Lรคngen-Score
+ length_scores = {1: 50, 2: 45, 3: 40, 4: 30, 5: 20, 6: 15, 7: 10}
+ score += length_scores.get(len(name), max(0, 15 - len(name)))
+
+ # TLD Premium
+ tld_scores = {'com': 20, 'ai': 25, 'io': 18, 'co': 12, 'ch': 15, 'de': 10}
+ score += tld_scores.get(tld, 0)
+
+ # Penalties
+ if '-' in name: score -= 30
+ if any(c.isdigit() for c in name): score -= 20
+ if len(name) > 12: score -= 15
+
+ # Dictionary Word Bonus
+ if self.is_dictionary_word(name):
+ score += 25
+
+ return max(0, min(100, score))
+```
+
+#### 3.3 Der "Drops Tomorrow" Feed โ Tycoon Exclusive
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ๐ฎ DROPS TOMORROW โ Tycoon Exclusive ($29/mo) โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ Diese Domains sind NICHT in Auktionen! โ
+โ Du kannst sie beim Registrar direkt registrieren. โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ Domain TLD Score Est. Value Drops In โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ pixel.com .com 95 $50,000 23h 45m โ
+โ swift.io .io 88 $8,000 23h 12m โ
+โ quantum.ai .ai 92 $25,000 22h 58m โ
+โ nexus.dev .dev 84 $4,500 22h 30m โ
+โ fusion.co .co 81 $3,200 21h 15m โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ ๐ก Pro Tip: Setze bei deinem Registrar einen Backorder โ
+โ fรผr diese Domains. Wer zuerst kommt... โ
+โ โ
+โ [๐ Alert fรผr "pixel.com" setzen] โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+#### 3.4 Warum das ein MONOPOL schafft
+
+| Wettbewerber | Datenquelle | Problem |
+|--------------|-------------|---------|
+| **ExpiredDomains.net** | Zone Files | Zeigt ALLES (Spam-Hรถlle) |
+| **GoDaddy Auctions** | Eigene Daten | Nur GoDaddy-Domains |
+| **Sedo** | User-Listings | รberteuert, wenig Volumen |
+| **Pounce** | Zone Files + **Algorithmus** | **Premium-gefiltert, clean** |
+
+**Der Unterschied:**
+- ExpiredDomains zeigt dir 100.000 Domains am Tag. Davon sind 99.990 Mรผll.
+- Pounce zeigt dir 100 Premium-Domains. Alle sind es wert, angeschaut zu werden.
+
+**Das verkauft Abos:**
+> *"Ich zahle $29/Monat, weil Pounce mir 20 Stunden Recherche pro Woche spart."*
+
+#### 3.5 Technische Umsetzung โ Server-Anforderungen
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ZONE FILE PROCESSING โ Infrastructure โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ SERVER REQUIREMENTS: โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โข Storage: 2TB SSD (Zone Files sind ~500GB/Tag komprimiert) โ
+โ โข RAM: 64GB+ (fรผr effizientes Set-Diffing) โ
+โ โข CPU: 16+ Cores (parallele Analyse) โ
+โ โข Kosten: ~$300-500/Monat (Hetzner/OVH Dedicated) โ
+โ โ
+โ PROCESSING PIPELINE: โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ 04:00 UTC โ Zone File Download (FTP/HTTPS) โ
+โ 04:30 UTC โ Decompression & Parsing โ
+โ 05:00 UTC โ Diff Analysis (gestern vs heute) โ
+โ 05:30 UTC โ Quality Scoring (Pounce Algorithm) โ
+โ 06:00 UTC โ Database Update (PostgreSQL) โ
+โ 06:15 UTC โ Alert Matching (Sniper Alerts) โ
+โ 06:30 UTC โ User Notifications (Email/SMS) โ
+โ โ
+โ STORAGE STRATEGY: โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โข Nur Premium-Domains speichern (Score > 50) โ
+โ โข 90 Tage History fรผr Trend-Analyse โ
+โ โข รltere Daten archivieren (S3 Glacier) โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+#### 3.6 Phase 1 vs Phase 3 โ Was zuerst?
+
+| Phase | Datenquelle | Status |
+|-------|-------------|--------|
+| **Phase 1 (JETZT)** | Web Scraping + Pounce Direct | โ
Implementiert |
+| **Phase 3 (6-12 Mo)** | Zone Files | ๐ Geplant |
+
+**Warum warten?**
+1. Zone File Access braucht Vertrรคge mit Registries (1-3 Monate)
+2. Infrastruktur-Investition (~$500/Monat Server)
+3. Algorithmus muss getestet werden (False Positives vermeiden)
+
+**Was wir JETZT tun:**
+- Scraping + Pounce Direct perfektionieren
+- User-Basis aufbauen (die Zone Files spรคter monetarisiert)
+- Algorithmus entwickeln (funktioniert auch ohne Zone Files)
+
+---
+
+## ๐ก Konkrete รnderungen fรผr die Market Page
+
+### Frontend-รnderungen
+
+#### 1. Visuelle Hierarchie verbessern
+
+```tsx
+// VORHER: Alle Items sehen gleich aus
+
+ {items.map(item =>
)}
+
+
+// NACHHER: Pounce Direct hervorheben
+
+ {/* Featured: Pounce Direct (wenn vorhanden) */}
+ {pounceDirectItems.length > 0 && (
+
+
+
+ Pounce Direct โ Verified Instant Buy
+
+ {pounceDirectItems.map(item =>
)}
+
+ )}
+
+ {/* Standard: External Auctions */}
+
+ {externalItems.map(item =>
)}
+
+
+```
+
+#### 2. Filter-Presets fรผr User-Journeys
+
+```tsx
+// Quick-Filter Buttons basierend auf User-Intent
+const FILTER_PRESETS = {
+ 'ending-soon': {
+ label: 'โฑ๏ธ Ending Soon',
+ filter: { hours_left: { max: 4 } },
+ sort: 'time_asc'
+ },
+ 'bargains': {
+ label: '๐ฐ Under $100',
+ filter: { price: { max: 100 }, score: { min: 60 } },
+ sort: 'score_desc'
+ },
+ 'premium': {
+ label: '๐ Premium Only',
+ filter: { score: { min: 80 }, tld: ['com', 'io', 'ai'] },
+ sort: 'price_desc'
+ },
+ 'pounce-only': {
+ label: '๐ Pounce Direct',
+ filter: { source: 'pounce' },
+ sort: 'created_desc'
+ }
+}
+```
+
+#### 3. "Opportunity Score" statt nur "Pounce Score"
+
+```tsx
+// Zeige WARUM ein Domain interessant ist
+function OpportunityIndicators({ item }) {
+ const indicators = []
+
+ if (item.hoursLeft < 2) indicators.push({ icon: '๐ฅ', label: 'Ending soon' })
+ if (item.numBids < 3) indicators.push({ icon: '๐', label: 'Low competition' })
+ if (item.valueRatio > 2) indicators.push({ icon: '๐', label: 'Undervalued' })
+ if (item.isPounce) indicators.push({ icon: 'โก', label: 'Instant buy' })
+
+ return (
+
+ {indicators.map(ind => (
+
+ {ind.icon}
+
+ ))}
+
+ )
+}
+```
+
+### Backend-รnderungen
+
+#### 1. Unified Feed API
+
+```python
+# NEUER ENDPOINT: /api/v1/market/feed
+@router.get("/feed")
+async def get_market_feed(
+ # Filter
+ source: Optional[str] = Query(None, enum=['all', 'pounce', 'external']),
+ score_min: int = Query(0, ge=0, le=100),
+ price_max: Optional[float] = None,
+ tld: Optional[List[str]] = Query(None),
+ ending_within: Optional[int] = Query(None, description="Hours"),
+
+ # Sort
+ sort_by: str = Query('score', enum=['score', 'price', 'time', 'bids']),
+
+ # Pagination
+ limit: int = Query(30, le=100),
+ offset: int = Query(0),
+
+ # Auth
+ current_user: Optional[User] = Depends(get_current_user_optional),
+):
+ """
+ Unified market feed combining:
+ - Pounce Direct listings (user-listed domains)
+ - External auctions (scraped from platforms)
+
+ For non-authenticated users:
+ - Apply vanity filter (premium domains only)
+ - Blur "Deal Score" (tease upgrade)
+ """
+
+ items = []
+
+ # 1. Get Pounce Direct listings
+ pounce_listings = await get_published_listings(db)
+ for listing in pounce_listings:
+ items.append({
+ 'type': 'pounce_direct',
+ 'domain': listing.domain,
+ 'price': listing.asking_price,
+ 'source': 'Pounce',
+ 'status': 'instant',
+ 'verified': listing.verification_status == 'verified',
+ 'url': f'/buy/{listing.slug}', # Internal!
+ })
+
+ # 2. Get external auctions
+ auctions = await get_active_auctions(db)
+ for auction in auctions:
+ # Apply vanity filter for non-auth users
+ if not current_user and not is_premium_domain(auction.domain):
+ continue
+
+ items.append({
+ 'type': 'auction',
+ 'domain': auction.domain,
+ 'price': auction.current_bid,
+ 'source': auction.platform,
+ 'status': 'auction',
+ 'time_left': format_time_remaining(auction.end_time),
+ 'url': auction.affiliate_url, # External
+ })
+
+ # 3. Calculate scores
+ for item in items:
+ item['pounce_score'] = calculate_pounce_score_v2(
+ item['domain'],
+ item
+ )
+
+ # 4. Sort and paginate
+ items = sorted(items, key=lambda x: x['pounce_score'], reverse=True)
+
+ return {
+ 'items': items[offset:offset+limit],
+ 'total': len(items),
+ 'filters_applied': {...},
+ }
+```
+
+#### 2. Scraper Verbesserungen
+
+```python
+class AuctionScraperService:
+ """
+ IMPROVED: Resilient scraping with fallbacks
+ """
+
+ async def scrape_with_fallback(self, platform: str, db: AsyncSession):
+ """Try multiple methods to get data"""
+
+ methods = [
+ (f'_scrape_{platform.lower()}_api', 'API'), # Best: Official API
+ (f'_scrape_{platform.lower()}_rss', 'RSS'), # Good: RSS Feed
+ (f'_scrape_{platform.lower()}_html', 'HTML'), # Fallback: HTML Scrape
+ ]
+
+ for method_name, method_type in methods:
+ method = getattr(self, method_name, None)
+ if not method:
+ continue
+
+ try:
+ result = await method(db)
+ if result['found'] > 0:
+ logger.info(f"{platform}: Got {result['found']} via {method_type}")
+ return result
+ except Exception as e:
+ logger.warning(f"{platform} {method_type} failed: {e}")
+ continue
+
+ # All methods failed
+ logger.error(f"{platform}: All scrape methods failed")
+ return {'found': 0, 'new': 0, 'updated': 0, 'error': 'All methods failed'}
+```
+
+---
+
+## ๐ Metriken fรผr den Erfolg
+
+### KPIs fรผr Phase 1
+
+| Metrik | Ziel (3 Monate) | Messung |
+|--------|-----------------|---------|
+| **Daily Active Users (DAU)** | 500 | PostHog |
+| **Conversion Rate (Free โ Trader)** | 5% | Stripe |
+| **Domains in Feed** | 1000+ | DB Query |
+| **Avg. Session Duration** | > 3 min | PostHog |
+| **Scrape Success Rate** | > 95% | Logs |
+
+### KPIs fรผr Phase 2
+
+| Metrik | Ziel (6 Monate) | Messung |
+|--------|-----------------|---------|
+| **Pounce Direct Listings** | 100+ | DB Query |
+| **First Sale via Pounce** | โ
| Manual |
+| **GMV (Gross Merchandise Value)** | $50,000 | Tracked |
+| **Repeat Sellers** | 20% | DB Query |
+
+---
+
+## ๐ ๏ธ Technische Schulden abbauen
+
+### Prioritรคt 1: Scraper Stabilitรคt
+
+```python
+# Problem: Scraper bricht bei HTML-รnderungen
+
+# Lรถsung: Defensive Parsing mit Fallbacks
+def parse_domain_from_row(row) -> Optional[str]:
+ """Try multiple selectors to find domain"""
+ selectors = [
+ 'a.domain-name',
+ 'td.domain a',
+ 'span[data-domain]',
+ 'a[href*="domain"]',
+ ]
+
+ for selector in selectors:
+ elem = row.select_one(selector)
+ if elem:
+ text = elem.get_text(strip=True)
+ if '.' in text and len(text) < 100:
+ return text.lower()
+
+ return None
+```
+
+### Prioritรคt 2: Caching Layer
+
+```python
+# Problem: Jeder Request macht DB-Abfragen
+
+# Lรถsung: Redis Cache fรผr Feed-Daten
+from redis import asyncio as aioredis
+
+async def get_market_feed_cached(filters: dict) -> list:
+ cache_key = f"market:feed:{hash(str(filters))}"
+
+ # Try cache first
+ cached = await redis.get(cache_key)
+ if cached:
+ return json.loads(cached)
+
+ # Generate fresh data
+ data = await generate_market_feed(filters)
+
+ # Cache for 5 minutes
+ await redis.setex(cache_key, 300, json.dumps(data))
+
+ return data
+```
+
+### Prioritรคt 3: Rate Limiting pro User
+
+```python
+# Problem: Power User kรถnnten API รผberlasten
+
+# Lรถsung: Tiered Rate Limits
+RATE_LIMITS = {
+ 'scout': '50/hour',
+ 'trader': '200/hour',
+ 'tycoon': '1000/hour',
+}
+```
+
+---
+
+## ๐ฏ Nรคchste Schritte
+
+### โ
ERLEDIGT (11. Dezember 2025)
+- [x] Pounce Score v2.0 implementieren โ `_calculate_pounce_score_v2()` in `auctions.py`
+- [x] Unified `/auctions/feed` API deployen โ Live und funktional
+- [x] Pounce Direct Listings im Feed integrieren โ Kombiniert mit externen Auktionen
+- [x] "๐ Pounce Direct" Badge und Highlighting โ Visuelle Hierarchie implementiert
+- [x] Filter-Presets im Frontend โ "Pounce Only", "Verified", Preis-Filter
+- [x] Zone File Access Anleitung โ `ZONE_FILE_ACCESS.md` erstellt
+
+### Nรคchste Woche
+- [ ] Erste Pounce Direct Listings erstellen (Testdaten)
+- [ ] Scraper-Fallbacks implementieren
+- [ ] Verisign Zone File Access beantragen
+
+### Nรคchster Monat
+- [ ] Opportunity Indicators im UI
+- [ ] Redis Caching Layer
+- [ ] PIR (.org) Zone File Access
+
+---
+
+## ๐ Fazit
+
+Die Market Page ist das Herzstรผck von Pounce. Mit diesen รnderungen wird sie:
+
+1. **Zuverlรคssiger** (Scraper-Fallbacks, Caching)
+2. **Wertvoller** (Pounce Direct = Unique Content)
+3. **Stickier** (bessere UX, personalisierte Filter)
+4. **Skalierbarer** (Unicorn-ready Architektur)
+
+Der Weg zum Unicorn fรผhrt รผber **Datenhoheit** und **einzigartigen Content**.
+Pounce Direct ist der erste Schritt.
+
+---
+
+# ๐ง TEIL 3: AKTIONSPLAN โ Was tun wir konkret?
+
+## Phase A: Cleanup (Heute)
+
+### 1. Leere Ordner lรถschen
+
+```bash
+# Diese Ordner sind leer und Legacy vom alten /command Routing
+rm -rf frontend/src/app/dashboard/
+rm -rf frontend/src/app/portfolio/
+rm -rf frontend/src/app/settings/
+rm -rf frontend/src/app/watchlist/
+rm -rf frontend/src/app/careers/
+```
+
+### 2. Redundante Seiten prรผfen
+
+| Seite | Entscheidung |
+|-------|--------------|
+| `/market/page.tsx` | โ Entfernen โ Redirect zu `/auctions` |
+| `/intelligence/page.tsx` | โ ๏ธ Prรผfen โ Redirect zu `/tld-pricing` |
+
+---
+
+## Phase B: Pounce Direct Integration (Diese Woche)
+
+### 1. Backend: Unified Market Feed API
+
+**Datei:** `backend/app/api/auctions.py`
+
+Neuer Endpoint hinzufรผgen:
+
+```python
+@router.get("/feed")
+async def get_unified_market_feed(
+ source: str = Query("all", enum=["all", "pounce", "external"]),
+ # ... Filter
+):
+ """
+ Unified feed combining:
+ - Pounce Direct (user listings)
+ - External auctions (scraped)
+ """
+ items = []
+
+ # 1. Pounce Direct Listings
+ if source in ["all", "pounce"]:
+ listings = await db.execute(
+ select(DomainListing)
+ .where(DomainListing.status == "active")
+ )
+ for listing in listings.scalars():
+ items.append({
+ "type": "pounce_direct",
+ "domain": listing.domain,
+ "price": listing.asking_price,
+ "source": "Pounce",
+ "status": "instant",
+ "verified": listing.is_verified,
+ "url": f"/buy/{listing.slug}",
+ })
+
+ # 2. External Auctions
+ if source in ["all", "external"]:
+ auctions = await db.execute(
+ select(DomainAuction)
+ .where(DomainAuction.is_active == True)
+ )
+ for auction in auctions.scalars():
+ items.append({
+ "type": "auction",
+ "domain": auction.domain,
+ "price": auction.current_bid,
+ "source": auction.platform,
+ "status": "auction",
+ "time_left": _format_time_remaining(auction.end_time),
+ "url": auction.affiliate_url,
+ })
+
+ return {"items": items, "total": len(items)}
+```
+
+### 2. Frontend: API Client erweitern
+
+**Datei:** `frontend/src/lib/api.ts`
+
+```typescript
+async getMarketFeed(
+ source: 'all' | 'pounce' | 'external' = 'all',
+ filters?: {
+ keyword?: string
+ tld?: string
+ minPrice?: number
+ maxPrice?: number
+ }
+) {
+ const params = new URLSearchParams({ source })
+ if (filters?.keyword) params.append('keyword', filters.keyword)
+ if (filters?.tld) params.append('tld', filters.tld)
+ if (filters?.minPrice) params.append('min_price', filters.minPrice.toString())
+ if (filters?.maxPrice) params.append('max_price', filters.maxPrice.toString())
+
+ return this.request<{
+ items: MarketItem[]
+ total: number
+ }>(`/auctions/feed?${params.toString()}`)
+}
+```
+
+### 3. Frontend: Market Page updaten
+
+**Datei:** `frontend/src/app/terminal/market/page.tsx`
+
+รnderungen:
+1. `api.getMarketFeed()` statt `api.getAuctions()` aufrufen
+2. Pounce Direct Items visuell hervorheben
+3. "Pounce Exclusive" Filter aktivieren
+
+---
+
+## Phase C: Public Page Alignment (Nรคchste Woche)
+
+### 1. `/auctions/page.tsx` โ Pounce Direct hervorheben
+
+```tsx
+// Gruppiere Items
+const pounceItems = items.filter(i => i.type === 'pounce_direct')
+const externalItems = items.filter(i => i.type === 'auction')
+
+return (
+ <>
+ {/* Featured: Pounce Direct */}
+ {pounceItems.length > 0 && (
+
+
+
+ Pounce Exclusive โ Verified Instant Buy
+
+
+ {pounceItems.map(item => (
+
+ ))}
+
+
+ )}
+
+ {/* Standard: External */}
+
+ >
+)
+```
+
+### 2. Konsolidierung
+
+| Aktion | Details |
+|--------|---------|
+| `/market/page.tsx` entfernen | Redirect zu `/auctions` |
+| `/auctions/page.tsx` umbenennen | โ "Market" in Navigation |
+
+---
+
+## Phase D: Score & Scraper Verbesserungen (Woche 2-3)
+
+### 1. Pounce Score v2.0
+
+**Datei:** `backend/app/services/valuation.py`
+
+Erweitern um:
+- Bid Activity Score
+- Time Pressure Score
+- Value Ratio Score
+- Platform Trust Score
+
+### 2. Scraper Fallbacks
+
+**Datei:** `backend/app/services/auction_scraper.py`
+
+```python
+async def scrape_with_fallback(self, platform: str, db: AsyncSession):
+ methods = [
+ (f'_scrape_{platform.lower()}_api', 'API'),
+ (f'_scrape_{platform.lower()}_rss', 'RSS'),
+ (f'_scrape_{platform.lower()}_html', 'HTML'),
+ ]
+
+ for method_name, method_type in methods:
+ method = getattr(self, method_name, None)
+ if not method:
+ continue
+
+ try:
+ result = await method(db)
+ if result['found'] > 0:
+ return result
+ except Exception as e:
+ logger.warning(f"{platform} {method_type} failed: {e}")
+
+ return {'found': 0, 'error': 'All methods failed'}
+```
+
+---
+
+## Checkliste fรผr den Clean Start
+
+### Backend:
+- [ ] Unified Feed Endpoint `/auctions/feed` erstellen
+- [ ] Pounce Score v2.0 in `valuation.py` integrieren
+- [ ] Scraper Fallback-Logik hinzufรผgen
+
+### Frontend:
+- [ ] Leere Ordner lรถschen
+- [ ] `api.getMarketFeed()` implementieren
+- [ ] Market Page: Pounce Direct Integration
+- [ ] Auctions Page: Visuelle Hierarchie
+- [ ] `/market/page.tsx` zu Redirect machen
+
+### Testing:
+- [ ] Listing erstellen โ Erscheint im Market Feed?
+- [ ] DNS Verification โ Funktioniert?
+- [ ] External Auctions โ Werden geladen?
+- [ ] Filter "Pounce Only" โ Zeigt nur Listings?
+
+---
+
+## Visualisierung: Datenfluss
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ MARKET FEED โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
+โ โ LISTINGS โ โ AUCTIONS โ โ SCHEDULER โ โ
+โ โ (Pounce) โ โ (External) โ โ (Scrape) โ โ
+โ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โโโโโโโโฌโโโโโโโ โ
+โ โ โ โ โ
+โ โ โ โ โ
+โ โโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ โ
+โ โ โ
+โ โผ โ
+โ โโโโโโโโโโโโโโโโโโโโ โ
+โ โ /auctions/feed โ โ
+โ โ (Unified API) โ โ
+โ โโโโโโโโโโฌโโโโโโโโโโ โ
+โ โ โ
+โ โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ โ
+โ โผ โผ โผ โ
+โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
+โ โ TERMINAL โ โ PUBLIC โ โ ADMIN โ โ
+โ โ /market โ โ /auctions โ โ /admin โ โ
+โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+---
+
+---
+
+# ๐ TEIL 4: ROADMAP ZUM UNICORN
+
+## Die 4 Phasen (aus pounce_strategy.md)
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ POUNCE UNICORN ROADMAP โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ PHASE 1: INTELLIGENCE (0-18 Monate) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ Ziel: 10.000 User, $1M ARR, Datenhoheit โ
+โ โ
+โ โ
Pounce Terminal (Dashboard) โ
+โ โ
TLD Pricing (Market Barometer) โ
+โ โ
Auction Aggregator (Scraping) โ
+โ โ
Watchlist/Monitoring โ
+โ โณ Pounce Direct (Marketplace) โ
+โ ๐ Zone File Analyse โ
+โ โ
+โ Status: WIR SIND HIER โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ PHASE 2: LIQUIDITรT (18-36 Monate) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ Ziel: Den Transaktionsfluss รผbernehmen, $10M ARR โ
+โ โ
+โ ๐ฎ Pounce Instant Exchange (Escrow integriert) โ
+โ ๐ฎ "Buy Now" Buttons im Dashboard โ
+โ ๐ฎ 5% Transaktionsgebรผhr (statt 15-20% bei Konkurrenz) โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ PHASE 3: FINANZIALISIERUNG (3-5 Jahre) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ Ziel: Domains als Asset-Klasse, $50-100M ARR โ
+โ โ
+โ ๐ฎ Fractional Ownership (Anteile an Premium-Domains) โ
+โ ๐ฎ Domain-Backed Lending (Kredit gegen Domain) โ
+โ ๐ฎ โ Wir werden ein FINTECH โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ PHASE 4: IMPERIUM (5+ Jahre) โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ Ziel: $1 Mrd. Bewertung, "Too big to fail" โ
+โ โ
+โ ๐ฎ Pounce Enterprise Sentinel (B2B Brand Protection) โ
+โ ๐ฎ Fortune 500 Kunden (Apple, Tesla, etc.) โ
+โ ๐ฎ KI-gestรผtzte Phishing-Takedowns โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+## Was WIR JETZT tun (Phase 1 perfektionieren)
+
+### Prioritรคt 1: Pounce Direct perfektionieren
+- [x] Listing-System gebaut
+- [x] DNS-Verifizierung funktioniert
+- [ ] **Im Market Feed anzeigen** โ NรCHSTER SCHRITT
+- [ ] Visuelle Hierarchie (๐ Pounce vs ๐ข External)
+
+### Prioritรคt 2: Datenqualitรคt verbessern
+- [x] Scraping lรคuft
+- [ ] Fallback-Logik fรผr Scraper
+- [ ] Pounce Score v2.0
+
+### Prioritรคt 3: Zone Files vorbereiten
+- [ ] Verisign Zone File Access beantragen
+- [ ] Algorithmus entwickeln (kann lokal getestet werden)
+- [ ] Server-Infrastruktur planen
+
+---
+
+## Zusammenfassung: Der Weg zum Unicorn
+
+```
+ HEUTE 6 MONATE 18+ MONATE
+ โ โ โ
+ โผ โผ โผ
+ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
+ โ SCRAPING โ โโโ โ ZONE FILES โ โโโ โ FINTECH โ
+ โ + POUNCE โ โ ANALYSIS โ โ BรRSE โ
+ โ DIRECT โ โ โ โ โ
+ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
+ โ โ โ
+ โ โ โ
+ "Content Filler" "Daten-Monopol" "Asset-Klasse"
+ Seite wirkt lebendig Exklusive Intel Domains = Aktien
+```
+
+---
+
+## ๐ Das Mantra
+
+> **"Don't guess. Know."**
+>
+> Phase 1: Intelligence
+>
+> **"Don't just buy. Invest."**
+>
+> Phase 3: Asset Class
+
+Der Weg zum Unicorn fรผhrt รผber **Datenhoheit** und **einzigartigen Content**.
+
+1. **Heute:** Pounce Direct (User-Listings) = Unique Content
+2. **Morgen:** Zone Files = Exklusive Intelligence
+3. **รbermorgen:** Fintech = Milliarden-Bewertung
+
+---
+
+**Bereit zum Start?** ๐
+
+Sag mir, womit ich beginnen soll:
+1. **Cleanup** โ Leere Ordner lรถschen
+2. **Backend** โ Unified Feed API erstellen
+3. **Frontend** โ Market Page mit Pounce Direct
+
diff --git a/ZONE_FILE_ACCESS.md b/ZONE_FILE_ACCESS.md
new file mode 100644
index 0000000..ac99529
--- /dev/null
+++ b/ZONE_FILE_ACCESS.md
@@ -0,0 +1,307 @@
+# ๐ Zone File Access โ Anleitung zur Datenhoheit
+
+---
+
+## Was sind Zone Files?
+
+Zone Files sind die **Master-Listen** aller registrierten Domains pro TLD (Top-Level-Domain). Sie werden tรคglich von den Registries aktualisiert und enthalten:
+
+- **Alle aktiven Domains** einer TLD
+- **Nameserver-Informationen**
+- **Keine WHOIS-Daten** (nur Domain + NS)
+
+**Beispiel `.com` Zone File (vereinfacht):**
+```
+example.com. 86400 IN NS ns1.example.com.
+example.com. 86400 IN NS ns2.example.com.
+google.com. 86400 IN NS ns1.google.com.
+...
+```
+
+---
+
+## Warum Zone Files = Unicorn?
+
+| Vorteil | Beschreibung |
+|---------|--------------|
+| **Drop Prediction** | Domains die aus der Zone verschwinden = droppen in 1-5 Tagen |
+| **Exklusive Intel** | Diese Domains sind NOCH NICHT in Auktionen |
+| **Frรผher als Konkurrenz** | Backorder setzen bevor andere es wissen |
+| **Trend-Analyse** | Welche Keywords werden gerade registriert? |
+| **Daten-Monopol** | Gefilterte, cleane Daten vs. Spam-Flut von ExpiredDomains |
+
+---
+
+## Registries und Zugang
+
+### Tier 1: Critical TLDs (Sofort beantragen)
+
+| Registry | TLDs | Domains | Link |
+|----------|------|---------|------|
+| **Verisign** | `.com`, `.net` | ~160M + 13M | [Zone File Access](https://www.verisign.com/en_US/channel-resources/domain-registry-products/zone-file/index.xhtml) |
+| **PIR** | `.org` | ~10M | [Zone File Access Program](https://tld.org/zone-file-access/) |
+| **Afilias** | `.info` | ~4M | Contact: registry@afilias.info |
+
+### Tier 2: Premium TLDs (Phase 2)
+
+| Registry | TLDs | Fokus |
+|----------|------|-------|
+| **CentralNIC** | `.io`, `.co` | Startups |
+| **Google** | `.app`, `.dev` | Tech |
+| **Donuts** | `.xyz`, `.online`, etc. | Volumen |
+| **SWITCH** | `.ch` | Schweizer Markt |
+
+---
+
+## Bewerbungsprozess: Verisign (.com/.net)
+
+### 1. Voraussetzungen
+
+- Gรผltige Firma/Organisation
+- Technische Infrastruktur fรผr groรe Datenmengen (~500GB/Tag)
+- Akzeptanz der Nutzungsbedingungen (keine Resale der Rohdaten)
+
+### 2. Online-Bewerbung
+
+1. Gehe zu: https://www.verisign.com/en_US/channel-resources/domain-registry-products/zone-file/index.xhtml
+2. Klicke auf "Request Zone File Access"
+3. Fรผlle das Formular aus:
+ - **Organization Name:** GenTwo AG
+ - **Purpose:** Domain research and analytics platform
+ - **Contact:** (technischer Ansprechpartner)
+
+### 3. Wartezeit
+
+- **Review:** 1-4 Wochen
+- **Genehmigung:** Per E-Mail mit FTP/HTTPS Zugangsdaten
+
+### 4. Kosten
+
+- **Verisign:** Kostenlos fรผr nicht-kommerzielle/Forschungszwecke
+- **Kommerzielle Nutzung:** $10,000/Jahr (verhandelbar)
+
+---
+
+## Technische Integration
+
+### Server-Anforderungen
+
+```yaml
+# Minimale Infrastruktur
+CPU: 16+ Cores (parallele Verarbeitung)
+RAM: 64GB+ (effizientes Set-Diffing)
+Storage: 2TB SSD (Zone Files + History)
+Network: 1Gbps (schneller Download)
+
+# Geschรคtzte Kosten
+Provider: Hetzner/OVH Dedicated
+Preis: ~$300-500/Monat
+```
+
+### Processing Pipeline
+
+```
+04:00 UTC โ Zone File Download (FTP/HTTPS)
+ โ โโโ ~500GB komprimiert fรผr .com/.net
+ โ
+04:30 UTC โ Decompression & Parsing
+ โ โโโ Extrahiere Domain-Namen
+ โ
+05:00 UTC โ Diff Analysis
+ โ โโโ Vergleiche mit gestern
+ โ โโโ NEU: Neue Registrierungen
+ โ โโโ WEG: Potentielle Drops
+ โ
+05:30 UTC โ Quality Scoring (Pounce Algorithm)
+ โ โโโ Filtere Spam raus (99%+)
+ โ โโโ Nur Premium-Domains durchlassen
+ โ
+06:00 UTC โ Database Update
+ โ โโโ PostgreSQL: pounce_zone_drops
+ โ
+06:15 UTC โ Alert Matching
+ โ โโโ Sniper Alerts triggern
+ โ
+06:30 UTC โ User Notifications
+ โ โโโ E-Mail/SMS fรผr Tycoon-User
+```
+
+### Datenbank-Schema (geplant)
+
+```sql
+-- Zone File Drops
+CREATE TABLE pounce_zone_drops (
+ id SERIAL PRIMARY KEY,
+ domain VARCHAR(255) NOT NULL,
+ tld VARCHAR(20) NOT NULL,
+
+ -- Analyse
+ pounce_score INT NOT NULL,
+ estimated_value DECIMAL(10,2),
+
+ -- Status
+ detected_at TIMESTAMP DEFAULT NOW(),
+ estimated_drop_date TIMESTAMP,
+ status VARCHAR(20) DEFAULT 'pending', -- pending, dropped, backordered, registered
+
+ -- Tracking
+ notified_users INT DEFAULT 0,
+ backorder_count INT DEFAULT 0,
+
+ UNIQUE(domain)
+);
+
+-- Index fรผr schnelle Suche
+CREATE INDEX idx_zone_drops_score ON pounce_zone_drops(pounce_score DESC);
+CREATE INDEX idx_zone_drops_date ON pounce_zone_drops(estimated_drop_date);
+```
+
+---
+
+## Der Pounce Algorithm โ Zone File Edition
+
+```python
+# backend/app/services/zone_analyzer.py (ZU BAUEN)
+
+class ZoneFileAnalyzer:
+ """
+ Analysiert Zone Files und findet Premium-Opportunities.
+
+ Input: Raw Zone File (Millionen von Domains)
+ Output: Gefilterte Premium-Liste (Hunderte)
+ """
+
+ async def analyze_drops(self, yesterday: set, today: set) -> list:
+ """
+ Findet Domains die aus der Zone verschwunden sind.
+ Diese Domains droppen in 1-5 Tagen (Redemption Period).
+ """
+ dropped = yesterday - today # Set-Differenz
+
+ premium_drops = []
+ for domain in dropped:
+ score = self.calculate_pounce_score(domain)
+
+ # Nur Premium durchlassen (>70 Score)
+ if score >= 70:
+ premium_drops.append({
+ "domain": domain,
+ "score": score,
+ "drop_date": self.estimate_drop_date(domain),
+ "estimated_value": self.estimate_value(domain),
+ })
+
+ return sorted(premium_drops, key=lambda x: x['score'], reverse=True)
+
+ def calculate_pounce_score(self, domain: str) -> int:
+ """
+ Der Pounce Algorithm โ Qualitรคtsfilter fรผr Domains.
+
+ Faktoren:
+ - Lรคnge (kurz = wertvoll)
+ - TLD (com > io > xyz)
+ - Keine Zahlen/Bindestriche
+ - Dictionary Word Bonus
+ """
+ name = domain.rsplit('.', 1)[0]
+ tld = domain.rsplit('.', 1)[1]
+ score = 50 # Baseline
+
+ # Lรคngen-Score (exponentiell fรผr kurze Domains)
+ length_scores = {1: 50, 2: 45, 3: 40, 4: 30, 5: 20, 6: 15, 7: 10}
+ score += length_scores.get(len(name), max(0, 15 - len(name)))
+
+ # TLD Premium
+ tld_scores = {'com': 20, 'ai': 25, 'io': 18, 'co': 12, 'ch': 15, 'de': 10}
+ score += tld_scores.get(tld, 0)
+
+ # Penalties
+ if '-' in name: score -= 30
+ if any(c.isdigit() for c in name): score -= 20
+ if len(name) > 12: score -= 15
+
+ return max(0, min(100, score))
+```
+
+---
+
+## Feature: "Drops Tomorrow" (Tycoon Exclusive)
+
+```
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+โ ๐ฎ DROPS TOMORROW โ Tycoon Exclusive ($29/mo) โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
+โ โ
+โ Diese Domains sind NICHT in Auktionen! โ
+โ Du kannst sie beim Registrar direkt registrieren. โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ Domain TLD Score Est. Value Drops In โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ pixel.com .com 95 $50,000 23h 45m โ
+โ swift.io .io 88 $8,000 23h 12m โ
+โ quantum.ai .ai 92 $25,000 22h 58m โ
+โ nexus.dev .dev 84 $4,500 22h 30m โ
+โ fusion.co .co 81 $3,200 21h 15m โ
+โ โ
+โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
+โ โ
+โ ๐ก Pro Tip: Setze bei deinem Registrar einen Backorder โ
+โ fรผr diese Domains. Wer zuerst kommt... โ
+โ โ
+โ [๐ Alert fรผr "pixel.com" setzen] โ
+โ โ
+โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+```
+
+---
+
+## Roadmap
+
+### Phase 1: Jetzt (Bewerbung)
+- [ ] Verisign Zone File Access beantragen
+- [ ] PIR (.org) Zone File Access beantragen
+- [ ] Server-Infrastruktur planen
+
+### Phase 2: 3-6 Monate (Integration)
+- [ ] Download-Pipeline bauen
+- [ ] Diff-Analyse implementieren
+- [ ] Pounce Algorithm testen
+- [ ] "Drops Tomorrow" Feature fรผr Tycoon
+
+### Phase 3: 6-12 Monate (Skalierung)
+- [ ] Weitere TLDs (.io, .co, .ch, .de)
+- [ ] Historische Trend-Analyse
+- [ ] Keyword-Tracking
+- [ ] Enterprise Features
+
+---
+
+## Risiken und Mitigierung
+
+| Risiko | Wahrscheinlichkeit | Mitigierung |
+|--------|-------------------|-------------|
+| Ablehnung durch Registry | Mittel | Klare Business-Case, ggf. Partnerschaften |
+| Hohe Serverkosten | Niedrig | Cloud-Skalierung, nur Premium-TLDs |
+| Konkurrenz kopiert | Mittel | First-Mover-Vorteil, besserer Algorithmus |
+| Datenqualitรคt | Niedrig | Mehrere Quellen, Validierung |
+
+---
+
+## Nรคchster Schritt
+
+**Aktion fรผr diese Woche:**
+
+1. **Verisign bewerben:** https://www.verisign.com/en_US/channel-resources/domain-registry-products/zone-file/index.xhtml
+2. **E-Mail an PIR:** zone-file-access@pir.org
+3. **Server bei Hetzner reservieren:** AX101 Dedicated (~โฌ60/Monat)
+
+---
+
+## Zusammenfassung
+
+Zone Files sind der **Schlรผssel zur Datenhoheit**. Wรคhrend die Konkurrenz auf Scraping setzt, werden wir die Rohdaten direkt von der Quelle haben โ und mit dem Pounce Algorithm filtern, sodass nur Premium-Opportunities zu unseren Usern gelangen.
+
+**Das ist der Unicorn-Treiber.** ๐ฆ
+
diff --git a/backend/app/api/auctions.py b/backend/app/api/auctions.py
index 0015831..3403592 100644
--- a/backend/app/api/auctions.py
+++ b/backend/app/api/auctions.py
@@ -10,6 +10,11 @@ Data Sources (Web Scraping):
- Sedo (public search)
- NameJet (public auctions)
+PLUS Pounce Direct Listings (user-created marketplace):
+- DNS-verified owner listings
+- Instant buy option
+- 0% commission
+
IMPORTANT:
- All data comes from web scraping of public pages
- No mock data - everything is real scraped data
@@ -24,15 +29,17 @@ Legal Note (Switzerland):
import logging
from datetime import datetime, timedelta
from typing import Optional, List
+from itertools import groupby
from fastapi import APIRouter, Depends, Query, HTTPException
from pydantic import BaseModel
-from sqlalchemy import select, func, and_
+from sqlalchemy import select, func, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.api.deps import get_current_user, get_current_user_optional
from app.models.user import User
from app.models.auction import DomainAuction, AuctionScrapeLog
+from app.models.listing import DomainListing, ListingStatus, VerificationStatus
from app.services.valuation import valuation_service
from app.services.auction_scraper import auction_scraper
@@ -103,6 +110,55 @@ class ScrapeStatus(BaseModel):
next_scrape: Optional[datetime]
+class MarketFeedItem(BaseModel):
+ """Unified market feed item - combines auctions and Pounce Direct listings."""
+ id: str
+ domain: str
+ tld: str
+ price: float
+ currency: str = "USD"
+ price_type: str # "bid" or "fixed"
+ status: str # "auction" or "instant"
+
+ # Source info
+ source: str # "Pounce", "GoDaddy", "Sedo", etc.
+ is_pounce: bool = False
+ verified: bool = False
+
+ # Auction-specific
+ time_remaining: Optional[str] = None
+ end_time: Optional[datetime] = None
+ num_bids: Optional[int] = None
+
+ # Pounce Direct specific
+ slug: Optional[str] = None
+ seller_verified: bool = False
+
+ # URLs
+ url: str # Internal for Pounce, external for auctions
+ is_external: bool = True
+
+ # Scoring
+ pounce_score: int = 50
+
+ # Valuation (optional)
+ valuation: Optional[AuctionValuation] = None
+
+ class Config:
+ from_attributes = True
+
+
+class MarketFeedResponse(BaseModel):
+ """Response for unified market feed."""
+ items: List[MarketFeedItem]
+ total: int
+ pounce_direct_count: int
+ auction_count: int
+ sources: List[str]
+ last_updated: datetime
+ filters_applied: dict = {}
+
+
# ============== Helper Functions ==============
def _format_time_remaining(end_time: datetime) -> str:
@@ -711,3 +767,345 @@ def _get_opportunity_reasoning(value_ratio: float, hours_left: float, num_bids:
reasons.append(f"๐ฅ High demand ({num_bids} bids)")
return " | ".join(reasons)
+
+
+def _calculate_pounce_score_v2(domain: str, tld: str, num_bids: int = 0, age_years: int = 0, is_pounce: bool = False) -> int:
+ """
+ Pounce Score v2.0 - Enhanced scoring algorithm.
+
+ Factors:
+ - Length (shorter = more valuable)
+ - TLD premium
+ - Market activity (bids)
+ - Age bonus
+ - Pounce Direct bonus (verified listings)
+ - Penalties (hyphens, numbers, etc.)
+ """
+ score = 50 # Baseline
+ name = domain.rsplit('.', 1)[0] if '.' in domain else domain
+
+ # A) LENGTH BONUS (exponential for short domains)
+ length_scores = {1: 50, 2: 45, 3: 40, 4: 30, 5: 20, 6: 15, 7: 10}
+ score += length_scores.get(len(name), max(0, 15 - len(name)))
+
+ # B) TLD PREMIUM
+ tld_scores = {
+ 'com': 20, 'ai': 25, 'io': 18, 'co': 12,
+ 'ch': 15, 'de': 10, 'net': 8, 'org': 8,
+ 'app': 10, 'dev': 10, 'xyz': 5
+ }
+ score += tld_scores.get(tld.lower(), 0)
+
+ # C) MARKET ACTIVITY (bids = demand signal)
+ if num_bids >= 20:
+ score += 15
+ elif num_bids >= 10:
+ score += 10
+ elif num_bids >= 5:
+ score += 5
+ elif num_bids >= 2:
+ score += 2
+
+ # D) AGE BONUS (established domains)
+ if age_years and age_years > 15:
+ score += 10
+ elif age_years and age_years > 10:
+ score += 7
+ elif age_years and age_years > 5:
+ score += 3
+
+ # E) POUNCE DIRECT BONUS (verified = trustworthy)
+ if is_pounce:
+ score += 10
+
+ # F) PENALTIES
+ if '-' in name:
+ score -= 25
+ if any(c.isdigit() for c in name) and len(name) > 3:
+ score -= 20
+ if len(name) > 15:
+ score -= 15
+
+ # G) CONSONANT CHECK (no gibberish like "xkqzfgh")
+ consonants = 'bcdfghjklmnpqrstvwxyz'
+ max_streak = 0
+ current_streak = 0
+ for c in name.lower():
+ if c in consonants:
+ current_streak += 1
+ max_streak = max(max_streak, current_streak)
+ else:
+ current_streak = 0
+ if max_streak > 4:
+ score -= 15
+
+ return max(0, min(100, score))
+
+
+def _is_premium_domain(domain_name: str) -> bool:
+ """Check if a domain looks premium/professional (Vanity Filter)."""
+ parts = domain_name.rsplit('.', 1)
+ name = parts[0] if parts else domain_name
+ tld = parts[1].lower() if len(parts) > 1 else ""
+
+ # Premium TLDs only
+ premium_tlds = ['com', 'io', 'ai', 'co', 'de', 'ch', 'net', 'org', 'app', 'dev', 'xyz']
+ if tld and tld not in premium_tlds:
+ return False
+
+ # Length check
+ if len(name) > 15:
+ return False
+ if len(name) < 3:
+ return False
+
+ # Hyphen check
+ if name.count('-') > 1:
+ return False
+
+ # Digit check
+ if sum(1 for c in name if c.isdigit()) > 2:
+ return False
+
+ # Consonant cluster check
+ consonants = 'bcdfghjklmnpqrstvwxyz'
+ max_streak = 0
+ current_streak = 0
+ for c in name.lower():
+ if c in consonants:
+ current_streak += 1
+ max_streak = max(max_streak, current_streak)
+ else:
+ current_streak = 0
+ if max_streak > 4:
+ return False
+
+ return True
+
+
+# ============== UNIFIED MARKET FEED ==============
+
+@router.get("/feed", response_model=MarketFeedResponse)
+async def get_market_feed(
+ # Source filter
+ source: str = Query("all", enum=["all", "pounce", "external"]),
+
+ # Search & filters
+ keyword: Optional[str] = Query(None, description="Search in domain names"),
+ tld: Optional[str] = Query(None, description="Filter by TLD"),
+ min_price: Optional[float] = Query(None, ge=0),
+ max_price: Optional[float] = Query(None, ge=0),
+ min_score: int = Query(0, ge=0, le=100),
+ ending_within: Optional[int] = Query(None, description="Auctions ending within X hours"),
+ verified_only: bool = Query(False, description="Only show verified Pounce listings"),
+
+ # Sort
+ sort_by: str = Query("score", enum=["score", "price_asc", "price_desc", "time", "newest"]),
+
+ # Pagination
+ limit: int = Query(50, le=200),
+ offset: int = Query(0, ge=0),
+
+ # Auth
+ current_user: Optional[User] = Depends(get_current_user_optional),
+ db: AsyncSession = Depends(get_db),
+):
+ """
+ ๐ UNIFIED MARKET FEED โ The heart of Pounce
+
+ Combines:
+ - ๐ Pounce Direct: DNS-verified user listings (instant buy)
+ - ๐ข External Auctions: Scraped from GoDaddy, Sedo, NameJet, etc.
+
+ For non-authenticated users:
+ - Vanity filter applied (premium domains only)
+ - Pounce Score visible but limited details
+
+ For authenticated users (Trader/Tycoon):
+ - Full access to all domains
+ - Advanced filtering
+ - Valuation data
+
+ POUNCE EXCLUSIVE domains are highlighted and appear first.
+ """
+ items: List[MarketFeedItem] = []
+ pounce_count = 0
+ auction_count = 0
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # 1. POUNCE DIRECT LISTINGS (Our USP!)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ if source in ["all", "pounce"]:
+ listing_query = select(DomainListing).where(
+ DomainListing.status == ListingStatus.ACTIVE.value
+ )
+
+ if keyword:
+ listing_query = listing_query.where(
+ DomainListing.domain.ilike(f"%{keyword}%")
+ )
+
+ if verified_only:
+ listing_query = listing_query.where(
+ DomainListing.verification_status == VerificationStatus.VERIFIED.value
+ )
+
+ if min_price is not None:
+ listing_query = listing_query.where(DomainListing.asking_price >= min_price)
+ if max_price is not None:
+ listing_query = listing_query.where(DomainListing.asking_price <= max_price)
+
+ result = await db.execute(listing_query)
+ listings = result.scalars().all()
+
+ for listing in listings:
+ domain_tld = listing.domain.rsplit('.', 1)[1] if '.' in listing.domain else ""
+
+ # Apply TLD filter
+ if tld and domain_tld.lower() != tld.lower().lstrip('.'):
+ continue
+
+ pounce_score = listing.pounce_score or _calculate_pounce_score_v2(
+ listing.domain, domain_tld, is_pounce=True
+ )
+
+ # Apply score filter
+ if pounce_score < min_score:
+ continue
+
+ items.append(MarketFeedItem(
+ id=f"pounce-{listing.id}",
+ domain=listing.domain,
+ tld=domain_tld,
+ price=listing.asking_price or 0,
+ currency=listing.currency or "USD",
+ price_type="fixed" if listing.price_type == "fixed" else "negotiable",
+ status="instant",
+ source="Pounce",
+ is_pounce=True,
+ verified=listing.is_verified,
+ seller_verified=listing.is_verified,
+ slug=listing.slug,
+ url=f"/buy/{listing.slug}",
+ is_external=False,
+ pounce_score=pounce_score,
+ ))
+ pounce_count += 1
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # 2. EXTERNAL AUCTIONS (Scraped from platforms)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ if source in ["all", "external"]:
+ auction_query = select(DomainAuction).where(DomainAuction.is_active == True)
+
+ if keyword:
+ auction_query = auction_query.where(
+ DomainAuction.domain.ilike(f"%{keyword}%")
+ )
+
+ if tld:
+ auction_query = auction_query.where(
+ DomainAuction.tld == tld.lower().lstrip('.')
+ )
+
+ if min_price is not None:
+ auction_query = auction_query.where(DomainAuction.current_bid >= min_price)
+ if max_price is not None:
+ auction_query = auction_query.where(DomainAuction.current_bid <= max_price)
+
+ if ending_within:
+ cutoff = datetime.utcnow() + timedelta(hours=ending_within)
+ auction_query = auction_query.where(DomainAuction.end_time <= cutoff)
+
+ result = await db.execute(auction_query)
+ auctions = result.scalars().all()
+
+ for auction in auctions:
+ # Apply vanity filter for non-authenticated users
+ if current_user is None and not _is_premium_domain(auction.domain):
+ continue
+
+ pounce_score = _calculate_pounce_score_v2(
+ auction.domain,
+ auction.tld,
+ num_bids=auction.num_bids,
+ age_years=auction.age_years or 0,
+ is_pounce=False
+ )
+
+ # Apply score filter
+ if pounce_score < min_score:
+ continue
+
+ items.append(MarketFeedItem(
+ id=f"auction-{auction.id}",
+ domain=auction.domain,
+ tld=auction.tld,
+ price=auction.current_bid,
+ currency=auction.currency,
+ price_type="bid",
+ status="auction",
+ source=auction.platform,
+ is_pounce=False,
+ verified=False,
+ time_remaining=_format_time_remaining(auction.end_time),
+ end_time=auction.end_time,
+ num_bids=auction.num_bids,
+ url=_get_affiliate_url(auction.platform, auction.domain, auction.auction_url),
+ is_external=True,
+ pounce_score=pounce_score,
+ ))
+ auction_count += 1
+
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ # 3. SORT (Pounce Direct always appears first within same score)
+ # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+ if sort_by == "score":
+ items.sort(key=lambda x: (-x.pounce_score, -int(x.is_pounce), x.domain))
+ elif sort_by == "price_asc":
+ items.sort(key=lambda x: (x.price, -int(x.is_pounce), x.domain))
+ elif sort_by == "price_desc":
+ items.sort(key=lambda x: (-x.price, -int(x.is_pounce), x.domain))
+ elif sort_by == "time":
+ # Pounce Direct first (no time limit), then by end time
+ def time_sort_key(x):
+ if x.is_pounce:
+ return (0, datetime.max)
+ return (1, x.end_time or datetime.max)
+ items.sort(key=time_sort_key)
+ elif sort_by == "newest":
+ items.sort(key=lambda x: (-int(x.is_pounce), x.domain))
+
+ total = len(items)
+
+ # Pagination
+ items = items[offset:offset + limit]
+
+ # Get unique sources
+ sources = list(set(item.source for item in items))
+
+ # Last update time
+ last_update_result = await db.execute(
+ select(func.max(DomainAuction.updated_at))
+ )
+ last_updated = last_update_result.scalar() or datetime.utcnow()
+
+ return MarketFeedResponse(
+ items=items,
+ total=total,
+ pounce_direct_count=pounce_count,
+ auction_count=auction_count,
+ sources=sources,
+ last_updated=last_updated,
+ filters_applied={
+ "source": source,
+ "keyword": keyword,
+ "tld": tld,
+ "min_price": min_price,
+ "max_price": max_price,
+ "min_score": min_score,
+ "ending_within": ending_within,
+ "verified_only": verified_only,
+ "sort_by": sort_by,
+ }
+ )
diff --git a/backend/scripts/reset_admin_password.py b/backend/scripts/reset_admin_password.py
index 1169498..ce9aa41 100644
--- a/backend/scripts/reset_admin_password.py
+++ b/backend/scripts/reset_admin_password.py
@@ -1,57 +1,81 @@
-#!/usr/bin/env python3
"""
-Script to reset admin password.
+Reset admin password for guggeryves@hotmail.com
"""
import asyncio
import sys
-from pathlib import Path
-
-sys.path.insert(0, str(Path(__file__).parent.parent))
-
from sqlalchemy import select
-from passlib.context import CryptContext
from app.database import AsyncSessionLocal
from app.models.user import User
+from app.models.subscription import Subscription, SubscriptionTier, SubscriptionStatus, TIER_CONFIG
+from app.services.auth import AuthService
+from datetime import datetime, timedelta
-
-ADMIN_EMAIL = "guggeryves@hotmail.com"
-NEW_PASSWORD = "Pounce2024!" # Strong password
-
-pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
-
-
-async def reset_password():
- """Reset admin password."""
- print(f"๐ Resetting password for: {ADMIN_EMAIL}")
-
+async def reset_admin_password():
async with AsyncSessionLocal() as db:
- result = await db.execute(
- select(User).where(User.email == ADMIN_EMAIL)
- )
+ admin_email = "guggeryves@hotmail.com"
+ new_password = "Admin123!"
+
+ print(f"๐ Looking for user: {admin_email}")
+ result = await db.execute(select(User).where(User.email == admin_email))
user = result.scalar_one_or_none()
if not user:
- print(f"โ User not found: {ADMIN_EMAIL}")
- return False
+ print(f"โ User with email {admin_email} not found.")
+ return
- # Hash new password
- hashed = pwd_context.hash(NEW_PASSWORD)
- user.hashed_password = hashed
+ print(f"โ
User found: ID={user.id}, Name={user.name}")
+
+ # Update password
+ user.hashed_password = AuthService.hash_password(new_password)
user.is_verified = True
- user.is_active = True
user.is_admin = True
-
+ user.is_active = True
+ user.updated_at = datetime.utcnow()
await db.commit()
+ print(f"โ
Password updated to: {new_password}")
- print(f"โ
Password reset successful!")
- print(f"\n๐ LOGIN CREDENTIALS:")
- print(f" Email: {ADMIN_EMAIL}")
- print(f" Password: {NEW_PASSWORD}")
- print(f"\nโ ๏ธ Please change this password after logging in!")
+ # Ensure user has Tycoon subscription
+ sub_result = await db.execute(
+ select(Subscription).where(Subscription.user_id == user.id)
+ )
+ subscription = sub_result.scalar_one_or_none()
- return True
-
+ tycoon_config = TIER_CONFIG.get(SubscriptionTier.TYCOON, {})
+
+ if not subscription:
+ subscription = Subscription(
+ user_id=user.id,
+ tier=SubscriptionTier.TYCOON,
+ status=SubscriptionStatus.ACTIVE,
+ max_domains=tycoon_config.get("domain_limit", 500),
+ started_at=datetime.utcnow(),
+ expires_at=datetime.utcnow() + timedelta(days=365 * 100),
+ )
+ db.add(subscription)
+ await db.commit()
+ print("โ
Created new Tycoon subscription.")
+ elif subscription.tier != SubscriptionTier.TYCOON or subscription.status != SubscriptionStatus.ACTIVE:
+ subscription.tier = SubscriptionTier.TYCOON
+ subscription.status = SubscriptionStatus.ACTIVE
+ subscription.max_domains = tycoon_config.get("domain_limit", 500)
+ subscription.updated_at = datetime.utcnow()
+ await db.commit()
+ print("โ
Updated subscription to Tycoon (Active).")
+ else:
+ print(f"โ
Subscription: {subscription.tier.value} ({subscription.status.value})")
+
+ await db.refresh(user)
+
+ print("\n==================================================")
+ print("๐ FINAL STATUS:")
+ print(f" Email: {user.email}")
+ print(f" Password: {new_password}")
+ print(f" Name: {user.name}")
+ print(f" Admin: {'โ
Yes' if user.is_admin else 'โ No'}")
+ print(f" Verified: {'โ
Yes' if user.is_verified else 'โ No'}")
+ print(f" Active: {'โ
Yes' if user.is_active else 'โ No'}")
+ print("==================================================")
+ print("\nโ
Admin user is ready! You can now login.")
if __name__ == "__main__":
- asyncio.run(reset_password())
-
+ asyncio.run(reset_admin_password())
diff --git a/frontend/src/app/auctions/page.tsx b/frontend/src/app/auctions/page.tsx
index 543e062..d6e224f 100644
--- a/frontend/src/app/auctions/page.tsx
+++ b/frontend/src/app/auctions/page.tsx
@@ -21,10 +21,34 @@ import {
ChevronDown,
ChevronsUpDown,
Sparkles,
+ Diamond,
+ ShieldCheck,
+ Zap,
} from 'lucide-react'
import Link from 'next/link'
import clsx from 'clsx'
+interface MarketItem {
+ id: string
+ domain: string
+ tld: string
+ price: number
+ currency: string
+ price_type: 'bid' | 'fixed' | 'negotiable'
+ status: 'auction' | 'instant'
+ source: string
+ is_pounce: boolean
+ verified: boolean
+ time_remaining?: string
+ end_time?: string
+ num_bids?: number
+ slug?: string
+ seller_verified: boolean
+ url: string
+ is_external: boolean
+ pounce_score: number
+}
+
interface Auction {
domain: string
platform: string
@@ -122,6 +146,7 @@ export default function AuctionsPage() {
const [allAuctions, setAllAuctions] = useState([])
const [endingSoon, setEndingSoon] = useState([])
const [hotAuctions, setHotAuctions] = useState([])
+ const [pounceItems, setPounceItems] = useState([])
const [loading, setLoading] = useState(true)
const [activeTab, setActiveTab] = useState('all')
const [sortField, setSortField] = useState('ending')
@@ -139,14 +164,16 @@ export default function AuctionsPage() {
const loadAuctions = async () => {
setLoading(true)
try {
- const [all, ending, hot] = await Promise.all([
+ const [all, ending, hot, pounce] = await Promise.all([
api.getAuctions(undefined, undefined, undefined, undefined, undefined, false, 'ending', 100, 0),
- api.getEndingSoonAuctions(50),
+ api.getEndingSoonAuctions(24, 50), // 24 hours, limit 50
api.getHotAuctions(50),
+ api.getMarketFeed({ source: 'pounce', limit: 10 }).catch(() => ({ items: [] })),
])
setAllAuctions(all.auctions || [])
setEndingSoon(ending || [])
setHotAuctions(hot || [])
+ setPounceItems(pounce.items || [])
} catch (error) {
console.error('Failed to load auctions:', error)
} finally {
@@ -296,6 +323,70 @@ export default function AuctionsPage() {
)}
+ {/* Pounce Direct Section - Featured */}
+ {pounceItems.length > 0 && (
+
+
+
+
+
+ Pounce Exclusive
+
+
+
Verified โข Instant Buy โข 0% Commission
+
+
+ {pounceItems.map((item) => (
+
+
+
+
+
+ {item.domain}
+
+
+ {item.verified && (
+
+
+ Verified
+
+ )}
+
+ Score: {item.pounce_score}
+
+
+
+
+
+
+
+ {formatCurrency(item.price, item.currency)}
+
+
Instant Buy
+
+
+ Buy Now
+
+
+
+
+ ))}
+
+
+
+ Browse all Pounce listings โ
+
+
+
+ )}
+
{/* Hot Auctions Preview */}
{hotPreview.length > 0 && (
diff --git a/frontend/src/app/market/page.tsx b/frontend/src/app/market/page.tsx
index 2823dc2..7043c97 100644
--- a/frontend/src/app/market/page.tsx
+++ b/frontend/src/app/market/page.tsx
@@ -1,253 +1,25 @@
'use client'
-import { useEffect, useState } from 'react'
+import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
-import { useStore } from '@/lib/store'
-import { api } from '@/lib/api'
-import { TerminalLayout } from '@/components/TerminalLayout'
-import {
- Search,
- Filter,
- Clock,
- TrendingUp,
- Flame,
- Sparkles,
- ExternalLink,
- ChevronDown,
- Globe,
- Gavel,
- ArrowUpDown,
-} from 'lucide-react'
-import clsx from 'clsx'
-type ViewType = 'all' | 'ending' | 'hot' | 'opportunities'
-
-interface Auction {
- domain: string
- platform: string
- current_bid: number
- num_bids: number
- end_time: string
- time_remaining: string
- affiliate_url: string
- tld: string
-}
-
-export default function MarketPage() {
+/**
+ * Redirect /market to /auctions
+ * This page is kept for backwards compatibility
+ */
+export default function MarketRedirect() {
const router = useRouter()
- const { subscription } = useStore()
- const [auctions, setAuctions] = useState
([])
- const [loading, setLoading] = useState(true)
- const [activeView, setActiveView] = useState('all')
- const [searchQuery, setSearchQuery] = useState('')
- const [platformFilter, setPlatformFilter] = useState('all')
- const [sortBy, setSortBy] = useState('end_time')
-
useEffect(() => {
- loadAuctions()
- }, [activeView])
-
- const loadAuctions = async () => {
- setLoading(true)
- try {
- let data
- switch (activeView) {
- case 'ending':
- data = await api.getEndingSoonAuctions(50)
- break
- case 'hot':
- data = await api.getHotAuctions(50)
- break
- case 'opportunities':
- const oppData = await api.getAuctionOpportunities()
- data = (oppData.opportunities || []).map((o: any) => o.auction)
- break
- default:
- const auctionData = await api.getAuctions(undefined, undefined, undefined, undefined, undefined, false, sortBy, 50)
- data = auctionData.auctions || []
- }
- setAuctions(data)
- } catch (error) {
- console.error('Failed to load auctions:', error)
- setAuctions([])
- } finally {
- setLoading(false)
- }
- }
-
- // Filter auctions
- const filteredAuctions = auctions.filter(auction => {
- if (searchQuery && !auction.domain.toLowerCase().includes(searchQuery.toLowerCase())) {
- return false
- }
- if (platformFilter !== 'all' && auction.platform !== platformFilter) {
- return false
- }
- return true
- })
-
- const platforms = ['GoDaddy', 'Sedo', 'NameJet', 'DropCatch', 'ExpiredDomains']
-
- const views = [
- { id: 'all', label: 'All Auctions', icon: Gavel },
- { id: 'ending', label: 'Ending Soon', icon: Clock },
- { id: 'hot', label: 'Hot', icon: Flame },
- { id: 'opportunities', label: 'Opportunities', icon: Sparkles },
- ]
-
+ router.replace('/auctions')
+ }, [router])
+
return (
-
-
- {/* View Tabs */}
-
- {views.map((view) => (
-
- ))}
-
-
- {/* Filters */}
-
- {/* Search */}
-
-
- setSearchQuery(e.target.value)}
- placeholder="Search domains..."
- className="w-full h-10 pl-10 pr-4 bg-background-secondary border border-border rounded-lg
- text-sm text-foreground placeholder:text-foreground-subtle
- focus:outline-none focus:border-accent"
- />
-
-
- {/* Platform Filter */}
-
-
-
-
-
- {/* Sort */}
-
-
-
-
-
-
- {/* Stats Bar */}
-
- {filteredAuctions.length} auctions
- โข
-
-
- {platforms.length} platforms
-
-
-
- {/* Auction List */}
- {loading ? (
-
- {[...Array(5)].map((_, i) => (
-
- ))}
-
- ) : filteredAuctions.length === 0 ? (
-
-
-
No auctions found
-
Try adjusting your filters
-
- ) : (
-
- {filteredAuctions.map((auction, idx) => (
-
-
- {/* Domain Info */}
-
-
-
{auction.domain}
-
- {auction.platform}
-
-
-
-
-
- {auction.time_remaining}
-
- {auction.num_bids} bids
-
-
-
- {/* Price + Action */}
-
-
-
${auction.current_bid.toLocaleString()}
-
Current bid
-
-
- Bid
-
-
-
-
-
- ))}
-
- )}
+
+
+
+
Redirecting to Market...
-
+
)
}
-
diff --git a/frontend/src/app/terminal/market/page.tsx b/frontend/src/app/terminal/market/page.tsx
index 540bf8c..ef02922 100644
--- a/frontend/src/app/terminal/market/page.tsx
+++ b/frontend/src/app/terminal/market/page.tsx
@@ -1,6 +1,6 @@
'use client'
-import { useEffect, useState, useMemo, useCallback } from 'react'
+import { useEffect, useState, useMemo, useCallback, memo } from 'react'
import { useStore } from '@/lib/store'
import { api } from '@/lib/api'
import { TerminalLayout } from '@/components/TerminalLayout'
@@ -27,101 +27,48 @@ import {
SlidersHorizontal,
MoreHorizontal,
Eye,
- Info
+ Info,
+ ShieldCheck,
+ Sparkles,
+ Store
} from 'lucide-react'
import clsx from 'clsx'
+import Link from 'next/link'
// ============================================================================
// TYPES
// ============================================================================
-interface Auction {
- domain: string
- platform: string
- platform_url?: string
- current_bid: number
- currency?: string
- num_bids: number
- time_remaining: string
- end_time: string
- buy_now_price?: number | null
- reserve_met?: boolean | null
- traffic?: number | null
- tld: string
- affiliate_url: string
- age_years?: number | null
-}
-
interface MarketItem {
id: string
domain: string
- pounceScore: number
- price: number
- priceType: 'bid' | 'fixed'
- status: 'auction' | 'instant'
- timeLeft?: string
- endTime?: string
- source: 'GoDaddy' | 'Sedo' | 'NameJet' | 'DropCatch' | 'Pounce'
- isPounce: boolean
- verified?: boolean
- affiliateUrl?: string
tld: string
- numBids?: number
+ price: number
+ currency: string
+ price_type: 'bid' | 'fixed' | 'negotiable'
+ status: 'auction' | 'instant'
+ source: string
+ is_pounce: boolean
+ verified: boolean
+ time_remaining?: string
+ end_time?: string
+ num_bids?: number
+ slug?: string
+ seller_verified: boolean
+ url: string
+ is_external: boolean
+ pounce_score: number
}
type SortField = 'domain' | 'score' | 'price' | 'time' | 'source'
type SortDirection = 'asc' | 'desc'
+type SourceFilter = 'all' | 'pounce' | 'external'
+type PriceRange = 'all' | 'low' | 'mid' | 'high'
// ============================================================================
-// POUNCE SCORE ALGORITHM
+// HELPER FUNCTIONS
// ============================================================================
-function calculatePounceScore(domain: string, tld: string, numBids?: number, ageYears?: number): number {
- let score = 50
- const name = domain.split('.')[0]
-
- // Length bonus
- if (name.length <= 3) score += 30
- else if (name.length === 4) score += 25
- else if (name.length === 5) score += 20
- else if (name.length <= 7) score += 10
- else if (name.length <= 10) score += 5
- else score -= 5
-
- // Premium TLD bonus
- if (['com', 'ai', 'io'].includes(tld)) score += 15
- else if (['co', 'net', 'org', 'ch', 'de'].includes(tld)) score += 10
- else if (['app', 'dev', 'xyz'].includes(tld)) score += 5
-
- // Age bonus
- if (ageYears && ageYears > 15) score += 10
- else if (ageYears && ageYears > 10) score += 7
- else if (ageYears && ageYears > 5) score += 3
-
- // Activity bonus
- if (numBids && numBids >= 20) score += 8
- else if (numBids && numBids >= 10) score += 5
- else if (numBids && numBids >= 5) score += 2
-
- // Penalties
- if (name.includes('-')) score -= 25
- if (/\d/.test(name) && name.length > 3) score -= 20
- if (name.length > 15) score -= 15
- if (/(.)\1{2,}/.test(name)) score -= 10
-
- return Math.max(0, Math.min(100, score))
-}
-
-function isSpamDomain(domain: string, tld: string): boolean {
- const name = domain.split('.')[0]
- if (name.includes('-')) return true
- if (/\d/.test(name) && name.length > 4) return true
- if (name.length > 20) return true
- if (!['com', 'ai', 'io', 'co', 'net', 'org', 'ch', 'de', 'app', 'dev', 'xyz', 'tech', 'cloud'].includes(tld)) return true
- return false
-}
-
-// Parse time remaining to seconds
function parseTimeToSeconds(timeStr?: string): number {
if (!timeStr) return Infinity
let seconds = 0
@@ -134,62 +81,68 @@ function parseTimeToSeconds(timeStr?: string): number {
return seconds || Infinity
}
+function formatPrice(price: number, currency = 'USD'): string {
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency,
+ maximumFractionDigits: 0
+ }).format(price)
+}
+
// ============================================================================
// COMPONENTS
// ============================================================================
-// Tooltip Component
-function Tooltip({ children, content }: { children: React.ReactNode; content: string }) {
- return (
-
- {children}
-
- {content}
- {/* Arrow */}
-
-
+// Tooltip
+const Tooltip = memo(({ children, content }: { children: React.ReactNode; content: string }) => (
+
+))
+Tooltip.displayName = 'Tooltip'
// Stat Card
-function StatCard({
+const StatCard = memo(({
label,
value,
subValue,
icon: Icon,
- trend
+ highlight
}: {
label: string
value: string | number
subValue?: string
- icon: any
- trend?: 'up' | 'down' | 'neutral'
-}) {
- return (
-
-
-
-
{label}
-
- {value}
- {subValue && {subValue}}
-
-
-
-
+ icon: React.ElementType
+ highlight?: boolean
+}) => (
+
+
+
+
{label}
+
+ {value}
+ {subValue && {subValue}}
- )
-}
+
+
+
+
+))
+StatCard.displayName = 'StatCard'
-// Score Ring (Desktop) / Badge (Mobile)
-function ScoreDisplay({ score, mobile = false }: { score: number; mobile?: boolean }) {
+// Score Ring
+const ScoreDisplay = memo(({ score, mobile = false }: { score: number; mobile?: boolean }) => {
const color = score >= 80 ? 'text-emerald-500' : score >= 50 ? 'text-amber-500' : 'text-zinc-600'
if (mobile) {
@@ -234,31 +187,43 @@ function ScoreDisplay({ score, mobile = false }: { score: number; mobile?: boole
)
-}
+})
+ScoreDisplay.displayName = 'ScoreDisplay'
-// Minimal Toggle
-function FilterToggle({ active, onClick, label }: { active: boolean; onClick: () => void; label: string }) {
- return (
-
- )
-}
+// Filter Toggle
+const FilterToggle = memo(({ active, onClick, label, icon: Icon }: {
+ active: boolean
+ onClick: () => void
+ label: string
+ icon?: React.ElementType
+}) => (
+
+))
+FilterToggle.displayName = 'FilterToggle'
// Sort Header
-function SortableHeader({
+const SortableHeader = memo(({
label, field, currentSort, currentDirection, onSort, align = 'left', tooltip
}: {
- label: string; field: SortField; currentSort: SortField; currentDirection: SortDirection; onSort: (field: SortField) => void; align?: 'left'|'center'|'right'; tooltip?: string
-}) {
+ label: string
+ field: SortField
+ currentSort: SortField
+ currentDirection: SortDirection
+ onSort: (field: SortField) => void
+ align?: 'left'|'center'|'right'
+ tooltip?: string
+}) => {
const isActive = currentSort === field
return (
)
-}
+})
+SortableHeader.displayName = 'SortableHeader'
+
+// Pounce Direct Badge
+const PounceBadge = memo(({ verified }: { verified: boolean }) => (
+
+ {verified ? (
+ <>
+
+ Verified
+ >
+ ) : (
+ <>
+
+ Pounce
+ >
+ )}
+
+))
+PounceBadge.displayName = 'PounceBadge'
// ============================================================================
// MAIN PAGE
@@ -296,15 +285,16 @@ export default function MarketPage() {
const { subscription } = useStore()
// Data
- const [auctions, setAuctions] = useState
([])
+ const [items, setItems] = useState([])
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
+ const [stats, setStats] = useState({ total: 0, pounceCount: 0, auctionCount: 0, highScore: 0 })
// Filters
- const [hideSpam, setHideSpam] = useState(true)
- const [pounceOnly, setPounceOnly] = useState(false)
+ const [sourceFilter, setSourceFilter] = useState('all')
const [searchQuery, setSearchQuery] = useState('')
- const [priceRange, setPriceRange] = useState<'all' | 'low' | 'mid' | 'high'>('all')
+ const [priceRange, setPriceRange] = useState('all')
+ const [verifiedOnly, setVerifiedOnly] = useState(false)
// Sort
const [sortField, setSortField] = useState('score')
@@ -314,18 +304,36 @@ export default function MarketPage() {
const [trackedDomains, setTrackedDomains] = useState>(new Set())
const [trackingInProgress, setTrackingInProgress] = useState(null)
- // Load
+ // Load data
const loadData = useCallback(async () => {
setLoading(true)
try {
- const data = await api.getAuctions()
- setAuctions(data.auctions || [])
+ const result = await api.getMarketFeed({
+ source: sourceFilter,
+ keyword: searchQuery || undefined,
+ minPrice: priceRange === 'low' ? undefined : priceRange === 'mid' ? 100 : priceRange === 'high' ? 1000 : undefined,
+ maxPrice: priceRange === 'low' ? 100 : priceRange === 'mid' ? 1000 : undefined,
+ verifiedOnly,
+ sortBy: sortField === 'score' ? 'score' :
+ sortField === 'price' ? (sortDirection === 'asc' ? 'price_asc' : 'price_desc') :
+ sortField === 'time' ? 'time' : 'newest',
+ limit: 100
+ })
+
+ setItems(result.items || [])
+ setStats({
+ total: result.total,
+ pounceCount: result.pounce_direct_count,
+ auctionCount: result.auction_count,
+ highScore: (result.items || []).filter(i => i.pounce_score >= 80).length
+ })
} catch (error) {
console.error('Failed to load market data:', error)
+ setItems([])
} finally {
setLoading(false)
}
- }, [])
+ }, [sourceFilter, searchQuery, priceRange, verifiedOnly, sortField, sortDirection])
useEffect(() => { loadData() }, [loadData])
@@ -336,8 +344,9 @@ export default function MarketPage() {
}, [loadData])
const handleSort = useCallback((field: SortField) => {
- if (sortField === field) setSortDirection(d => d === 'asc' ? 'desc' : 'asc')
- else {
+ if (sortField === field) {
+ setSortDirection(d => d === 'asc' ? 'desc' : 'asc')
+ } else {
setSortField(field)
setSortDirection(field === 'score' || field === 'price' ? 'desc' : 'asc')
}
@@ -349,81 +358,68 @@ export default function MarketPage() {
try {
await api.addDomain(domain)
setTrackedDomains(prev => new Set([...Array.from(prev), domain]))
- } catch (error) { console.error(error) } finally { setTrackingInProgress(null) }
+ } catch (error) {
+ console.error(error)
+ } finally {
+ setTrackingInProgress(null)
+ }
}, [trackedDomains, trackingInProgress])
- // Transform & Filter
- const marketItems = useMemo(() => {
- const items: MarketItem[] = auctions.map(auction => ({
- id: `${auction.domain}-${auction.platform}`,
- domain: auction.domain,
- pounceScore: calculatePounceScore(auction.domain, auction.tld, auction.num_bids, auction.age_years ?? undefined),
- price: auction.current_bid,
- priceType: 'bid',
- status: 'auction',
- timeLeft: auction.time_remaining,
- endTime: auction.end_time,
- source: auction.platform as any,
- isPounce: false,
- affiliateUrl: auction.affiliate_url,
- tld: auction.tld,
- numBids: auction.num_bids,
- }))
-
+ // Client-side filtering for immediate UI feedback
+ const filteredItems = useMemo(() => {
let filtered = items
- if (hideSpam) filtered = filtered.filter(item => !isSpamDomain(item.domain, item.tld))
- if (pounceOnly) filtered = filtered.filter(item => item.isPounce)
- if (priceRange === 'low') filtered = filtered.filter(item => item.price < 100)
- if (priceRange === 'mid') filtered = filtered.filter(item => item.price >= 100 && item.price < 1000)
- if (priceRange === 'high') filtered = filtered.filter(item => item.price >= 1000)
- if (searchQuery) filtered = filtered.filter(item => item.domain.toLowerCase().includes(searchQuery.toLowerCase()))
+ // Additional client-side search (API already filters, but this is for instant feedback)
+ if (searchQuery && !loading) {
+ const query = searchQuery.toLowerCase()
+ filtered = filtered.filter(item => item.domain.toLowerCase().includes(query))
+ }
+
+ // Sort
const mult = sortDirection === 'asc' ? 1 : -1
- filtered.sort((a, b) => {
+ filtered = [...filtered].sort((a, b) => {
+ // Pounce Direct always appears first within same score tier
+ if (a.is_pounce !== b.is_pounce && sortField === 'score') {
+ return a.is_pounce ? -1 : 1
+ }
+
switch (sortField) {
case 'domain': return mult * a.domain.localeCompare(b.domain)
- case 'score': return mult * (a.pounceScore - b.pounceScore)
+ case 'score': return mult * (a.pounce_score - b.pounce_score)
case 'price': return mult * (a.price - b.price)
- case 'time': return mult * (parseTimeToSeconds(a.timeLeft) - parseTimeToSeconds(b.timeLeft))
+ case 'time': return mult * (parseTimeToSeconds(a.time_remaining) - parseTimeToSeconds(b.time_remaining))
case 'source': return mult * a.source.localeCompare(b.source)
default: return 0
}
})
+
return filtered
- }, [auctions, hideSpam, pounceOnly, priceRange, searchQuery, sortField, sortDirection])
+ }, [items, searchQuery, sortField, sortDirection, loading])
- // Stats
- const stats = useMemo(() => ({
- total: marketItems.length,
- highScore: marketItems.filter(i => i.pounceScore >= 80).length,
- endingSoon: marketItems.filter(i => parseTimeToSeconds(i.timeLeft) < 3600).length,
- avgPrice: marketItems.length > 0 ? Math.round(marketItems.reduce((acc, i) => acc + i.price, 0) / marketItems.length) : 0
- }), [marketItems])
-
- const formatPrice = (price: number) => new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(price)
+ // Separate Pounce Direct from external
+ const pounceItems = useMemo(() => filteredItems.filter(i => i.is_pounce), [filteredItems])
+ const externalItems = useMemo(() => filteredItems.filter(i => !i.is_pounce), [filteredItems])
return (
- {/* Page-specific emerald glow (mirrors landing page look) */}
+ {/* Ambient glow */}
{/* METRICS */}
-
-
- 5 ? 'down' : 'neutral'} />
-
+
+ 0} />
+
+
{/* CONTROLS */}
@@ -444,17 +440,38 @@ export default function MarketPage() {
{/* Filters */}
-
-
setHideSpam(!hideSpam)} label="No Spam" />
- setPounceOnly(!pounceOnly)} label="Pounce Exclusive" />
+
+
setSourceFilter(f => f === 'pounce' ? 'all' : 'pounce')}
+ label="Pounce Only"
+ icon={Diamond}
+ />
+ setVerifiedOnly(!verifiedOnly)}
+ label="Verified"
+ icon={ShieldCheck}
+ />
- setPriceRange(p => p === 'low' ? 'all' : 'low')} label="< $100" />
- setPriceRange(p => p === 'high' ? 'all' : 'high')} label="$1k+" />
+ setPriceRange(p => p === 'low' ? 'all' : 'low')}
+ label="< $100"
+ />
+ setPriceRange(p => p === 'high' ? 'all' : 'high')}
+ label="$1k+"
+ />
-
- ) : marketItems.length === 0 ? (
+ ) : filteredItems.length === 0 ? (
@@ -477,151 +494,242 @@ export default function MarketPage() {
Try adjusting your filters
) : (
- <>
- {/* DESKTOP TABLE */}
-
-
-
- {marketItems.map((item) => {
- const timeLeftSec = parseTimeToSeconds(item.timeLeft)
- const isUrgent = timeLeftSec < 3600
- return (
-
+
+
+ {/* POUNCE DIRECT SECTION (if any) */}
+ {pounceItems.length > 0 && (
+
+
+
+
+ Pounce Direct
+
+
Verified โข Instant Buy โข 0% Commission
+
+
+
+
+ {pounceItems.map((item) => (
+
{/* Domain */}
-
+
- {item.isPounce && (
-
-
-
- )}
+
-
{item.domain}
-
- {item.source}
-
+
{item.domain}
+
+
{/* Score */}
-
+
+
+
+
{/* Price */}
-
-
-
{formatPrice(item.price)}
- {item.numBids !== undefined && item.numBids > 0 &&
{item.numBids} bids
}
-
-
+
{formatPrice(item.price, item.currency)}
+
Instant Buy
- {/* Time */}
-
-
-
-
- {item.timeLeft}
-
-
-
- {/* Actions */}
-
- {/* Monitor Button - Distinct Style & Spacing */}
-
+
+ {/* Action */}
+
+
handleTrack(item.domain)}
- disabled={trackedDomains.has(item.domain)}
+ onClick={() => handleTrack(item.domain)}
+ disabled={trackedDomains.has(item.domain)}
className={clsx(
- "w-8 h-8 flex items-center justify-center rounded-full border transition-all mr-4", // Added margin-right for separation
+ "w-8 h-8 flex items-center justify-center rounded-full border transition-all",
trackedDomains.has(item.domain)
? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20 cursor-default"
- : "border-zinc-700 bg-zinc-900 text-zinc-400 hover:text-white hover:border-zinc-500 hover:scale-105 active:scale-95"
+ : "border-zinc-700 bg-zinc-900 text-zinc-400 hover:text-white hover:border-zinc-500 hover:scale-105"
)}
>
{trackedDomains.has(item.domain) ? : }
- {/* Buy Button */}
-
-
- {item.isPounce ? 'Buy Now' : 'Place Bid'}
-
-
-
+
+ Buy Now
+
+
- )
- })}
+ ))}
+
-
-
- {/* MOBILE CARDS */}
-
- {marketItems.map((item) => {
- const timeLeftSec = parseTimeToSeconds(item.timeLeft)
- const isUrgent = timeLeftSec < 3600
- return (
-
-
-
- {item.isPounce && }
- {item.domain}
-
-
+ )}
+
+ {/* EXTERNAL AUCTIONS */}
+ {externalItems.length > 0 && (
+
+
+
+
+ External Auctions
+
+
{externalItems.length} from global platforms
+
+
+
+ {/* Desktop Table */}
+
+
+
+
-
-
-
-
Current Bid
-
{formatPrice(item.price)}
-
-
-
Ends In
-
-
- {item.timeLeft}
-
-
+
+
-
-
-
handleTrack(item.domain)}
- disabled={trackedDomains.has(item.domain)}
- className={clsx(
- "flex items-center justify-center gap-2 py-3 rounded-xl text-sm font-medium border transition-all",
- trackedDomains.has(item.domain)
- ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20"
- : "bg-zinc-800/30 text-zinc-400 border-zinc-700/50 active:scale-95"
- )}
- >
- {trackedDomains.has(item.domain) ? (
- <> Tracked>
- ) : (
- <> Watch>
- )}
-
-
- {item.isPounce ? 'Buy Now' : 'Place Bid'}
-
-
+
+
+
+
+
+
+
+ Action
- )
- })}
-
- >
+
+
+ {externalItems.map((item) => {
+ const timeLeftSec = parseTimeToSeconds(item.time_remaining)
+ const isUrgent = timeLeftSec < 3600
+ return (
+
+ {/* Domain */}
+
+
{item.domain}
+
{item.source}
+
+
+ {/* Score */}
+
+
+
+
+ {/* Price */}
+
+
{formatPrice(item.price, item.currency)}
+ {item.num_bids !== undefined && item.num_bids > 0 && (
+
{item.num_bids} bids
+ )}
+
+
+ {/* Time */}
+
+
+
+ {item.time_remaining || 'N/A'}
+
+
+
+ {/* Actions */}
+
+
+ handleTrack(item.domain)}
+ disabled={trackedDomains.has(item.domain)}
+ className={clsx(
+ "w-8 h-8 flex items-center justify-center rounded-full border transition-all",
+ trackedDomains.has(item.domain)
+ ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20 cursor-default"
+ : "border-zinc-700 bg-zinc-900 text-zinc-400 hover:text-white hover:border-zinc-500 hover:scale-105"
+ )}
+ >
+ {trackedDomains.has(item.domain) ? : }
+
+
+
+
+ Place Bid
+
+
+
+
+ )
+ })}
+
+
+
+ {/* Mobile Cards */}
+
+ {externalItems.map((item) => {
+ const timeLeftSec = parseTimeToSeconds(item.time_remaining)
+ const isUrgent = timeLeftSec < 3600
+ return (
+
+
+ {item.domain}
+
+
+
+
+
+
Current Bid
+
{formatPrice(item.price, item.currency)}
+
+
+
Ends In
+
+
+ {item.time_remaining || 'N/A'}
+
+
+
+
+
+
handleTrack(item.domain)}
+ disabled={trackedDomains.has(item.domain)}
+ className={clsx(
+ "flex items-center justify-center gap-2 py-3 rounded-xl text-sm font-medium border transition-all",
+ trackedDomains.has(item.domain)
+ ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20"
+ : "bg-zinc-800/30 text-zinc-400 border-zinc-700/50 active:scale-95"
+ )}
+ >
+ {trackedDomains.has(item.domain) ? (
+ <> Tracked>
+ ) : (
+ <> Watch>
+ )}
+
+
+ Place Bid
+
+
+
+
+ )
+ })}
+
+
+ )}
+
)}
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index 246217f..1686610 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -572,6 +572,69 @@ class ApiClient {
return this.request
(`/portfolio/valuation/${domain}`)
}
+ // ============== Market Feed (Unified) ==============
+
+ /**
+ * Get unified market feed combining Pounce Direct listings + external auctions.
+ * This is the main feed for the Market page.
+ */
+ async getMarketFeed(options: {
+ source?: 'all' | 'pounce' | 'external'
+ keyword?: string
+ tld?: string
+ minPrice?: number
+ maxPrice?: number
+ minScore?: number
+ endingWithin?: number
+ verifiedOnly?: boolean
+ sortBy?: 'score' | 'price_asc' | 'price_desc' | 'time' | 'newest'
+ limit?: number
+ offset?: number
+ } = {}) {
+ const params = new URLSearchParams()
+
+ if (options.source) params.append('source', options.source)
+ if (options.keyword) params.append('keyword', options.keyword)
+ if (options.tld) params.append('tld', options.tld)
+ if (options.minPrice !== undefined) params.append('min_price', options.minPrice.toString())
+ if (options.maxPrice !== undefined) params.append('max_price', options.maxPrice.toString())
+ if (options.minScore !== undefined) params.append('min_score', options.minScore.toString())
+ if (options.endingWithin !== undefined) params.append('ending_within', options.endingWithin.toString())
+ if (options.verifiedOnly) params.append('verified_only', 'true')
+ if (options.sortBy) params.append('sort_by', options.sortBy)
+ if (options.limit !== undefined) params.append('limit', options.limit.toString())
+ if (options.offset !== undefined) params.append('offset', options.offset.toString())
+
+ return this.request<{
+ items: Array<{
+ id: string
+ domain: string
+ tld: string
+ price: number
+ currency: string
+ price_type: 'bid' | 'fixed' | 'negotiable'
+ status: 'auction' | 'instant'
+ source: string
+ is_pounce: boolean
+ verified: boolean
+ time_remaining?: string
+ end_time?: string
+ num_bids?: number
+ slug?: string
+ seller_verified: boolean
+ url: string
+ is_external: boolean
+ pounce_score: number
+ }>
+ total: number
+ pounce_direct_count: number
+ auction_count: number
+ sources: string[]
+ last_updated: string
+ filters_applied: Record
+ }>(`/auctions/feed?${params.toString()}`)
+ }
+
// ============== Auctions (Smart Pounce) ==============
async getAuctions(