Jeens made front end design with all mvp functions without vision ai

This commit is contained in:
Jeen Koster 2025-08-04 22:20:30 +02:00
parent 5e419a7bff
commit 4b82d495b2
4 changed files with 1376 additions and 0 deletions

22
.env Normal file
View file

@ -0,0 +1,22 @@
# === AI CONFIGURATION ===
# Your API key or access token
AI_API_KEY=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
# Name or ID of the AI model
AI_MODEL_NAME=deepseek/deepseek-chat-v3-0324:free
# Endpoint or base URL for DeepSeek API (update if different)
AI_API_URL=https://api.deepseek.com/v1
# Optional: AI task-specific configuration
AI_TASK=keyword_generation
AI_RENAME_STRATEGY=descriptive # Options: 'timestamped', 'uuid', 'descriptive'
# === GENERAL SETTINGS ===
# Environment type
NODE_ENV=development
# Optional: Logging or debugging
ENABLE_LOGGING=true

246
index.html Normal file
View file

@ -0,0 +1,246 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SEO Image Renamer - AI-Powered Image SEO Tool</title>
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<header>
<div class="container">
<div class="logo">
<h1>SEO Image Renamer</h1>
</div>
<nav>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#how-it-works">How It Works</a></li>
<li><a href="#pricing">Pricing</a></li>
<li><a href="#" class="btn btn-primary">Sign In</a></li>
</ul>
</nav>
</div>
</header>
<main>
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>Boost Your Website's SEO with AI-Powered Image Naming</h1>
<p>Stop manually renaming images for SEO. Our AI tool automatically generates SEO-friendly filenames based on your keywords and image content.</p>
<a href="#upload-section" class="btn btn-large btn-primary">Try It Free</a>
</div>
<div class="hero-image">
<img src="https://images.unsplash.com/photo-1558655146-d09347e92766?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=600&h=400&q=80" alt="SEO Image Renamer Dashboard">
</div>
</div>
</section>
<section id="upload-section" class="upload-section">
<div class="container">
<h2>Rename Your Images with AI</h2>
<p class="section-description">Upload your images, provide keywords, and let our AI generate SEO-optimized filenames.</p>
<div class="upload-container">
<div id="drop-area" class="drop-area">
<div class="drop-area-content">
<i class="fas fa-cloud-upload-alt fa-3x"></i>
<h3>Drag & Drop your images here</h3>
<p>or</p>
<button id="browse-btn" class="btn btn-secondary">Browse Files</button>
<input type="file" id="file-input" accept="image/*" multiple style="display: none;">
</div>
</div>
</div>
<div id="keywords-section" class="keywords-section" style="display: none;">
<h3>Enter Keywords for Your Images</h3>
<p>Provide keywords that describe your images to help our AI generate better filenames.</p>
<div class="keywords-input">
<input type="text" id="keyword-input" placeholder="Enter keywords (e.g., beach vacation, summer party)">
<button id="enhance-btn" class="btn btn-secondary" disabled>
<i class="fas fa-magic"></i> Enhance with AI
</button>
</div>
<div id="keywords-display" class="keywords-display">
<!-- Keywords will be displayed here -->
</div>
</div>
<div id="images-preview" class="images-preview" style="display: none;">
<h3>Your Images</h3>
<div id="images-container" class="images-container">
<!-- Images will be displayed here -->
</div>
<div class="actions">
<button id="download-btn" class="btn btn-primary btn-large" disabled>
<i class="fas fa-download"></i> Download Renamed Images as ZIP
</button>
</div>
</div>
</div>
</section>
<section id="features" class="features">
<div class="container">
<h2>Powerful Features for Better SEO</h2>
<p class="section-description">Our AI-powered tool helps you optimize your images for search engines without the manual work.</p>
<div class="features-grid">
<div class="feature-card">
<i class="fas fa-robot fa-2x"></i>
<h3>AI-Powered Naming</h3>
<p>Our advanced AI generates SEO-friendly filenames that help your images rank higher in search results.</p>
</div>
<div class="feature-card">
<i class="fas fa-eye fa-2x"></i>
<h3>Image Recognition</h3>
<p>AI analyzes your images to understand content and context for more accurate naming.</p>
</div>
<div class="feature-card">
<i class="fas fa-key fa-2x"></i>
<h3>Keyword Enhancement</h3>
<p>Enhance your keywords with AI-suggested synonyms for better SEO performance.</p>
</div>
<div class="feature-card">
<i class="fas fa-file-archive fa-2x"></i>
<h3>Easy Download</h3>
<p>Download all your renamed images in a single ZIP file for easy implementation.</p>
</div>
</div>
</div>
</section>
<section id="how-it-works" class="how-it-works">
<div class="container">
<h2>How It Works</h2>
<p class="section-description">Get better SEO for your images in just three simple steps.</p>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<h3>Upload Images</h3>
<p>Drag and drop your images or browse your files to upload them to our platform.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>Add Keywords</h3>
<p>Provide keywords that describe your images, or let our AI enhance them for better SEO.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>Download & Implement</h3>
<p>Download your renamed images as a ZIP file and use them on your website.</p>
</div>
</div>
</div>
</section>
<section id="pricing" class="pricing">
<div class="container">
<h2>Simple, Transparent Pricing</h2>
<p class="section-description">Choose the plan that works best for you.</p>
<div class="pricing-grid">
<div class="pricing-card">
<h3>Basic</h3>
<div class="price">$0<span>/month</span></div>
<ul>
<li>50 images per month</li>
<li>AI-powered naming</li>
<li>Keyword enhancement</li>
<li>ZIP download</li>
</ul>
<button class="btn btn-secondary">Get Started</button>
</div>
<div class="pricing-card featured">
<div class="featured-badge">Most Popular</div>
<h3>Pro</h3>
<div class="price">$9<span>/month</span></div>
<ul>
<li>500 images per month</li>
<li>AI-powered naming</li>
<li>Keyword enhancement</li>
<li>ZIP download</li>
<li>Priority support</li>
</ul>
<button class="btn btn-primary">Get Started</button>
</div>
<div class="pricing-card">
<h3>Max</h3>
<div class="price">$19<span>/month</span></div>
<ul>
<li>1000 images per month</li>
<li>AI-powered naming</li>
<li>Keyword enhancement</li>
<li>ZIP download</li>
<li>Priority support</li>
<li>Advanced analytics</li>
</ul>
<button class="btn btn-secondary">Get Started</button>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-content">
<div class="footer-logo">
<h2>SEO Image Renamer</h2>
<p>AI-powered image SEO optimization</p>
</div>
<div class="footer-links">
<div class="footer-column">
<h4>Product</h4>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#how-it-works">How It Works</a></li>
<li><a href="#pricing">Pricing</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Company</h4>
<ul>
<li><a href="#">About Us</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="footer-column">
<h4>Legal</h4>
<ul>
<li><a href="#">Privacy Policy</a></li>
<li><a href="#">Terms of Service</a></li>
</ul>
</div>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 SEO Image Renamer. All rights reserved.</p>
</div>
</div>
</footer>
<!-- Include JSZip library for ZIP functionality -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script src="script.js"></script>
</body>
</html>

