diff --git a/app.js b/app.js
new file mode 100644
index 0000000..70d5379
--- /dev/null
+++ b/app.js
@@ -0,0 +1,323 @@
+document.addEventListener('DOMContentLoaded', () => {
+ // DOM Elements
+ const keywordsContainer = document.getElementById('keywords-container');
+ const refreshBtn = document.getElementById('refresh-btn');
+ const loading = document.getElementById('loading');
+ const selectedKeywordText = document.getElementById('selected-keyword-text');
+ const generatePromptBtn = document.getElementById('generate-prompt-btn');
+ const promptResult = document.getElementById('prompt-result');
+ const promptTitle = document.getElementById('prompt-title');
+ const promptBody = document.getElementById('prompt-body');
+ const copyPromptBtn = document.getElementById('copy-prompt-btn');
+ const copySuccess = document.getElementById('copy-success');
+
+ // State
+ let selectedKeyword = null;
+ let currentPrompt = null;
+
+ // Fetch trending keywords with opportunity scoring
+ const fetchTrendingKeywords = async () => {
+ try {
+ loading.classList.remove('hidden');
+ keywordsContainer.innerHTML = '';
+
+ // Step 1: Get trending topics from multiple sources
+ const trendingTopics = await fetchTrendingTopics();
+
+ // Step 2: Enrich with search volume and result count data
+ const enrichedKeywords = await enrichKeywordsWithOpportunityData(trendingTopics);
+
+ // Step 3: Calculate opportunity score and sort
+ const keywordsWithOpportunity = calculateOpportunityScore(enrichedKeywords);
+
+ if (keywordsWithOpportunity.length > 0) {
+ renderKeywords(keywordsWithOpportunity);
+ } else {
+ useBackupKeywords();
+ }
+ } catch (error) {
+ console.error('Error fetching trending keywords:', error);
+ useBackupKeywords();
+ } finally {
+ loading.classList.add('hidden');
+ }
+ };
+
+ // Fetch trending topics from multiple sources for diversity
+ const fetchTrendingTopics = async () => {
+ try {
+ // Source 1: Wikipedia trending articles
+ const wikiResponse = await fetch('https://wikimedia.org/api/rest_v1/metrics/pageviews/top/en.wikipedia/all-access/2023/01/all-days');
+ const wikiData = await wikiResponse.json();
+
+ let combinedTopics = [];
+
+ // Process Wikipedia data
+ if (wikiData && wikiData.items && wikiData.items[0] && wikiData.items[0].articles) {
+ const wikiTopics = wikiData.items[0].articles
+ .filter(article => !article.article.startsWith('Special:') &&
+ !article.article.startsWith('Main_Page') &&
+ !article.article.startsWith('Wikipedia:'))
+ .slice(0, 15)
+ .map(article => ({
+ keyword: article.article.replace(/_/g, ' '),
+ searchVolume: article.views,
+ source: 'wikipedia'
+ }));
+
+ combinedTopics = [...combinedTopics, ...wikiTopics];
+ }
+
+ // Source 2: Use Google Trends API-like data (simulated)
+ // In a real implementation, you would use Google Trends API
+ const techTrendingTopics = [
+ { keyword: 'quantum computing applications', searchVolume: 85000 },
+ { keyword: 'edge computing use cases', searchVolume: 62000 },
+ { keyword: 'zero-knowledge proofs', searchVolume: 45000 },
+ { keyword: 'synthetic data generation', searchVolume: 73000 },
+ { keyword: 'federated learning models', searchVolume: 58000 },
+ { keyword: 'serverless architecture patterns', searchVolume: 67000 },
+ { keyword: 'homomorphic encryption', searchVolume: 41000 },
+ { keyword: 'computer vision in healthcare', searchVolume: 89000 },
+ { keyword: 'explainable ai techniques', searchVolume: 76000 },
+ { keyword: 'graph neural networks', searchVolume: 52000 }
+ ].map(item => ({ ...item, source: 'tech_trends' }));
+
+ combinedTopics = [...combinedTopics, ...techTrendingTopics];
+
+ // Source 3: Emerging research topics (simulated)
+ const emergingTopics = [
+ { keyword: 'biomimetic materials science', searchVolume: 32000 },
+ { keyword: 'neuromorphic computing chips', searchVolume: 28000 },
+ { keyword: 'digital twin technology applications', searchVolume: 47000 },
+ { keyword: 'post-quantum cryptography standards', searchVolume: 39000 },
+ { keyword: 'spatial computing interfaces', searchVolume: 43000 }
+ ].map(item => ({ ...item, source: 'emerging_research' }));
+
+ combinedTopics = [...combinedTopics, ...emergingTopics];
+
+ // Deduplicate and return
+ return Array.from(new Map(combinedTopics.map(item =>
+ [item.keyword, item])).values());
+
+ } catch (error) {
+ console.error('Error fetching trending topics:', error);
+ return [];
+ }
+ };
+
+ // Enrich keywords with search result counts
+ const enrichKeywordsWithOpportunityData = async (keywords) => {
+ // In a production environment, you would use a real search API
+ // Here we'll simulate the data with a deterministic algorithm
+
+ return Promise.all(keywords.map(async (keyword) => {
+ // Simulate API call to get search result count
+ // In reality, you would use a search engine API
+
+ // Algorithm to generate realistic but varied result counts
+ // Technical/specific keywords tend to have fewer results
+ const wordCount = keyword.keyword.split(' ').length;
+ const containsTechnicalTerms = /\b(api|algorithm|framework|protocol|quantum|neural|encryption|serverless|computing)\b/i.test(keyword.keyword);
+ const isNiche = wordCount >= 3 || containsTechnicalTerms;
+
+ // Base result count - more specific terms have fewer results
+ let baseResultCount;
+ if (isNiche) {
+ baseResultCount = Math.floor(50000 + Math.random() * 500000);
+ } else {
+ baseResultCount = Math.floor(1000000 + Math.random() * 50000000);
+ }
+
+ // Add some randomness but keep it deterministic for the same keyword
+ const seed = keyword.keyword.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
+ const pseudoRandom = Math.sin(seed) * 10000;
+ const resultCount = Math.max(10000, Math.floor(baseResultCount + pseudoRandom));
+
+ return {
+ ...keyword,
+ resultCount
+ };
+ }));
+ };
+
+ // Calculate opportunity score based on search volume vs. result count
+ const calculateOpportunityScore = (keywords) => {
+ // Calculate the opportunity score
+ // Higher score = high search volume + low result count (better opportunity)
+ const keywordsWithScore = keywords.map(keyword => {
+ // Normalize search volume (0-100)
+ const maxSearchVolume = Math.max(...keywords.map(k => k.searchVolume));
+ const normalizedSearchVolume = (keyword.searchVolume / maxSearchVolume) * 100;
+
+ // Normalize result count inversely (fewer results = higher score)
+ const maxResultCount = Math.max(...keywords.map(k => k.resultCount));
+ const normalizedResultCount = 100 - ((keyword.resultCount / maxResultCount) * 100);
+
+ // Calculate opportunity score (weighted average)
+ // 60% weight to search volume, 40% weight to inverse result count
+ const opportunityScore = (normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4);
+
+ // Calculate competition level (lower is better)
+ const competitionLevel = keyword.resultCount / keyword.searchVolume;
+
+ return {
+ ...keyword,
+ opportunityScore: Math.round(opportunityScore),
+ competitionLevel: Math.round(competitionLevel)
+ };
+ });
+
+ // Sort by opportunity score (highest first)
+ return keywordsWithScore
+ .sort((a, b) => b.opportunityScore - a.opportunityScore)
+ .slice(0, 10);
+ };
+
+ // Use backup keywords when API fails
+ const useBackupKeywords = () => {
+ const backupKeywords = [
+ { keyword: 'quantum machine learning algorithms', opportunityScore: 95, searchVolume: 42000, resultCount: 156000, competitionLevel: 4 },
+ { keyword: 'zero-trust network architecture', opportunityScore: 92, searchVolume: 68000, resultCount: 310000, competitionLevel: 5 },
+ { keyword: 'synthetic data generation techniques', opportunityScore: 89, searchVolume: 51000, resultCount: 245000, competitionLevel: 5 },
+ { keyword: 'edge computing security frameworks', opportunityScore: 87, searchVolume: 73000, resultCount: 420000, competitionLevel: 6 },
+ { keyword: 'federated learning privacy', opportunityScore: 85, searchVolume: 47000, resultCount: 280000, competitionLevel: 6 },
+ { keyword: 'explainable ai for healthcare', opportunityScore: 82, searchVolume: 59000, resultCount: 390000, competitionLevel: 7 },
+ { keyword: 'post-quantum cryptography implementation', opportunityScore: 80, searchVolume: 38000, resultCount: 265000, competitionLevel: 7 },
+ { keyword: 'neuromorphic computing applications', opportunityScore: 78, searchVolume: 31000, resultCount: 225000, competitionLevel: 7 },
+ { keyword: 'graph neural networks for recommendation', opportunityScore: 76, searchVolume: 44000, resultCount: 340000, competitionLevel: 8 },
+ { keyword: 'digital twin technology standards', opportunityScore: 74, searchVolume: 53000, resultCount: 420000, competitionLevel: 8 }
+ ];
+
+ renderKeywords(backupKeywords);
+ };
+
+ // Render keywords
+ const renderKeywords = (keywords) => {
+ keywordsContainer.innerHTML = '';
+
+ keywords.forEach(keyword => {
+ const keywordCard = document.createElement('div');
+ keywordCard.className = 'keyword-card';
+ keywordCard.dataset.keyword = keyword.keyword;
+
+ // Display opportunity score and metrics
+ const score = keyword.opportunityScore || keyword.score || 0;
+ const searchVolume = keyword.searchVolume ? `${(keyword.searchVolume/1000).toFixed(1)}K` : 'N/A';
+ const resultCount = keyword.resultCount ? `${(keyword.resultCount/1000).toFixed(1)}K` : 'N/A';
+ const competitionLevel = keyword.competitionLevel || 'N/A';
+
+ keywordCard.innerHTML = `
+
${keyword.keyword}
+
+
Opportunity: ${score}
+
Search: ${searchVolume}
+
Results: ${resultCount}
+
Competition: ${competitionLevel}
+
+ `;
+
+ keywordCard.addEventListener('click', () => selectKeyword(keyword.keyword, keywordCard));
+
+ keywordsContainer.appendChild(keywordCard);
+ });
+ };
+
+ // Select a keyword
+ const selectKeyword = (keyword, card) => {
+ // Remove selected class from all cards
+ document.querySelectorAll('.keyword-card').forEach(k => k.classList.remove('selected'));
+
+ // Add selected class to clicked card
+ card.classList.add('selected');
+
+ // Update selected keyword
+ selectedKeyword = keyword;
+ selectedKeywordText.textContent = keyword;
+
+ // Enable generate prompt button
+ generatePromptBtn.disabled = false;
+
+ // Hide prompt result if visible
+ promptResult.classList.add('hidden');
+ };
+
+ // Generate article prompt
+ const generatePrompt = () => {
+ if (!selectedKeyword) return;
+
+ try {
+ loading.classList.remove('hidden');
+ generatePromptBtn.disabled = true;
+
+ // Find the selected keyword data
+ const keywordData = Array.from(document.querySelectorAll('.keyword-card')).find(
+ card => card.dataset.keyword === selectedKeyword
+ );
+
+ // Get opportunity metrics if available
+ const opportunityScore = keywordData?.querySelector('.opportunity-score .highlight')?.textContent || 'high';
+ const searchVolume = keywordData?.querySelector('.search-volume')?.textContent.split(': ')[1] || 'significant';
+ const resultCount = keywordData?.querySelector('.result-count')?.textContent.split(': ')[1] || 'limited';
+
+ // Generate a ChatGPT-friendly prompt based on the keyword and opportunity metrics
+ const prompt = {
+ title: `Write a comprehensive article about "${selectedKeyword}" (High-Opportunity Keyword)`,
+ content: `Write a well-researched, engaging, and informative article about "${selectedKeyword}". This is a high-opportunity keyword with ${searchVolume} monthly searches but only ${resultCount} competing results.
+
+1. Start with an attention-grabbing introduction that explains why "${selectedKeyword}" is important or relevant today
+2. Include at least 5 main sections with appropriate headings
+3. Incorporate current statistics, trends, and expert opinions
+4. Address common questions or misconceptions about "${selectedKeyword}"
+5. Provide practical tips, applications, or future predictions related to "${selectedKeyword}"
+6. End with a compelling conclusion that summarizes key points and offers final thoughts
+7. Optimize the content for SEO while maintaining high-quality, valuable information
+
+The article should be approximately 1500-2000 words, written in a professional yet accessible tone, and formatted for easy online reading with appropriate headings, subheadings, and bullet points where relevant.`
+ };
+
+ currentPrompt = prompt;
+ displayPrompt(prompt);
+ } catch (error) {
+ console.error('Error generating prompt:', error);
+ alert('Failed to generate prompt. Please try again.');
+ } finally {
+ loading.classList.add('hidden');
+ generatePromptBtn.disabled = false;
+ }
+ };
+
+ // Display the generated prompt
+ const displayPrompt = (prompt) => {
+ promptTitle.textContent = prompt.title;
+ promptBody.textContent = prompt.content;
+ promptResult.classList.remove('hidden');
+ };
+
+ // Copy prompt to clipboard
+ const copyPrompt = () => {
+ if (!currentPrompt) return;
+
+ const fullPrompt = `${currentPrompt.title}\n\n${currentPrompt.content}`;
+
+ navigator.clipboard.writeText(fullPrompt)
+ .then(() => {
+ copySuccess.classList.remove('hidden');
+ setTimeout(() => {
+ copySuccess.classList.add('hidden');
+ }, 2000);
+ })
+ .catch(err => {
+ console.error('Failed to copy prompt:', err);
+ alert('Failed to copy prompt to clipboard');
+ });
+ };
+
+ // Event listeners
+ refreshBtn.addEventListener('click', fetchTrendingKeywords);
+ generatePromptBtn.addEventListener('click', generatePrompt);
+ copyPromptBtn.addEventListener('click', copyPrompt);
+
+ // Initial load
+ fetchTrendingKeywords();
+});
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a5767e0
--- /dev/null
+++ b/index.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+ Trending Keywords & Article Prompts
+
+
+
+
+
+
+
+
+
+ Today's Trending Keywords
+
+
+
Loading...
+
+
+
+
+
+
+
+ Article Prompt Generator
+
+
Selected keyword: None selected
+
+
+
+
+
Your ChatGPT Prompt
+
+
+
+
+
+ Copied!
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/app.js b/public/app.js
new file mode 100644
index 0000000..43e5cdb
--- /dev/null
+++ b/public/app.js
@@ -0,0 +1,145 @@
+document.addEventListener('DOMContentLoaded', () => {
+ // DOM Elements
+ const keywordsContainer = document.getElementById('keywords-container');
+ const refreshBtn = document.getElementById('refresh-btn');
+ const loading = document.getElementById('loading');
+ const selectedKeywordText = document.getElementById('selected-keyword-text');
+ const generatePromptBtn = document.getElementById('generate-prompt-btn');
+ const promptResult = document.getElementById('prompt-result');
+ const promptTitle = document.getElementById('prompt-title');
+ const promptBody = document.getElementById('prompt-body');
+ const copyPromptBtn = document.getElementById('copy-prompt-btn');
+ const copySuccess = document.getElementById('copy-success');
+
+ // State
+ let selectedKeyword = null;
+ let currentPrompt = null;
+
+ // Fetch trending keywords
+ const fetchTrendingKeywords = async () => {
+ try {
+ loading.classList.remove('hidden');
+ keywordsContainer.innerHTML = '';
+
+ const response = await fetch('/api/trending-keywords');
+ const data = await response.json();
+
+ if (data.keywords && data.keywords.length > 0) {
+ renderKeywords(data.keywords);
+ } else {
+ keywordsContainer.innerHTML = 'No trending keywords found. Please try again later.
';
+ }
+ } catch (error) {
+ console.error('Error fetching trending keywords:', error);
+ keywordsContainer.innerHTML = 'Failed to load trending keywords. Please try again.
';
+ } finally {
+ loading.classList.add('hidden');
+ }
+ };
+
+ // Render keywords
+ const renderKeywords = (keywords) => {
+ keywordsContainer.innerHTML = '';
+
+ keywords.forEach(keyword => {
+ const keywordCard = document.createElement('div');
+ keywordCard.className = 'keyword-card';
+ keywordCard.dataset.keyword = keyword.keyword;
+
+ keywordCard.innerHTML = `
+ ${keyword.keyword}
+ Trend score: ${keyword.score}
+ `;
+
+ keywordCard.addEventListener('click', () => selectKeyword(keyword.keyword, keywordCard));
+
+ keywordsContainer.appendChild(keywordCard);
+ });
+ };
+
+ // Select a keyword
+ const selectKeyword = (keyword, card) => {
+ // Remove selected class from all cards
+ document.querySelectorAll('.keyword-card').forEach(k => k.classList.remove('selected'));
+
+ // Add selected class to clicked card
+ card.classList.add('selected');
+
+ // Update selected keyword
+ selectedKeyword = keyword;
+ selectedKeywordText.textContent = keyword;
+
+ // Enable generate prompt button
+ generatePromptBtn.disabled = false;
+
+ // Hide prompt result if visible
+ promptResult.classList.add('hidden');
+ };
+
+ // Generate article prompt
+ const generatePrompt = async () => {
+ if (!selectedKeyword) return;
+
+ try {
+ loading.classList.remove('hidden');
+ generatePromptBtn.disabled = true;
+
+ const response = await fetch('/api/generate-prompt', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ keyword: selectedKeyword }),
+ });
+
+ const data = await response.json();
+
+ if (data.prompt) {
+ currentPrompt = data.prompt;
+ displayPrompt(data.prompt);
+ } else {
+ alert('Failed to generate prompt. Please try again.');
+ }
+ } catch (error) {
+ console.error('Error generating prompt:', error);
+ alert('Failed to generate prompt. Please try again.');
+ } finally {
+ loading.classList.add('hidden');
+ generatePromptBtn.disabled = false;
+ }
+ };
+
+ // Display the generated prompt
+ const displayPrompt = (prompt) => {
+ promptTitle.textContent = prompt.title;
+ promptBody.textContent = prompt.content;
+ promptResult.classList.remove('hidden');
+ };
+
+ // Copy prompt to clipboard
+ const copyPrompt = () => {
+ if (!currentPrompt) return;
+
+ const fullPrompt = `${currentPrompt.title}\n\n${currentPrompt.content}`;
+
+ navigator.clipboard.writeText(fullPrompt)
+ .then(() => {
+ copySuccess.classList.remove('hidden');
+ setTimeout(() => {
+ copySuccess.classList.add('hidden');
+ }, 2000);
+ })
+ .catch(err => {
+ console.error('Failed to copy prompt:', err);
+ alert('Failed to copy prompt to clipboard');
+ });
+ };
+
+ // Event listeners
+ refreshBtn.addEventListener('click', fetchTrendingKeywords);
+ generatePromptBtn.addEventListener('click', generatePrompt);
+ copyPromptBtn.addEventListener('click', copyPrompt);
+
+ // Initial load
+ fetchTrendingKeywords();
+});
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..a5767e0
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+ Trending Keywords & Article Prompts
+
+
+
+
+
+
+
+
+
+ Today's Trending Keywords
+
+
+
Loading...
+
+
+
+
+
+
+
+ Article Prompt Generator
+
+
Selected keyword: None selected
+
+
+
+
+
Your ChatGPT Prompt
+
+
+
+
+
+ Copied!
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/styles.css b/public/styles.css
new file mode 100644
index 0000000..ad044e2
--- /dev/null
+++ b/public/styles.css
@@ -0,0 +1,182 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Poppins', sans-serif;
+ background-color: #f5f7fa;
+ color: #333;
+ line-height: 1.6;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ text-align: center;
+ margin-bottom: 40px;
+ padding: 20px 0;
+ background: linear-gradient(135deg, #6e8efb, #a777e3);
+ color: white;
+ border-radius: 10px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+header h1 {
+ font-size: 2.5rem;
+ margin-bottom: 10px;
+}
+
+header p {
+ font-size: 1.1rem;
+ opacity: 0.9;
+}
+
+section {
+ background: white;
+ border-radius: 10px;
+ padding: 25px;
+ margin-bottom: 30px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+}
+
+h2 {
+ margin-bottom: 20px;
+ color: #444;
+ font-weight: 600;
+}
+
+.refresh-container {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+button {
+ background-color: #6e8efb;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 500;
+ transition: background-color 0.3s;
+}
+
+button:hover {
+ background-color: #5a7df7;
+}
+
+button:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+}
+
+.loading {
+ margin-left: 15px;
+ color: #666;
+}
+
+.keywords-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 15px;
+}
+
+.keyword-card {
+ background: #f9f9f9;
+ border: 1px solid #eee;
+ border-radius: 8px;
+ padding: 15px;
+ cursor: pointer;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.keyword-card:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+}
+
+.keyword-card.selected {
+ border: 2px solid #6e8efb;
+ background-color: #f0f4ff;
+}
+
+.keyword-name {
+ font-weight: 500;
+ margin-bottom: 5px;
+}
+
+.keyword-score {
+ font-size: 0.9rem;
+ color: #888;
+}
+
+.selected-keyword {
+ margin-bottom: 20px;
+ padding: 10px;
+ background-color: #f0f4ff;
+ border-radius: 5px;
+}
+
+.prompt-result {
+ margin-top: 30px;
+ border: 1px solid #eee;
+ border-radius: 8px;
+ padding: 20px;
+}
+
+.prompt-content {
+ background-color: #f9f9f9;
+ padding: 20px;
+ border-radius: 5px;
+ margin-top: 15px;
+}
+
+#prompt-title {
+ margin-bottom: 15px;
+ color: #333;
+}
+
+#prompt-body {
+ white-space: pre-wrap;
+ line-height: 1.7;
+}
+
+.copy-container {
+ margin-top: 20px;
+ display: flex;
+ align-items: center;
+}
+
+.copy-success {
+ margin-left: 10px;
+ color: #4CAF50;
+}
+
+footer {
+ text-align: center;
+ margin-top: 40px;
+ padding: 20px 0;
+ color: #777;
+}
+
+.hidden {
+ display: none;
+}
+
+@media (max-width: 768px) {
+ .keywords-container {
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ }
+
+ header h1 {
+ font-size: 2rem;
+ }
+}
\ No newline at end of file
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..156fd9a
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,187 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Poppins', sans-serif;
+ background-color: #f5f7fa;
+ color: #333;
+ line-height: 1.6;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 20px;
+}
+
+header {
+ text-align: center;
+ margin-bottom: 40px;
+ padding: 20px 0;
+ background: linear-gradient(135deg, #6e8efb, #a777e3);
+ color: white;
+ border-radius: 10px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+header h1 {
+ font-size: 2.5rem;
+ margin-bottom: 10px;
+}
+
+header p {
+ font-size: 1.1rem;
+ opacity: 0.9;
+}
+
+section {
+ background: white;
+ border-radius: 10px;
+ padding: 25px;
+ margin-bottom: 30px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+}
+
+h2 {
+ margin-bottom: 20px;
+ color: #444;
+ font-weight: 600;
+}
+
+.refresh-container {
+ display: flex;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+button {
+ background-color: #6e8efb;
+ color: white;
+ border: none;
+ padding: 10px 20px;
+ border-radius: 5px;
+ cursor: pointer;
+ font-size: 1rem;
+ font-weight: 500;
+ transition: background-color 0.3s;
+}
+
+button:hover {
+ background-color: #5a7df7;
+}
+
+button:disabled {
+ background-color: #cccccc;
+ cursor: not-allowed;
+}
+
+.loading {
+ margin-left: 15px;
+ color: #666;
+}
+
+.keywords-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 15px;
+}
+
+.keyword-card {
+ background: #f9f9f9;
+ border: 1px solid #eee;
+ border-radius: 8px;
+ padding: 15px;
+ cursor: pointer;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.keyword-card:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
+}
+
+.keyword-card.selected {
+ border: 2px solid #6e8efb;
+ background-color: #f0f4ff;
+}
+
+.keyword-name {
+ font-weight: 500;
+ margin-bottom: 5px;
+}
+
+.keyword-metrics {
+ margin-top: 8px;
+ font-size: 0.85rem;
+ color: #666;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 5px;
+}
+
+.opportunity-score {
+ grid-column: 1 / -1;
+ font-weight: 500;
+ color: #444;
+}
+
+.highlight {
+ color: #6e8efb;
+ font-weight: 600;
+}
+
+.selected-keyword {
+ margin-bottom: 20px;
+ padding: 10px;
+ background-color: #f0f4ff;
+ border-radius: 5px;
+}
+
+.prompt-result {
+ margin-top: 30px;
+ border: 1px solid #eee;
+ border-radius: 8px;
+ padding: 20px;
+}
+
+.prompt-content {
+ background-color: #f9f9f9;
+ padding: 20px;
+ border-radius: 5px;
+ margin-top: 15px;
+}
+
+#prompt-title {
+ margin-bottom: 15px;
+ color: #333;
+}
+
+#prompt-body {
+ white-space: pre-wrap;
+ line-height: 1.7;
+}
+
+.copy-container {
+ margin-top: 20px;
+ display: flex;
+ align-items: center;
+}
+
+.copy-success {
+ margin-left: 10px;
+ color: #4CAF50;
+}
+
+footer {
+ text-align: center;
+ margin-top: 40px;
+ padding: 20px 0;
+ color: #777;
+}
+
+.hidden {
+ display: none;
+}
\ No newline at end of file