Files

1 line
50 KiB
JSON

{"repo": "andrewpedot/darkmine", "files": {"app/page.tsx": {"abec6ee7288694db6727aad2fef1968b305bc332": {"t": 1785596456.2419956, "n": 48575, "h": "b80decd6a2eb6888", "c": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport Link from 'next/link';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuLabel,\n} from '@/components/ui/dropdown-menu';\nimport { useToast } from '@/hooks/use-toast';\nimport { searchVideos } from './actions/search-videos';\nimport { analyzeMarketKeywords } from './actions/analyze-market';\n\nconst MOCK_CARDS = [\n {\n id: \"mock-1\",\n title: \"I Infiltrated the World's Most Secretive Financial Cults\",\n titlePtBR: \"Eu Me Infiltrei nos Cultos Financeiros Mais Secretos do Mundo\",\n channel: \"ShadowEconomy\",\n channelUrl: \"https://www.youtube.com/@ShadowEconomy\",\n channelCreatedAt: \"2021\",\n subscribers: \"12.4K\",\n views: \"2.1M\",\n publishedAt: \"18 Jan 2025\",\n outlierMultiplier: \"169x\",\n thumbnail: \"finance\",\n thumbnailUrl: \"\",\n lifespan: { type: \"evergreen\", label: \"🌲 Evergreen / Perene\" },\n statusPt: null,\n statusEs: null,\n niche: \"Finanças\",\n ctr: \"9.4%\",\n avgView: \"14min 32s\",\n score: 97,\n vph: \"1.8K\",\n vphRaw: 1800,\n outlierMultiplierRaw: 169,\n viewsRaw: 2100000,\n viewsPerDay: 1800 * 24,\n publishedAtRaw: new Date(Date.now() - 60 * 24 * 3600 * 1000).toISOString(),\n },\n {\n id: \"mock-2\",\n title: \"The Disappearance That the Government Tried to Erase Forever\",\n titlePtBR: \"O Desaparecimento Que o Governo Tentou Apagar Para Sempre\",\n channel: \"CrimeDark\",\n channelUrl: \"https://www.youtube.com/@CrimeDark\",\n channelCreatedAt: \"2019\",\n subscribers: \"8.7K\",\n views: \"1.4M\",\n publishedAt: \"03 Mar 2025\",\n outlierMultiplier: \"160x\",\n thumbnail: \"crime\",\n thumbnailUrl: \"\",\n lifespan: { type: \"hype\", label: \"🔥 Hype / Curto Prazo\" },\n statusPt: null,\n statusEs: null,\n niche: \"True Crime\",\n ctr: \"11.2%\",\n avgView: \"22min 08s\",\n score: 93,\n vph: \"2.3K\",\n vphRaw: 2300,\n outlierMultiplierRaw: 160,\n viewsRaw: 1400000,\n viewsPerDay: 2300 * 24,\n publishedAtRaw: new Date(Date.now() - 10 * 24 * 3600 * 1000).toISOString(),\n }\n];\n\nconst NICHES = [\"Todos\", \"Finanças\", \"True Crime\", \"Tech\", \"História\", \"Psicologia\", \"Geopolítica\", \"Estoicismo\", \"Espaço/Ciência\"];\n\nconst THUMBNAIL_COLORS: Record<string, string> = {\n finance: \"linear-gradient(135deg, #0f2027, #1a3a2a, #0d1b2a)\",\n crime: \"linear-gradient(135deg, #1a0a0a, #2d0f0f, #0d0d1a)\",\n tech: \"linear-gradient(135deg, #070d1f, #0a1628, #0d1117)\",\n psychology: \"linear-gradient(135deg, #1a1025, #2d1b4e, #130b20)\",\n geopolitics: \"linear-gradient(135deg, #0a192f, #112240, #020c1b)\",\n stoicism: \"linear-gradient(135deg, #2a2a2a, #3f3f3f, #1a1a1a)\",\n space: \"linear-gradient(135deg, #000000, #0a0a2a, #00001a)\",\n default: \"linear-gradient(135deg, #111827, #374151, #1f2937)\",\n};\n\nconst THUMBNAIL_ICONS: Record<string, string> = {\n finance: \"💰\",\n crime: \"🕵️\",\n tech: \"🤖\",\n psychology: \"🧠\",\n geopolitics: \"🌍\",\n stoicism: \"🏛️\",\n space: \"🚀\",\n default: \"📹\",\n};\n\nconst MARKETS = [\n { code: 'de', label: 'Alemão', flag: '🇩🇪', region: 'DE', lang: 'de' },\n { code: 'fr', label: 'Francês', flag: '🇫🇷', region: 'FR', lang: 'fr' },\n { code: 'es', label: 'Espanhol', flag: '🇪🇸', region: 'ES', lang: 'es' },\n { code: 'it', label: 'Italiano', flag: '🇮🇹', region: 'IT', lang: 'it' },\n { code: 'pl', label: 'Polonês', flag: '🇵🇱', region: 'PL', lang: 'pl' },\n { code: 'hu', label: 'Húngaro', flag: '🇭🇺', region: 'HU', lang: 'hu' },\n { code: 'el', label: 'Grego', flag: '🇬🇷', region: 'GR', lang: 'el' },\n { code: 'cs', label: 'Tcheco', flag: '🇨🇿', region: 'CZ', lang: 'cs' },\n { code: 'he', label: 'Hebraico', flag: '🇮🇱', region: 'IL', lang: 'he' },\n];\n\nfunction parseDuration(pt: string) {\n if (!pt) return \"N/A\";\n const match = pt.match(/PT(\\d+H)?(\\d+M)?(\\d+S)?/);\n if (!match) return pt;\n const h = match[1] ? match[1].replace('H', '') : '';\n const m = match[2] ? match[2].replace('M', '') : '0';\n const s = match[3] ? match[3].replace('S', '') : '00';\n \n if (h) return `${h}h ${m}min ${s}s`;\n return `${m}min ${s}s`;\n}\n\nfunction formatNumber(num: number) {\n if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';\n if (num >= 1000) return (num / 1000).toFixed(1) + 'K';\n return Math.round(num).toString();\n}\n\nfunction getThumbnailType(niche: string) {\n if (niche === 'Finanças') return 'finance';\n if (niche === 'True Crime') return 'crime';\n if (niche === 'Tech') return 'tech';\n if (niche === 'Psicologia') return 'psychology';\n if (niche === 'Geopolítica') return 'geopolitics';\n if (niche === 'Estoicismo') return 'stoicism';\n if (niche === 'Espaço/Ciência') return 'space';\n return 'default';\n}\n\nfunction formatScore(multiplierRaw: number, vphRaw: number) {\n return Math.min(Math.round(multiplierRaw * 10 + (vphRaw > 1000 ? 20 : 0)), 99);\n}\n\nfunction ScoreRing({ score }: { score: number }) {\n const r = 18;\n const circ = 2 * Math.PI * r;\n const offset = circ - (score / 100) * circ;\n return (\n <div className=\"flex flex-col items-center\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 48 48\">\n <circle cx=\"24\" cy=\"24\" r={r} fill=\"none\" stroke=\"rgba(255,255,255,0.07)\" strokeWidth=\"4\" />\n <circle\n cx=\"24\" cy=\"24\" r={r}\n fill=\"none\"\n stroke={score >= 90 ? \"#a855f7\" : score >= 70 ? \"#22d3ee\" : \"#f59e0b\"}\n strokeWidth=\"4\"\n strokeDasharray={circ}\n strokeDashoffset={offset}\n strokeLinecap=\"round\"\n transform=\"rotate(-90 24 24)\"\n style={{ filter: `drop-shadow(0 0 6px ${score >= 90 ? \"#a855f7\" : \"#22d3ee\"})` }}\n />\n <text x=\"24\" y=\"28\" textAnchor=\"middle\" fontSize=\"11\" fontWeight=\"700\" fill=\"#e2e8f0\" fontFamily=\"JetBrains Mono\">\n {score}\n </text>\n </svg>\n <span className=\"text-[9px] text-gray-500 mt-0.5 font-mono uppercase tracking-wider\">Score</span>\n </div>\n );\n}\n\ninterface AnalyzedMarket {\n langCode: string;\n status: 'blue_ocean' | 'saturated';\n analysis?: string;\n}\n\nfunction VideoCard({ card }: { card: any }) {\n const [hovered, setHovered] = useState(false);\n const [clicked, setClicked] = useState(false);\n const [showPtBR, setShowPtBR] = useState(false);\n const [translatedTitle, setTranslatedTitle] = useState('');\n const [isTranslating, setIsTranslating] = useState(false);\n const [saved, setSaved] = useState(false);\n const [analyzedMarkets, setAnalyzedMarkets] = useState<AnalyzedMarket[]>(\n card.analyzedMarkets || []\n );\n const [isChecking, setIsChecking] = useState(false);\n const [selectedMarket, setSelectedMarket] = useState<string | null>(null);\n\n useEffect(() => {\n const lib = JSON.parse(localStorage.getItem('darkmine_library') || '[]');\n if (lib.find((c: any) => c.id === card.id)) {\n setSaved(true);\n }\n }, [card.id]);\n\n const handleSave = async (e: any) => {\n e.stopPropagation();\n \n if (saved) {\n const lib = JSON.parse(localStorage.getItem('darkmine_library') || '[]');\n const newLib = lib.map((c: any) => c.id === card.id ? { ...c, analyzedMarkets: analyzedMarkets } : c);\n localStorage.setItem('darkmine_library', JSON.stringify(newLib));\n setSaved(false);\n window.dispatchEvent(new Event('libraryUpdated'));\n return;\n }\n\n const saveToApi = async () => {\n try {\n const res = await fetch('/api/library/save', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n title: card.title,\n title_en: card.title,\n language: 'en',\n source: 'darkmine',\n source_id: card.id,\n source_session_id: card.sessionId || null,\n vpd: card.vphRaw || card.viewsPerDay || 0,\n anomaly_ratio: card.outlierMultiplierRaw || card.outlierMultiplier || 0,\n dark_score: card.facelessScore || 0,\n opportunity_score: card.score || 0,\n tags: [card.niche || 'darkmine'],\n }),\n });\n const data = await res.json();\n if (!data.success) {\n console.error('Failed to save to library:', data.error);\n }\n } catch (err) {\n console.error('Save to library error:', err);\n }\n };\n\n const newCard = { ...card, libraryStatus: 'Pendente', analyzedMarkets };\n const lib = JSON.parse(localStorage.getItem('darkmine_library') || '[]');\n lib.push(newCard);\n localStorage.setItem('darkmine_library', JSON.stringify(lib));\n setSaved(true);\n saveToApi();\n window.dispatchEvent(new Event('libraryUpdated'));\n };\n\n const checkMarket = async (keywords: string, region: string, lang: string) => {\n const apiKey = process.env.NEXT_PUBLIC_YOUTUBE_API_KEY;\n if (!apiKey) throw new Error(\"API Key missing\");\n const now = new Date();\n const originalVph = card.vphRaw;\n\n const res = await fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=5&q=${encodeURIComponent(keywords)}&type=video&regionCode=${region}&relevanceLanguage=${lang}&key=${apiKey}`);\n const data = await res.json();\n if (!data.items || data.items.length === 0) return 'Oceano Azul';\n \n const vIds = data.items.map((i: any) => i.id.videoId).join(',');\n const vRes = await fetch(`https://www.googleapis.com/youtube/v3/videos?part=statistics,snippet&id=${vIds}&key=${apiKey}`);\n const vData = await vRes.json();\n \n let hasRecentHighVph = false;\n let highViewsFound = false;\n \n for (const v of vData.items || []) {\n const pub = new Date(v.snippet.publishedAt);\n const hours = Math.max((now.getTime() - pub.getTime()) / (1000 * 60 * 60), 1);\n const views = parseInt(v.statistics.viewCount || '0', 10);\n const vph = views / hours;\n \n if (views > 500000) {\n highViewsFound = true;\n }\n \n if (hours < 24 * 365 && vph >= originalVph * 0.1) {\n hasRecentHighVph = true;\n }\n }\n \n if (hasRecentHighVph || highViewsFound) return 'Saturado';\n return 'Oceano Azul';\n };\n\n const { toast } = useToast();\n\n const handleCheckMarket = async (market: typeof MARKETS[0]) => {\n setIsChecking(true);\n setSelectedMarket(market.code);\n \n try {\n const aiAnalysis = await analyzeMarketKeywords(card.title, market.label, market.code);\n \n const result = await checkMarket(aiAnalysis.keywords, market.region, market.lang);\n const status = result === 'Oceano Azul' ? 'blue_ocean' : 'saturated';\n \n setAnalyzedMarkets(prev => {\n const exists = prev.find(m => m.langCode === market.code);\n if (exists) {\n return prev.map(m => m.langCode === market.code ? { ...m, status, analysis: aiAnalysis.analise_textual } : m);\n }\n return [...prev, { langCode: market.code, status, analysis: aiAnalysis.analise_textual }];\n });\n \n return result;\n } catch (e) {\n console.error(e);\n const result = 'Saturado';\n \n setAnalyzedMarkets(prev => {\n const exists = prev.find(m => m.langCode === market.code);\n if (exists) {\n return prev.map(m => m.langCode === market.code ? { ...m, status: 'saturated', analysis: 'Erro ao analisar o mercado.' } : m);\n }\n return [...prev, { langCode: market.code, status: 'saturated', analysis: 'Erro ao analisar o mercado.' }];\n });\n \n return result;\n } finally {\n setIsChecking(false);\n setSelectedMarket(null);\n }\n };\n\n let targetMarket = 'Brasil (PT-BR)';\n let btnText = 'Gerar Estrutura de Roteiro';\n \n // Define mercado alvo baseado no primeiro Oceano Azul encontrado\n const blueOceanMarket = analyzedMarkets.find(m => m.status === 'blue_ocean');\n if (blueOceanMarket) {\n const marketInfo = MARKETS.find(m => m.code === blueOceanMarket.langCode);\n if (marketInfo) {\n targetMarket = `${marketInfo.label} (${marketInfo.code.toUpperCase()})`;\n btnText = `Clonar para ${marketInfo.label}`;\n }\n }\n\n const handleTranslate = async (e: any) => {\n e.stopPropagation();\n if (showPtBR) {\n setShowPtBR(false);\n return;\n }\n \n if (!translatedTitle && card.title) {\n setIsTranslating(true);\n try {\n const res = await fetch(`https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=pt&dt=t&q=${encodeURIComponent(card.title)}`);\n const data = await res.json();\n const translatedText = data[0].map((item: any) => item[0]).join('');\n setTranslatedTitle(translatedText);\n } catch (error) {\n console.error(error);\n setTranslatedTitle(card.title);\n }\n setIsTranslating(false);\n }\n setShowPtBR(true);\n };\n\n const handleOpenVideo = (e: any) => {\n e.stopPropagation();\n window.open(card.videoUrl || `https://www.youtube.com/watch?v=${card.id}`, '_blank');\n };\n\n return (\n <div\n className=\"video-card rounded-2xl neon-border-purple card-glass flex flex-col relative\"\n onMouseEnter={() => setHovered(true)}\n onMouseLeave={() => setHovered(false)}\n >\n {/* Thumbnail */}\n <div\n className=\"relative overflow-hidden flex-shrink-0 group-thumb\"\n style={{ height: '176px', background: THUMBNAIL_COLORS[card.thumbnail] || THUMBNAIL_COLORS.default }}\n >\n {card.thumbnailUrl && (\n // eslint-disable-next-line @next/next/no-img-element\n <img src={card.thumbnailUrl} alt={card.title} className=\"absolute inset-0 w-full h-full object-cover opacity-60 mix-blend-overlay\" />\n )}\n <div className=\"scan-line\" />\n <div\n className=\"absolute inset-0 flex flex-col items-center justify-center pointer-events-none\"\n >\n <span className=\"text-5xl mb-2\" style={{ filter: 'drop-shadow(0 0 16px rgba(168,85,247,0.6))' }}>\n {THUMBNAIL_ICONS[card.thumbnail] || THUMBNAIL_ICONS.default}\n </span>\n <span className=\"text-xs font-mono text-gray-500 tracking-widest uppercase\">{card.niche}</span>\n </div>\n \n {/* Top-left: lifespan */}\n {card.lifespan && (\n <div className=\"absolute top-2 left-2 flex gap-1.5 flex-wrap pointer-events-none z-10\">\n <span className={`text-[10px] font-bold px-2 py-0.5 rounded-full border ${card.lifespan.type === 'evergreen' ? 'bg-emerald-900/80 border-emerald-500/50 text-emerald-300' : 'bg-orange-900/80 border-orange-500/50 text-orange-300'}`}>\n {card.lifespan.label}\n </span>\n </div>\n )}\n \n {/* Top-right: bookmark + multiplier */}\n <div className=\"absolute top-2 right-2 flex flex-col items-end gap-1.5 z-10\">\n <button\n id={`bookmark-${card.id}`}\n onClick={handleSave}\n title={saved ? 'Salvo na Biblioteca' : 'Guardar na Biblioteca'}\n className=\"w-7 h-7 rounded-lg flex items-center justify-center transition-all cursor-pointer\"\n style={{\n background: saved ? 'rgba(168,85,247,0.85)' : 'rgba(8,11,18,0.75)',\n border: saved ? '1px solid #a855f7' : '1px solid rgba(255,255,255,0.15)',\n boxShadow: saved ? '0 0 10px rgba(168,85,247,0.5)' : 'none',\n }}\n >\n <svg className=\"w-3.5 h-3.5 pointer-events-none\" fill={saved ? 'white' : 'none'} stroke=\"white\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z\" />\n </svg>\n </button>\n <div className=\"outlier-badge bg-purple-950/80 border border-purple-400/70 rounded-lg px-2.5 py-1 flex items-center gap-1 pointer-events-none\">\n <span className=\"text-purple-300 font-black text-sm font-mono\">\n {typeof card.outlierMultiplier === 'number' ? `${card.outlierMultiplier}x` : card.outlierMultiplier}\n </span>\n <span className=\"text-purple-400/80 text-[9px] font-mono\">views/subs</span>\n </div>\n </div>\n\n {/* Hover overlay with play button */}\n <div className={`card-thumbnail-overlay absolute inset-0 flex flex-col items-center justify-center gap-3 transition-opacity ${hovered ? 'opacity-100' : 'opacity-0'}`} style={{ background: 'rgba(13,17,23,0.45)' }}>\n <div \n className=\"bg-black/50 hover:bg-red-600/80 transition-colors border border-white/20 rounded-full px-4 py-2 backdrop-blur-md flex items-center gap-2 cursor-pointer pointer-events-auto\" \n onClick={handleOpenVideo}\n >\n <svg className=\"w-4 h-4 text-white\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\" /></svg>\n <span className=\"text-white text-xs font-bold\">Assistir</span>\n </div>\n </div>\n\n {card.avgView && (\n <div className=\"absolute bottom-2 right-2 bg-black/80 border border-white/10 text-white text-[10px] font-mono px-2 py-0.5 rounded pointer-events-none\">\n {card.avgView}\n </div>\n )}\n </div>\n\n {/* Card body */}\n <div className=\"p-4 flex flex-col flex-1 gap-3\">\n {/* Title + Score */}\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-1 flex flex-col gap-1.5 min-w-0\">\n <h3 className=\"text-sm font-semibold text-gray-100 leading-snug line-clamp-2\" title={showPtBR ? (translatedTitle || card.titlePtBR || card.title) : card.title}>\n {showPtBR ? (translatedTitle || card.titlePtBR || 'Traduzindo...') : card.title}\n </h3>\n {/* Bilingual toggle */}\n <button\n id={`toggle-idioma-${card.id}`}\n onClick={handleTranslate}\n disabled={isTranslating}\n className=\"inline-flex items-center gap-1 text-[10px] font-mono w-fit px-2 py-0.5 rounded-md transition-all hover:bg-white/10\"\n style={{\n background: showPtBR ? 'rgba(34,211,238,0.1)' : 'rgba(255,255,255,0.04)',\n border: showPtBR ? '1px solid rgba(34,211,238,0.35)' : '1px solid rgba(255,255,255,0.08)',\n color: showPtBR ? '#22d3ee' : '#6b7280',\n }}\n >\n <svg className={`w-3 h-3 ${isTranslating ? 'animate-spin' : ''}`} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n {isTranslating ? (\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\" />\n ) : (\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129\" />\n )}\n </svg>\n {isTranslating ? 'Traduzindo...' : showPtBR ? '🇧🇷 PT-BR (clique para EN)' : '🌐 Ver título em PT-BR'}\n </button>\n </div>\n {card.score != null && <ScoreRing score={card.score} />}\n </div>\n\n {/* Novas Badges do Radar de Anomalias */}\n <div className=\"flex flex-wrap gap-2 mt-2\">\n <div className=\"bg-orange-950/40 border border-orange-500/50 text-orange-400 px-2 py-1 rounded text-[10px] font-bold flex items-center gap-1.5\">\n 🔥 VPH: {card.vph || (card.vphRaw ? card.vphRaw.toLocaleString('pt-BR') : card.viewsPerDay ? Math.round(card.viewsPerDay / 24).toLocaleString('pt-BR') : '0')}\n </div>\n {card.syntheticMedia && (\n <div className=\"bg-green-950/40 border border-green-500/50 text-green-400 px-2 py-1 rounded text-[10px] font-bold flex items-center gap-1.5\">\n 🤖 IA Confirmada\n </div>\n )}\n <div className=\"bg-blue-950/40 border border-blue-500/50 text-blue-400 px-2 py-1 rounded text-[10px] font-bold flex items-center gap-1.5\">\n 📈 Anomalia: {typeof card.outlierMultiplier === 'number' ? `${card.outlierMultiplier}x` : card.outlierMultiplier}\n </div>\n {card.facelessScore != null && (\n <div className={`px-2 py-1 rounded text-[10px] font-bold flex items-center gap-1.5 ${\n card.facelessScore >= 80\n ? 'bg-purple-950/60 border border-purple-400/60 text-purple-300'\n : card.facelessScore >= 50\n ? 'bg-cyan-950/40 border border-cyan-500/50 text-cyan-400'\n : 'bg-gray-900/40 border border-gray-600/40 text-gray-400'\n }`}>\n 🕶️ Dark Score: {card.facelessScore}\n </div>\n )}\n </div>\n\n\n {/* Stats grid */}\n <div className=\"grid grid-cols-2 gap-2 mt-auto\">\n {/* Canal + external link */}\n <div className=\"stat-pill flex items-center gap-1.5\">\n <span className=\"text-purple-400\">👤</span>\n <div className=\"flex-1 min-w-0\">\n <div className=\"text-[9px] text-gray-500 uppercase tracking-wider\">Canal</div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[11px] text-gray-200 font-semibold truncate\" title={card.channel}>{card.channel}</span>\n <a\n id={`link-canal-${card.id}`}\n href={card.channelUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n title=\"Abrir canal no YouTube\"\n className=\"flex-shrink-0 text-gray-600 hover:text-red-400 transition-colors\"\n onClick={e => e.stopPropagation()}\n >\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\" />\n </svg>\n </a>\n </div>\n </div>\n </div>\n <div className=\"stat-pill flex items-center gap-1.5\">\n <span className=\"text-emerald-400\">📊</span>\n <div>\n <div className=\"text-[9px] text-gray-500 uppercase tracking-wider\">Inscritos</div>\n <div className=\"text-[11px] text-gray-200 font-semibold\">{card.subscribers}</div>\n </div>\n </div>\n <div className=\"stat-pill flex items-center gap-1.5\">\n <span className=\"text-cyan-400\">👁️</span>\n <div>\n <div className=\"text-[9px] text-gray-500 uppercase tracking-wider\">Views</div>\n <div className=\"text-[11px] text-gray-200 font-semibold\">{card.views}</div>\n </div>\n </div>\n <div className=\"stat-pill flex items-center gap-1.5\">\n <span className=\"text-yellow-400\">📅</span>\n <div>\n <div className=\"text-[9px] text-gray-500 uppercase tracking-wider\">Data</div>\n <div className=\"text-[11px] text-gray-200 font-semibold\">{card.publishedAt}</div>\n </div>\n </div>\n {/* Canal criado em — full width */}\n <div className=\"stat-pill col-span-2 flex items-center gap-1.5\">\n <span className=\"text-violet-400\">🏛️</span>\n <div>\n <div className=\"text-[9px] text-gray-500 uppercase tracking-wider\">Canal criado em</div>\n <div className=\"text-[11px] text-gray-200 font-semibold font-mono\">{card.channelCreatedAt}</div>\n </div>\n </div>\n </div>\n\n {/* Market Tags */}\n {analyzedMarkets.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5 mt-3\">\n {analyzedMarkets.map((market) => (\n <span\n key={market.langCode}\n title={market.analysis}\n className={`text-[9px] font-mono uppercase tracking-widest px-1.5 py-0.5 rounded border inline-flex items-center gap-1 transition-all cursor-help\n ${market.status === 'blue_ocean' \n ? 'bg-cyan-900/20 border-cyan-400/50 text-cyan-300' \n : 'bg-red-900/10 border-red-500/20 text-red-400/70'\n }`}\n >\n #{market.langCode}\n </span>\n ))}\n </div>\n )}\n\n {/* CTR + VPH row — only for mock/enriched cards */}\n {(card.ctr || card.vph) && (\n <div className=\"flex items-center justify-between border-t border-white/5 pt-3\">\n <div className=\"flex items-center gap-3\">\n {card.ctr && (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-[10px] text-gray-500 font-mono uppercase\">CTR</span>\n <span className=\"text-xs font-bold text-emerald-400 font-mono\">{card.ctr}</span>\n </div>\n )}\n {card.ctr && card.vph && <div className=\"w-px h-3 bg-white/10\" />}\n {card.vph && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-sm\">🔥</span>\n <span className=\"text-xs font-black text-orange-400 font-mono\">{card.vph}</span>\n <span className=\"text-[9px] text-gray-600 font-mono uppercase\">VPH</span>\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-1.5\">\n <div className={`w-2 h-2 rounded-full ${(card.vphRaw || 0) > 100 ? 'bg-emerald-400 animate-pulse' : 'bg-gray-500'}`} />\n <span className=\"text-[10px] text-gray-500\">{(card.vphRaw || 0) > 100 ? 'Alta tração' : 'Tração normal'}</span>\n </div>\n </div>\n )}\n\n {/* Dynamic Action Section */}\n <div className=\"mt-auto flex flex-col gap-2 pt-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <button\n disabled={isChecking}\n className={`w-full py-1.5 rounded-lg text-[10px] font-mono uppercase tracking-widest border transition-all flex items-center justify-center gap-2\n ${isChecking \n ? 'border-purple-500/40 bg-purple-950/20 text-purple-300' \n : analyzedMarkets.filter(m => m.status === 'blue_ocean').length > 0\n ? 'border-cyan-400/50 bg-cyan-900/20 text-cyan-300 shadow-[0_0_8px_rgba(34,211,238,0.2)]'\n : analyzedMarkets.filter(m => m.status === 'saturated').length > 0\n ? 'border-red-500/30 bg-red-900/10 text-red-400/70 shadow-[0_0_8px_rgba(239,68,68,0.2)]'\n : 'border-white/10 text-gray-400 hover:text-white hover:border-purple-500/40 hover:bg-purple-950/20'\n }`}\n >\n {isChecking ? (\n <>\n <svg className=\"w-3.5 h-3.5 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\"><circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"3\"></circle><path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"></path></svg>\n Analisando {selectedMarket && MARKETS.find(m => m.code === selectedMarket)?.label}...\n </>\n ) : (\n <>\n <span>🌍</span>\n Analisar Mercados\n <svg className=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </>\n )}\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent \n className=\"w-56 border-purple-500/20 bg-[#0d1117]/95 backdrop-blur-xl\"\n sideOffset={5}\n align=\"start\"\n >\n <DropdownMenuLabel className=\"text-[9px] font-mono text-gray-500 uppercase tracking-widest\">\n Idiomas Alto RPM\n </DropdownMenuLabel>\n {MARKETS.map((market) => (\n <DropdownMenuItem\n key={market.code}\n onClick={() => handleCheckMarket(market)}\n disabled={isChecking}\n className=\"cursor-pointer hover:bg-purple-950/30 focus:bg-purple-950/30\"\n >\n <span className=\"text-base mr-2\">{market.flag}</span>\n <span className=\"text-xs font-mono text-gray-300 flex-1\">{market.label}</span>\n <span className=\"text-[9px] font-mono text-gray-600 uppercase\">{market.code}</span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n \n \n <button\n id={`gerar-roteiro-${card.id}`}\n onClick={() => {\n setClicked(true);\n window.open(`/hook?title=${encodeURIComponent(card.title)}&market=${encodeURIComponent(targetMarket)}`, '_blank');\n setTimeout(() => setClicked(false), 2000);\n }}\n className=\"btn-primary w-full py-2.5 rounded-xl text-sm font-bold text-white flex items-center justify-center gap-2\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\" />\n </svg>\n {clicked ? 'Redirecionando...' : btnText}\n </button>\n </div>\n </div>\n </div>\n );\n}\n\nexport default function DarkMinePage() {\n const [activeNiche, setActiveNiche] = useState('Todos');\n const [searchQuery, setSearchQuery] = useState('');\n const [maxSubs, setMaxSubs] = useState('50000');\n const [mining, setMining] = useState(false);\n const [mined, setMined] = useState(false);\n const [showSettings, setShowSettings] = useState(false);\n const [postFilter, setPostFilter] = useState<'Todos' | 'Evergreen' | 'Hype'>('Todos');\n const [errorMsg, setErrorMsg] = useState('');\n const [cards, setCards] = useState<any[]>(MOCK_CARDS);\n const [onlyAI, setOnlyAI] = useState(false);\n\n const displayCards = [...cards].filter(c => {\n const now = new Date();\n const pubDate = new Date(c.publishedAtRaw || 0);\n const daysOld = (now.getTime() - pubDate.getTime()) / (1000 * 3600 * 24);\n if (postFilter === 'Evergreen') return daysOld > 30;\n if (postFilter === 'Hype') return daysOld < 14;\n return true;\n }).sort((a, b) => {\n if (postFilter === 'Hype') return (b.vphRaw || 0) - (a.vphRaw || 0);\n return (b.outlierMultiplierRaw || 0) - (a.outlierMultiplierRaw || 0);\n });\n\n const isTrendingMode = !searchQuery.trim();\n\n const handleMine = async () => {\n setMining(true);\n setMined(false);\n setErrorMsg('');\n\n try {\n const limitSubs = parseInt(maxSubs, 10) || 50000;\n // Passa a query do usuário (pode ser vazia — nesse caso a action usa query padrão por nicho)\n const bestResults = await searchVideos(searchQuery.trim(), limitSubs, activeNiche, onlyAI);\n\n setCards(bestResults);\n setMined(true);\n } catch (err: any) {\n console.error(err);\n // Em produção, erros de Server Action vêm sem .message (apenas digest)\n // Detectamos os casos mais comuns pelo texto quando disponível\n const msg: string = err?.message || '';\n if (msg.includes('expired') || msg.includes('keyExpired')) {\n setErrorMsg('🔑 YouTube API Key expirada. Renove a chave no Google Cloud Console e atualize o .env.local (NEXT_PUBLIC_YOUTUBE_API_KEY).');\n } else if (msg.includes('quota') || msg.includes('quotaExceeded')) {\n setErrorMsg('⚠️ Cota da YouTube API esgotada para hoje. Tente novamente amanhã.');\n } else if (msg.includes('API Key') || msg.includes('keyInvalid')) {\n setErrorMsg('🔑 YouTube API Key inválida ou não configurada. Verifique suas variáveis de ambiente.');\n } else if (msg) {\n setErrorMsg(msg);\n } else {\n // Erro sem mensagem = Server Action falhou em produção\n setErrorMsg('❌ Erro ao conectar com a API do YouTube. Verifique se a API Key está válida e configurada corretamente nas variáveis de ambiente da Netlify.');\n }\n } finally {\n setMining(false);\n }\n };\n\n return (\n <div className=\"min-h-screen grid-bg relative overflow-x-hidden\">\n {/* Ambient glow blobs */}\n <div className=\"fixed top-0 left-1/4 w-96 h-96 rounded-full pointer-events-none\" style={{ background: 'radial-gradient(circle, rgba(124,58,237,0.08) 0%, transparent 70%)', filter: 'blur(40px)' }} />\n <div className=\"fixed bottom-1/4 right-1/4 w-80 h-80 rounded-full pointer-events-none\" style={{ background: 'radial-gradient(circle, rgba(34,211,238,0.05) 0%, transparent 70%)', filter: 'blur(40px)' }} />\n\n {/* ── HEADER ── */}\n <header className=\"sticky top-0 z-50 border-b border-white/5\" style={{ background: 'rgba(8,11,18,0.85)', backdropFilter: 'blur(20px)' }}>\n <div className=\"max-w-7xl mx-auto px-6 h-16 flex items-center justify-between\">\n {/* Logo */}\n <div className=\"flex items-center gap-3\">\n <div className=\"w-8 h-8 rounded-lg flex items-center justify-center\" style={{ background: 'linear-gradient(135deg, #7c3aed, #a855f7)', boxShadow: '0 0 16px rgba(168,85,247,0.5)' }}>\n <svg className=\"w-4 h-4 text-white\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5\" stroke=\"white\" strokeWidth=\"2\" fill=\"none\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </div>\n <div>\n <span className=\"text-xl font-black tracking-tight text-gradient-purple\">Dark Miner</span>\n <span className=\"ml-2 text-[10px] font-mono text-purple-500/70 uppercase tracking-widest hidden sm:inline\">Intelligence</span>\n </div>\n </div>\n\n {/* Right side */}\n <div className=\"flex items-center gap-3\">\n <div className=\"hidden sm:flex items-center gap-1.5 px-3 py-1.5 rounded-lg\" style={{ background: 'rgba(52,211,153,0.08)', border: '1px solid rgba(52,211,153,0.2)' }}>\n <div className=\"w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse\" />\n <span className=\"text-[11px] font-mono text-emerald-400\">API Conectada</span>\n </div>\n <button\n id=\"btn-configuracoes\"\n onClick={() => setShowSettings(!showSettings)}\n className=\"flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-medium text-gray-300 border border-white/10 transition-all hover:border-purple-500/40 hover:text-purple-300 hover:bg-purple-950/20\"\n >\n <svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z\" />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Configurações\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"max-w-7xl mx-auto px-6 py-8 space-y-10\">\n\n {/* ── SEARCH & CONTROL AREA ── */}\n <section className=\"rounded-2xl p-6 space-y-5 neon-border-purple card-glass relative\">\n <div className=\"flex items-center gap-2 mb-1\">\n <div className=\"w-1 h-5 rounded-full\" style={{ background: 'linear-gradient(180deg, #a855f7, #7c3aed)' }} />\n <h2 className=\"text-sm font-semibold text-gray-300 uppercase tracking-widest\">Motor de Mineração</h2>\n </div>\n\n {/* Search row */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <div className=\"relative flex-1\">\n <svg className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <circle cx=\"11\" cy=\"11\" r=\"8\" /><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M21 21l-4.35-4.35\" />\n </svg>\n <input\n id=\"campo-pesquisa\"\n type=\"text\"\n value={searchQuery}\n onChange={e => setSearchQuery(e.target.value)}\n onKeyDown={e => e.key === 'Enter' && handleMine()}\n placeholder={isTrendingMode ? '🔥 Modo Trending — deixe vazio para ver o que o YouTube está recomendando agora' : 'Ex: conspiracy, finance collapse, true crime cold case...'}\n className=\"input-dark w-full pl-10 pr-4 py-3 rounded-xl text-sm font-mono\"\n />\n </div>\n <div className=\"relative sm:w-52\">\n <svg className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-500\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0\" />\n </svg>\n <input\n id=\"campo-limite-inscritos\"\n type=\"number\"\n value={maxSubs}\n onChange={e => setMaxSubs(e.target.value)}\n placeholder=\"Max Inscritos\"\n className=\"input-dark w-full pl-10 pr-4 py-3 rounded-xl text-sm font-mono\"\n />\n <span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-[10px] text-gray-600 font-mono\">subs</span>\n </div>\n </div>\n\n {/* Niche filters + AI toggle */}\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-xs text-gray-600 font-mono uppercase tracking-wider mr-1\">Nicho:</span>\n {NICHES.map(n => (\n <button\n key={n}\n id={`filtro-${n.toLowerCase().replace(' ', '-')}`}\n onClick={() => setActiveNiche(n)}\n className={`tag-filter px-3.5 py-1.5 rounded-full text-xs font-semibold border transition-all ${activeNiche === n ? 'active' : 'border-white/10 text-gray-400'}`}\n >\n {n}\n </button>\n ))}\n {/* Separador */}\n <div className=\"w-px h-5 bg-white/10 mx-1\" />\n {/* Toggle IA Confirmada */}\n <button\n id=\"toggle-apenas-ia\"\n onClick={() => setOnlyAI(v => !v)}\n title={onlyAI ? 'Filtro IA ativo: apenas vídeos com contensSyntheticMedia=true' : 'Ativar para buscar APENAS conteúdo com IA declarada'}\n className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-semibold border transition-all ${\n onlyAI\n ? 'bg-green-950/60 border-green-400/70 text-green-300 shadow-[0_0_8px_rgba(74,222,128,0.25)]'\n : 'border-white/10 text-gray-500 hover:border-green-500/40 hover:text-green-400'\n }`}\n >\n 🤖 {onlyAI ? 'Apenas IA (ativo)' : 'Apenas IA'}\n </button>\n </div>\n\n {/* Aviso quando filtro IA está ativo */}\n {onlyAI && (\n <div className=\"flex items-start gap-2 bg-green-950/20 border border-green-500/30 text-green-300 text-xs px-4 py-2.5 rounded-xl\">\n <span className=\"text-base flex-shrink-0\">🤖</span>\n <span>\n <strong>Filtro IA ativo:</strong> Apenas vídeos onde o criador declarou uso de IA no YouTube Studio (<code className=\"text-green-400\">containsSyntheticMedia = true</code>). Resultado pode ser menor — muitos canais usam IA mas não marcam a opção.\n </span>\n </div>\n )}\n\n {/* Error Message */}\n {errorMsg && (\n <div className=\"bg-red-950/50 border border-red-500/50 text-red-200 text-sm px-4 py-3 rounded-xl flex items-center gap-2\">\n <svg className=\"w-5 h-5 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n {errorMsg}\n </div>\n )}\n\n {/* Mine button + Sort dropdown */}\n <div className=\"flex gap-3 items-stretch\">\n <button\n id=\"btn-minerar\"\n onClick={handleMine}\n disabled={mining}\n className=\"btn-primary flex-1 py-4 rounded-xl text-base font-black text-white flex items-center justify-center gap-3 disabled:opacity-70 disabled:cursor-not-allowed\"\n >\n {mining ? (\n <>\n <svg className=\"w-5 h-5 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n Minerando Oportunidades...\n </>\n ) : mined ? (\n <>✅ {cards.length} Outliers Encontrados — {isTrendingMode ? 'Atualizar Trending' : 'Minerar Novamente'}</>\n ) : (\n <>\n <svg className=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13 10V3L4 14h7v7l9-11h-7z\" />\n </svg>\n {isTrendingMode ? '🔥 Ver Trending Agora' : 'Minerar Oportunidades'}\n </>\n )}\n </button>\n {/* Post-Search Filters */}\n <div className=\"flex bg-black/40 border border-white/10 rounded-xl p-1 gap-1\">\n {['Todos', 'Evergreen', 'Hype'].map((filter) => (\n <button\n key={filter}\n onClick={() => setPostFilter(filter as any)}\n className={`px-4 py-2 rounded-lg text-sm font-semibold transition-all flex items-center gap-1.5 ${postFilter === filter ? 'bg-purple-600/20 text-purple-300 shadow-[0_0_10px_rgba(168,85,247,0.1)]' : 'text-gray-400 hover:text-white hover:bg-white/5'}`}\n >\n {filter === 'Evergreen' && '🌲 '}\n {filter === 'Hype' && '🔥 '}\n {filter}\n </button>\n ))}\n </div>\n </div>\n </section>\n\n {/* ── TRENDING OUTLIERS ── */}\n <section>\n <div className=\"flex items-center justify-between mb-6 flex-wrap gap-4\">\n <div className=\"flex items-center gap-3\">\n <h2 className=\"text-xl font-black text-white\">\n 🔥 <span className=\"text-gradient-purple\">Trending Outliers</span>\n <span className=\"text-gray-400 font-normal text-base ml-2\">(Oportunidades do Dia)</span>\n </h2>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"w-2 h-2 rounded-full bg-purple-400\" style={{ animation: 'blink 1.5s ease-in-out infinite' }} />\n <span className=\"text-xs font-mono text-gray-500\">Live · Atualizado Agora</span>\n </div>\n </div>\n\n {/* Cards grid */}\n <div className=\"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5\">\n {displayCards.map(card => (\n <VideoCard key={card.id} card={card} />\n ))}\n </div>\n \n {cards.length === 0 && !mining && (\n <div className=\"py-20 text-center flex flex-col items-center gap-3 opacity-60\">\n <span className=\"text-4xl\">🕵️</span>\n <p className=\"text-gray-400 font-mono text-sm\">Nenhum resultado. Realize uma mineração acima.</p>\n </div>\n )}\n </section>\n\n {/* ── BOTTOM HINT ── */}\n <div className=\"flex items-center justify-center gap-2 pb-6 opacity-40\">\n <svg className=\"w-4 h-4 text-gray-600\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <span className=\"text-xs text-gray-600 font-mono\">Dark Miner v1.0 Alpha · Conectado à API do YouTube</span>\n </div>\n </main>\n\n {/* Settings Modal */}\n {showSettings && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-6\" style={{ background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(8px)' }} onClick={() => setShowSettings(false)}>\n <div className=\"rounded-2xl p-6 w-full max-w-md neon-border-purple card-glass\" onClick={e => e.stopPropagation()}>\n <h3 className=\"text-lg font-bold text-white mb-4 flex items-center gap-2\">\n <svg className=\"w-5 h-5 text-purple-400\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z\" />\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\" />\n </svg>\n Configurações\n </h3>\n <div className=\"space-y-4\">\n {[\n { label: 'YouTube Data API Key', placeholder: 'AIza...', type: 'password', defaultValue: process.env.NEXT_PUBLIC_YOUTUBE_API_KEY ? '********' : '' },\n { label: 'OpenAI API Key (roteiros)', placeholder: 'sk-...', type: 'password' },\n { label: 'Idioma Alvo', placeholder: 'pt-BR', type: 'text' },\n ].map(field => (\n <div key={field.label}>\n <label className=\"block text-xs text-gray-500 font-mono uppercase tracking-wider mb-1.5\">{field.label}</label>\n <input type={field.type} placeholder={field.placeholder} defaultValue={field.defaultValue} className=\"input-dark w-full px-4 py-2.5 rounded-xl text-sm font-mono\" />\n </div>\n ))}\n <button className=\"btn-primary w-full py-3 rounded-xl text-sm font-bold text-white mt-2\" onClick={() => setShowSettings(false)}>\n Salvar Configurações\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"}}}}