Premium overhaul based on review feedback
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled

- Fix Command Center loading on mobile (add mobile sidebar menu)
- Rename 'Market' to 'Auctions' in navigation (clearer naming)
- Add Vanity Filter for public auctions (hide spam domains)
  - Premium TLDs only for public (.com, .io, .ai, etc.)
  - Max 15 chars, max 1 hyphen, max 2 digits
  - No random consonant strings
- Improve pricing page differentiation
  - Highlight 'Smart spam filter' for Trader
  - Show 'Curated list' vs 'Raw feed'
  - Add sublabels for key features
- Add background effects to Command Center
- Improve responsive design
This commit is contained in:
yves.gugger
2025-12-10 08:53:41 +01:00
parent f648457353
commit 35d943a372
7 changed files with 315 additions and 59 deletions

View File

@ -224,6 +224,15 @@ async def search_auctions(
# Build query
query = select(DomainAuction).where(DomainAuction.is_active == True)
# VANITY FILTER: For public (non-logged-in) users, only show premium-looking domains
# This ensures the first impression is high-quality, not spam domains
if current_user is None:
# Premium TLDs only (no .cc, .website, .info spam clusters)
premium_tlds = ['com', 'io', 'ai', 'co', 'de', 'ch', 'net', 'org', 'app', 'dev', 'xyz']
query = query.where(DomainAuction.tld.in_(premium_tlds))
# No domains with more than 15 characters (excluding TLD)
# Note: We filter further in Python for complex rules
if keyword:
query = query.where(DomainAuction.domain.ilike(f"%{keyword}%"))
@ -266,6 +275,49 @@ async def search_auctions(
result = await db.execute(query)
auctions = list(result.scalars().all())
# VANITY FILTER PART 2: Apply Python-side filtering for public users
# This ensures only premium-looking domains are shown to non-logged-in users
if current_user is None:
def is_premium_domain(domain_name: str) -> bool:
"""Check if a domain looks premium/professional"""
# Extract just the domain part (without TLD)
parts = domain_name.rsplit('.', 1)
name = parts[0] if parts else domain_name
# Rule 1: No more than 15 characters
if len(name) > 15:
return False
# Rule 2: No more than 1 hyphen
if name.count('-') > 1:
return False
# Rule 3: No more than 2 digits total
digit_count = sum(1 for c in name if c.isdigit())
if digit_count > 2:
return False
# Rule 4: Must be at least 3 characters
if len(name) < 3:
return False
# Rule 5: No random-looking strings (too many consonants in a row)
consonants = 'bcdfghjklmnpqrstvwxyz'
consonant_streak = 0
max_streak = 0
for c in name.lower():
if c in consonants:
consonant_streak += 1
max_streak = max(max_streak, consonant_streak)
else:
consonant_streak = 0
if max_streak > 4:
return False
return True
auctions = [a for a in auctions if is_premium_domain(a.domain)]
# Convert to response with valuations
listings = []
for auction in auctions:

0
frontend/src/app/intelligence/page.tsx Normal file → Executable file
View File

View File

@ -22,9 +22,11 @@ const tiers = [
{ text: '5 domains to track', highlight: false, available: true },
{ text: 'Daily availability scans', highlight: false, available: true },
{ text: 'Email alerts', highlight: false, available: true },
{ text: 'TLD price overview', highlight: false, available: true },
{ text: 'Raw auction feed', highlight: false, available: true, sublabel: 'Unfiltered' },
{ text: 'Curated auction list', highlight: false, available: false },
{ text: 'Deal scores & valuations', highlight: false, available: false },
],
cta: 'Hunt Free',
cta: 'Start Free',
highlighted: false,
badge: null,
isPaid: false,
@ -35,20 +37,19 @@ const tiers = [
icon: TrendingUp,
price: '9',
period: '/mo',
description: 'Hunt with precision. Daily intel.',
description: 'The smart investor\'s choice.',
features: [
{ text: '50 domains to track', highlight: true, available: true },
{ text: 'Hourly scans', highlight: true, available: true },
{ text: 'Email alerts', highlight: false, available: true },
{ text: 'Full TLD market data', highlight: false, available: true },
{ text: 'Domain valuation', highlight: true, available: true },
{ text: 'Portfolio (25 domains)', highlight: true, available: true },
{ text: 'Hourly scans', highlight: true, available: true, sublabel: '24x faster' },
{ text: 'Smart spam filter', highlight: true, available: true, sublabel: 'Curated list' },
{ text: 'Deal scores & valuations', highlight: true, available: true },
{ text: 'Portfolio tracking (25)', highlight: true, available: true },
{ text: '90-day price history', highlight: false, available: true },
{ text: 'Expiry tracking', highlight: true, available: true },
{ text: 'Expiry date tracking', highlight: true, available: true },
],
cta: 'Start Trading',
cta: 'Upgrade to Trader',
highlighted: true,
badge: 'Most Popular',
badge: 'Best Value',
isPaid: true,
},
{
@ -57,14 +58,15 @@ const tiers = [
icon: Crown,
price: '29',
period: '/mo',
description: 'Dominate the market. No limits.',
description: 'For serious domain investors.',
features: [
{ text: '500 domains to track', highlight: true, available: true },
{ text: 'Real-time scans (10 min)', highlight: true, available: true },
{ text: 'Priority email alerts', highlight: false, available: true },
{ text: 'Real-time scans', highlight: true, available: true, sublabel: 'Every 10 min' },
{ text: 'Priority alerts', highlight: true, available: true },
{ text: 'Unlimited portfolio', highlight: true, available: true },
{ text: 'Full price history', highlight: true, available: true },
{ text: 'Advanced valuation', highlight: true, available: true },
{ text: 'API access', highlight: true, available: true, sublabel: 'Coming soon' },
],
cta: 'Go Tycoon',
highlighted: false,
@ -76,8 +78,10 @@ const tiers = [
const comparisonFeatures = [
{ name: 'Watchlist Domains', scout: '5', trader: '50', tycoon: '500' },
{ name: 'Check Frequency', scout: 'Daily', trader: 'Hourly', tycoon: '10 min' },
{ name: 'Auction Feed', scout: 'Raw (unfiltered)', trader: 'Curated (spam-free)', tycoon: 'Curated (spam-free)' },
{ name: 'Deal Scores', scout: '—', trader: 'check', tycoon: 'check' },
{ name: 'Portfolio Domains', scout: '—', trader: '25', tycoon: 'Unlimited' },
{ name: 'Domain Valuation', scout: '—', trader: 'check', tycoon: 'check' },
{ name: 'Domain Valuation', scout: '—', trader: 'check', tycoon: 'Advanced' },
{ name: 'Price History', scout: '—', trader: '90 days', tycoon: 'Unlimited' },
{ name: 'Expiry Tracking', scout: '—', trader: 'check', tycoon: 'check' },
]
@ -230,9 +234,24 @@ export default function PricingPage() {
<ul className="space-y-3 mb-8 flex-1">
{tier.features.map((feature) => (
<li key={feature.text} className="flex items-start gap-3">
<Check className="w-4 h-4 mt-0.5 shrink-0 text-accent" strokeWidth={2.5} />
<span className="text-body-sm text-foreground">
{feature.available ? (
<Check className={clsx(
"w-4 h-4 mt-0.5 shrink-0",
feature.highlight ? "text-accent" : "text-foreground-muted"
)} strokeWidth={2.5} />
) : (
<X className="w-4 h-4 mt-0.5 shrink-0 text-foreground-subtle" strokeWidth={2} />
)}
<span className={clsx(
"text-body-sm",
feature.available ? "text-foreground" : "text-foreground-subtle line-through"
)}>
{feature.text}
{feature.sublabel && (
<span className="ml-1.5 text-xs text-accent font-medium">
{feature.sublabel}
</span>
)}
</span>
</li>
))}

79
frontend/src/components/CommandCenterLayout.tsx Normal file → Executable file
View File

@ -27,14 +27,22 @@ export function CommandCenterLayout({
const [notificationsOpen, setNotificationsOpen] = useState(false)
const [searchOpen, setSearchOpen] = useState(false)
const [searchQuery, setSearchQuery] = useState('')
const [mounted, setMounted] = useState(false)
// Ensure component is mounted before rendering
useEffect(() => {
setMounted(true)
}, [])
// Load sidebar state from localStorage
useEffect(() => {
const saved = localStorage.getItem('sidebar-collapsed')
if (saved) {
setSidebarCollapsed(saved === 'true')
if (mounted) {
const saved = localStorage.getItem('sidebar-collapsed')
if (saved) {
setSidebarCollapsed(saved === 'true')
}
}
}, [])
}, [mounted])
useEffect(() => {
checkAuth()
@ -50,10 +58,14 @@ export function CommandCenterLayout({
const availableDomains = domains?.filter(d => d.is_available) || []
const hasNotifications = availableDomains.length > 0
if (isLoading) {
// Show loading only if we're still checking auth
if (!mounted || isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-background">
<div className="w-6 h-6 border-2 border-accent border-t-transparent rounded-full animate-spin" />
<div className="flex flex-col items-center gap-4">
<div className="w-8 h-8 border-2 border-accent border-t-transparent rounded-full animate-spin" />
<p className="text-sm text-foreground-muted">Loading Command Center...</p>
</div>
</div>
)
}
@ -64,6 +76,12 @@ export function CommandCenterLayout({
return (
<div className="min-h-screen bg-background">
{/* Background Effects */}
<div className="fixed inset-0 pointer-events-none">
<div className="absolute top-[-20%] left-1/2 -translate-x-1/2 w-[1200px] h-[800px] bg-accent/[0.02] rounded-full blur-[120px]" />
<div className="absolute bottom-[-10%] right-[-10%] w-[600px] h-[600px] bg-accent/[0.015] rounded-full blur-[100px]" />
</div>
{/* Sidebar */}
<Sidebar
collapsed={sidebarCollapsed}
@ -73,38 +91,51 @@ export function CommandCenterLayout({
{/* Main Content Area */}
<div
className={clsx(
"min-h-screen transition-all duration-300",
sidebarCollapsed ? "ml-[72px]" : "ml-[240px]"
"relative min-h-screen transition-all duration-300",
// Desktop: adjust for sidebar
"lg:ml-[240px]",
sidebarCollapsed && "lg:ml-[72px]",
// Mobile: no margin, just padding for menu button
"ml-0 pt-16 lg:pt-0"
)}
>
{/* Top Bar */}
<header className="sticky top-0 z-30 h-16 sm:h-20 bg-background/80 backdrop-blur-xl border-b border-border/50">
<div className="h-full px-6 flex items-center justify-between">
<div className="h-full px-4 sm:px-6 flex items-center justify-between">
{/* Left: Title */}
<div>
<div className="ml-10 lg:ml-0">
{title && (
<h1 className="text-xl sm:text-2xl font-display text-foreground">{title}</h1>
<h1 className="text-lg sm:text-xl lg:text-2xl font-display text-foreground">{title}</h1>
)}
{subtitle && (
<p className="text-sm text-foreground-muted mt-0.5">{subtitle}</p>
<p className="text-xs sm:text-sm text-foreground-muted mt-0.5 hidden sm:block">{subtitle}</p>
)}
</div>
{/* Right: Actions */}
<div className="flex items-center gap-3">
<div className="flex items-center gap-2 sm:gap-3">
{/* Quick Search */}
<button
onClick={() => setSearchOpen(true)}
className="hidden sm:flex items-center gap-2 h-9 px-4 bg-foreground/5 hover:bg-foreground/10
className="hidden md:flex items-center gap-2 h-9 px-4 bg-foreground/5 hover:bg-foreground/10
border border-border/50 rounded-lg text-sm text-foreground-muted
hover:text-foreground transition-all duration-200"
>
<Search className="w-4 h-4" />
<span>Search domains...</span>
<kbd className="hidden md:inline-flex items-center h-5 px-1.5 bg-background border border-border
<span>Search...</span>
<kbd className="hidden lg:inline-flex items-center h-5 px-1.5 bg-background border border-border
rounded text-[10px] text-foreground-subtle font-mono">K</kbd>
</button>
{/* Mobile Search */}
<button
onClick={() => setSearchOpen(true)}
className="md:hidden flex items-center justify-center w-9 h-9 text-foreground-muted
hover:text-foreground hover:bg-foreground/5 rounded-lg transition-colors"
>
<Search className="w-4.5 h-4.5" />
</button>
{/* Notifications */}
<div className="relative">
<button
@ -178,7 +209,7 @@ export function CommandCenterLayout({
</header>
{/* Page Content */}
<main className="p-6 lg:p-8">
<main className="relative p-4 sm:p-6 lg:p-8">
{children}
</main>
</div>
@ -186,7 +217,7 @@ export function CommandCenterLayout({
{/* Quick Search Modal */}
{searchOpen && (
<div
className="fixed inset-0 z-50 bg-background/80 backdrop-blur-sm flex items-start justify-center pt-[20vh]"
className="fixed inset-0 z-[60] bg-background/80 backdrop-blur-sm flex items-start justify-center pt-[15vh] sm:pt-[20vh] px-4"
onClick={() => setSearchOpen(false)}
>
<div
@ -204,10 +235,15 @@ export function CommandCenterLayout({
outline-none text-lg"
autoFocus
/>
<kbd className="hidden sm:flex items-center h-6 px-2 bg-background border border-border
rounded text-xs text-foreground-subtle font-mono">ESC</kbd>
<button
onClick={() => setSearchOpen(false)}
className="flex items-center h-6 px-2 bg-background border border-border
rounded text-xs text-foreground-subtle font-mono hover:text-foreground transition-colors"
>
ESC
</button>
</div>
<div className="p-4 text-center text-foreground-muted text-sm">
<div className="p-6 text-center text-foreground-muted text-sm">
Start typing to search...
</div>
</div>
@ -235,4 +271,3 @@ function KeyboardShortcut({ onTrigger, keys }: { onTrigger: () => void, keys: st
return null
}

View File

@ -38,7 +38,7 @@ export function Header() {
// Public navigation - same for all visitors
const publicNavItems = [
{ href: '/auctions', label: 'Market', icon: Gavel },
{ href: '/auctions', label: 'Auctions', icon: Gavel },
{ href: '/tld-pricing', label: 'TLD Intel', icon: TrendingUp },
{ href: '/pricing', label: 'Pricing', icon: CreditCard },
]

View File

@ -17,6 +17,8 @@ import {
Zap,
Shield,
CreditCard,
Menu,
X,
} from 'lucide-react'
import { useState, useEffect } from 'react'
import clsx from 'clsx'
@ -32,6 +34,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
// Internal state for uncontrolled mode
const [internalCollapsed, setInternalCollapsed] = useState(false)
const [mobileOpen, setMobileOpen] = useState(false)
// Use controlled or uncontrolled state
const collapsed = controlledCollapsed ?? internalCollapsed
@ -45,6 +48,11 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
}
}, [])
// Close mobile menu on route change
useEffect(() => {
setMobileOpen(false)
}, [pathname])
// Save collapsed state
const toggleCollapsed = () => {
const newState = !collapsed
@ -56,7 +64,10 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
const tierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap
const TierIcon = tierIcon
// Navigation items
// Count available domains for notification badge
const availableCount = domains?.filter(d => d.is_available).length || 0
// Navigation items - renamed "Market" to "Auctions" per review
const navItems = [
{
href: '/dashboard',
@ -68,7 +79,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
href: '/watchlist',
label: 'Watchlist',
icon: Eye,
badge: domains?.filter(d => d.is_available).length || null,
badge: availableCount || null,
},
{
href: '/portfolio',
@ -77,8 +88,8 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
badge: null,
},
{
href: '/market',
label: 'Market',
href: '/auctions',
label: 'Auctions',
icon: Gavel,
badge: null,
},
@ -99,15 +110,8 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
return pathname.startsWith(href)
}
return (
<aside
className={clsx(
"fixed left-0 top-0 bottom-0 z-40 flex flex-col",
"bg-background-secondary/50 backdrop-blur-xl border-r border-border",
"transition-all duration-300 ease-in-out",
collapsed ? "w-[72px]" : "w-[240px]"
)}
>
const SidebarContent = () => (
<>
{/* Logo */}
<div className={clsx(
"h-16 sm:h-20 flex items-center border-b border-border/50",
@ -135,6 +139,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
<Link
key={item.href}
href={item.href}
onClick={() => setMobileOpen(false)}
className={clsx(
"group flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200",
isActive(item.href)
@ -174,6 +179,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
{user?.is_admin && (
<Link
href="/admin"
onClick={() => setMobileOpen(false)}
className={clsx(
"group flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200",
pathname.startsWith('/admin')
@ -192,6 +198,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
<Link
key={item.href}
href={item.href}
onClick={() => setMobileOpen(false)}
className={clsx(
"group flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200",
isActive(item.href)
@ -247,7 +254,10 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
{/* Logout */}
<button
onClick={logout}
onClick={() => {
logout()
setMobileOpen(false)
}}
className={clsx(
"w-full flex items-center gap-3 px-3 py-2.5 rounded-xl transition-all duration-200",
"text-foreground-muted hover:text-foreground hover:bg-foreground/5"
@ -259,12 +269,12 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
</button>
</div>
{/* Collapse Toggle */}
{/* Collapse Toggle - Desktop only */}
<button
onClick={toggleCollapsed}
className={clsx(
"absolute -right-3 top-24 w-6 h-6 bg-background-secondary border border-border rounded-full",
"flex items-center justify-center text-foreground-muted hover:text-foreground",
"hidden lg:flex absolute -right-3 top-24 w-6 h-6 bg-background-secondary border border-border rounded-full",
"items-center justify-center text-foreground-muted hover:text-foreground",
"hover:bg-foreground/5 transition-all duration-200 shadow-sm"
)}
>
@ -274,7 +284,60 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
<ChevronLeft className="w-3.5 h-3.5" />
)}
</button>
</aside>
</>
)
return (
<>
{/* Mobile Menu Button */}
<button
onClick={() => setMobileOpen(true)}
className="lg:hidden fixed top-4 left-4 z-50 w-10 h-10 bg-background-secondary border border-border
rounded-xl flex items-center justify-center text-foreground-muted hover:text-foreground
transition-colors shadow-lg"
>
<Menu className="w-5 h-5" />
</button>
{/* Mobile Overlay */}
{mobileOpen && (
<div
className="lg:hidden fixed inset-0 z-40 bg-background/80 backdrop-blur-sm"
onClick={() => setMobileOpen(false)}
/>
)}
{/* Mobile Sidebar */}
<aside
className={clsx(
"lg:hidden fixed left-0 top-0 bottom-0 z-50 w-[280px] flex flex-col",
"bg-background-secondary border-r border-border",
"transition-transform duration-300 ease-in-out",
mobileOpen ? "translate-x-0" : "-translate-x-full"
)}
>
{/* Close button */}
<button
onClick={() => setMobileOpen(false)}
className="absolute top-4 right-4 w-8 h-8 flex items-center justify-center
text-foreground-muted hover:text-foreground transition-colors"
>
<X className="w-5 h-5" />
</button>
<SidebarContent />
</aside>
{/* Desktop Sidebar */}
<aside
className={clsx(
"hidden lg:flex fixed left-0 top-0 bottom-0 z-40 flex-col",
"bg-background-secondary/50 backdrop-blur-xl border-r border-border",
"transition-all duration-300 ease-in-out",
collapsed ? "w-[72px]" : "w-[240px]"
)}
>
<SidebarContent />
</aside>
</>
)
}

87
report.md Normal file
View File

@ -0,0 +1,87 @@
Das ist ein gewaltiger Schritt nach vorne! 🚀
Die Seiten wirken jetzt kohärent, professionell und haben eine klare psychologische Führung (Hook -> Value -> Gate -> Sign Up). Besonders der Wechsel auf **$9 für den Einstieg** (Trader) ist smart das ist ein "No-Brainer"-Preis für Impulse-Käufe.
Hier ist mein Feedback zu den einzelnen Seiten mit Fokus auf Conversion und UX:
---
### 1. Navigation & Globales Layout
Die Navigation ist **perfekt minimalistisch**.
* `Market | TLD Intel | Pricing` Das sind genau die drei Säulen.
* **Vorschlag:** Ich würde "Market" eventuell in **"Auctions"** oder **"Live Market"** umbenennen. "Market" ist etwas vage. "Auctions" triggert eher das Gefühl "Hier gibt es Schnäppchen".
---
### 2. Landing Page
**Das Starke:**
* Die Headline *"The market never sleeps. You should."* ist Weltklasse.
* Der Ticker mit den Live-Preisen erzeugt sofort FOMO (Fear Of Missing Out).
* Die Sektion "TLD Intelligence" mit den "Sign in to view"-Overlays bei den Daten ist ein **exzellenter Conversion-Treiber**. Der User sieht, dass da Daten *sind*, aber er muss sich anmelden (kostenlos), um sie zu sehen. Das ist der perfekte "Account-Erstellungs-Köder".
**Kritikpunkt / To-Do:**
* **Der "Search"-Fokus:** Du schreibst *"Try dream.com..."*, aber visuell muss dort ein **riesiges Input-Feld** sein. Das muss das dominante Element sein.
* **Der Ticker:** Achte darauf, dass der Ticker technisch sauber läuft (marquee/scrolling). Im Text oben wiederholt sich die Liste statisch auf der echten Seite muss das fließen.
---
### 3. Market / Auctions Page (WICHTIG!)
Hier sehe ich das **größte Risiko**.
Dein Konzept ("Unlock Smart Opportunities") ist super. Aber die **Beispiel-Daten**, die du auf der Public-Seite zeigst, sind gefährlich.
**Das Problem:**
In deiner Liste stehen Dinge wie:
* `fgagtqjisqxyoyjrjfizxshtw.xyz`
* `52gao1588.cc`
* `professional-packing-services...website`
Wenn ein neuer User das sieht, denkt er: **"Das ist eine Spam-Seite voll mit Schrott."** Er wird sich nicht anmelden.
**Die Lösung (Der "Vanity-Filter"):**
Du musst für die **öffentliche Seite (ausgeloggt)** einen harten Filter in den Code bauen. Zeige ausgeloggten Usern **NUR** Domains an, die schön aussehen.
* Regel 1: Keine Zahlen (außer bei kurzen Domains).
* Regel 2: Keine Bindestriche (Hyphens).
* Regel 3: Länge < 12 Zeichen.
* Regel 4: Nur .com, .io, .ai, .co, .de, .ch (Keine .cc, .website Spam-Cluster).
**Warum?**
Der User soll denken: "Wow, hier gibt es Premium-Domains wie `nexus.dev`". Er darf den Müll nicht sehen, bevor er eingeloggt ist (und selbst dann solltest du den Müll filtern, wie wir besprochen haben).
---
### 4. TLD Pricing Page
**Sehr gut gelöst.**
* Die "Moving Now"-Karten oben (.ai +35%) sind der Haken.
* Die Tabelle darunter mit "Sign in" zu sperren (Blur-Effekt oder Schloss-Icon), ist genau richtig.
* Der User bekommt genug Info ("Aha, .com ist beliebt"), aber für die Details ("Ist der Trend steigend?") muss er 'Scout' werden.
---
### 5. Pricing Page
Die neue Struktur mit **Scout (Free) / Trader ($9) / Tycoon ($29)** ist viel besser als das alte $19-Modell.
**Optimierung der Tabelle:**
Du musst den Unterschied zwischen **Scout** und **Trader** noch schärfer machen, damit die Leute die $9 bezahlen.
| Feature | Scout (Free) | Trader ($9) | Warum Upgrade? |
| :--- | :--- | :--- | :--- |
| **Auctions** | Raw Feed (Ungefiltert) | **Smart Clean Feed** | *"Ich will den Spam nicht sehen."* |
| **Data** | Nur Preise | **Valuation & Deal Score** | *"Ich will wissen, ob es ein Schnäppchen ist."* |
| **Updates** | Täglich | **Stündlich** | *"Ich will schneller sein als andere."* |
**Wichtig:** Füge in der "Trader"-Spalte explizit **"Spam Filters"** oder **"Curated List"** hinzu. Das ist Zeitersparnis, und dafür zahlen Leute.
---
### Zusammenfassung & Tone of Voice
Der Tone of Voice ist jetzt konsistent: **Analytisch, Knapp, Strategisch.**
* *Alt:* "Jage Domains." (Bisschen spielerisch)
* *Neu:* "Don't guess. Know." (Professionell, B2B-tauglich)
**Letzter Check vor dem Launch:**
1. **Mobile View:** Prüfe die riesige Tabelle auf dem Handy. Wahrscheinlich musst du auf Mobile Spalten ausblenden (z.B. nur Domain + Preis + Button zeigen).
2. **Der Filter:** Bitte, bitte filtere die `fgagtqjis...xyz` Domains auf der Startseite raus. Das ist der wichtigste Punkt für den ersten Eindruck.
Das sieht nach einem Produkt aus, für das ich meine Kreditkarte zücken würde. Gute Arbeit!