420
script.js Normal file
View file

@ -0,0 +1,420 @@
// Global variables
let uploadedImages = [];
let keywords = [];
let generatedNames = [];
// DOM elements
const dropArea = document.getElementById('drop-area');
const fileInput = document.getElementById('file-input');
const browseBtn = document.getElementById('browse-btn');
const keywordsSection = document.getElementById('keywords-section');
const keywordInput = document.getElementById('keyword-input');
const enhanceBtn = document.getElementById('enhance-btn');
const keywordsDisplay = document.getElementById('keywords-display');
const imagesPreview = document.getElementById('images-preview');
const imagesContainer = document.getElementById('images-container');
const downloadBtn = document.getElementById('download-btn');
// AI Configuration (in a real app, this would be loaded from .env)
const AI_CONFIG = {
API_KEY: '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',
MODEL_NAME: 'deepseek/deepseek-chat-v3-0324:free',
API_URL: 'https://openrouter.ai/api/v1/chat/completions'
};
// Event listeners
document.addEventListener('DOMContentLoaded', () => {
// Upload area event listeners
dropArea.addEventListener('click', () => fileInput.click());
browseBtn.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', handleFileSelect);
// Drag and drop events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false);
});
['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false);
});
dropArea.addEventListener('drop', handleDrop, false);
// Keyword events
keywordInput.addEventListener('input', toggleEnhanceButton);
enhanceBtn.addEventListener('click', enhanceKeywords);
// Download button
downloadBtn.addEventListener('click', downloadImages);
});
// Prevent default drag behaviors
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
// Highlight drop area when item is dragged over it
function highlight() {
dropArea.classList.add('dragover');
}
// Remove highlight when item is dragged out of drop area
function unhighlight() {
dropArea.classList.remove('dragover');
}
// Handle dropped files
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
// Handle file selection
function handleFileSelect(e) {
const files = e.target.files;
handleFiles(files);
}
// Process uploaded files
function handleFiles(files) {
if (files.length === 0) return;
// Convert FileList to Array
const filesArray = Array.from(files);
// Filter only image files
const imageFiles = filesArray.filter(file => file.type.startsWith('image/'));
if (imageFiles.length === 0) {
alert('Please select image files only.');
return;
}
// Clear previous images
uploadedImages = [];
// Process each image file
let processedCount = 0;
imageFiles.forEach(file => {
const reader = new FileReader();
reader.onload = (e) => {
uploadedImages.push({
file: file,
name: file.name,
size: file.size,
type: file.type,
src: e.target.result,
newName: generateFileName(file.name)
});
processedCount++;
// Show keywords section after all files are processed
if (processedCount === imageFiles.length) {
keywordsSection.style.display = 'block';
imagesPreview.style.display = 'block';
updateImagesPreview();
}
};
reader.readAsDataURL(file);
});
}
// Generate a simple filename based on original name
function generateFileName(originalName) {
// Remove extension
const nameWithoutExt = originalName.substring(0, originalName.lastIndexOf('.'));
// Replace non-alphanumeric characters with spaces
const cleanName = nameWithoutExt.replace(/[^a-zA-Z0-9]/g, ' ');
// Capitalize first letter and make it SEO friendly
return cleanName.charAt(0).toUpperCase() + cleanName.slice(1);
}
// Toggle enhance button based on keyword input
function toggleEnhanceButton() {
enhanceBtn.disabled = keywordInput.value.trim() === '';
}
// Enhance keywords with AI
async function enhanceKeywords() {
const keywordText = keywordInput.value.trim();
if (keywordText === '') return;
// Show loading state
enhanceBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Enhancing...';
enhanceBtn.disabled = true;
try {
// Call AI API to enhance keywords
const enhancedKeywords = await callAIKeywordEnhancement(keywordText);
// Split keywords by comma or space
const newKeywords = enhancedKeywords.split(/[, ]+/).filter(k => k !== '');
// Add new keywords to the list
newKeywords.forEach(keyword => {
if (!keywords.includes(keyword)) {
keywords.push(keyword);
}
});
// Update keywords display
updateKeywordsDisplay();
// Clear input
keywordInput.value = '';
// Generate new filenames for images
await generateNewFileNamesWithAI();
} catch (error) {
console.error('Error enhancing keywords:', error);
alert('An error occurred while enhancing keywords. Please try again.');
} finally {
// Reset button
enhanceBtn.innerHTML = '<i class="fas fa-magic"></i> Enhance with AI';
enhanceBtn.disabled = keywordInput.value.trim() === '';
}
}
// Call AI API to enhance keywords
async function callAIKeywordEnhancement(keywords) {
const prompt = `Enhance these keywords for SEO image optimization. Provide 10 additional related keywords that would help images rank better in search engines. Return only the keywords separated by commas, nothing else. Keywords: ${keywords}`;
const response = await fetch(AI_CONFIG.API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${AI_CONFIG.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: AI_CONFIG.MODEL_NAME,
messages: [
{
role: "user",
content: prompt
}
]
})
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content.trim();
}
// Generate new filenames for images based on keywords using AI
async function generateNewFileNamesWithAI() {
if (keywords.length === 0) return;
// Show loading state for each image
document.querySelectorAll('.new-name-input').forEach(input => {
input.disabled = true;
input.placeholder = 'Generating AI filename...';
});
try {
// Generate new filename for each image
for (let i = 0; i < uploadedImages.length; i++) {
const image = uploadedImages[i];
const keywordString = keywords.slice(0, 5).join(', ');
// Call AI to generate a descriptive filename
const aiGeneratedName = await callAIFilenameGeneration(image.name, keywordString);
// Update the image with the new name
const nameWithoutExt = image.name.substring(0, image.name.lastIndexOf('.'));
const extension = image.name.substring(image.name.lastIndexOf('.'));
const newName = `${aiGeneratedName.substring(0, 50)}${extension}`;
image.newName = newName;
}
// Update images preview
updateImagesPreview();
// Enable download button
downloadBtn.disabled = false;
} catch (error) {
console.error('Error generating filenames:', error);
alert('An error occurred while generating filenames. Please try again.');
// Revert to simple filename generation
generateNewFileNames();
} finally {
// Re-enable inputs
document.querySelectorAll('.new-name-input').forEach(input => {
input.disabled = false;
input.placeholder = '';
});
}
}
// Call AI API to generate filename
async function callAIFilenameGeneration(originalName, keywords) {
const prompt = `Generate an SEO-optimized filename for an image. The original filename is "${originalName}" and the keywords are: ${keywords}. Create a descriptive, SEO-friendly filename that is 3-6 words long. Use only letters, numbers, and hyphens. Do not include the file extension. Return only the filename, nothing else.`;
const response = await fetch(AI_CONFIG.API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${AI_CONFIG.API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: AI_CONFIG.MODEL_NAME,
messages: [
{
role: "user",
content: prompt
}
]
})
});
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
const data = await response.json();
return data.choices[0].message.content.trim().replace(/[^a-zA-Z0-9\- ]/g, '').replace(/\s+/g, '-');
}
// Fallback function to generate new filenames without AI
function generateNewFileNames() {
if (keywords.length === 0) return;
uploadedImages.forEach((image, index) => {
// Combine keywords with the original filename
const keywordString = keywords.slice(0, 3).join(' ');
const nameWithoutExt = image.name.substring(0, image.name.lastIndexOf('.'));
const extension = image.name.substring(image.name.lastIndexOf('.'));
// Create new name
const newName = `${keywordString} ${nameWithoutExt}`.substring(0, 50) + extension;
image.newName = newName;
});
// Update images preview
updateImagesPreview();
// Enable download button
downloadBtn.disabled = false;
}
// Update keywords display
function updateKeywordsDisplay() {
keywordsDisplay.innerHTML = '';
keywords.forEach((keyword, index) => {
const keywordChip = document.createElement('div');
keywordChip.className = 'keyword-chip';
keywordChip.innerHTML = `
<span>${keyword}</span>
<button class="remove-keyword" data-index="${index}">&times;</button>
`;
keywordsDisplay.appendChild(keywordChip);
});
// Add event listeners to remove buttons
document.querySelectorAll('.remove-keyword').forEach(button => {
button.addEventListener('click', (e) => {
const index = parseInt(e.target.getAttribute('data-index'));
keywords.splice(index, 1);
updateKeywordsDisplay();
generateNewFileNames();
});
});
}
// Update images preview
function updateImagesPreview() {
imagesContainer.innerHTML = '';
uploadedImages.forEach((image, index) => {
const imageCard = document.createElement('div');
imageCard.className = 'image-card';
imageCard.innerHTML = `
<img src="${image.src}" alt="${image.name}" class="image-thumbnail">
<div class="image-info">
<div class="original-name">Original: ${image.name}</div>
<div class="new-name-container">
<label>New name:</label>
<input type="text" class="new-name-input" value="${image.newName}" data-index="${index}">
</div>
</div>
`;
imagesContainer.appendChild(imageCard);
});
// Add event listeners to name inputs
document.querySelectorAll('.new-name-input').forEach(input => {
input.addEventListener('input', (e) => {
const index = parseInt(e.target.getAttribute('data-index'));
uploadedImages[index].newName = e.target.value;
});
});
}
// Download images as ZIP
async function downloadImages() {
if (uploadedImages.length === 0) return;
// Show loading state
downloadBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Preparing Download...';
downloadBtn.disabled = true;
try {
// Create a new ZIP file
const zip = new JSZip();
// Add each image to the ZIP with its new name
for (const image of uploadedImages) {
// Convert data URL to blob
const blob = await fetch(image.src).then(res => res.blob());
zip.file(image.newName, blob);
}
// Generate the ZIP file
const content = await zip.generateAsync({type: "blob"});
// Create download link
const url = URL.createObjectURL(content);
const a = document.createElement('a');
a.href = url;
a.download = 'renamed-images.zip';
document.body.appendChild(a);
a.click();
// Clean up
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
// Reset button
downloadBtn.innerHTML = '<i class="fas fa-download"></i> Download Renamed Images as ZIP';
downloadBtn.disabled = false;
} catch (error) {
console.error('Error creating ZIP file:', error);
alert('An error occurred while creating the ZIP file. Please try again.');
// Reset button
downloadBtn.innerHTML = '<i class="fas fa-download"></i> Download Renamed Images as ZIP';
downloadBtn.disabled = false;
}
}
// Initialize the page
function init() {
// Set up any initial state
downloadBtn.disabled = true;
}
// Call init when page loads
init();

688
styles.css Normal file
View file

@ -0,0 +1,688 @@
/* Reset and base styles */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8f9fa;
}
.container {
width: 90%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
/* Header styles */
header {
background-color: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
header .container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
}
.logo h1 {
font-size: 1.8rem;
font-weight: 700;
color: #2c3e50;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 30px;
}
nav ul li a {
text-decoration: none;
color: #333;
font-weight: 500;
transition: color 0.3s;
}
nav ul li a:hover {
color: #3498db;
}
.btn {
display: inline-block;
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
border: none;
font-size: 1rem;
}
.btn-primary {
background-color: #3498db;
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn-secondary {
background-color: #2c3e50;
color: white;
}
.btn-secondary:hover {
background-color: #1a252f;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn-large {
padding: 15px 30px;
font-size: 1.1rem;
}
/* Hero section */
.hero {
padding: 80px 0;
background: linear-gradient(135deg, #3498db 0%, #2c3e50 100%);
color: white;
text-align: center;
}
.hero .container {
display: flex;
flex-direction: column;
align-items: center;
}
.hero-content {
max-width: 800px;
margin-bottom: 40px;
}
.hero h1 {
font-size: 2.5rem;
margin-bottom: 20px;
font-weight: 700;
}
.hero p {
font-size: 1.2rem;
margin-bottom: 30px;
opacity: 0.9;
}
.hero-image {
max-width: 600px;
width: 100%;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.hero-image img {
width: 100%;
height: auto;
display: block;
}
/* Upload section */
.upload-section {
padding: 80px 0;
background-color: #fff;
}
.upload-section h2 {
text-align: center;
font-size: 2.2rem;
margin-bottom: 15px;
color: #2c3e50;
}
.section-description {
text-align: center;
font-size: 1.1rem;
color: #7f8c8d;
max-width: 700px;
margin: 0 auto 50px;
}
.drop-area {
border: 3px dashed #3498db;
border-radius: 10px;
padding: 60px 20px;
text-align: center;
background-color: #f8f9fa;
transition: all 0.3s;
cursor: pointer;
}
.drop-area:hover, .drop-area.dragover {
background-color: #e3f2fd;
border-color: #2980b9;
}
.drop-area-content i {
color: #3498db;
margin-bottom: 20px;
}
.drop-area-content h3 {
margin-bottom: 15px;
color: #2c3e50;
}
.drop-area-content p {
margin-bottom: 20px;
color: #7f8c8d;
}
.keywords-section {
margin-top: 50px;
}
.keywords-section h3 {
text-align: center;
margin-bottom: 15px;
color: #2c3e50;
}
.keywords-input {
display: flex;
max-width: 600px;
margin: 0 auto 30px;
gap: 10px;
}
.keywords-input input {
flex: 1;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1rem;
}
.keywords-input input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.keywords-display {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
margin-top: 20px;
}
.keyword-chip {
background-color: #3498db;
color: white;
padding: 8px 15px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 5px;
}
.keyword-chip .remove-keyword {
background: none;
border: none;
color: white;
cursor: pointer;
font-size: 1rem;
}
.images-preview {
margin-top: 50px;
}
.images-preview h3 {
text-align: center;
margin-bottom: 30px;
color: #2c3e50;
}
.images-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 30px;
margin-bottom: 40px;
}
.image-card {
background: #fff;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.image-card:hover {
transform: translateY(-5px);
}
.image-thumbnail {
width: 100%;
height: 200px;
object-fit: cover;
}
.image-info {
padding: 20px;
}
.image-info .original-name {
font-size: 0.9rem;
color: #7f8c8d;
margin-bottom: 10px;
word-break: break-all;
}
.image-info .new-name {
font-weight: 600;
color: #2c3e50;
word-break: break-all;
}
.image-info input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1rem;
margin-top: 10px;
}
.image-info input:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
}
.actions {
text-align: center;
}
/* Features section */
.features {
padding: 80px 0;
background-color: #f8f9fa;
}
.features h2 {
text-align: center;
font-size: 2.2rem;
margin-bottom: 15px;
color: #2c3e50;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 30px;
margin-top: 50px;
}
.feature-card {
background: #fff;
padding: 30px;
border-radius: 10px;
text-align: center;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
transition: transform 0.3s;
}
.feature-card:hover {
transform: translateY(-10px);
}
.feature-card i {
color: #3498db;
margin-bottom: 20px;
}
.feature-card h3 {
margin-bottom: 15px;
color: #2c3e50;
}
.feature-card p {
color: #7f8c8d;
}
/* How it works section */
.how-it-works {
padding: 80px 0;
background-color: #fff;
}
.how-it-works h2 {
text-align: center;
font-size: 2.2rem;
margin-bottom: 15px;
color: #2c3e50;
}
.steps {
display: flex;
justify-content: space-between;
margin-top: 50px;
flex-wrap: wrap;
}
.step {
flex: 1;
min-width: 250px;
text-align: center;
padding: 0 20px;
position: relative;
}
.step:not(:last-child):after {
content: "";
position: absolute;
top: 40px;
right: 0;
width: 50%;
height: 2px;
background-color: #3498db;
}
.step-number {
width: 80px;
height: 80px;
background-color: #3498db;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: 700;
margin: 0 auto 20px;
}
.step h3 {
margin-bottom: 15px;
color: #2c3e50;
}
.step p {
color: #7f8c8d;
}
/* Pricing section */
.pricing {
padding: 80px 0;
background-color: #f8f9fa;
}
.pricing h2 {
text-align: center;
font-size: 2.2rem;
margin-bottom: 15px;
color: #2c3e50;
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
margin-top: 50px;
}
.pricing-card {
background: #fff;
border-radius: 10px;
padding: 40px 30px;
text-align: center;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
position: relative;
overflow: hidden;
}
.pricing-card.featured {
transform: scale(1.05);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
border: 2px solid #3498db;
}
.featured-badge {
position: absolute;
top: 20px;
right: -30px;
background-color: #3498db;
color: white;
padding: 5px 30px;
transform: rotate(45deg);
font-size: 0.8rem;
font-weight: 600;
}
.pricing-card h3 {
font-size: 1.8rem;
margin-bottom: 20px;
color: #2c3e50;
}
.price {
font-size: 3rem;
font-weight: 700;
color: #2c3e50;
margin-bottom: 30px;
}
.price span {
font-size: 1rem;
color: #7f8c8d;
}
.pricing-card ul {
list-style: none;
margin-bottom: 30px;
text-align: left;
}
.pricing-card ul li {
padding: 10px 0;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
}
.pricing-card ul li:before {
content: "\f00c";
font-family: "Font Awesome 5 Free";
font-weight: 900;
color: #3498db;
margin-right: 10px;
}
.pricing-card .btn {
width: 100%;
}
/* Footer */
footer {
background-color: #2c3e50;
color: white;
padding: 60px 0 30px;
}
.footer-content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 40px;
}
.footer-logo h2 {
font-size: 1.8rem;
margin-bottom: 10px;
}
.footer-logo p {
opacity: 0.7;
}
.footer-links {
display: flex;
gap: 50px;
}
.footer-column h4 {
margin-bottom: 20px;
position: relative;
padding-bottom: 10px;
}
.footer-column h4:after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 50px;
height: 2px;
background-color: #3498db;
}
.footer-column ul {
list-style: none;
}
.footer-column ul li {
margin-bottom: 10px;
}
.footer-column ul li a {
color: #bbb;
text-decoration: none;
transition: color 0.3s;
}
.footer-column ul li a:hover {
color: #3498db;
}
.footer-bottom {
text-align: center;
padding-top: 30px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: #bbb;
}
/* Responsive design */
@media (max-width: 992px) {
.hero .container {
flex-direction: column;
}
.hero-content {
margin-bottom: 50px;
}
.steps {
flex-direction: column;
gap: 50px;
}
.step:not(:last-child):after {
display: none;
}
.footer-content {
flex-direction: column;
gap: 40px;
}
.footer-links {
gap: 30px;
}
}
@media (max-width: 768px) {
header .container {
flex-direction: column;
gap: 20px;
}
nav ul {
flex-wrap: wrap;
justify-content: center;
}
nav ul li {
margin: 5px 10px;
}
.hero {
padding: 60px 0;
}
.hero h1 {
font-size: 2rem;
}
.hero p {
font-size: 1rem;
}
.keywords-input {
flex-direction: column;
}
.pricing-card.featured {
transform: scale(1);
}
}
@media (max-width: 576px) {
.container {
width: 95%;
}
.hero {
padding: 40px 0;
}
.hero h1 {
font-size: 1.8rem;
}
.upload-section, .features, .how-it-works, .pricing {
padding: 60px 0;
}
.section-description {
font-size: 1rem;
}
.drop-area {
padding: 40px 15px;
}
.images-container {
grid-template-columns: 1fr;
}
}