feat: adiciona arquivos no template-04
This commit is contained in:
parent
e824be6aba
commit
c947596066
42 changed files with 9904 additions and 0 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
20
Template-04/README.md
Normal file
20
Template-04/README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<div align="center">
|
||||||
|
<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
# Run and deploy your AI Studio app
|
||||||
|
|
||||||
|
This contains everything you need to run your app locally.
|
||||||
|
|
||||||
|
View your app in AI Studio: https://ai.studio/apps/b9e0fe6d-c7c5-42ea-8fbe-61d65dc0914c
|
||||||
|
|
||||||
|
## Run Locally
|
||||||
|
|
||||||
|
**Prerequisites:** Node.js
|
||||||
|
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
`npm install`
|
||||||
|
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
|
||||||
|
3. Run the app:
|
||||||
|
`npm run dev`
|
||||||
32
Template-04/index.html
Normal file
32
Template-04/index.html
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>SEO Authority | Insights Técnicos e Estratégicos</title>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%232563eb'%3E%3Cpath d='M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5'/%3E%3C/svg%3E" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
<style>
|
||||||
|
/* Hide Google Translate Bar */
|
||||||
|
.goog-te-banner-frame.skiptranslate, .goog-te-gadget-icon { display: none !important; }
|
||||||
|
body { top: 0px !important; }
|
||||||
|
.goog-te-menu-value { display: none !important; }
|
||||||
|
#google_translate_element { display: none; }
|
||||||
|
.goog-tooltip { display: none !important; }
|
||||||
|
.goog-tooltip:hover { display: none !important; }
|
||||||
|
.goog-text-highlight { background-color: transparent !important; border: none !important; box-shadow: none !important; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="google_translate_element"></div>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function googleTranslateElementInit() {
|
||||||
|
new google.translate.TranslateElement({pageLanguage: 'pt', includedLanguages: 'en,es,pt', layout: google.translate.TranslateElement.InlineLayout.SIMPLE, autoDisplay: false}, 'google_translate_element');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
6
Template-04/metadata.json
Normal file
6
Template-04/metadata.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "Apex SEO",
|
||||||
|
"description": "Plataforma profissional de alta densidade para inteligência de SEO técnico e crescimento arquitetônico estratégico.",
|
||||||
|
"requestFramePermissions": [],
|
||||||
|
"majorCapabilities": []
|
||||||
|
}
|
||||||
6068
Template-04/package-lock.json
generated
Normal file
6068
Template-04/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
42
Template-04/package.json
Normal file
42
Template-04/package.json
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"name": "react-example",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "tsx server.ts",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"lint": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@google/genai": "^1.51.0",
|
||||||
|
"@google/generative-ai": "^0.24.1",
|
||||||
|
"@tailwindcss/vite": "^4.1.14",
|
||||||
|
"@vitejs/plugin-react": "^5.0.4",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
|
"express": "^4.22.1",
|
||||||
|
"lucide-react": "^0.546.0",
|
||||||
|
"motion": "^12.23.24",
|
||||||
|
"react": "^19.0.1",
|
||||||
|
"react-dom": "^19.0.1",
|
||||||
|
"react-helmet-async": "^3.0.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"react-router-dom": "^7.14.2",
|
||||||
|
"recharts": "^3.8.1",
|
||||||
|
"tailwind-merge": "^3.5.0",
|
||||||
|
"vite": "^6.2.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/node": "^22.14.0",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"tailwindcss": "^4.1.14",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
"typescript": "~5.8.2",
|
||||||
|
"vite": "^6.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
Template-04/public/robots.txt
Normal file
5
Template-04/public/robots.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Sitemaps
|
||||||
|
Sitemap: https://seu-dominio.com/sitemap.xml
|
||||||
42
Template-04/public/sitemap.xml
Normal file
42
Template-04/public/sitemap.xml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<changefreq>daily</changefreq>
|
||||||
|
<priority>1.0</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/sobre</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/contato</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.7</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/metodologia</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<changefreq>monthly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/artigo/futuro-do-seo-2026</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<priority>0.9</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/artigo/eeat-guia-completo</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<priority>0.9</priority>
|
||||||
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://seo-authority.digital/artigo/sinais-de-usuario-ranking</loc>
|
||||||
|
<lastmod>2026-05-04</lastmod>
|
||||||
|
<priority>0.9</priority>
|
||||||
|
</url>
|
||||||
|
</urlset>
|
||||||
97
Template-04/server.ts
Normal file
97
Template-04/server.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import express from 'express';
|
||||||
|
import { createServer as createViteServer } from 'vite';
|
||||||
|
import { GoogleGenAI } from "@google/genai";
|
||||||
|
import path from 'path';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
async function startServer() {
|
||||||
|
const app = express();
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
// Lazy Gemini Setup
|
||||||
|
let ai: GoogleGenAI | null = null;
|
||||||
|
|
||||||
|
const getAI = () => {
|
||||||
|
const apiKey = process.env.GEMINI_API_KEY;
|
||||||
|
if (!apiKey) {
|
||||||
|
throw new Error("GEMINI_API_KEY environment variable is required");
|
||||||
|
}
|
||||||
|
if (!ai) {
|
||||||
|
ai = new GoogleGenAI({ apiKey });
|
||||||
|
}
|
||||||
|
return ai;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Translation API
|
||||||
|
app.post('/api/translate', async (req, res) => {
|
||||||
|
const { text, targetLang, context, isObject } = req.body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const client = getAI();
|
||||||
|
|
||||||
|
let prompt = "";
|
||||||
|
if (isObject) {
|
||||||
|
prompt = `Translate the following JSON object values to ${targetLang}.
|
||||||
|
Keep the keys exactly the same.
|
||||||
|
Preserve markdown formatting in values.
|
||||||
|
Return ONLY the translated JSON object.
|
||||||
|
|
||||||
|
Object:
|
||||||
|
${JSON.stringify(text)}`;
|
||||||
|
} else {
|
||||||
|
prompt = `Translate the following ${context || 'text'} to ${targetLang}.
|
||||||
|
Preserve all markdown formatting, technical terms, and HTML structures if present.
|
||||||
|
Do not add any commentary or prefix. Return only the translated string.
|
||||||
|
|
||||||
|
Content:
|
||||||
|
${text}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await client.models.generateContent({
|
||||||
|
model: "gemini-3-flash-preview",
|
||||||
|
contents: prompt,
|
||||||
|
config: isObject ? { responseMimeType: "application/json" } : undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
let resultText = response.text || '';
|
||||||
|
|
||||||
|
if (isObject) {
|
||||||
|
try {
|
||||||
|
res.json({ translatedText: JSON.parse(resultText) });
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Parse Error:", resultText);
|
||||||
|
res.json({ translatedText: text }); // Fallback to original if parse fails
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.json({ translatedText: resultText });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Translation Error:", error);
|
||||||
|
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to translate" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Vite Integration
|
||||||
|
if (process.env.NODE_ENV !== "production") {
|
||||||
|
const vite = await createViteServer({
|
||||||
|
server: { middlewareMode: true },
|
||||||
|
appType: "spa",
|
||||||
|
});
|
||||||
|
app.use(vite.middlewares);
|
||||||
|
} else {
|
||||||
|
const distPath = path.join(process.cwd(), 'dist');
|
||||||
|
app.use(express.static(distPath));
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
res.sendFile(path.join(distPath, 'index.html'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.listen(PORT, "0.0.0.0", () => {
|
||||||
|
console.log(`Server running at http://0.0.0.0:${PORT}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startServer();
|
||||||
50
Template-04/src/App.tsx
Normal file
50
Template-04/src/App.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
||||||
|
import { HelmetProvider } from 'react-helmet-async';
|
||||||
|
import Layout from './components/Layout';
|
||||||
|
import Home from './pages/Home';
|
||||||
|
import BlogPost from './pages/BlogPost';
|
||||||
|
import CategoryPage from './pages/CategoryPage';
|
||||||
|
import About from './pages/About';
|
||||||
|
import Methodology from './pages/Methodology';
|
||||||
|
import Privacy from './pages/Privacy';
|
||||||
|
import Terms from './pages/Terms';
|
||||||
|
import Contact from './pages/Contact';
|
||||||
|
import Archive from './pages/Archive';
|
||||||
|
import NotFound from './pages/NotFound';
|
||||||
|
import Bookmarks from './pages/Bookmarks';
|
||||||
|
|
||||||
|
import { LanguageProvider } from './contexts/LanguageContext';
|
||||||
|
import { BookmarksProvider } from './contexts/BookmarksContext';
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
return (
|
||||||
|
<HelmetProvider>
|
||||||
|
<LanguageProvider>
|
||||||
|
<BookmarksProvider>
|
||||||
|
<Router>
|
||||||
|
<Layout>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="/artigo/:slug" element={<BlogPost />} />
|
||||||
|
<Route path="/categoria/:categorySlug" element={<CategoryPage />} />
|
||||||
|
<Route path="/sobre" element={<About />} />
|
||||||
|
<Route path="/metodologia" element={<Methodology />} />
|
||||||
|
<Route path="/privacidade" element={<Privacy />} />
|
||||||
|
<Route path="/termos" element={<Terms />} />
|
||||||
|
<Route path="/contato" element={<Contact />} />
|
||||||
|
<Route path="/arquivo" element={<Archive />} />
|
||||||
|
<Route path="/leituras-salvas" element={<Bookmarks />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</Routes>
|
||||||
|
</Layout>
|
||||||
|
</Router>
|
||||||
|
</BookmarksProvider>
|
||||||
|
</LanguageProvider>
|
||||||
|
</HelmetProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
143
Template-04/src/components/ArticleCard.tsx
Normal file
143
Template-04/src/components/ArticleCard.tsx
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Article } from '../types';
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { Bookmark, Clock, ArrowRight, BarChart2 } from 'lucide-react';
|
||||||
|
import { useBookmarks } from '../contexts/BookmarksContext';
|
||||||
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
|
interface ArticleCardProps {
|
||||||
|
article: Article;
|
||||||
|
featured?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ArticleCard({ article, featured = false }: ArticleCardProps) {
|
||||||
|
const { toggleBookmark, isBookmarked } = useBookmarks();
|
||||||
|
const bookmarked = isBookmarked(article.id);
|
||||||
|
|
||||||
|
if (featured) {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="group relative flex flex-col md:flex-row gap-0 glass-card glass-card-hover overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="md:w-1/2 relative aspect-video md:aspect-auto overflow-hidden">
|
||||||
|
<Link to={`/artigo/${article.slug}`} className="block h-full">
|
||||||
|
<img
|
||||||
|
src={article.image}
|
||||||
|
alt={article.title}
|
||||||
|
className="h-full w-full object-cover transition-transform duration-700 group-hover:scale-110"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-r from-black/60 to-transparent" />
|
||||||
|
<div className="absolute top-6 left-6">
|
||||||
|
<div className="px-3 py-1 bg-brand text-white text-[10px] font-bold rounded-lg shadow-lg">Resultado em Destaque</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:w-1/2 p-8 lg:p-12 flex flex-col justify-center">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<span className="text-xs font-bold uppercase tracking-widest text-brand">{article.category}</span>
|
||||||
|
<div className="h-px w-8 bg-white/10" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link to={`/artigo/${article.slug}`}>
|
||||||
|
<h2 className="text-2xl sm:text-4xl font-bold text-white leading-tight mb-6 group-hover:text-brand transition-colors tracking-tight">
|
||||||
|
{article.title}
|
||||||
|
</h2>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<p className="text-slate-400 text-sm leading-relaxed mb-8 line-clamp-3">
|
||||||
|
{article.excerpt}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="mt-auto flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<img src={article.author.avatar} alt={article.author.name} className="h-10 w-10 rounded-full border border-white/10 grayscale hover:grayscale-0 transition-all" />
|
||||||
|
<div>
|
||||||
|
<p className="text-xs font-bold text-white">{article.author.name}</p>
|
||||||
|
<p className="text-[10px] text-slate-500 uppercase tracking-widest">Lead Strategist</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => toggleBookmark(article.id)}
|
||||||
|
className={cn(
|
||||||
|
"h-10 w-10 rounded-xl border flex items-center justify-center transition-all",
|
||||||
|
bookmarked ? "bg-brand border-brand text-white" : "border-white/10 text-white hover:border-brand hover:text-brand"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Bookmark size={16} fill={bookmarked ? "currentColor" : "none"} />
|
||||||
|
</button>
|
||||||
|
<Link to={`/artigo/${article.slug}`} className="h-10 w-12 bg-white/5 hover:bg-brand text-white hover:text-white rounded-xl flex items-center justify-center transition-all">
|
||||||
|
<ArrowRight size={18} />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 15 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="group relative h-full flex flex-col glass-card glass-card-hover p-4"
|
||||||
|
>
|
||||||
|
<div className="relative aspect-[16/10] bg-black rounded-xl overflow-hidden mb-6">
|
||||||
|
<Link to={`/artigo/${article.slug}`} className="block h-full">
|
||||||
|
<img
|
||||||
|
src={article.image}
|
||||||
|
alt={article.title}
|
||||||
|
className="h-full w-full object-cover transition-transform duration-700 group-hover:scale-110"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
<div className="absolute top-4 left-4">
|
||||||
|
<div className="px-2 py-1 bg-black/60 backdrop-blur-md rounded-lg text-white text-[10px] font-bold uppercase tracking-widest">{article.category}</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => toggleBookmark(article.id)}
|
||||||
|
className={cn(
|
||||||
|
"absolute top-4 right-4 h-8 w-8 rounded-lg border backdrop-blur-md flex items-center justify-center transition-all",
|
||||||
|
bookmarked ? "bg-brand border-brand text-white opacity-100" : "bg-black/60 border-white/10 text-white hover:bg-brand hover:text-white opacity-0 group-hover:opacity-100"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Bookmark size={14} fill={bookmarked ? "currentColor" : "none"} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="px-2 pb-4 flex flex-col flex-grow">
|
||||||
|
<div className="flex items-center gap-2 mb-4 text-brand">
|
||||||
|
<BarChart2 size={12} />
|
||||||
|
<span className="text-[10px] font-bold uppercase tracking-widest">Análise de Crescimento</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link to={`/artigo/${article.slug}`} className="flex-grow group/link">
|
||||||
|
<h3 className="text-xl font-bold text-white leading-tight mb-4 group-hover/link:text-brand transition-colors tracking-tight">
|
||||||
|
{article.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed line-clamp-2 mb-8">
|
||||||
|
{article.excerpt}
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="mt-auto pt-6 border-t border-white/5 flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<img src={article.author.avatar} alt={article.author.name} className="h-8 w-8 rounded-full border border-white/10" />
|
||||||
|
<p className="text-xs font-bold text-slate-400">{article.author.name}</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-slate-600 text-[10px] font-bold">
|
||||||
|
<Clock size={12} />
|
||||||
|
<span>{article.readTime}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
Template-04/src/components/CategoryBadge.tsx
Normal file
31
Template-04/src/components/CategoryBadge.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Category } from '../types';
|
||||||
|
import { cn } from '../lib/utils';
|
||||||
|
import { CATEGORY_MAP } from '../constants';
|
||||||
|
|
||||||
|
interface CategoryBadgeProps {
|
||||||
|
category: Category;
|
||||||
|
variant?: 'solid' | 'outline' | 'glass';
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CategoryBadge({ category, variant = 'solid', className }: CategoryBadgeProps) {
|
||||||
|
const mapping = CATEGORY_MAP[category] || { name: category, slug: category.toLowerCase().replace(/\s+/g, '-') };
|
||||||
|
const baseStyles = "inline-flex items-center rounded-full px-3 py-1 text-[10px] font-bold uppercase tracking-wider transition-all hover:scale-105 active:scale-95";
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
solid: "bg-brand text-white hover:bg-brand-dark",
|
||||||
|
outline: "border border-brand/10 text-brand hover:bg-brand/5",
|
||||||
|
glass: "bg-white/80 backdrop-blur-sm text-slate-900 shadow-sm hover:bg-white"
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={`/categoria/${mapping.slug}`}
|
||||||
|
className={cn(baseStyles, variants[variant], className)}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{mapping.name}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
91
Template-04/src/components/Footer.tsx
Normal file
91
Template-04/src/components/Footer.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Twitter, Linkedin, Github, TrendingUp, ArrowRight } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="bg-black border-t border-white/5 pt-24 pb-12 relative overflow-hidden">
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-12 gap-16 mb-20">
|
||||||
|
<div className="md:col-span-12 lg:col-span-4">
|
||||||
|
<Link to="/" className="flex items-center gap-2 mb-8 group">
|
||||||
|
<div className="h-8 w-8 bg-brand rounded-lg flex items-center justify-center text-white shadow-lg shadow-brand/20 group-hover:scale-110 transition-transform">
|
||||||
|
<TrendingUp size={18} />
|
||||||
|
</div>
|
||||||
|
<span className="font-bold text-xl tracking-tight text-white">
|
||||||
|
Apex<span className="text-brand">SEO</span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed max-w-sm mb-8">
|
||||||
|
Engenharia de precisão para o horizonte da busca orgânica. Ajudamos empresas de elite e scale-ups a escalar receita através de autoridade técnica e arquitetura estratégica de conteúdo.
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{[Twitter, Linkedin, Github].map((Icon, i) => (
|
||||||
|
<a
|
||||||
|
key={i}
|
||||||
|
href="#"
|
||||||
|
className="h-10 w-10 flex items-center justify-center rounded-xl border border-white/10 text-slate-400 hover:text-brand hover:border-brand/40 hover:bg-brand/5 transition-all"
|
||||||
|
>
|
||||||
|
<Icon size={18} />
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:col-span-4 lg:col-span-2">
|
||||||
|
<h4 className="text-white text-xs font-bold uppercase tracking-widest mb-8">Plataforma</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-medium text-slate-500">
|
||||||
|
<li><Link to="/#servicos" className="hover:text-brand transition-colors">Serviços</Link></li>
|
||||||
|
<li><Link to="/arquivo" className="hover:text-brand transition-colors">Estudos de Caso</Link></li>
|
||||||
|
<li><Link to="/metodologia" className="hover:text-brand transition-colors">Metodologia</Link></li>
|
||||||
|
<li><Link to="/arquivo" className="hover:text-brand transition-colors">Insights</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:col-span-4 lg:col-span-2">
|
||||||
|
<h4 className="text-white text-xs font-bold uppercase tracking-widest mb-8">Empresa</h4>
|
||||||
|
<ul className="space-y-4 text-xs font-medium text-slate-500">
|
||||||
|
<li><Link to="/sobre" className="hover:text-brand transition-colors">Sobre Nós</Link></li>
|
||||||
|
<li><Link to="/contato" className="hover:text-brand transition-colors">Contato</Link></li>
|
||||||
|
<li><Link to="/privacidade" className="hover:text-brand transition-colors">Privacidade</Link></li>
|
||||||
|
<li><Link to="/termos" className="hover:text-brand transition-colors">Termos de Uso</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="md:col-span-4 lg:col-span-4">
|
||||||
|
<div className="glass-card p-8">
|
||||||
|
<h5 className="text-white font-bold text-sm mb-4">Escale sua autoridade</h5>
|
||||||
|
<p className="text-slate-500 text-xs mb-6 leading-relaxed">
|
||||||
|
Receba nosso briefing técnico mensal sobre o estado da busca orgânica e algoritmos.
|
||||||
|
</p>
|
||||||
|
<form className="flex gap-2" onSubmit={(e) => e.preventDefault()}>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="Seu melhor e-mail"
|
||||||
|
className="flex-grow h-11 bg-black/50 border border-white/10 rounded-xl px-4 text-xs text-white focus:border-brand outline-none transition-all"
|
||||||
|
/>
|
||||||
|
<button className="h-11 px-6 bg-brand text-white font-bold text-xs rounded-xl hover:bg-brand-light transition-all flex items-center gap-2">
|
||||||
|
Assinar <ArrowRight size={14} />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="pt-10 border-t border-white/5 flex flex-col md:flex-row justify-between items-center gap-6 text-[10px] font-bold uppercase tracking-widest text-slate-600">
|
||||||
|
<p>
|
||||||
|
© {currentYear} Apex SEO Agency. Todos os direitos reservados.
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
|
||||||
|
<span>Sistema Operacional</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-slate-800">Apex_Core_v4.2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
194
Template-04/src/components/Header.tsx
Normal file
194
Template-04/src/components/Header.tsx
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Search, Menu, X, Globe, Bookmark, TrendingUp } from 'lucide-react';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { useBookmarks } from '../contexts/BookmarksContext';
|
||||||
|
import { cn } from '../lib/utils';
|
||||||
|
|
||||||
|
interface HeaderProps {
|
||||||
|
onSearchOpen: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Header({ onSearchOpen }: HeaderProps) {
|
||||||
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||||
|
const [scrolled, setScrolled] = useState(false);
|
||||||
|
const { lang, setLang } = useLanguage();
|
||||||
|
const [isLangOpen, setIsLangOpen] = useState(false);
|
||||||
|
const { bookmarks } = useBookmarks();
|
||||||
|
|
||||||
|
const languages = [
|
||||||
|
{ code: 'pt-br', label: 'PT', flag: 'BR' },
|
||||||
|
{ code: 'en', label: 'EN', flag: 'US' },
|
||||||
|
{ code: 'es', label: 'ES', flag: 'ES' }
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => setScrolled(window.scrollY > 20);
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
return () => window.removeEventListener('scroll', handleScroll);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ name: 'Serviços', path: '/#servicos' },
|
||||||
|
{ name: 'Cases', path: '/arquivo' },
|
||||||
|
{ name: 'Sobre', path: '/sobre' },
|
||||||
|
{ name: 'Metodologia', path: '/metodologia' },
|
||||||
|
{ name: 'Insights', path: '/arquivo' }
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className={cn(
|
||||||
|
"fixed top-0 z-50 w-full transition-all duration-500",
|
||||||
|
scrolled ? "bg-black/80 backdrop-blur-xl border-b border-white/5 py-4" : "bg-transparent py-6"
|
||||||
|
)}>
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-12">
|
||||||
|
<Link to="/" className="flex items-center gap-2 group">
|
||||||
|
<div className="h-8 w-8 bg-brand rounded-lg flex items-center justify-center text-white shadow-lg shadow-brand/20 group-hover:scale-110 transition-transform">
|
||||||
|
<TrendingUp size={18} />
|
||||||
|
</div>
|
||||||
|
<span className="font-bold text-xl tracking-tight text-white">
|
||||||
|
Apex<span className="text-brand">SEO</span>
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<nav className="hidden lg:flex items-center gap-8">
|
||||||
|
{navItems.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
to={item.path}
|
||||||
|
className="text-sm font-medium text-slate-400 hover:text-white transition-colors py-2"
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="hidden sm:flex items-center gap-4 px-4 relative">
|
||||||
|
<button
|
||||||
|
onClick={() => setIsLangOpen(!isLangOpen)}
|
||||||
|
className="flex items-center gap-1.5 text-xs font-semibold text-slate-500 hover:text-white transition-colors py-2"
|
||||||
|
>
|
||||||
|
<Globe size={14} />
|
||||||
|
<span>{lang === 'pt-br' ? 'PT' : lang.toUpperCase()}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<AnimatePresence>
|
||||||
|
{isLangOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
|
animate={{ opacity: 1, y: 0, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, y: 10, scale: 0.95 }}
|
||||||
|
className="absolute top-full right-0 mt-1 bg-slate-900 border border-white/10 rounded-lg p-1 min-w-[80px] shadow-xl"
|
||||||
|
>
|
||||||
|
{languages.map((l) => (
|
||||||
|
<button
|
||||||
|
key={l.code}
|
||||||
|
onClick={() => {
|
||||||
|
setLang(l.code);
|
||||||
|
setIsLangOpen(false);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"w-full text-left px-3 py-1.5 rounded-md text-[10px] font-bold transition-colors",
|
||||||
|
lang === l.code ? "bg-brand text-white" : "text-slate-400 hover:bg-white/5 hover:text-white"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{l.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="h-10 w-10 flex items-center justify-center text-white/70 hover:text-brand transition-colors"
|
||||||
|
onClick={onSearchOpen}
|
||||||
|
>
|
||||||
|
<Search size={20} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/bookmarks"
|
||||||
|
className="relative h-10 w-10 flex items-center justify-center text-white/70 hover:text-brand transition-colors"
|
||||||
|
>
|
||||||
|
<Bookmark size={20} />
|
||||||
|
{bookmarks.length > 0 && (
|
||||||
|
<span className="absolute top-2 right-2 h-2 w-2 bg-brand rounded-full border border-black" />
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link to="/contato" className="hidden md:flex btn-primary !h-10 !px-6 !text-xs ml-4 flex items-center justify-center">
|
||||||
|
Auditoria Grátis
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="lg:hidden h-10 w-10 flex items-center justify-center text-white ml-2"
|
||||||
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||||
|
>
|
||||||
|
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Mobile Menu */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{isMenuOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -20 }}
|
||||||
|
className="lg:hidden absolute top-full left-0 w-full bg-black border-t border-white/5 py-8"
|
||||||
|
>
|
||||||
|
<div className="section-container flex flex-col gap-6">
|
||||||
|
{navItems.map((item) => (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
to={item.path}
|
||||||
|
className="text-2xl font-bold text-white hover:text-brand transition-colors"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
<div className="h-px w-full bg-white/5 my-4" />
|
||||||
|
|
||||||
|
<div className="flex gap-4">
|
||||||
|
{languages.map((l) => (
|
||||||
|
<button
|
||||||
|
key={l.code}
|
||||||
|
onClick={() => {
|
||||||
|
setLang(l.code);
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"flex-grow py-3 px-4 rounded-xl border text-sm font-bold transition-all",
|
||||||
|
lang === l.code
|
||||||
|
? "bg-brand border-brand text-white"
|
||||||
|
: "bg-white/[0.03] border-white/10 text-slate-400"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{l.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to="/contato"
|
||||||
|
className="btn-primary w-full mt-4 flex items-center justify-center h-12"
|
||||||
|
onClick={() => setIsMenuOpen(false)}
|
||||||
|
>
|
||||||
|
Solicitar Auditoria
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
Template-04/src/components/Layout.tsx
Normal file
53
Template-04/src/components/Layout.tsx
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
|
import Header from './Header';
|
||||||
|
import Footer from './Footer';
|
||||||
|
import LeadMagnet from './LeadMagnet';
|
||||||
|
import SearchOverlay from './SearchOverlay';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
|
|
||||||
|
interface LayoutProps {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Layout({ children }: LayoutProps) {
|
||||||
|
const { pathname, hash } = useLocation();
|
||||||
|
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||||
|
|
||||||
|
// Scroll to top or to hash on route change
|
||||||
|
useEffect(() => {
|
||||||
|
if (hash) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const id = hash.replace('#', '');
|
||||||
|
const element = document.getElementById(id);
|
||||||
|
if (element) {
|
||||||
|
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
}, [pathname, hash]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col bg-[#020203] selection:bg-brand/10 selection:text-brand text-slate-300">
|
||||||
|
<Header onSearchOpen={() => setIsSearchOpen(true)} />
|
||||||
|
<SearchOverlay isOpen={isSearchOpen} onClose={() => setIsSearchOpen(false)} />
|
||||||
|
<main className="flex-grow">
|
||||||
|
<AnimatePresence mode="wait">
|
||||||
|
<motion.div
|
||||||
|
key={pathname}
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -10 }}
|
||||||
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
</main>
|
||||||
|
<LeadMagnet />
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
108
Template-04/src/components/LeadMagnet.tsx
Normal file
108
Template-04/src/components/LeadMagnet.tsx
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
|
import { X, Download, ShieldCheck, ArrowRight, Mail } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function LeadMagnet() {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [dismissed, setDismissed] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if user already dismissed or converted
|
||||||
|
const hasSeen = localStorage.getItem('seo_lead_magnet_dismissed');
|
||||||
|
if (hasSeen) return;
|
||||||
|
|
||||||
|
const handleMouseLeave = (e: MouseEvent) => {
|
||||||
|
if (e.clientY <= 0 && !dismissed) {
|
||||||
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
const scrollPercent = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;
|
||||||
|
if (scrollPercent > 50 && !dismissed) {
|
||||||
|
setIsOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('mouseleave', handleMouseLeave);
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('mouseleave', handleMouseLeave);
|
||||||
|
window.removeEventListener('scroll', handleScroll);
|
||||||
|
};
|
||||||
|
}, [dismissed]);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsOpen(false);
|
||||||
|
setDismissed(true);
|
||||||
|
localStorage.setItem('seo_lead_magnet_dismissed', 'true');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence>
|
||||||
|
{isOpen && (
|
||||||
|
<div className="fixed inset-0 z-[200] flex items-center justify-center p-6 bg-slate-900/60 backdrop-blur-sm">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
className="bg-[#08080a] border border-white/10 shadow-2xl max-w-2xl w-full overflow-hidden flex flex-col relative"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={handleClose}
|
||||||
|
className="absolute top-4 right-4 h-8 w-8 bg-white/5 hover:bg-brand hover:text-black text-slate-500 flex items-center justify-center z-20 transition-all"
|
||||||
|
aria-label="Fechar"
|
||||||
|
>
|
||||||
|
<X size={16} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="p-8 md:p-12">
|
||||||
|
<div className="flex items-center gap-3 mb-6">
|
||||||
|
<div className="h-10 w-10 bg-brand/10 flex items-center justify-center text-brand skew-x-[-6deg]">
|
||||||
|
<ShieldCheck size={20} className="skew-x-[6deg]" />
|
||||||
|
</div>
|
||||||
|
<span className="text-[9px] font-mono font-black text-brand uppercase tracking-[0.4em]">Resource_Found</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 className="text-3xl sm:text-4xl font-display font-black text-white italic tracking-tight leading-none mb-6">
|
||||||
|
Dominate the<br /><span className="text-brand">Neural_Index 2026.</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p className="text-slate-500 font-mono text-[11px] leading-relaxed uppercase tracking-widest mb-10 max-w-lg">
|
||||||
|
Download our exclusive blueprint for technical systems and semantic authority protocols.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form className="space-y-4" onSubmit={(e) => { e.preventDefault(); handleClose(); }}>
|
||||||
|
<div className="relative">
|
||||||
|
<Mail className="absolute left-0 top-1/2 -translate-y-1/2 text-brand" size={16} />
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
placeholder="ENTER_ENDPOINT_ID..."
|
||||||
|
required
|
||||||
|
className="w-full h-12 pl-8 border-b border-white/10 bg-transparent text-white placeholder:text-slate-800 focus:outline-none focus:border-brand transition-all font-mono text-xs uppercase tracking-widest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn-primary w-full"
|
||||||
|
>
|
||||||
|
Sync_Now <Download size={16} />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="mt-8 pt-8 border-t border-white/5 flex flex-wrap gap-x-8 gap-y-4">
|
||||||
|
<div className="flex items-center gap-2 text-[8px] font-black text-slate-700 uppercase tracking-widest">
|
||||||
|
<ArrowRight size={10} className="text-brand" /> Interactive Protocol
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-[8px] font-black text-slate-700 uppercase tracking-widest">
|
||||||
|
<ArrowRight size={10} className="text-brand" /> Audit Framework
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
62
Template-04/src/components/SEO.tsx
Normal file
62
Template-04/src/components/SEO.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { Helmet } from 'react-helmet-async';
|
||||||
|
|
||||||
|
interface SEOProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
image?: string;
|
||||||
|
url?: string;
|
||||||
|
type?: string;
|
||||||
|
keywords?: string[];
|
||||||
|
articleData?: {
|
||||||
|
publishedTime?: string;
|
||||||
|
author?: string;
|
||||||
|
authorAvatar?: string;
|
||||||
|
tags?: string[];
|
||||||
|
section?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SEO({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
image,
|
||||||
|
url = 'https://apexseo.agency',
|
||||||
|
type = 'website',
|
||||||
|
keywords,
|
||||||
|
articleData
|
||||||
|
}: SEOProps) {
|
||||||
|
const siteTitle = `${title} | Apex SEO Agency`;
|
||||||
|
const metaImage = image || 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200&h=630&fit=crop';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Helmet>
|
||||||
|
{/* Base */}
|
||||||
|
<title>{siteTitle}</title>
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<link rel="canonical" href={url} />
|
||||||
|
|
||||||
|
{/* Open Graph / Facebook */}
|
||||||
|
<meta property="og:type" content={type} />
|
||||||
|
<meta property="og:url" content={url} />
|
||||||
|
<meta property="og:title" content={siteTitle} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:image" content={metaImage} />
|
||||||
|
{articleData?.publishedTime && <meta property="article:published_time" content={articleData.publishedTime} />}
|
||||||
|
{articleData?.author && <meta property="article:author" content={articleData.author} />}
|
||||||
|
{articleData?.section && <meta property="article:section" content={articleData.section} />}
|
||||||
|
{articleData?.tags && articleData.tags.map(tag => <meta key={tag} property="article:tag" content={tag} />)}
|
||||||
|
|
||||||
|
{/* Twitter */}
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:url" content={url} />
|
||||||
|
<meta name="twitter:title" content={siteTitle} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content={metaImage} />
|
||||||
|
|
||||||
|
{keywords && <meta name="keywords" content={keywords.join(', ')} />}
|
||||||
|
|
||||||
|
{/* Theme */}
|
||||||
|
<meta name="theme-color" content="#030303" />
|
||||||
|
</Helmet>
|
||||||
|
);
|
||||||
|
}
|
||||||
133
Template-04/src/components/SearchOverlay.tsx
Normal file
133
Template-04/src/components/SearchOverlay.tsx
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
import { motion, AnimatePresence } from 'motion/react';
|
||||||
|
import { X, Search as SearchIcon, ArrowRight, TrendingUp } from 'lucide-react';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import { Article } from '../types';
|
||||||
|
|
||||||
|
interface SearchOverlayProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SearchOverlay({ isOpen, onClose }: SearchOverlayProps) {
|
||||||
|
const [query, setQuery] = useState('');
|
||||||
|
const [results, setResults] = useState<Article[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (query.trim().length > 1) {
|
||||||
|
const filtered = articles.filter(a =>
|
||||||
|
a.title.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
a.category.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
a.excerpt.toLowerCase().includes(query.toLowerCase())
|
||||||
|
).slice(0, 6);
|
||||||
|
setResults(filtered);
|
||||||
|
} else {
|
||||||
|
setResults([]);
|
||||||
|
}
|
||||||
|
}, [query]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleEsc = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') onClose();
|
||||||
|
};
|
||||||
|
window.addEventListener('keydown', handleEsc);
|
||||||
|
return () => window.removeEventListener('keydown', handleEsc);
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
} else {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'unset';
|
||||||
|
};
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 z-[9999] bg-black/95 backdrop-blur-2xl flex flex-col pt-32"
|
||||||
|
>
|
||||||
|
<div className="section-container max-w-4xl relative z-10">
|
||||||
|
<div className="flex items-center justify-between mb-8">
|
||||||
|
<h2 className="text-xl font-bold text-white flex items-center gap-3">
|
||||||
|
<div className="h-8 w-8 bg-brand rounded-lg flex items-center justify-center text-white">
|
||||||
|
<TrendingUp size={16} />
|
||||||
|
</div>
|
||||||
|
Buscar Insights
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="h-10 w-10 flex items-center justify-center text-slate-500 hover:text-white rounded-full bg-white/5 transition-all"
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative mb-16 group">
|
||||||
|
<SearchIcon size={24} className="absolute left-6 top-1/2 -translate-y-1/2 text-slate-600 group-focus-within:text-brand transition-colors" />
|
||||||
|
<input
|
||||||
|
autoFocus
|
||||||
|
type="text"
|
||||||
|
placeholder="Buscar cases, protocolos, insights..."
|
||||||
|
className="w-full h-18 bg-white/[0.03] border border-white/10 focus:border-brand rounded-2xl px-16 text-white font-medium outline-none transition-all placeholder:text-slate-700"
|
||||||
|
value={query}
|
||||||
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 gap-3 max-h-[60vh] overflow-y-auto pr-4 custom-scrollbar">
|
||||||
|
{results.length > 0 ? (
|
||||||
|
results.map((article) => (
|
||||||
|
<Link
|
||||||
|
key={article.id}
|
||||||
|
to={`/artigo/${article.slug}`}
|
||||||
|
onClick={onClose}
|
||||||
|
className="block p-6 glass-card glass-card-hover group"
|
||||||
|
>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<span className="text-[10px] font-bold text-brand uppercase tracking-widest">{article.category}</span>
|
||||||
|
<div className="h-1 w-1 rounded-full bg-white/10" />
|
||||||
|
<span className="text-[10px] text-slate-600 font-bold uppercase">{article.readTime}</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold text-white group-hover:text-brand transition-colors leading-tight">{article.title}</h3>
|
||||||
|
</div>
|
||||||
|
<div className="h-10 w-10 rounded-full border border-white/5 flex items-center justify-center text-slate-600 group-hover:text-brand group-hover:border-brand/40 transition-all">
|
||||||
|
<ArrowRight size={18} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))
|
||||||
|
) : query.trim() ? (
|
||||||
|
<div className="text-center py-20 p-8 glass-card">
|
||||||
|
<p className="text-slate-500 font-medium">Nenhum resultado encontrado para "{query}"</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||||
|
{['SEO', 'Técnico', 'Conteúdo', 'Crescimento'].map((tag) => (
|
||||||
|
<button
|
||||||
|
key={tag}
|
||||||
|
onClick={() => setQuery(tag)}
|
||||||
|
className="p-4 glass-card text-slate-500 font-bold text-xs uppercase tracking-widest hover:border-brand hover:text-brand transition-all text-center"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
107
Template-04/src/components/TableOfContents.tsx
Normal file
107
Template-04/src/components/TableOfContents.tsx
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { cn } from '../lib/utils';
|
||||||
|
import { List } from 'lucide-react';
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
|
||||||
|
interface TOCItem {
|
||||||
|
id: string;
|
||||||
|
text: string;
|
||||||
|
level: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function TableOfContents({ content }: { content: string }) {
|
||||||
|
const [activeId, setActiveId] = useState<string>('');
|
||||||
|
const [headings, setHeadings] = useState<TOCItem[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Extract headings (H2 and H3 only for clarity)
|
||||||
|
const headingLines = content.split('\n').filter(line =>
|
||||||
|
line.startsWith('## ') || line.startsWith('### ')
|
||||||
|
);
|
||||||
|
|
||||||
|
const extractedHeadings = headingLines.map(line => {
|
||||||
|
const level = line.startsWith('###') ? 3 : 2;
|
||||||
|
const text = line.replace(/^#+\s+/, '').trim();
|
||||||
|
const id = text.toLowerCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\w-]/g, '');
|
||||||
|
return { id, text, level };
|
||||||
|
});
|
||||||
|
|
||||||
|
setHeadings(extractedHeadings);
|
||||||
|
|
||||||
|
// Initial check (delay slightly for DOM to be ready)
|
||||||
|
setTimeout(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setActiveId(entry.target.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ rootMargin: '-10% 0% -80% 0%' }
|
||||||
|
);
|
||||||
|
|
||||||
|
extractedHeadings.forEach((h) => {
|
||||||
|
const el = document.getElementById(h.id);
|
||||||
|
if (el) observer.observe(el);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, 500);
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
if (headings.length === 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-fit">
|
||||||
|
<div className="mb-10 flex items-center justify-between">
|
||||||
|
<h5 className="text-[10px] font-black text-slate-300 uppercase tracking-[0.2em] flex items-center gap-2">
|
||||||
|
<div className="h-1 w-4 bg-brand" />
|
||||||
|
Roteiro
|
||||||
|
</h5>
|
||||||
|
<span className="text-[9px] font-bold text-slate-200">
|
||||||
|
{headings.length} SEÇÕES
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<nav className="flex flex-col">
|
||||||
|
{headings.map((h, i) => (
|
||||||
|
<a
|
||||||
|
key={h.id}
|
||||||
|
href={`#${h.id}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
document.getElementById(h.id)?.scrollIntoView({ behavior: 'smooth' });
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"group py-3 border-b border-slate-50 flex items-baseline gap-4 transition-all relative",
|
||||||
|
activeId === h.id
|
||||||
|
? "text-brand"
|
||||||
|
: "text-slate-400 hover:text-slate-900",
|
||||||
|
h.level === 3 && "pl-8"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className={cn(
|
||||||
|
"text-[9px] font-mono transition-colors",
|
||||||
|
activeId === h.id ? "text-brand" : "text-slate-200 group-hover:text-slate-400"
|
||||||
|
)}>
|
||||||
|
{i + 1 < 10 ? `0${i + 1}` : i + 1}
|
||||||
|
</span>
|
||||||
|
<span className="text-[11px] font-bold leading-tight">
|
||||||
|
{h.text}
|
||||||
|
</span>
|
||||||
|
{activeId === h.id && (
|
||||||
|
<motion.div
|
||||||
|
layoutId="active-toc"
|
||||||
|
className="absolute left-0 top-0 bottom-0 w-[2px] bg-brand"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
126
Template-04/src/constants.ts
Normal file
126
Template-04/src/constants.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { Category } from './types';
|
||||||
|
|
||||||
|
export const CATEGORY_MAP: Record<Category, { name: string; slug: string }> = {
|
||||||
|
'Estratégia': { name: 'Estratégia', slug: 'estrategia' },
|
||||||
|
'Técnico': { name: 'Técnico', slug: 'tecnico' },
|
||||||
|
'Autoridade': { name: 'Autoridade', slug: 'autoridade' },
|
||||||
|
'Negócios': { name: 'Negócios', slug: 'negocios' },
|
||||||
|
'SEO para Iniciantes': { name: 'SEO para Iniciantes', slug: 'iniciantes' },
|
||||||
|
'SEO Técnico': { name: 'SEO Técnico', slug: 'seo-tecnico' },
|
||||||
|
'SEO On Page': { name: 'SEO On Page', slug: 'on-page' },
|
||||||
|
'SEO Off Page': { name: 'SEO Off Page', slug: 'off-page' },
|
||||||
|
'Link Building': { name: 'Link Building', slug: 'link-building' },
|
||||||
|
'Ferramentas de SEO': { name: 'Ferramentas de SEO', slug: 'ferramentas' },
|
||||||
|
'SEO para E-commerce': { name: 'SEO para E-commerce', slug: 'e-commerce' },
|
||||||
|
'SEO para Blogs': { name: 'SEO para Blogs', slug: 'blogs' },
|
||||||
|
'Atualizações do Google': { name: 'Atualizações do Google', slug: 'google-updates' }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCategoryBySlug = (slug: string) => {
|
||||||
|
return Object.entries(CATEGORY_MAP).find(([_, value]) => value.slug === slug);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const translations: Record<string, any> = {
|
||||||
|
'pt-br': {
|
||||||
|
newsletter: 'Newsletter',
|
||||||
|
subscribe: 'Assinar Agora',
|
||||||
|
readMore: 'Ler artigo',
|
||||||
|
latest: 'Últimas',
|
||||||
|
allArticles: 'Todos os Artigos',
|
||||||
|
sections: 'Seções',
|
||||||
|
publishing: 'Publicação',
|
||||||
|
contact: 'Contato',
|
||||||
|
about: 'Sobre',
|
||||||
|
privacy: 'Privacidade',
|
||||||
|
terms: 'Termos',
|
||||||
|
featured: 'Destaque',
|
||||||
|
recent: 'Recentes',
|
||||||
|
explore: 'Explorar Insights',
|
||||||
|
search: 'Buscar',
|
||||||
|
viewAll: 'Ver Todos',
|
||||||
|
manifesto: 'Manifesto',
|
||||||
|
methodology: 'Metodologia',
|
||||||
|
heroTitle: 'A Nova Ordem da Busca Semântica.',
|
||||||
|
heroSubtitle: 'EDITORIAL DE SEO 2026',
|
||||||
|
heroDescription: 'O recurso definitivo para profissionais que buscam excelência técnica e autoridade duradoura no ecossistema de busca.',
|
||||||
|
readManifesto: 'Ler Manifesto',
|
||||||
|
publishedIn: 'Publicado em',
|
||||||
|
newsletterTitle: 'Newsletter Semanal',
|
||||||
|
newsletterDescription: 'Inscreva-se para receber as últimas novidades.',
|
||||||
|
emailPlaceholder: 'seu@email.com',
|
||||||
|
subscribeButton: 'Inscrever',
|
||||||
|
trendsTitle: 'TENDÊNCIAS',
|
||||||
|
footerNewsletterTitle: 'Entre no Círculo de Autoridade',
|
||||||
|
footerNewsletterSubtitle: 'Assine para receber análises profundas sobre mudanças algorítmicas, IA e estratégias de busca que o resto do mercado ainda não viu.',
|
||||||
|
footerNewsletterInput: 'seu@email.com',
|
||||||
|
footerNewsletterDisclaimer: 'Nós respeitamos sua caixa de entrada. Zero spam, apenas inteligência de busca.'
|
||||||
|
},
|
||||||
|
'en': {
|
||||||
|
newsletter: 'Newsletter',
|
||||||
|
subscribe: 'Subscribe Now',
|
||||||
|
readMore: 'Read more',
|
||||||
|
latest: 'Latest',
|
||||||
|
allArticles: 'All Articles',
|
||||||
|
sections: 'Sections',
|
||||||
|
publishing: 'Publishing',
|
||||||
|
contact: 'Contact',
|
||||||
|
about: 'About',
|
||||||
|
privacy: 'Privacy',
|
||||||
|
terms: 'Terms',
|
||||||
|
featured: 'Featured',
|
||||||
|
recent: 'Recent',
|
||||||
|
explore: 'Explore Insights',
|
||||||
|
search: 'Search',
|
||||||
|
viewAll: 'View All',
|
||||||
|
manifesto: 'Manifesto',
|
||||||
|
methodology: 'Methodology',
|
||||||
|
heroTitle: 'The New Order of Semantic Search.',
|
||||||
|
heroSubtitle: 'SEO EDITORIAL 2026',
|
||||||
|
heroDescription: 'The definitive resource for professionals seeking technical excellence and lasting authority in the search ecosystem.',
|
||||||
|
readManifesto: 'Read Manifesto',
|
||||||
|
publishedIn: 'Published in',
|
||||||
|
newsletterTitle: 'Weekly Newsletter',
|
||||||
|
newsletterDescription: 'Subscribe to get the latest news.',
|
||||||
|
emailPlaceholder: 'your@email.com',
|
||||||
|
subscribeButton: 'Subscribe',
|
||||||
|
trendsTitle: 'TRENDS',
|
||||||
|
footerNewsletterTitle: 'Join the Circle of Authority',
|
||||||
|
footerNewsletterSubtitle: 'Subscribe to receive deep insights into algorithmic changes, AI, and search strategies that the rest of the market hasn\'t seen yet.',
|
||||||
|
footerNewsletterInput: 'your@email.com',
|
||||||
|
footerNewsletterDisclaimer: 'We respect your inbox. Zero spam, only search intelligence.'
|
||||||
|
},
|
||||||
|
'es': {
|
||||||
|
newsletter: 'Boletín',
|
||||||
|
subscribe: 'Suscríbete Ahora',
|
||||||
|
readMore: 'Leer más',
|
||||||
|
latest: 'Últimos',
|
||||||
|
allArticles: 'Todos los Artículos',
|
||||||
|
sections: 'Secciones',
|
||||||
|
publishing: 'Publicación',
|
||||||
|
contact: 'Contacto',
|
||||||
|
about: 'Sobre',
|
||||||
|
privacy: 'Privacidad',
|
||||||
|
terms: 'Términos',
|
||||||
|
featured: 'Destacado',
|
||||||
|
recent: 'Recientes',
|
||||||
|
explore: 'Explorar Insights',
|
||||||
|
search: 'Buscar',
|
||||||
|
viewAll: 'Ver Todo',
|
||||||
|
manifesto: 'Manifiesto',
|
||||||
|
methodology: 'Metodología',
|
||||||
|
heroTitle: 'El Nuevo Orden de la Búsqueda Semántica.',
|
||||||
|
heroSubtitle: 'EDITORIAL SEO 2026',
|
||||||
|
heroDescription: 'El recurso definitivo para profesionales que buscan excelencia técnica y autoridad duradera en el ecosistema de búsqueda.',
|
||||||
|
readManifesto: 'Leer Manifiesto',
|
||||||
|
publishedIn: 'Publicado en',
|
||||||
|
newsletterTitle: 'Boletín Semanal',
|
||||||
|
newsletterDescription: 'Suscríbete para recibir las últimas noticias.',
|
||||||
|
emailPlaceholder: 'tu@email.com',
|
||||||
|
subscribeButton: 'Suscríbete',
|
||||||
|
trendsTitle: 'TENDENCIAS',
|
||||||
|
footerNewsletterTitle: 'Únete al Círculo de Autoridad',
|
||||||
|
footerNewsletterSubtitle: 'Suscríbete para recibir análisis profundos sobre cambios algorítmicos, IA y estrategias de búsqueda que el resto del mercado aún no ha visto.',
|
||||||
|
footerNewsletterInput: 'tu@email.com',
|
||||||
|
footerNewsletterDisclaimer: 'Respetamos tu bandeja de entrada. Cero spam, solo inteligencia de búsqueda.'
|
||||||
|
}
|
||||||
|
};
|
||||||
52
Template-04/src/contexts/BookmarksContext.tsx
Normal file
52
Template-04/src/contexts/BookmarksContext.tsx
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
interface BookmarksContextType {
|
||||||
|
bookmarks: string[];
|
||||||
|
toggleBookmark: (id: string) => void;
|
||||||
|
isBookmarked: (id: string) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BookmarksContext = createContext<BookmarksContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export function BookmarksProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const [bookmarks, setBookmarks] = useState<string[]>([]);
|
||||||
|
|
||||||
|
// Load from localStorage on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const saved = localStorage.getItem('seo_bookmarks');
|
||||||
|
if (saved) {
|
||||||
|
try {
|
||||||
|
setBookmarks(JSON.parse(saved));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse bookmarks', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Sync with localStorage
|
||||||
|
const toggleBookmark = (id: string) => {
|
||||||
|
setBookmarks(prev => {
|
||||||
|
const next = prev.includes(id)
|
||||||
|
? prev.filter(item => item !== id)
|
||||||
|
: [...prev, id];
|
||||||
|
localStorage.setItem('seo_bookmarks', JSON.stringify(next));
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBookmarked = (id: string) => bookmarks.includes(id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BookmarksContext.Provider value={{ bookmarks, toggleBookmark, isBookmarked }}>
|
||||||
|
{children}
|
||||||
|
</BookmarksContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBookmarks() {
|
||||||
|
const context = useContext(BookmarksContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useBookmarks must be used within a BookmarksProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
64
Template-04/src/contexts/LanguageContext.tsx
Normal file
64
Template-04/src/contexts/LanguageContext.tsx
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
export type Language = 'pt-br' | 'en' | 'es';
|
||||||
|
|
||||||
|
interface LanguageContextType {
|
||||||
|
lang: Language;
|
||||||
|
setLang: (lang: Language) => void;
|
||||||
|
translate: (text: any, context?: string, isObject?: boolean) => Promise<any>;
|
||||||
|
isTranslating: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const [lang, setLang] = useState<Language>(() => {
|
||||||
|
const saved = localStorage.getItem('app-lang');
|
||||||
|
return (saved as Language) || 'pt-br';
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('app-lang', lang);
|
||||||
|
document.documentElement.lang = lang;
|
||||||
|
|
||||||
|
// Trigger Google Translate Widget
|
||||||
|
const triggerGoogleTranslate = () => {
|
||||||
|
const select = document.querySelector('.goog-te-combo') as HTMLSelectElement;
|
||||||
|
if (select) {
|
||||||
|
const googleLangCode = lang === 'pt-br' ? 'pt' : lang;
|
||||||
|
if (select.value !== googleLangCode) {
|
||||||
|
select.value = googleLangCode;
|
||||||
|
select.dispatchEvent(new Event('change'));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try multiple times as the widget loads asynchronously
|
||||||
|
let attempts = 0;
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
attempts++;
|
||||||
|
const success = triggerGoogleTranslate();
|
||||||
|
if (success || attempts > 10) {
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, [lang]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LanguageContext.Provider value={{ lang, setLang, translate: async (t) => t, isTranslating: false }}>
|
||||||
|
{children}
|
||||||
|
</LanguageContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLanguage = () => {
|
||||||
|
const context = useContext(LanguageContext);
|
||||||
|
if (context === undefined) {
|
||||||
|
throw new Error('useLanguage must be used within a LanguageProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
654
Template-04/src/data/articles.ts
Normal file
654
Template-04/src/data/articles.ts
Normal file
|
|
@ -0,0 +1,654 @@
|
||||||
|
import { Article } from '../types';
|
||||||
|
|
||||||
|
export const articles: Article[] = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
slug: 'o-impacto-da-ia-generativa-na-busca-organica-2026',
|
||||||
|
title: 'O Impacto da IA Generativa na Busca Orgânica em 2026',
|
||||||
|
excerpt: 'Como as Search Generative Experiences (SGE) estão moldando o comportamento do usuário e o que isso significa para o CTR.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-05-01',
|
||||||
|
image: 'https://images.unsplash.com/photo-1673191898498-9bac443b2407?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '12 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas'
|
||||||
|
},
|
||||||
|
metaTitle: 'IA Generativa e SEO em 2026 | Análise Técnica',
|
||||||
|
metaDescription: "SEO em 2026: Entenda como a IA Generativa e o SGE transformaram o tráfego orgânico. Conheça as táticas de 'Conteúdo de Valor Incrementado' para vencer o Zero-Click.",
|
||||||
|
tags: ['IA', 'SGE', 'Algoritmo', 'Google'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# O Impacto da IA Generativa na Busca Orgânica em 2026
|
||||||
|
|
||||||
|
A busca não é mais o que costumava ser. Em 2026, a integração total da IA nas páginas de resultados (SERPs) criou um novo paradigma: a busca não apenas por links, mas por respostas diretas e inteligíveis.
|
||||||
|
|
||||||
|
## O Desafio do Zero-Click
|
||||||
|
Com a IA resolvendo dúvidas complexas diretamente no topo da busca, o volume de cliques para sites informativos superficiais despencou. A estratégia vencedora agora foca em **Conteúdo de Valor Incrementado** — informações que a IA ainda não consegue sintetizar sem falhas.
|
||||||
|
|
||||||
|
### O que são Insights de Propriedade?
|
||||||
|
Insights de propriedade são dados, opiniões de especialistas e experiências reais que não existem em Large Language Models (LLMs). É a sua "fórmula secreta" que obriga o usuário a clicar para entender a profundidade do assunto.
|
||||||
|
|
||||||
|
## Estratégias de Sobrevivência
|
||||||
|
1. **Foco na Cauda Longa Hiper-Específica:** Consultas onde a opinião humana é necessária.
|
||||||
|
2. **Autoridade de Tópico (Topic Clusters):** Ser o dono do assunto, não apenas da palavra-chave.
|
||||||
|
3. **Optimizing for SGE Sources:** Estruturar dados para que seu site seja a fonte citada pela IA.
|
||||||
|
|
||||||
|
## Conclusão
|
||||||
|
A IA não matou o SEO; ela elevou o nível do jogo. Quem produz conteúdo medíocre está fora. Quem produz autoridade técnica está mais forte do que nunca.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
slug: 'eeat-a-biblia-da-autoridade-digital',
|
||||||
|
title: 'E-E-A-T: A Bíblia da Autoridade Digital Moderna',
|
||||||
|
excerpt: 'Análise profunda sobre Experiência, Especialidade, Autoridade e Confiança no novo algoritmo.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-04-28',
|
||||||
|
image: 'https://images.unsplash.com/photo-1552664730-d307ca884978?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '15 min',
|
||||||
|
author: {
|
||||||
|
name: 'Beatriz Costa',
|
||||||
|
role: 'Head of Content',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=beatriz'
|
||||||
|
},
|
||||||
|
metaTitle: 'Guia Definitivo E-E-A-T para SEO em 2026',
|
||||||
|
metaDescription: "Domine o E-E-A-T em 2026. Saiba por que a experiência humana é o diferencial contra a IA e como construir autoridade real para dominar os rankings do Google.",
|
||||||
|
tags: ['EEAT', 'Autoridade', 'Confiança', 'Especialidade'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# E-E-A-T: A Bíblia da Autoridade Digital Moderna
|
||||||
|
|
||||||
|
Se você quer rankear em 2026, você precisa entender o E-E-A-T. Não é um fator de ranking direto, mas uma diretriz que permeia todo o algoritmo do Google.
|
||||||
|
|
||||||
|
## O "E" de Experiência
|
||||||
|
Adicionado há alguns anos, o fator experiência tornou-se crucial. O Google quer saber: você realmente usou este produto? Você realmente viveu essa situação?
|
||||||
|
|
||||||
|
<blockquote>
|
||||||
|
"A experiência humana é o único dado que a IA não consegue alucinar com perfeição em 2026."
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
## Como demonstrar experiência?
|
||||||
|
- Use fotos originais, não apenas de bancos de imagem.
|
||||||
|
- Narre estudos de caso reais.
|
||||||
|
- Evite generalismos. Use "Eu descobri que..." em vez de "É geralmente aceito que...".
|
||||||
|
|
||||||
|
## Especialidade e Autoridade
|
||||||
|
Estes pilares são construídos através da consistência. Não adianta falar de SEO um dia e de culinária no outro se você quer ser visto como um especialista em tecnologia.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
slug: 'auditoria-tecnica-seo-infraestrutura-performance',
|
||||||
|
title: 'Auditoria Técnica: Garantindo uma Infraestrutura de Elite',
|
||||||
|
excerpt: 'O guia definitivo para Core Web Vitals, Rendering e Crawl Budget em sites de larga escala.',
|
||||||
|
category: 'Técnico',
|
||||||
|
publishedAt: '2026-04-25',
|
||||||
|
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '18 min',
|
||||||
|
author: {
|
||||||
|
name: 'Marcos Tech',
|
||||||
|
role: 'SEO Engineer',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=marcos'
|
||||||
|
},
|
||||||
|
metaTitle: 'Auditoria Técnica de SEO 2026 | Guia para Desenvolvedores',
|
||||||
|
metaDescription: "Guia definitivo de Auditoria Técnica SEO 2026. Otimize INP, LCP e Rendering para garantir uma infraestrutura de elite e performance máxima em sites de larga escala.",
|
||||||
|
tags: ['Technical SEO', 'Performance', 'Web Vitals', 'Infrastructure'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Auditoria Técnica: Garantindo uma Infraestrutura de Elite
|
||||||
|
|
||||||
|
Em 2026, a performance técnica não é um diferencial — é o requisito mínimo para entrar na arena.
|
||||||
|
|
||||||
|
## O novo Core Web Vital: Interaction to Next Paint (INP)
|
||||||
|
O INP substituiu oficialmente o FID como a métrica de interatividade. Ele mede quão rápido seu site responde a qualquer interação do usuário.
|
||||||
|
|
||||||
|
### Otimizando para INP
|
||||||
|
- Reduza o tempo de execução do JavaScript principal.
|
||||||
|
- Use Web Workers para tarefas pesadas.
|
||||||
|
- Evite layouts complexos que causem reflow excessivo.
|
||||||
|
|
||||||
|
## Indexação e Crawl Budget
|
||||||
|
Para sites com milhares de páginas, cada segundo do bot do Google no seu servidor conta.
|
||||||
|
- Use Server-Side Rendering (SSR) de forma inteligente.
|
||||||
|
- Mantenha um Sitemap dinâmico e limpo.
|
||||||
|
- Monitore erros de 404 e cadeias de redirect.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
slug: 'link-building-atraves-de-dados-proprietarios',
|
||||||
|
title: 'Link Building através de Dados Proprietários',
|
||||||
|
excerpt: 'Esqueça o outreach em massa. Aprenda a criar pesquisas que atraem links de forma orgânica.',
|
||||||
|
category: 'Autoridade',
|
||||||
|
publishedAt: '2026-04-20',
|
||||||
|
image: 'https://images.unsplash.com/photo-1557838923-2985c318be48?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '10 min',
|
||||||
|
author: {
|
||||||
|
name: 'João Links',
|
||||||
|
role: 'PR Specialist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=joao'
|
||||||
|
},
|
||||||
|
metaTitle: 'Estratégias de Link Building com Dados | SEO Authority',
|
||||||
|
metaDescription: "Transforme dados em imãs de links. Aprenda a criar pesquisas proprietárias e reports de mercado que geram backlinks editoriais espontâneos de alta autoridade.",
|
||||||
|
tags: ['Link Building', 'Backlinks', 'Content Marketing', 'PR'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Link Building através de Dados Proprietários
|
||||||
|
|
||||||
|
O link building clássico de "troca de favores" está cada vez menos eficiente. O Google valoriza links editoriais espontâneos. A melhor forma de conseguir isso? Ser a fonte da notícia.
|
||||||
|
|
||||||
|
## Criando Imãs de Links
|
||||||
|
Quando você produz um infográfico com dados originais do seu setor, outros blogs e portais de notícias vão citar você como referência.
|
||||||
|
|
||||||
|
### Tipos de Conteúdo que Atraem Links:
|
||||||
|
1. **Pesquisas de Mercado:** Tendências para o próximo ano.
|
||||||
|
2. **Calculadoras e Ferramentas:** Soluções para problemas práticos.
|
||||||
|
3. **Análises de Impacto:** Como uma mudança econômica afetou seu nicho.
|
||||||
|
|
||||||
|
## A Importância do Texto Âncora
|
||||||
|
Links com âncoras variadas e naturais para páginas de autoridade (não apenas para a home) são o sinal mais forte de um perfil de links saudável.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
slug: 'arquitetura-de-informacao-pillar-pages-e-clusters',
|
||||||
|
title: 'Arquitetura de Informação: Pillar Pages e Topic Clusters',
|
||||||
|
excerpt: 'Como estruturar seu blog para que o Google entenda sua autoridade em cada pilar de conteúdo.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-04-15',
|
||||||
|
image: 'https://images.unsplash.com/photo-1542744094-3a31f272c490?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '14 min',
|
||||||
|
author: {
|
||||||
|
name: 'Beatriz Costa',
|
||||||
|
role: 'Head of Content',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=beatriz'
|
||||||
|
},
|
||||||
|
metaTitle: 'Arquitetura de Conteúdo: Pillar Pages e Clusters em 2026',
|
||||||
|
metaDescription: "Topic Clusters e Pillar Pages: Descubra como estruturar seu site para dominar a autoridade semântica em 2026. O guia completo para organizar conteúdo estrategicamente.",
|
||||||
|
tags: ['Architecture', 'Content Strategy', 'Topic Clusters'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Arquitetura de Informação: Pillar Pages e Topic Clusters
|
||||||
|
|
||||||
|
Organizar o conteúdo do seu blog é tão importante quanto escrevê-lo. Uma estrutura de **Topic Clusters** ajuda o Google a mapear sua expertise.
|
||||||
|
|
||||||
|
## O Que é uma Pillar Page?
|
||||||
|
É um guia abrangente sobre um tema central. Por exemplo, "SEO para Iniciantes".
|
||||||
|
|
||||||
|
## O Que são Clusters?
|
||||||
|
São artigos específicos que aprofundam tópicos mencionados na Pillar Page. Por exemplo, "O que é Robots.txt?".
|
||||||
|
|
||||||
|
### O Link Mágico
|
||||||
|
Todos os artigos satélites (clusters) devem apontar de volta para a Pillar Page. Isso cria uma teia de autoridade.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
slug: 'seo-para-e-commerce-otimizando-paginas-de-categoria',
|
||||||
|
title: 'SEO para E-commerce: O Segredo das Páginas de Categoria',
|
||||||
|
excerpt: 'Por que as páginas de categoria trazem mais tráfego que as páginas de produto e como otimizá-las.',
|
||||||
|
category: 'Negócios',
|
||||||
|
publishedAt: '2026-04-10',
|
||||||
|
image: 'https://images.unsplash.com/photo-1556742049-045a728bbd66?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '11 min',
|
||||||
|
author: {
|
||||||
|
name: 'Ricardo Vendas',
|
||||||
|
role: 'E-commerce Expert',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=ricardo'
|
||||||
|
},
|
||||||
|
metaTitle: 'SEO para E-commerce: Guias de Categoria em 2026',
|
||||||
|
metaDescription: "SEO para E-commerce: Por que as categorias são sua maior fonte de tráfego? Aprenda táticas avançadas para otimizar páginas de categoria e converter mais no Google.",
|
||||||
|
tags: ['E-commerce', 'Category Pages', 'Product SEO'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# SEO para E-commerce: O Segredo das Páginas de Categoria
|
||||||
|
|
||||||
|
Muitos donos de loja focam nas páginas de produto. Mas o volume real de busca está nas **Categorias**.
|
||||||
|
|
||||||
|
## Por que focar em Categorias?
|
||||||
|
Um usuário que busca "tênis de corrida" está em uma fase de consideração muito mais valiosa do que quem busca o modelo específico "Nike Pegasus 41 Azul".
|
||||||
|
|
||||||
|
### Otimização On-Page para Categorias:
|
||||||
|
- **Texto de Apoio:** Não enterre o texto no final da página. Use introduções úteis.
|
||||||
|
- **Interlinkagem:** Linke para seus produtos mais populares diretamente da categoria.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
slug: 'schema-markup-avancado-e-dados-estruturados',
|
||||||
|
title: 'Schema Markup Avançado: Indo além do básico',
|
||||||
|
excerpt: 'Utilizando JSON-LD para fornecer contexto profundo aos mecanismos de busca.',
|
||||||
|
category: 'Técnico',
|
||||||
|
publishedAt: '2026-04-05',
|
||||||
|
image: 'https://images.unsplash.com/photo-1555066931-4365d14bab8c?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '13 min',
|
||||||
|
author: {
|
||||||
|
name: 'Marcos Tech',
|
||||||
|
role: 'SEO Engineer',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=marcos'
|
||||||
|
},
|
||||||
|
metaTitle: 'Schema Markup Avançado JSON-LD | Guia Técnico 2026',
|
||||||
|
metaDescription: "Schema Markup 2026: Vá além do básico com JSON-LD. Aprenda a fornecer contexto profundo à IA do Google e conquistar Rich Snippets poderosos para seu site.",
|
||||||
|
tags: ['Schema', 'Structured Data', 'JSON-LD', 'Rich Snippets'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Schema Markup Avançado: Indo além do básico
|
||||||
|
|
||||||
|
Dados estruturados são o vocabulário que o Google usa para entender o seu conteúdo. Em 2026, ignorar o Schema é como falar uma língua diferente do buscador.
|
||||||
|
|
||||||
|
## O Poder do JSON-LD
|
||||||
|
Prefira sempre a implementação via JSON-LD no cabeçalho ou rodapé do HTML.
|
||||||
|
|
||||||
|
### Esquemas Indispensáveis:
|
||||||
|
- **Article/BlogPosting:** Para conteúdo editorial.
|
||||||
|
- **FAQPage:** Para ocupar mais espaço na SERP.
|
||||||
|
- **Person/Organization:** Para reforçar o E-E-A-T do autor.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
slug: 'seo-off-page-alem-dos-backlinks',
|
||||||
|
title: 'SEO Off-Page: A Autoridade além dos Backlinks',
|
||||||
|
excerpt: 'Menções de marca, sinais sociais e a construção de reputação no ambiente digital.',
|
||||||
|
category: 'Autoridade',
|
||||||
|
publishedAt: '2026-04-01',
|
||||||
|
image: 'https://images.unsplash.com/photo-1557838923-2985c318be48?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '12 min',
|
||||||
|
author: {
|
||||||
|
name: 'João Links',
|
||||||
|
role: 'PR Specialist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=joao'
|
||||||
|
},
|
||||||
|
metaTitle: 'SEO Off-Page e Reputação de Marca em 2026',
|
||||||
|
metaDescription: "SEO Off-Page em 2026: Autoridade é mais que backlinks. Entenda o peso das menções de marca, sinais sociais e co-ocorrência na reputação digital moderna.",
|
||||||
|
tags: ['Off Page', 'Reputation', 'Branding', 'Signals'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# SEO Off-Page: A Autoridade além dos Backlinks
|
||||||
|
|
||||||
|
O Google está cada vez melhor em entender menções de marca, mesmo quando elas não vêm acompanhadas de um link (Unlinked Brand Mentions).
|
||||||
|
|
||||||
|
## Sinais de Co-ocorrência
|
||||||
|
Se o nome da sua marca aparece frequentemente perto de palavras-chave como "melhor agência de SEO", o Google começa a associar sua entidade a esse tópico.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
slug: 'psicologia-de-busca-entendendo-a-intencao',
|
||||||
|
title: 'Psicologia de Busca: Entendendo a Intenção Real',
|
||||||
|
excerpt: 'Por que o volume de busca é uma métrica vaidosa e como focar no que realmente converte.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-03-25',
|
||||||
|
image: 'https://images.unsplash.com/photo-1518349619113-03114f06ac3a?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '14 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas'
|
||||||
|
},
|
||||||
|
metaTitle: 'Psicologia e Intenção de Busca em SEO (2026)',
|
||||||
|
metaDescription: "Pare de perseguir volume e entenda a Intenção de Busca. Guia de Psicologia de Busca para mapear a jornada do usuário e converter tráfego em resultados reais.",
|
||||||
|
tags: ['Psychology', 'User Intent', 'Keywords', 'Funnel'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Psicologia de Busca: Entendendo a Intenção Real
|
||||||
|
|
||||||
|
Nem toda busca é criada igual. O usuário pode estar procurando informação, navegando para um site específico ou pronto para comprar.
|
||||||
|
|
||||||
|
## Otimizando para a Intenção
|
||||||
|
Se o seu artigo é um guia ensinando "como fazer", mas tenta vender um produto agressivamente no primeiro parágrafo, você está quebrando a intenção do usuário.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
slug: 'otimizacao-para-busca-por-voz-e-assistentes',
|
||||||
|
title: 'Otimização para Busca por Voz e Assistentes em 2026',
|
||||||
|
excerpt: 'Como estruturar seu conteúdo para ser a resposta única da Alexa, Siri e Google Assistant.',
|
||||||
|
category: 'Técnico',
|
||||||
|
publishedAt: '2026-03-20',
|
||||||
|
image: 'https://images.unsplash.com/photo-1589254065878-42c9da997008?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '9 min',
|
||||||
|
author: {
|
||||||
|
name: 'Marcos Tech',
|
||||||
|
role: 'SEO Engineer',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=marcos'
|
||||||
|
},
|
||||||
|
metaTitle: 'SEO para Busca por Voz 2026 | Dicas Práticas',
|
||||||
|
metaDescription: "SEO para Busca por Voz: Como ser a resposta única da Alexa, Siri e Google Assistant em 2026. Otimize seu conteúdo para NLP e linguagem natural hoje.",
|
||||||
|
tags: ['Voice Search', 'NLP', 'Conversational SEO'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Otimização para Busca por Voz e Assistentes em 2026
|
||||||
|
|
||||||
|
Com o avanço do Processamento de Linguagem Natural (NLP), a busca por voz tornou-se extremamente precisa.
|
||||||
|
|
||||||
|
## Linguagem Natural
|
||||||
|
As pessoas não falam como escrevem. Em vez de "melhor SEO curso", elas perguntam "Ei Google, qual é o melhor curso de SEO disponível hoje?".
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '11',
|
||||||
|
slug: 'seo-local-para-profissionais-liberais',
|
||||||
|
title: 'SEO Local: Dominando sua Região em 2026',
|
||||||
|
excerpt: 'Como profissionais liberais podem dominar o Google Maps e a busca local.',
|
||||||
|
category: 'Negócios',
|
||||||
|
publishedAt: '2026-03-15',
|
||||||
|
image: 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '8 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas'
|
||||||
|
},
|
||||||
|
metaTitle: 'Guia SEO Local 2026 | Google My Business',
|
||||||
|
metaDescription: "SEO Local 2026: Como dominar o Map Pack e Google My Business. Atraia clientes qualificados em sua região com estratégias de autoridade geográfica e IA local.",
|
||||||
|
tags: ['Local SEO', 'Google Maps'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# SEO Local: Dominando sua Região em 2026
|
||||||
|
|
||||||
|
O SEO Local tornou-se a espinha dorsal de pequenas e médias empresas. Em 2026, a presença no Google Maps não é apenas uma conveniência, é o destino final da jornada do consumidor local.
|
||||||
|
|
||||||
|
## Otimização do Perfil da Empresa
|
||||||
|
|
||||||
|
Manter suas informações atualizadas é o passo zero. Mas a autoridade local vai além disso: as avaliações com fotos e palavras-chave específicas do local agora pesam mais do que nunca no "Map Pack".
|
||||||
|
|
||||||
|
### O que mudou?
|
||||||
|
|
||||||
|
- **Respostas de IA Locais:** O Google Assistant agora usa os dados do seu perfil para responder perguntas complexas sobre disponibilidade e serviços.
|
||||||
|
- **Links Locais:** Citações em portais de notícias do seu bairro ou cidade transferem uma autoridade geográfica impossível de ignorar.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '12',
|
||||||
|
slug: 'erros-comuns-migracao-de-site',
|
||||||
|
title: 'Migração de Site: Como não perder tráfego',
|
||||||
|
excerpt: 'O checklist de sobrevivência para mudar seu domínio ou plataforma sem desastres.',
|
||||||
|
category: 'Técnico',
|
||||||
|
publishedAt: '2026-03-10',
|
||||||
|
image: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '20 min',
|
||||||
|
author: {
|
||||||
|
name: 'Marcos Tech',
|
||||||
|
role: 'SEO Engineer',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=marcos_dev'
|
||||||
|
},
|
||||||
|
metaTitle: 'Migração de Site 2026 | Checklist de SEO',
|
||||||
|
metaDescription: "Checklist Definitivo de Migração de Site em 2026. Aprenda a gerenciar redirects 301, monitorar GSC e evitar quedas de tráfego traumáticas durante mudanças.",
|
||||||
|
tags: ['Migration', 'Redirects'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Migração de Site: Como não perder tráfego
|
||||||
|
|
||||||
|
Migrar um site é um dos processos mais arriscados em SEO. Um erro no mapeamento de URLs pode destruir anos de autoridade acumulada em poucos dias.
|
||||||
|
|
||||||
|
## O Checklist de Sobrevivência
|
||||||
|
|
||||||
|
1. **Mapeamento de 1 para 1:** Garanta que cada URL antiga tenha um destino lógico na nova estrutura.
|
||||||
|
2. **Teste em Staging:** Nunca faça o deploy sem testar os redirects em um ambiente controlado.
|
||||||
|
3. **Monitoramento Granular:** Após a virada, acompanhe o Google Search Console diariamente para identificar erros 404 inesperados.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '13',
|
||||||
|
slug: 'futuro-da-busca-por-video-youtube-seo',
|
||||||
|
title: 'O Futuro da Busca por Vídeo e YouTube SEO',
|
||||||
|
excerpt: 'Como os vídeos estão sendo indexados diretamente nas SERPs principais.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-03-05',
|
||||||
|
image: 'https://images.unsplash.com/photo-1492724441997-5dc865305da7?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '10 min',
|
||||||
|
author: {
|
||||||
|
name: 'Beatriz Costa',
|
||||||
|
role: 'Head of Content',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=beatriz_c'
|
||||||
|
},
|
||||||
|
metaTitle: 'Vertical Search: YouTube e Vídeo SEO em 2026',
|
||||||
|
metaDescription: "Google Video SEO & YouTube 2026: Como dominar a busca multimodal. Aprenda a otimizar momentos chave, transcrições e capítulos para visibilidade máxima.",
|
||||||
|
tags: ['Video SEO', 'YouTube'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# O Futuro da Busca por Vídeo e YouTube SEO
|
||||||
|
|
||||||
|
Em 2026, a busca é multimodal. Vídeos curtos e tutoriais aprofundados estão ocupando espaços premium na página principal do Google.
|
||||||
|
|
||||||
|
## Indexação de Momentos Chave
|
||||||
|
|
||||||
|
O Google não só indexa o vídeo, mas também os capítulos e as transcrições, permitindo que o usuário "caia" exatamente no segundo que resolve sua dúvida.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '14',
|
||||||
|
slug: 'ferramentas-gratuitas-seo-indispensaveis',
|
||||||
|
title: '15 Ferramentas Gratuitas de SEO Indispensáveis',
|
||||||
|
excerpt: 'O arsenal que você precisa para gerenciar seus projetos sem gastar um centavo.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-03-01',
|
||||||
|
image: 'https://images.unsplash.com/photo-1551288049-bbdac8a28a1e?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '12 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas_s'
|
||||||
|
},
|
||||||
|
metaTitle: 'Ferramentas Gratuitas de SEO 2026',
|
||||||
|
metaDescription: "As 15 Melhores Ferramentas Gratuitas de SEO para 2026. Tenha o arsenal técnico completo sem gastar um centavo: de Search Console a monitoramento de tendências.",
|
||||||
|
tags: ['Tools', 'Free'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# 15 Ferramentas Gratuitas de SEO Indispensáveis
|
||||||
|
|
||||||
|
Você não precisa de assinaturas caras de mil dólares por mês para começar a rankear. Aqui está o arsenal básico para 2026:
|
||||||
|
|
||||||
|
1. **Google Search Console:** A fonte da verdade direta do buscador.
|
||||||
|
2. **AnswerThePublic:** Para entender a intenção de busca.
|
||||||
|
3. **Screaming Frog (Versão Free):** Para auditorias técnicas básicas.
|
||||||
|
4. **Google Trends:** Para capturar tendências sazonais antes da concorrência.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '15',
|
||||||
|
slug: 'seo-para-saas-estratatégias-de-growth',
|
||||||
|
title: 'SEO para SaaS: Estratégias de Growth Através do Orgânico',
|
||||||
|
excerpt: 'Como empresas de software escalam aquisição de usuários usando conteúdo técnico.',
|
||||||
|
category: 'Negócios',
|
||||||
|
publishedAt: '2026-02-25',
|
||||||
|
image: 'https://images.unsplash.com/photo-1526628953301-3e589a6a8b74?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '15 min',
|
||||||
|
author: {
|
||||||
|
name: 'Beatriz Costa',
|
||||||
|
role: 'Head of Content',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=beatriz'
|
||||||
|
},
|
||||||
|
metaTitle: 'SaaS SEO Strategy 2026 | Growth Marketing',
|
||||||
|
metaDescription: "Growth SEO para SaaS: Como escalar a aquisição de usuários no orgânico. Aprenda a estruturar o funil de conteúdo para converter buscas em usuários ativos de software.",
|
||||||
|
tags: ['SaaS', 'Growth'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# SEO para SaaS: Estratégias de Growth
|
||||||
|
|
||||||
|
O custo de aquisição (CAC) via anúncios subiu 400% nos últimos anos. Para empresas SaaS, o orgânico tornou-se o canal de crescimento mais sustentável.
|
||||||
|
|
||||||
|
## O Funil de Conteúdo SaaS
|
||||||
|
|
||||||
|
- **TOFU:** Resolvendo problemas genéricos (ex: "Como gerenciar projetos").
|
||||||
|
- **MOFU:** Comparativos (ex: "Software X vs Software Y").
|
||||||
|
- **BOFU:** Intenção direta (ex: "Melhor CRM para imobiliárias").
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '16',
|
||||||
|
slug: 'analise-de-concorrencia-descobrindo-gaps',
|
||||||
|
title: 'Análise de Concorrência: Descobrindo Gaps de Conteúdo',
|
||||||
|
excerpt: 'Como encontrar o que seus concorrentes não estão cobrindo e dominar essas buscas.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-02-20',
|
||||||
|
image: 'https://images.unsplash.com/photo-1542744173-8e7e53415bb0?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '9 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas'
|
||||||
|
},
|
||||||
|
metaTitle: 'Análise de Concorrentes SEO 2026',
|
||||||
|
metaDescription: "Descubra os pontos fracos dos seus concorrentes. Guia de Análise de Concorrência SEO para identificar gaps de conteúdo e dominar nichos subexplorados.",
|
||||||
|
tags: ['Competition', 'Strategy'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Análise de Concorrência: Gaps de Conteúdo
|
||||||
|
|
||||||
|
Não tente ganhar dos gigantes nas palavras-chave mais difíceis. Procure os **Gaps**. Onde a resposta deles é incompleta? Onde eles estão desatualizados?
|
||||||
|
|
||||||
|
## Ferramentas de Gap
|
||||||
|
|
||||||
|
Use relatórios de "Content Gap" para ver onde seus dois maiores concorrentes rankeiam mas você não. Esse é o seu roteiro de conteúdo para o próximo trimestre.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '17',
|
||||||
|
slug: 'otimizacao-de-imagens-para-google-discover',
|
||||||
|
title: 'Otimização de Imagens para o Google Discover',
|
||||||
|
excerpt: 'Como aparecer no feed de notícias do Google com imagens de impacto e tags corretas.',
|
||||||
|
category: 'Técnico',
|
||||||
|
publishedAt: '2026-02-15',
|
||||||
|
image: 'https://images.unsplash.com/photo-1493612276216-ee3925520721?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '7 min',
|
||||||
|
author: {
|
||||||
|
name: 'Marcos Tech',
|
||||||
|
role: 'SEO Engineer',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=marcos'
|
||||||
|
},
|
||||||
|
metaTitle: 'Google Discover SEO 2026',
|
||||||
|
metaDescription: "Google Discover SEO: O segredo para atrair milhões de acessos via imagens de impacto. Requisitos técnicos e táticas visuais para dominar o feed em 2026.",
|
||||||
|
tags: ['Images', 'Discover'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Imagens no Google Discover
|
||||||
|
|
||||||
|
O Discover é visual. Se a sua imagem não "parar o polegar" do usuário, seu CTR será nulo.
|
||||||
|
|
||||||
|
## Requisitos Técnicos
|
||||||
|
|
||||||
|
- **Tamanho:** Mínimo de 1200px de largura.
|
||||||
|
- **Alt Text:** Descritivo, mas focado no contexto visual.
|
||||||
|
- **Qualidade:** Evite fotos de estúdio genéricas; fotos reais com impacto emocional performam 3x melhor.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '18',
|
||||||
|
slug: 'seo-para-news-otimizando-artigos-de-noticias',
|
||||||
|
title: 'SEO para News: Otimizando Artigos em Tempo Real',
|
||||||
|
excerpt: 'Técnicas para aparecer no Google News e Top Stories de forma instantânea.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-02-10',
|
||||||
|
image: 'https://images.unsplash.com/photo-1495020689067-958852a7765e?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '11 min',
|
||||||
|
author: {
|
||||||
|
name: 'Beatriz Costa',
|
||||||
|
role: 'Head of Content',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=beatriz'
|
||||||
|
},
|
||||||
|
metaTitle: 'Google News SEO 2026',
|
||||||
|
metaDescription: "Domine o Top Stories e Google News. Aprenda técnicas de SEO em tempo real para portais de notícias, focando em velocidade de indexação e autoridade do autor.",
|
||||||
|
tags: ['News', 'Real Time'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# SEO para News
|
||||||
|
|
||||||
|
Notícia é o ápice da temporariedade. Rankear no "Top Stories" exige uma combinação de autoridade de marca e velocidade de indexação técnica.
|
||||||
|
|
||||||
|
## Entidades e Jornalismo
|
||||||
|
|
||||||
|
O Google agora identifica a entidade do jornalista. Quem escreveu? Esse autor tem histórico no assunto? Isso é o E-E-A-T aplicado ao jornalismo digital.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '19',
|
||||||
|
slug: 'impacto-dos-links-nofollow-e-ugc',
|
||||||
|
title: 'O Impacto Real dos Links Nofollow, Sponsored e UGC',
|
||||||
|
excerpt: 'Analisando como os hints de atributo de link influenciam o algoritmo moderno.',
|
||||||
|
category: 'Autoridade',
|
||||||
|
publishedAt: '2026-02-05',
|
||||||
|
image: 'https://images.unsplash.com/photo-1544197150-b99a580bb7a8?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '13 min',
|
||||||
|
author: {
|
||||||
|
name: 'João Links',
|
||||||
|
role: 'PR Specialist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=joao'
|
||||||
|
},
|
||||||
|
metaTitle: 'Link Attributes 2026 | Nofollow vs Sponsored',
|
||||||
|
metaDescription: "Nofollow, Sponsored ou UGC? Entenda o impacto real dos atributos de link no algoritmo do Google em 2026 e quando usar cada um para sua autoridade.",
|
||||||
|
tags: ['Attributes', 'Links'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Links Nofollow e UGC em 2026
|
||||||
|
|
||||||
|
Desde que se tornaram "hints", os links nofollow pararam de ser ignorados. O Google agora decide se quer ou não passar autoridade por eles.
|
||||||
|
|
||||||
|
## Quando usar cada um?
|
||||||
|
|
||||||
|
- **UGC:** Para links em comentários de blog ou fóruns.
|
||||||
|
- **Sponsored:** Para qualquer tipo de publieditorial or link pago.
|
||||||
|
- **Nofollow:** Para links que você não quer necessariamente endossar.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '20',
|
||||||
|
slug: 'monetizacao-blog-seo-autoridade',
|
||||||
|
title: 'Monetização de Blog através de SEO de Autoridade',
|
||||||
|
excerpt: 'Como transformar seu tráfego orgânico em uma máquina de receita sustentável.',
|
||||||
|
category: 'Negócios',
|
||||||
|
publishedAt: '2026-02-01',
|
||||||
|
image: 'https://images.unsplash.com/photo-1553729459-efe14ef6055d?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '16 min',
|
||||||
|
author: {
|
||||||
|
name: 'Ricardo Vendas',
|
||||||
|
role: 'E-commerce Expert',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=ricardo'
|
||||||
|
},
|
||||||
|
metaTitle: 'Monetização SEO 2026 | Guia de Lucratividade',
|
||||||
|
metaDescription: "Como lucrar com seu blog de autoridade em 2026. Saia das métricas de vaidade e transforme seu tráfego orgânico em uma máquina de receita sustentável.",
|
||||||
|
tags: ['Monetization', 'Business'],
|
||||||
|
lang: 'pt-br',
|
||||||
|
content: `
|
||||||
|
# Monetização de Blogs de Autoridade
|
||||||
|
|
||||||
|
Tráfego é vaidade, lucro é sanidade. Em 2026, os blogs de nicho mais lucrativos saíram do AdSense e foram para modelos de afiliados de alto ticket e produtos digitais próprios.
|
||||||
|
|
||||||
|
## Estrutura de Conversão
|
||||||
|
|
||||||
|
Cada artigo deve ter um objetivo de conversão claro. Se o usuário veio por uma dúvida técnica, ofereça uma ferramenta ou curso que resolva essa dor.
|
||||||
|
`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '21',
|
||||||
|
slug: 'the-future-of-organic-search-2026',
|
||||||
|
title: 'The Future of Organic Search in 2026',
|
||||||
|
excerpt: 'How Search Generative Experiences are redefining digital landscapes.',
|
||||||
|
category: 'Estratégia',
|
||||||
|
publishedAt: '2026-05-04',
|
||||||
|
image: 'https://images.unsplash.com/photo-1451187580459-43490279c0fa?auto=format&fit=crop&q=80&w=1600',
|
||||||
|
readTime: '10 min',
|
||||||
|
author: {
|
||||||
|
name: 'Dr. Lucas Silva',
|
||||||
|
role: 'Lead Strategist',
|
||||||
|
avatar: 'https://i.pravatar.cc/150?u=lucas'
|
||||||
|
},
|
||||||
|
metaTitle: 'The Future of Search | English Edition',
|
||||||
|
metaDescription: "Organic Search 2026: Deep dive into the future of SEO, SGE, and AI-driven user behavior. Learn how to stay ahead in the evolving digital landscape.",
|
||||||
|
tags: ['AI', 'SEO', 'Future'],
|
||||||
|
lang: 'en',
|
||||||
|
content: `
|
||||||
|
# The Future of Organic Search in 2026
|
||||||
|
|
||||||
|
The search landscape is evolving rapidly. Artificial intelligence is no longer just a backend tool; it's the interface itself.
|
||||||
|
|
||||||
|
## SGE and User Behavior
|
||||||
|
|
||||||
|
Search Generative Experience (SGE) provides direct answers at the top of the SERP, reducing the need for clicks on simple informational queries. Content creators must adapt by offering unique insights that go beyond simple data aggregation.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
76
Template-04/src/index.css
Normal file
76
Template-04/src/index.css
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
@theme {
|
||||||
|
--font-sans: "Inter", system-ui, sans-serif;
|
||||||
|
--font-mono: "JetBrains Mono", monospace;
|
||||||
|
|
||||||
|
--color-brand: #4f46e5; /* Premium Indigo */
|
||||||
|
--color-brand-light: #818cf8;
|
||||||
|
--color-brand-dark: #3730a3;
|
||||||
|
|
||||||
|
--radius-xl: 1rem;
|
||||||
|
--radius-2xl: 1.5rem;
|
||||||
|
--radius-3xl: 2rem;
|
||||||
|
|
||||||
|
--spacing-section: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide Google Translate Bar */
|
||||||
|
.goog-te-banner-frame.skiptranslate, .goog-te-banner-frame, #goog-gt-tt, .goog-te-balloon-frame {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
body { top: 0 !important; }
|
||||||
|
.goog-tooltip { display: none !important; }
|
||||||
|
.goog-text-highlight { background-color: transparent !important; border: none !important; box-shadow: none !important; }
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
@apply bg-[#030303] text-slate-400 font-sans antialiased selection:bg-brand/30 selection:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
@apply font-sans tracking-tight text-white font-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.glass-card {
|
||||||
|
@apply bg-white/[0.03] border border-white/10 backdrop-blur-md rounded-2xl transition-all duration-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-card-hover {
|
||||||
|
@apply hover:bg-white/[0.05] hover:border-white/20 hover:-translate-y-1 hover:shadow-2xl hover:shadow-brand/5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@apply h-10 px-5 bg-brand text-white font-bold text-[11px] uppercase tracking-wider rounded-lg transition-all hover:bg-brand-light active:scale-95 flex items-center justify-center gap-2 shadow-lg shadow-brand/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline {
|
||||||
|
@apply h-10 px-5 border border-white/10 text-white font-bold text-[11px] uppercase tracking-wider rounded-lg transition-all hover:bg-white/5 flex items-center justify-center gap-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-container {
|
||||||
|
@apply mx-auto max-w-7xl w-full px-6 lg:px-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Typography Utilities */
|
||||||
|
.heading-hero {
|
||||||
|
@apply text-5xl sm:text-7xl lg:text-8xl font-extrabold tracking-tighter leading-[1.1] text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heading-section {
|
||||||
|
@apply text-3xl sm:text-4xl lg:text-5xl font-bold tracking-tight text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decorative elements */
|
||||||
|
.brand-glow {
|
||||||
|
@apply absolute blur-[120px] opacity-20 pointer-events-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-pattern {
|
||||||
|
background-image: radial-gradient(rgba(255,255,255,0.05) 1px, transparent 1px);
|
||||||
|
background-size: 40px 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Template-04/src/lib/utils.ts
Normal file
14
Template-04/src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { clsx, type ClassValue } from 'clsx';
|
||||||
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDate(date: string) {
|
||||||
|
return new Intl.DateTimeFormat('pt-BR', {
|
||||||
|
day: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
year: 'numeric',
|
||||||
|
}).format(new Date(date));
|
||||||
|
}
|
||||||
10
Template-04/src/main.tsx
Normal file
10
Template-04/src/main.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {StrictMode} from 'react';
|
||||||
|
import {createRoot} from 'react-dom/client';
|
||||||
|
import App from './App.tsx';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
);
|
||||||
67
Template-04/src/pages/About.tsx
Normal file
67
Template-04/src/pages/About.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { Target, Zap, ShieldCheck, ArrowRight, Activity } from 'lucide-react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Sobre Nossa Agência | Apex SEO"
|
||||||
|
description="Pioneirismo na próxima geração da busca orgânica. Unimos complexidade técnica ao crescimento real de receita."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="relative pt-32 pb-32 overflow-hidden bg-black">
|
||||||
|
<div className="brand-glow top-0 left-1/2 -translate-x-1/2 h-[500px] w-[500px] bg-brand/10" />
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="max-w-4xl">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
>
|
||||||
|
<h1 className="text-5xl sm:text-7xl lg:text-8xl font-bold text-white mb-10 tracking-tight">
|
||||||
|
Escalando a <br/><span className="text-brand">Elite Global.</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="text-xl text-slate-400 leading-relaxed max-w-2xl mb-16">
|
||||||
|
Na Apex, não acreditamos em métricas de vaidade ou volume por volume. Acreditamos em autoridade arquitetônica, relevância de alta intenção e no fim do SEO genérico.
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid lg:grid-cols-3 gap-8 py-16">
|
||||||
|
{[
|
||||||
|
{ icon: Target, title: 'Intenção Estratégica', desc: 'Focamos nos nós da jornada do usuário que realmente geram receita, não apenas picos de tráfego.' },
|
||||||
|
{ icon: Zap, title: 'Velocidade Técnica', desc: 'Performance é nossa base. Construímos ecossistemas de busca rápidos e resilientes para crawlers modernos.' },
|
||||||
|
{ icon: ShieldCheck, title: 'Prova de Autoridade', desc: 'Modelagem de E-E-A-T calculada que constrói confiança autêntica e estabilidade de ranking sustentável.' }
|
||||||
|
].map((item, i) => (
|
||||||
|
<div key={i} className="glass-card p-10 group hover:border-brand/30 transition-all">
|
||||||
|
<div className="h-12 w-12 rounded-xl bg-white/[0.03] border border-white/10 flex items-center justify-center text-brand mb-8 group-hover:bg-brand group-hover:text-white transition-all">
|
||||||
|
<item.icon size={22} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-2xl font-bold text-white mb-4">{item.title}</h3>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed">
|
||||||
|
{item.desc}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="py-24 flex flex-col items-center border-t border-white/5 mt-16">
|
||||||
|
<blockquote className="max-w-4xl text-center font-bold text-white text-3xl sm:text-5xl tracking-tight leading-tight px-6 italic">
|
||||||
|
"O SEO moderno não é mais sobre palavras-chave; <span className="text-brand">é sobre se tornar a fonte definitiva</span> de verdade no seu setor."
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<div className="mt-16">
|
||||||
|
<Link to="/contato" className="btn-primary px-12 group">
|
||||||
|
Escale sua Empresa
|
||||||
|
<ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
57
Template-04/src/pages/Archive.tsx
Normal file
57
Template-04/src/pages/Archive.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import ArticleCard from '../components/ArticleCard';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { translations } from '../constants';
|
||||||
|
|
||||||
|
export default function Archive() {
|
||||||
|
const { lang } = useLanguage();
|
||||||
|
const t = translations[lang];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Arquivo de Insights | Apex SEO"
|
||||||
|
description="Explore nossa coleção completa de protocolos, guias e estudos de caso sobre autoridade orgânica."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="pt-24 pb-20 min-h-screen bg-[#050505]">
|
||||||
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
|
<header className="mb-16 relative">
|
||||||
|
<div className="absolute top-0 right-0 w-[400px] h-[400px] bg-brand/5 blur-[100px] -z-10 rounded-full" />
|
||||||
|
<div className="flex items-center gap-3 mb-8">
|
||||||
|
<div className="h-px w-8 bg-brand" />
|
||||||
|
<span className="text-[9px] font-black uppercase tracking-[0.4em] text-brand">Arquivo_Central_Apex</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl sm:text-7xl font-display font-black text-white tracking-tight leading-[0.9] mb-8 uppercase italic">
|
||||||
|
Insights<br /><span className="text-brand">Arquivados.</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs font-mono text-slate-600 max-w-xl font-black leading-relaxed uppercase tracking-widest pl-6 border-l border-white/10">
|
||||||
|
Uma sequência cronológica de inteligência de busca e autoridade estratégica. [APEX_DATA_v4.2]
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-12">
|
||||||
|
{articles.map((article, index) => (
|
||||||
|
<motion.div
|
||||||
|
key={article.id}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: index * 0.05 }}
|
||||||
|
>
|
||||||
|
<ArticleCard article={article} />
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{articles.length === 0 && (
|
||||||
|
<div className="text-center py-48 glass rounded-[3rem] border border-white/5">
|
||||||
|
<p className="text-slate-500 font-black uppercase tracking-[0.4em] text-[10px]">Nenhum_Registro_Encontrado [404]</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
354
Template-04/src/pages/BlogPost.tsx
Normal file
354
Template-04/src/pages/BlogPost.tsx
Normal file
|
|
@ -0,0 +1,354 @@
|
||||||
|
import { useParams, Link, useNavigate } from 'react-router-dom';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import { Clock, Calendar, Share2, Bookmark, ArrowRight, MessageCircle, Loader2, Sparkles, Twitter, Linkedin, Facebook, Link2, CheckCircle2, TrendingUp } from 'lucide-react';
|
||||||
|
import { motion, AnimatePresence, useScroll, useSpring } from 'motion/react';
|
||||||
|
import { formatDate, cn } from '../lib/utils';
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { translations } from '../constants';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Article } from '../types';
|
||||||
|
import { useBookmarks } from '../contexts/BookmarksContext';
|
||||||
|
import TableOfContents from '../components/TableOfContents';
|
||||||
|
|
||||||
|
export default function BlogPost() {
|
||||||
|
const { slug } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { lang, translate } = useLanguage();
|
||||||
|
const { toggleBookmark, isBookmarked } = useBookmarks();
|
||||||
|
|
||||||
|
const { scrollYProgress } = useScroll();
|
||||||
|
const scaleX = useSpring(scrollYProgress, {
|
||||||
|
stiffness: 100,
|
||||||
|
damping: 30,
|
||||||
|
restDelta: 0.001
|
||||||
|
});
|
||||||
|
|
||||||
|
const [translatedArticle, setTranslatedArticle] = useState<Article | undefined>();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
const [showToast, setShowToast] = useState(false);
|
||||||
|
|
||||||
|
const originalArticle = articles.find(a => a.slug === slug);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!originalArticle) return;
|
||||||
|
|
||||||
|
const translateFullArticle = async () => {
|
||||||
|
// If language is Portuguese, just use the original
|
||||||
|
if (lang === 'pt-br') {
|
||||||
|
setTranslatedArticle(originalArticle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// Use Gemini to translate all parts in one shot
|
||||||
|
const translated = await translate({
|
||||||
|
title: originalArticle.title,
|
||||||
|
excerpt: originalArticle.excerpt,
|
||||||
|
content: originalArticle.content,
|
||||||
|
metaTitle: originalArticle.metaTitle,
|
||||||
|
metaDescription: originalArticle.metaDescription
|
||||||
|
}, 'full article content', true);
|
||||||
|
|
||||||
|
setTranslatedArticle({
|
||||||
|
...originalArticle,
|
||||||
|
...translated
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Auto-translation error:", error);
|
||||||
|
setTranslatedArticle(originalArticle);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
translateFullArticle();
|
||||||
|
}, [lang, originalArticle, slug, translate]);
|
||||||
|
|
||||||
|
const copyToClipboard = () => {
|
||||||
|
navigator.clipboard.writeText(window.location.href);
|
||||||
|
setIsCopied(true);
|
||||||
|
setTimeout(() => setIsCopied(false), 2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const article = translatedArticle || originalArticle;
|
||||||
|
|
||||||
|
if (!article) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center p-4">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-4xl font-bold mb-4">Artigo não encontrado</h1>
|
||||||
|
<button onClick={() => navigate('/')} className="btn-primary">Voltar para a Home</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relatedArticles = articles.filter(a => a.category === article.category && a.id !== article.id).slice(0, 2);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title={article.metaTitle}
|
||||||
|
description={article.metaDescription}
|
||||||
|
type="article"
|
||||||
|
image={article.image}
|
||||||
|
keywords={article.tags}
|
||||||
|
articleData={{
|
||||||
|
publishedTime: article.publishedAt,
|
||||||
|
author: article.author.name,
|
||||||
|
authorAvatar: article.author.avatar,
|
||||||
|
tags: article.tags,
|
||||||
|
section: article.category
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<article className="pb-32 bg-black relative">
|
||||||
|
<motion.div
|
||||||
|
className="fixed top-20 left-0 right-0 h-1 bg-brand origin-left z-[60]"
|
||||||
|
style={{ scaleX }}
|
||||||
|
/>
|
||||||
|
<AnimatePresence>
|
||||||
|
{loading && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed inset-0 z-[100] bg-black/95 backdrop-blur-xl flex items-center justify-center p-6 text-center"
|
||||||
|
>
|
||||||
|
<div className="max-w-md w-full">
|
||||||
|
<div className="relative mb-8 inline-block">
|
||||||
|
<div className="absolute inset-0 bg-brand/20 animate-ping rounded-full" />
|
||||||
|
<div className="relative h-16 w-16 bg-brand/10 border border-brand/30 rounded-2xl flex items-center justify-center">
|
||||||
|
<Loader2 className="w-8 h-8 text-brand animate-spin" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-white mb-2 tracking-tight">
|
||||||
|
Otimizando Perspectiva
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-500 text-xs font-mono uppercase tracking-[0.2em]">
|
||||||
|
[ Aplicando Algoritmos de Crescimento ]
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
{/* Hero Header */}
|
||||||
|
<header className="pt-24 pb-12 bg-black relative border-b border-white/5">
|
||||||
|
<div className="brand-glow top-0 left-1/4 h-[300px] w-[300px] bg-brand/20" />
|
||||||
|
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="max-w-4xl">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="inline-flex items-center gap-3 px-3 py-1 bg-brand/5 border border-brand/20 rounded-full text-brand text-[10px] font-bold uppercase tracking-widest mb-8"
|
||||||
|
>
|
||||||
|
Protocolo de Estratégia // {article.category}
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.h1
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1 }}
|
||||||
|
className="text-4xl sm:text-6xl lg:text-7xl font-bold text-white leading-[1.1] mb-12 tracking-tight"
|
||||||
|
>
|
||||||
|
{article.title}
|
||||||
|
</motion.h1>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center gap-12">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<img src={article.author.avatar} alt={article.author.name} className="h-12 w-12 rounded-full border border-white/10" />
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-bold text-white">{article.author.name}</p>
|
||||||
|
<p className="text-[10px] uppercase tracking-widest text-slate-500 font-bold">Lead Strategist</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="h-10 w-px bg-white/10" />
|
||||||
|
|
||||||
|
<div className="flex gap-10">
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="text-slate-600 text-[10px] font-bold uppercase tracking-widest">Publicado</span>
|
||||||
|
<span className="text-white text-xs font-medium">{formatDate(article.publishedAt)}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<span className="text-slate-600 text-[10px] font-bold uppercase tracking-widest">Tempo de Leitura</span>
|
||||||
|
<span className="text-white text-xs font-medium">{article.readTime}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Featured Image */}
|
||||||
|
<div className="bg-black py-12">
|
||||||
|
<div className="section-container">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.98 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
className="w-full relative h-[40vh] lg:h-[60vh] rounded-3xl overflow-hidden shadow-2xl"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={article.image}
|
||||||
|
alt={article.title}
|
||||||
|
className="h-full w-full object-cover transition-transform duration-[3s]"
|
||||||
|
referrerPolicy="no-referrer"
|
||||||
|
/>
|
||||||
|
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content Layout */}
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="grid lg:grid-cols-12 gap-16 relative">
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="lg:col-span-8 py-10">
|
||||||
|
<div className="markdown-body text-slate-400">
|
||||||
|
<ReactMarkdown
|
||||||
|
components={{
|
||||||
|
h2: ({ children }) => {
|
||||||
|
const id = String(children).toLowerCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^\w-]/g, '');
|
||||||
|
return (
|
||||||
|
<h2 id={id} className="group relative scroll-mt-32 font-bold text-white border-brand">
|
||||||
|
{children}
|
||||||
|
</h2>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{article.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tags */}
|
||||||
|
<div className="mt-20 flex flex-wrap gap-2 pt-12 border-t border-white/5">
|
||||||
|
{article.tags.map(tag => (
|
||||||
|
<span key={tag} className="px-4 py-2 bg-white/[0.03] border border-white/10 rounded-xl text-slate-500 text-[10px] font-bold uppercase tracking-widest hover:border-brand hover:text-brand transition-all cursor-default">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Author Detail */}
|
||||||
|
<div className="mt-24 p-8 glass-card border-brand/10">
|
||||||
|
<div className="flex flex-col sm:flex-row items-center gap-8">
|
||||||
|
<img src={article.author.avatar} alt={article.author.name} className="h-20 w-20 rounded-2xl border border-white/10 shadow-xl" />
|
||||||
|
<div className="text-left">
|
||||||
|
<h4 className="text-xl font-bold text-white mb-2">{article.author.name}</h4>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed mb-4">
|
||||||
|
{article.author.role}, especializado em SEO arquitetônico e estratégias de conversão de alto ticket.
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<button className="text-xs font-bold text-brand uppercase tracking-widest hover:text-white transition-colors">LinkedIn</button>
|
||||||
|
<button className="text-xs font-bold text-brand uppercase tracking-widest hover:text-white transition-colors">Twitter</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sidebar */}
|
||||||
|
<aside className="lg:col-span-4 py-10">
|
||||||
|
<div className="sticky top-32 space-y-16">
|
||||||
|
<TableOfContents content={article.content} />
|
||||||
|
|
||||||
|
{/* Social Share */}
|
||||||
|
<div className="pt-12 border-t border-white/5">
|
||||||
|
<h5 className="text-[10px] font-bold text-white uppercase tracking-widest mb-8">Compartilhar Insight</h5>
|
||||||
|
<div className="grid grid-cols-4 gap-3">
|
||||||
|
{[Twitter, Linkedin, Facebook, Link2].map((Icon, i) => (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
onClick={i === 3 ? copyToClipboard : undefined}
|
||||||
|
className="h-12 flex items-center justify-center rounded-xl border border-white/10 bg-white/[0.02] text-slate-500 hover:text-brand hover:border-brand/40 transition-all group"
|
||||||
|
>
|
||||||
|
<Icon size={18} className="group-hover:scale-110 transition-transform" />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Related Outcomes */}
|
||||||
|
{relatedArticles.length > 0 && (
|
||||||
|
<div className="pt-12 border-t border-white/5">
|
||||||
|
<h5 className="text-[10px] font-bold text-white uppercase tracking-widest mb-8">Resultados Relacionados</h5>
|
||||||
|
<div className="space-y-8">
|
||||||
|
{relatedArticles.map(rel => (
|
||||||
|
<Link key={rel.id} to={`/artigo/${rel.slug}`} className="group block">
|
||||||
|
<div className="flex gap-4 items-start">
|
||||||
|
<div className="w-20 h-20 rounded-xl overflow-hidden shrink-0 border border-white/5">
|
||||||
|
<img src={rel.image} className="w-full h-full object-cover transition-transform group-hover:scale-110" referrerPolicy="no-referrer" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-[10px] font-bold text-brand uppercase mb-2 block">{rel.category}</span>
|
||||||
|
<h6 className="font-bold text-white text-sm leading-snug group-hover:text-brand transition-colors">
|
||||||
|
{rel.title}
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Newsletter Box */}
|
||||||
|
<div className="p-8 glass-card border-brand/20 bg-gradient-to-br from-brand/10 to-transparent">
|
||||||
|
<div className="h-12 w-12 bg-brand rounded-2xl flex items-center justify-center text-white mb-6 shadow-xl shadow-brand/20">
|
||||||
|
<Sparkles size={20} />
|
||||||
|
</div>
|
||||||
|
<h5 className="text-xl font-bold text-white mb-2">Trimestral de Crescimento</h5>
|
||||||
|
<p className="text-slate-500 text-xs mb-8 leading-relaxed">Junte-se a mais de 5.000 profissionais de growth recebendo nossos insights técnicos de SEO.</p>
|
||||||
|
<button className="btn-primary w-full shadow-lg shadow-brand/20">
|
||||||
|
Assinar Agora
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
{/* Toast Notification */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{isCopied && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
className="fixed bottom-12 left-1/2 -translate-x-1/2 z-[100] px-6 py-3 bg-white text-black text-xs font-bold rounded-full shadow-2xl flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<CheckCircle2 size={16} className="text-emerald-500" />
|
||||||
|
Link copiado para a área de transferência
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
|
<section className="bg-black py-24 border-t border-white/5 relative overflow-hidden">
|
||||||
|
<div className="brand-glow bottom-0 left-1/2 -translate-x-1/2 h-[400px] w-[400px] bg-brand/10" />
|
||||||
|
<div className="section-container text-center relative z-10">
|
||||||
|
<div className="w-16 h-16 glass-card rounded-2xl flex items-center justify-center mx-auto mb-10 group">
|
||||||
|
<TrendingUp size={28} className="text-brand group-hover:scale-110 transition-transform" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-3xl sm:text-5xl font-bold text-white mb-6">Gere Autoridade Real.</h2>
|
||||||
|
<p className="text-lg text-slate-500 mb-12 font-medium max-w-2xl mx-auto leading-relaxed">Nossa metodologia transforma a busca orgânica padrão em uma máquina de receita de alta performance.</p>
|
||||||
|
<div className="flex flex-wrap justify-center gap-6">
|
||||||
|
<Link to="/" className="btn-primary px-10">Voltar para Estratégia</Link>
|
||||||
|
<Link to="/contato" className="btn-outline px-10">Consultar nosso Time</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
100
Template-04/src/pages/Bookmarks.tsx
Normal file
100
Template-04/src/pages/Bookmarks.tsx
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { Bookmark as BookmarkIcon, ArrowRight, Trash2 } from 'lucide-react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { useBookmarks } from '../contexts/BookmarksContext';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import CategoryBadge from '../components/CategoryBadge';
|
||||||
|
|
||||||
|
export default function Bookmarks() {
|
||||||
|
const { bookmarks, toggleBookmark } = useBookmarks();
|
||||||
|
|
||||||
|
const savedArticles = articles.filter(a => bookmarks.includes(a.id));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="pt-36 pb-20 min-h-screen bg-[#050505]">
|
||||||
|
<SEO
|
||||||
|
title="Insights Salvos | Apex SEO"
|
||||||
|
description="Sua coleção personalizada de protocolos e insights para ler depois."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="max-w-7xl mx-auto px-6">
|
||||||
|
<header className="mb-16 relative">
|
||||||
|
<div className="absolute top-0 right-0 w-[400px] h-[400px] bg-brand/5 blur-[100px] -z-10 rounded-full" />
|
||||||
|
<div className="flex items-center gap-4 mb-8">
|
||||||
|
<div className="h-0.5 w-8 bg-brand" />
|
||||||
|
<span className="text-[9px] font-black uppercase tracking-[0.4em] text-brand">Biblioteca_Pessoal_Apex</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl sm:text-7xl font-display font-black text-white tracking-tight leading-[0.9] mb-8 uppercase italic">
|
||||||
|
Insights<br /><span className="text-brand">Salvos.</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-xs font-mono text-slate-600 max-w-xl font-black leading-relaxed uppercase tracking-widest pl-8 border-l border-white/10">
|
||||||
|
Revise seus padrões neurais extraídos e aplique-os ao seu crescimento arquitetônico.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{savedArticles.length > 0 ? (
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-16">
|
||||||
|
{savedArticles.map((article, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={article.id}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: i * 0.1 }}
|
||||||
|
className="group glass-dark rounded-[3rem] border border-white/10 p-10 flex flex-col hover:border-brand/40 transition-all h-full shadow-2xl relative overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 right-0 w-24 h-24 bg-brand/5 blur-2xl rounded-full translate-x-1/2 -translate-y-1/2" />
|
||||||
|
<div className="flex justify-between items-start mb-10 relative z-10">
|
||||||
|
<CategoryBadge category={article.category} variant="outline" />
|
||||||
|
<button
|
||||||
|
onClick={() => toggleBookmark(article.id)}
|
||||||
|
className="p-3 glass rounded-xl text-slate-500 hover:text-red-500 hover:border-red-500/20 transition-all"
|
||||||
|
title="Remover"
|
||||||
|
>
|
||||||
|
<Trash2 size={18} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link to={`/artigo/${article.slug}`} className="flex-grow relative z-10">
|
||||||
|
<h3 className="text-2xl font-display font-black text-white group-hover:text-brand transition-colors mb-8 leading-tight italic uppercase tracking-tight">
|
||||||
|
{article.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed mb-10 line-clamp-3 font-medium uppercase tracking-widest text-[10px]">
|
||||||
|
{article.excerpt}
|
||||||
|
</p>
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`/artigo/${article.slug}`}
|
||||||
|
className="inline-flex items-center gap-4 text-brand font-black uppercase text-[10px] tracking-[0.3em] group/link relative z-10"
|
||||||
|
>
|
||||||
|
Continuar_Leitura
|
||||||
|
<div className="h-10 w-10 rounded-xl glass flex items-center justify-center text-brand group-hover/link:bg-brand group-hover/link:text-slate-950 transition-all shadow-[0_0_15px_rgba(0,242,255,0.1)]">
|
||||||
|
<ArrowRight size={16} />
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="glass-dark rounded-[4rem] p-32 text-center border border-white/5 relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,rgba(0,242,255,0.02),transparent_70%)]" />
|
||||||
|
<div className="h-24 w-24 glass rounded-[2rem] flex items-center justify-center text-slate-700 mx-auto mb-12 border border-white/10 group shadow-2xl relative z-10">
|
||||||
|
<BookmarkIcon size={40} className="group-hover:text-brand transition-colors" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-4xl font-display font-black text-white mb-6 uppercase italic tracking-tighter relative z-10">Biblioteca_Vazia</h2>
|
||||||
|
<p className="text-slate-500 mb-16 max-w-md mx-auto font-medium uppercase tracking-[0.2em] text-xs relative z-10">
|
||||||
|
Explore nossos bancos de dados editoriais e salve insights para escalonamento futuro.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="btn-primary"
|
||||||
|
>
|
||||||
|
Iniciar_Busca
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
84
Template-04/src/pages/CategoryPage.tsx
Normal file
84
Template-04/src/pages/CategoryPage.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { useParams, Link } from 'react-router-dom';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import ArticleCard from '../components/ArticleCard';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { ChevronRight } from 'lucide-react';
|
||||||
|
|
||||||
|
import { useLanguage } from '../contexts/LanguageContext';
|
||||||
|
import { CATEGORY_MAP } from '../constants';
|
||||||
|
|
||||||
|
export default function CategoryPage() {
|
||||||
|
const { categorySlug } = useParams();
|
||||||
|
const { lang } = useLanguage();
|
||||||
|
|
||||||
|
const getCategoryData = (slug: string) => {
|
||||||
|
const entry = Object.entries(CATEGORY_MAP).find(([_, value]) => value.slug === slug);
|
||||||
|
|
||||||
|
if (entry) {
|
||||||
|
const [originalName, data] = entry;
|
||||||
|
return {
|
||||||
|
name: data.name,
|
||||||
|
originalName: originalName,
|
||||||
|
description: `Análises profundas, estudos de caso e táticas avançadas sobre ${data.name.toLowerCase()}.`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: slug.charAt(0).toUpperCase() + slug.slice(1).replace(/-/g, ' '),
|
||||||
|
originalName: slug,
|
||||||
|
description: 'Artigos e análises sobre o ecossistema digital.'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const { name: categoryName, originalName, description: categoryDescription } = getCategoryData(categorySlug || '');
|
||||||
|
|
||||||
|
const filteredArticles = articles.filter(a => {
|
||||||
|
// If we have a map entry, match by original category name
|
||||||
|
if (originalName) {
|
||||||
|
return a.category === originalName;
|
||||||
|
}
|
||||||
|
// Fallback strategy
|
||||||
|
const slugfiedCategory = a.category.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/\s+/g, '-');
|
||||||
|
return slugfiedCategory === categorySlug;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title={`Artigos sobre ${categoryName}`}
|
||||||
|
description={`Confira guias e dicas exclusivas sobre ${categoryName} em nosso blog de autoridade SEO.`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="bg-[#020204] pt-24 pb-16 border-b border-white/5 relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 bg-grid opacity-5" />
|
||||||
|
<div className="mx-auto max-w-7xl px-6 relative z-10">
|
||||||
|
<div className="flex items-center gap-4 text-brand font-black uppercase tracking-[0.4em] text-[9px] mb-8 italic">
|
||||||
|
<div className="h-[1px] w-8 bg-brand" />
|
||||||
|
<span>CLUSTER_EDITORIAL</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl sm:text-7xl font-display font-black text-white tracking-tight leading-[0.9] uppercase italic">{categoryName}</h1>
|
||||||
|
<p className="text-xs font-mono text-slate-500 mt-8 max-w-2xl font-black leading-relaxed uppercase tracking-widest border-l border-white/10 pl-6">{categoryDescription}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="py-24 bg-[#020204]">
|
||||||
|
<div className="mx-auto max-w-7xl px-6">
|
||||||
|
{filteredArticles.length > 0 ? (
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-12">
|
||||||
|
{filteredArticles.map(article => (
|
||||||
|
<ArticleCard key={article.id} article={article} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-48 bg-white/5 border border-white/5">
|
||||||
|
<h2 className="text-xs font-mono font-black text-slate-700 uppercase tracking-[0.4em] mb-8 italic">NENHUM_INSIGHT_INDEXADO</h2>
|
||||||
|
<Link to="/" className="btn-primary">
|
||||||
|
REINICIAR_HOME
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
152
Template-04/src/pages/Contact.tsx
Normal file
152
Template-04/src/pages/Contact.tsx
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { Mail, MessageCircle, Send, ArrowRight } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import Layout from '../components/Layout';
|
||||||
|
|
||||||
|
export default function Contact() {
|
||||||
|
const [submitted, setSubmitted] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setSubmitted(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Contato | Apex SEO Growth"
|
||||||
|
description="Conecte-se com nossos estrategistas para auditorias enterprise, estratégia de conteúdo de alta intenção ou dúvidas gerais."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="pt-32 pb-24 bg-black relative overflow-hidden">
|
||||||
|
<div className="brand-glow top-0 right-1/4 h-[500px] w-[500px] bg-brand/10" />
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="grid lg:grid-cols-2 gap-20">
|
||||||
|
{/* Left Column: Info */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: -20 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
className="space-y-16"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-3 mb-8">
|
||||||
|
<div className="h-1.5 w-1.5 rounded-full bg-brand" />
|
||||||
|
<span className="text-xs font-bold text-brand uppercase tracking-widest">Fale com nosso time</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-5xl sm:text-7xl font-bold text-white mb-8 tracking-tight">
|
||||||
|
Inicie sua <br /><span className="text-brand">Fase de Crescimento.</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg text-slate-400 max-w-md leading-relaxed">
|
||||||
|
Pronto para implantar estratégias orgânicas de alta autoridade? Nossos estrategistas seniores estão à disposição para uma sincronização técnica.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="flex gap-6 group">
|
||||||
|
<div className="h-14 w-14 rounded-2xl border border-white/10 bg-white/[0.03] flex items-center justify-center text-brand transition-all group-hover:bg-brand group-hover:text-white group-hover:scale-110">
|
||||||
|
<Mail size={22} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-white uppercase tracking-widest text-[10px] mb-2">Central de E-mail</h4>
|
||||||
|
<p className="text-slate-500 font-medium text-sm">hello@apexseo.agency</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="https://wa.me/5511999999999"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex gap-6 group"
|
||||||
|
>
|
||||||
|
<div className="h-14 w-14 rounded-2xl border border-white/10 bg-white/[0.03] flex items-center justify-center text-brand transition-all group-hover:bg-brand group-hover:text-white group-hover:scale-110">
|
||||||
|
<MessageCircle size={22} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-white uppercase tracking-widest text-[10px] mb-2">Canal Direto</h4>
|
||||||
|
<p className="text-slate-500 font-medium text-sm group-hover:text-brand transition-colors">+55 (11) 99999-9999</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
{/* Right Column: Form */}
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="glass-card p-10 md:p-14 relative"
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-brand/5 blur-[80px]" />
|
||||||
|
|
||||||
|
{submitted ? (
|
||||||
|
<div className="h-full flex flex-col items-center justify-center text-center space-y-8 py-20 relative z-10">
|
||||||
|
<div className="h-20 w-20 bg-brand rounded-3xl flex items-center justify-center text-white shadow-xl shadow-brand/20">
|
||||||
|
<Send size={32} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-3xl font-bold text-white mb-4">Briefing Recebido</h2>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed">Seus dados foram processados. Um estrategista entrará em contato em até 24 horas úteis.</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setSubmitted(false)}
|
||||||
|
className="text-brand font-bold uppercase text-[10px] tracking-widest hover:text-white transition-colors border-b border-brand/20 pb-1"
|
||||||
|
>
|
||||||
|
Enviar nova solicitação
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-8 relative z-10">
|
||||||
|
<div className="grid md:grid-cols-2 gap-8">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-500">Nome Completo</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
placeholder="Ex: Marcus Thorne"
|
||||||
|
className="w-full bg-white/[0.03] border border-white/10 rounded-xl px-4 py-3 text-white text-sm focus:outline-none focus:border-brand transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-500">E-mail Corporativo</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
type="email"
|
||||||
|
placeholder="Ex: marcus@cloudscale.com"
|
||||||
|
className="w-full bg-white/[0.03] border border-white/10 rounded-xl px-4 py-3 text-white text-sm focus:outline-none focus:border-brand transition-all"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-500">Tipo de Projeto</label>
|
||||||
|
<select className="w-full bg-white/[0.03] border border-white/10 rounded-xl px-4 py-3 text-white text-sm focus:outline-none focus:border-brand transition-all appearance-none">
|
||||||
|
<option className="bg-slate-900">Auditoria Técnica</option>
|
||||||
|
<option className="bg-slate-900">Arquitetura de Conteúdo</option>
|
||||||
|
<option className="bg-slate-900">Crescimento Enterprise</option>
|
||||||
|
<option className="bg-slate-900">Outras Consultas</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-[10px] font-bold uppercase tracking-widest text-slate-500">Como podemos ajudar?</label>
|
||||||
|
<textarea
|
||||||
|
required
|
||||||
|
placeholder="Descreva seus desafios orgânicos atuais..."
|
||||||
|
rows={4}
|
||||||
|
className="w-full bg-white/[0.03] border border-white/10 rounded-xl px-4 py-3 text-white text-sm focus:outline-none focus:border-brand transition-all resize-none"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button className="btn-primary w-full h-12 group">
|
||||||
|
Enviar Solicitação Estratégica
|
||||||
|
<ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
338
Template-04/src/pages/Home.tsx
Normal file
338
Template-04/src/pages/Home.tsx
Normal file
|
|
@ -0,0 +1,338 @@
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { articles } from '../data/articles';
|
||||||
|
import ArticleCard from '../components/ArticleCard';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { ArrowRight, TrendingUp, Search, BarChart3, Globe, Zap, CheckCircle2, Star, Quote, Activity } from 'lucide-react';
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { ResponsiveContainer, AreaChart, Area, XAxis, YAxis, Tooltip, CartesianGrid } from 'recharts';
|
||||||
|
|
||||||
|
const chartData = [
|
||||||
|
{ name: 'Jan', traffic: 4000 },
|
||||||
|
{ name: 'Feb', traffic: 7000 },
|
||||||
|
{ name: 'Mar', traffic: 12000 },
|
||||||
|
{ name: 'Apr', traffic: 18000 },
|
||||||
|
{ name: 'May', traffic: 32000 },
|
||||||
|
{ name: 'Jun', traffic: 54000 },
|
||||||
|
{ name: 'Jul', traffic: 89000 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const logos = [
|
||||||
|
{ name: 'TechFlow', color: 'text-slate-500' },
|
||||||
|
{ name: 'CloudScale', color: 'text-slate-500' },
|
||||||
|
{ name: 'FutureNode', color: 'text-slate-500' },
|
||||||
|
{ name: 'DataCore', color: 'text-slate-500' },
|
||||||
|
{ name: 'PulseAI', color: 'text-slate-500' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const services = [
|
||||||
|
{
|
||||||
|
title: 'SEO de Precisão',
|
||||||
|
description: 'Estratégias de otimização orientadas por dados que comandam autoridade nos rankings e crescimento orgânico sustentável.',
|
||||||
|
icon: Search,
|
||||||
|
color: 'text-brand'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Estratégia de Conteúdo',
|
||||||
|
description: 'Conteúdo arquitetônico de alta intenção projetado para converter tráfego de elite em clientes leais.',
|
||||||
|
icon: Zap,
|
||||||
|
color: 'text-blue-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Análise de Performance',
|
||||||
|
description: 'Rastreamento multi-nó em tempo real e modelagem de atribuição para total transparência de resultados.',
|
||||||
|
icon: BarChart3,
|
||||||
|
color: 'text-emerald-500'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Infraestrutura Técnica',
|
||||||
|
description: 'Otimizações avançadas de backend que maximizam a eficiência de rastreamento e a velocidade do site.',
|
||||||
|
icon: Globe,
|
||||||
|
color: 'text-purple-500'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const featuredArticles = articles.slice(0, 3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<SEO title="Apex SEO | Agência de Crescimento Premium" description="Escale sua autoridade com estratégias de SEO de alto nível projetadas para resultados exponenciais." />
|
||||||
|
|
||||||
|
{/* Hero Section */}
|
||||||
|
<section className="relative pt-32 pb-24 lg:pt-48 lg:pb-40 overflow-hidden">
|
||||||
|
<div className="brand-glow top-0 left-1/4 h-[400px] w-[400px] bg-brand/30" />
|
||||||
|
<div className="brand-glow bottom-1/4 right-0 h-[500px] w-[500px] bg-blue-500/20" />
|
||||||
|
<div className="absolute inset-0 grid-pattern opacity-10 pointer-events-none" />
|
||||||
|
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: -30 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
>
|
||||||
|
<div className="inline-flex items-center gap-2 px-3 py-1 bg-white/[0.03] border border-white/10 rounded-full text-xs font-semibold text-brand mb-8">
|
||||||
|
<span className="relative flex h-2 w-2">
|
||||||
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-brand opacity-75"></span>
|
||||||
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-brand"></span>
|
||||||
|
</span>
|
||||||
|
Escalando empresas de elite e scale-ups
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="heading-hero mb-8">
|
||||||
|
Domine a <span className="text-transparent bg-clip-text bg-gradient-to-r from-brand to-brand-light">Autoridade Estrutural</span> da Busca.
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p className="text-lg sm:text-xl text-slate-400 mb-12 max-w-xl leading-relaxed">
|
||||||
|
SEO estratégico e arquitetura de conteúdo para empresas de alto crescimento. Transformamos a busca orgânica em seu canal de receita mais consistente.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
|
<Link to="/contato" className="btn-primary group">
|
||||||
|
Agendar Chamada Estratégica
|
||||||
|
<ArrowRight size={16} className="group-hover:translate-x-1 transition-transform" />
|
||||||
|
</Link>
|
||||||
|
<Link to="/arquivo" className="btn-outline">
|
||||||
|
Ver Estudos de Caso
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 flex items-center gap-10">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-3xl font-bold text-white tracking-tighter">480%</span>
|
||||||
|
<span className="text-[10px] uppercase tracking-[0.2em] font-bold text-slate-500">Crescimento Médio</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-10 w-px bg-white/10" />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-3xl font-bold text-white tracking-tighter">1.2M+</span>
|
||||||
|
<span className="text-[10px] uppercase tracking-[0.2em] font-bold text-slate-500">Tráfego Escalado</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ duration: 1, delay: 0.2 }}
|
||||||
|
className="glass-card p-4 lg:p-8 relative"
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 right-0 p-6">
|
||||||
|
<div className="flex items-center gap-2 text-brand font-bold text-xs uppercase tracking-widest">
|
||||||
|
<Activity size={14} /> Monitoramento ROI Ativo
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-bold mb-8 px-2 flex items-center gap-3">
|
||||||
|
<BarChart3 size={20} className="text-brand" />
|
||||||
|
Momentum de Tráfego Orgânico
|
||||||
|
</h3>
|
||||||
|
<div className="h-[300px] w-full">
|
||||||
|
<ResponsiveContainer width="100%" height="100%">
|
||||||
|
<AreaChart data={chartData}>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="colorTraffic" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="5%" stopColor="#4f46e5" stopOpacity={0.3}/>
|
||||||
|
<stop offset="95%" stopColor="#4f46e5" stopOpacity={0}/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="rgba(255,255,255,0.05)" />
|
||||||
|
<XAxis
|
||||||
|
dataKey="name"
|
||||||
|
axisLine={false}
|
||||||
|
tickLine={false}
|
||||||
|
tick={{fill: 'rgba(255,255,255,0.3)', fontSize: 10}}
|
||||||
|
dy={10}
|
||||||
|
/>
|
||||||
|
<YAxis hide />
|
||||||
|
<Tooltip
|
||||||
|
contentStyle={{ backgroundColor: '#0f172a', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', fontSize: '10px' }}
|
||||||
|
itemStyle={{ color: '#818cf8', fontWeight: 'bold' }}
|
||||||
|
/>
|
||||||
|
<Area
|
||||||
|
type="monotone"
|
||||||
|
dataKey="traffic"
|
||||||
|
stroke="#4f46e5"
|
||||||
|
strokeWidth={3}
|
||||||
|
fillOpacity={1}
|
||||||
|
fill="url(#colorTraffic)"
|
||||||
|
/>
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 grid grid-cols-3 gap-4 pt-8 border-t border-white/5">
|
||||||
|
{[
|
||||||
|
{ label: 'Visibilidade SERP', value: '+142%', color: 'text-emerald-500' },
|
||||||
|
{ label: 'Conversão', value: '+24%', color: 'text-blue-500' },
|
||||||
|
{ label: 'Eficiência Crawl', value: '99.8%', color: 'text-brand' }
|
||||||
|
].map((stat, i) => (
|
||||||
|
<div key={i} className="text-center">
|
||||||
|
<p className="text-[10px] uppercase font-bold text-slate-600 mb-1">{stat.label}</p>
|
||||||
|
<p className={`text-sm font-bold ${stat.color}`}>{stat.value}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Social Proof */}
|
||||||
|
<section className="py-12 border-y border-white/5 bg-white/[0.01]">
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="flex flex-wrap items-center justify-center gap-12 lg:justify-between opacity-50 grayscale hover:grayscale-0 transition-all">
|
||||||
|
{logos.map((logo, i) => (
|
||||||
|
<span key={i} className="text-2xl font-black tracking-tighter text-slate-500 hover:text-white cursor-default">
|
||||||
|
{logo.name}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Services Section */}
|
||||||
|
<section id="servicos" className="py-spacing-section">
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="text-center max-w-3xl mx-auto mb-24">
|
||||||
|
<h2 className="heading-section mb-6">Nossa Metodologia Central</h2>
|
||||||
|
<p className="text-slate-400 text-lg leading-relaxed">
|
||||||
|
Não apenas otimizamos meta tags. Reconstrutímos a autoridade estratégica do seu site usando modelagem de dados proprietária e arquitetura de conteúdo de alta densidade.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-8">
|
||||||
|
{services.map((service, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={i}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ delay: i * 0.1 }}
|
||||||
|
className="glass-card glass-card-hover p-8 group"
|
||||||
|
>
|
||||||
|
<div className={`h-12 w-12 rounded-xl bg-white/[0.03] border border-white/10 flex items-center justify-center mb-6 group-hover:scale-110 group-hover:bg-brand group-hover:text-white transition-all`}>
|
||||||
|
<service.icon size={22} className={service.color + " group-hover:text-white transition-colors"} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold mb-4">{service.title}</h3>
|
||||||
|
<p className="text-slate-500 text-sm leading-relaxed mb-6">
|
||||||
|
{service.description}
|
||||||
|
</p>
|
||||||
|
<Link to="/arquivo" className="flex items-center gap-2 text-xs font-bold text-brand uppercase tracking-widest">
|
||||||
|
Ver Protocolo <ArrowRight size={14} />
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Case Studies */}
|
||||||
|
<section className="py-spacing-section bg-white/[0.01] border-y border-white/5 relative overflow-hidden">
|
||||||
|
<div className="brand-glow top-0 right-0 h-[600px] w-[600px] bg-brand/10" />
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-end gap-10 mb-20">
|
||||||
|
<div>
|
||||||
|
<h2 className="heading-section mb-4">Resultados Estratégicos</h2>
|
||||||
|
<p className="text-slate-500 max-w-xl">
|
||||||
|
Resultados reais dos protocolos de otimização deep-tech que implementamos para clientes SaaS e Enterprise de elite.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Link to="/arquivo" className="btn-outline">
|
||||||
|
Explorar Todos os Cases
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
|
{featuredArticles.map(article => (
|
||||||
|
<ArticleCard key={article.id} article={article} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Process Section */}
|
||||||
|
<section className="py-spacing-section">
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="grid lg:grid-cols-2 gap-20 items-center">
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute -left-10 top-1/2 -translate-y-1/2 w-px h-full bg-white/10" />
|
||||||
|
<div className="space-y-16">
|
||||||
|
{[
|
||||||
|
{ step: '01', title: 'Auditoria Deep-Tech', desc: 'Análise abrangente de indexação e mapeamento de vulnerabilidades estruturais.' },
|
||||||
|
{ step: '02', title: 'Arquitetura de Autoridade', desc: 'Modelagem estratégica de tópicos e extração semântica de alta intenção.' },
|
||||||
|
{ step: '03', title: 'Execução de Precisão', desc: 'Implantação de infraestrutura otimizada e módulos de conteúdo engenheirados.' },
|
||||||
|
{ step: '04', title: 'Escalabilidade Exponencial', desc: 'Iteração contínua baseada em atribuição de performance em tempo real.' }
|
||||||
|
].map((item, i) => (
|
||||||
|
<div key={i} className="flex gap-8 group">
|
||||||
|
<span className="text-4xl font-black text-white/10 group-hover:text-brand transition-colors duration-500">{item.step}</span>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-xl font-bold mb-2 group-hover:text-white transition-colors">{item.title}</h4>
|
||||||
|
<p className="text-slate-500 max-w-md">{item.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: 30 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className="p-12 glass-card relative overflow-hidden"
|
||||||
|
>
|
||||||
|
<div className="absolute top-0 right-0 w-32 h-32 bg-brand/20 blur-[80px]" />
|
||||||
|
<Quote size={40} className="text-brand mb-8 opacity-50" />
|
||||||
|
<p className="text-2xl font-semibold text-white leading-relaxed mb-12 italic">
|
||||||
|
"A transição de uma agência genérica para a abordagem técnica da Apex foi o catalisador para o nosso maior trimestre de receita até hoje."
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="h-14 w-14 rounded-full bg-brand/20 border border-brand/40 overflow-hidden">
|
||||||
|
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop" alt="Founder" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-white uppercase tracking-tight">Marcus Thorne</p>
|
||||||
|
<p className="text-xs text-brand uppercase font-bold tracking-widest">CMO // CloudScale Analytics</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-12 flex gap-4 pt-10 border-t border-white/5">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-2xl font-bold text-white tracking-tighter">300%</span>
|
||||||
|
<span className="text-[10px] uppercase font-bold text-slate-600">Crescimento</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col ml-12">
|
||||||
|
<span className="text-2xl font-bold text-white tracking-tighter">180K</span>
|
||||||
|
<span className="text-[10px] uppercase font-bold text-slate-600">Novos Leads</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* CTA Section */}
|
||||||
|
<section className="py-spacing-section pb-24">
|
||||||
|
<div className="section-container">
|
||||||
|
<div className="glass-card p-12 lg:p-24 relative overflow-hidden text-center bg-gradient-to-br from-brand/10 via-transparent to-blue-500/10">
|
||||||
|
<div className="brand-glow top-0 left-0 h-full w-full bg-brand/5 blur-[120px]" />
|
||||||
|
<div className="relative z-10 max-w-3xl mx-auto">
|
||||||
|
<h2 className="text-4xl lg:text-6xl font-bold tracking-tight text-white mb-8">
|
||||||
|
Pronto para escalar sua <span className="italic block mt-2 text-brand-light">Autoridade Orgânica?</span>
|
||||||
|
</h2>
|
||||||
|
<p className="text-slate-400 text-lg mb-12 opacity-80">
|
||||||
|
Junte-se a um coletivo de elite formado por empresas de alto crescimento que escalam receita através de SEO de precisão.
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-6">
|
||||||
|
<Link to="/contato" className="btn-primary min-w-[240px] !h-14 !text-sm">
|
||||||
|
Solicitar Briefing Estratégico
|
||||||
|
</Link>
|
||||||
|
<p className="text-xs font-mono uppercase tracking-[0.2em] text-slate-600">
|
||||||
|
Vagas limitadas para estratégia Q3.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
97
Template-04/src/pages/Methodology.tsx
Normal file
97
Template-04/src/pages/Methodology.tsx
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import { BarChart3, Database, Search, FileCheck, ArrowRight, TrendingUp } from 'lucide-react';
|
||||||
|
|
||||||
|
export default function Methodology() {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
icon: Database,
|
||||||
|
title: "Inteligência de Dados",
|
||||||
|
desc: "Analisamos mais de 1 milhão de SERPs mensais para identificar padrões sutis de mudança nos algoritmos do Google, Anthropic e OpenAI."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: Search,
|
||||||
|
title: "Análise Semântica",
|
||||||
|
desc: "Nossos modelos proprietários identificam quais entidades e contextos estão gerando os maiores ganhos de autoridade orgânica."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: BarChart3,
|
||||||
|
title: "Teste Empírico de Campo",
|
||||||
|
desc: "Implementamos hipóteses em redes e ambientes controlados antes de documentá-las como protocolos recomendados."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: FileCheck,
|
||||||
|
title: "Validação Estratégica",
|
||||||
|
desc: "Cada insight é revisado por estrategistas com mais de uma década de experiência em busca de alto ticket."
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Nossa Metodologia | Apex SEO"
|
||||||
|
description="Saiba como a Apex cria relatórios de SEO de alta performance e análises técnicas. Dados reais, testes empíricos e transparência."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="pt-32 pb-32 bg-black relative overflow-hidden">
|
||||||
|
<div className="brand-glow top-0 right-1/4 h-[400px] w-[400px] bg-brand/10" />
|
||||||
|
<div className="section-container relative z-10">
|
||||||
|
<div className="max-w-4xl mb-24">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
>
|
||||||
|
<span className="text-brand font-bold uppercase tracking-widest text-xs mb-6 block">Protocolo de Qualidade</span>
|
||||||
|
<h1 className="text-5xl sm:text-7xl lg:text-8xl font-bold text-white mb-10 tracking-tight">
|
||||||
|
Nossa<br />Metodologia.
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-slate-400 leading-relaxed font-medium border-l-2 border-brand/30 pl-6 max-w-2xl">
|
||||||
|
O SEO deixou de ser uma ciência exata para se tornar uma mistura de ciência de dados e psicologia humana. Aqui está como deciframos o algoritmo.
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={i}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
transition={{ delay: i * 0.1 }}
|
||||||
|
className="glass-card p-12 group hover:border-brand/30 transition-all"
|
||||||
|
>
|
||||||
|
<div className="h-14 w-14 rounded-2xl bg-brand/20 text-brand flex items-center justify-center mb-10 group-hover:bg-brand group-hover:text-white transition-all shadow-xl shadow-brand/10">
|
||||||
|
<step.icon size={26} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-white/5 font-bold text-8xl block mb-2 tracking-tighter leading-none select-none">0{i+1}</span>
|
||||||
|
<h3 className="text-2xl font-bold text-white mb-4 tracking-tight">{step.title}</h3>
|
||||||
|
<p className="text-slate-500 font-medium leading-relaxed">{step.desc}</p>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-24 p-12 glass-card bg-gradient-to-br from-brand/10 to-transparent border-brand/20 text-center max-w-3xl mx-auto">
|
||||||
|
<div className="h-12 w-12 bg-brand rounded-xl flex items-center justify-center text-white mx-auto mb-8 shadow-xl shadow-brand/20">
|
||||||
|
<TrendingUp size={24} />
|
||||||
|
</div>
|
||||||
|
<h4 className="text-2xl font-bold text-white mb-6 tracking-tight">Uplink de Dados Estratégicos</h4>
|
||||||
|
<p className="text-slate-500 font-medium mb-10 leading-relaxed">
|
||||||
|
Parceiros de agência e assinantes elite ganham acesso aos nossos datasets proprietários de flutuação de SERP e blueprints de auditoria estrutural.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to="/contato"
|
||||||
|
className="btn-primary group"
|
||||||
|
>
|
||||||
|
Acessar Briefing Premium
|
||||||
|
<ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
59
Template-04/src/pages/NotFound.tsx
Normal file
59
Template-04/src/pages/NotFound.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Home, ArrowLeft, Search } from 'lucide-react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
import Layout from '../components/Layout';
|
||||||
|
|
||||||
|
export default function NotFound() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-[80vh] flex items-center justify-center pt-20">
|
||||||
|
<SEO
|
||||||
|
title="404 - Página Não Encontrada"
|
||||||
|
description="A página que você está procurando não existe ou foi movida."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="max-w-xl w-full px-6 text-center">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
className="mb-8"
|
||||||
|
>
|
||||||
|
<div className="inline-flex h-24 w-24 items-center justify-center rounded-3xl bg-brand/10 text-brand mb-8 rotate-12">
|
||||||
|
<Search size={48} />
|
||||||
|
</div>
|
||||||
|
<h1 className="text-8xl sm:text-9xl font-display font-black text-white mb-8 tracking-[-0.04em] leading-[0.9]">404</h1>
|
||||||
|
<h2 className="text-2xl font-display font-black text-white uppercase tracking-widest mb-6">Página Não Encontrada</h2>
|
||||||
|
<p className="text-slate-500 text-lg leading-relaxed mb-10 font-medium max-w-md mx-auto border-l-2 border-white/5 pl-6 text-left">
|
||||||
|
O conteúdo que você busca parece ter sido reindexado ou movido para uma nova dimensão semântica. Vamos te levar de volta para um lugar seguro.
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||||
|
<Link
|
||||||
|
to="/"
|
||||||
|
className="btn-primary w-full sm:w-auto inline-flex items-center gap-3 group"
|
||||||
|
>
|
||||||
|
<Home size={18} />
|
||||||
|
Voltar para Home
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
onClick={() => window.history.back()}
|
||||||
|
className="btn-outline w-full sm:w-auto inline-flex items-center gap-3 group"
|
||||||
|
>
|
||||||
|
<ArrowLeft size={18} />
|
||||||
|
Página Anterior
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-20 pt-10 border-t border-white/5">
|
||||||
|
<p className="text-[10px] font-bold text-slate-600 uppercase tracking-[0.3em] mb-8">Navegue pelas categorias</p>
|
||||||
|
<div className="flex flex-wrap justify-center gap-2">
|
||||||
|
{['Estratégia', 'IA', 'Autoridade', 'Técnico'].map((cat) => (
|
||||||
|
<span key={cat} className="px-4 py-2 bg-white/[0.03] border border-white/10 text-slate-500 rounded-lg text-xs font-bold uppercase tracking-widest">{cat}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
40
Template-04/src/pages/Privacy.tsx
Normal file
40
Template-04/src/pages/Privacy.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
|
||||||
|
export default function Privacy() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Privacy Policy | Apex SEO"
|
||||||
|
description="Learn how we handle your data and our commitment to architectural transparency."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="pt-36 pb-24 bg-black">
|
||||||
|
<div className="section-container max-w-4xl">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="mb-20 border-l border-brand pl-8 py-2"
|
||||||
|
>
|
||||||
|
<span className="text-brand text-[10px] font-bold uppercase tracking-widest mb-4 block italic">Legal Protocol // v1.2</span>
|
||||||
|
<h1 className="text-4xl sm:text-6xl font-bold text-white mb-6 tracking-tight">Privacy <br/><span className="text-brand">Protocols.</span></h1>
|
||||||
|
<p className="text-[10px] font-mono text-slate-600 uppercase tracking-widest font-bold">Last Updated: May 2026</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="markdown-body text-slate-400">
|
||||||
|
<p>Your privacy is paramount at Apex. It is our policy to respect your privacy regarding any information we may collect through our website and other platforms we own and operate.</p>
|
||||||
|
|
||||||
|
<h2>1. Information Gathering</h2>
|
||||||
|
<p>We only ask for personal information when it is vital to provide you with a specific service, such as strategy briefs or newsletter subscriptions. We collect it by fair and lawful means, with your knowledge and consent.</p>
|
||||||
|
|
||||||
|
<h2>2. Data Utilization</h2>
|
||||||
|
<p>We do not share any personally identifying information publicly or with third-parties, except when required by law. Cookie usage is strictly limited to analytical traffic monitoring (Google Analytics) to refine your consultative experience.</p>
|
||||||
|
|
||||||
|
<h2>3. Security Standards</h2>
|
||||||
|
<p>We only retain collected information for as long as necessary to provide you with your requested service. What data we store, we protect within commercially acceptable means to prevent loss and theft, as well as unauthorized access, disclosure, copying, use, or modification.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
Template-04/src/pages/Terms.tsx
Normal file
38
Template-04/src/pages/Terms.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import SEO from '../components/SEO';
|
||||||
|
|
||||||
|
export default function Terms() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SEO
|
||||||
|
title="Terms of Service | Apex SEO"
|
||||||
|
description="Review the terms of use for our growth platforms and digital strategy briefings."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="pt-36 pb-24 bg-black">
|
||||||
|
<div className="section-container max-w-4xl">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className="mb-20 border-l border-brand pl-8 py-2"
|
||||||
|
>
|
||||||
|
<span className="text-brand text-[10px] font-bold uppercase tracking-widest mb-4 block italic">Structure Logs // v2.1</span>
|
||||||
|
<h1 className="text-4xl sm:text-6xl font-bold text-white mb-6 tracking-tight">Terms of <br/><span className="text-brand">Service.</span></h1>
|
||||||
|
<p className="text-[10px] font-mono text-slate-600 uppercase tracking-widest font-bold">Last Stamp: May 2026</p>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className="markdown-body text-slate-400">
|
||||||
|
<h2>1. Agreement</h2>
|
||||||
|
<p>By accessing the Apex SEO website, you are agreeing to be bound by these terms of service, all applicable laws and regulations, and agree that you are responsible for compliance with any applicable local laws.</p>
|
||||||
|
|
||||||
|
<h2>2. Content License</h2>
|
||||||
|
<p>All published content is protected by international copyright laws. Citation of brief excerpts is permitted provided a direct link of attribution to the original source is included. Commercial utilization of our datasets without prior explicit authorization is strictly prohibited.</p>
|
||||||
|
|
||||||
|
<h2>3. Disclaimer</h2>
|
||||||
|
<p>The materials on Apex SEO's website are provided on an 'as is' basis. Apex SEO makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including, without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
57
Template-04/src/services/gemini.ts
Normal file
57
Template-04/src/services/gemini.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { GoogleGenAI } from "@google/genai";
|
||||||
|
|
||||||
|
const ai = new GoogleGenAI({ apiKey: (process.env.GEMINI_API_KEY as string) });
|
||||||
|
|
||||||
|
export async function translateText(text: string, targetLang: string) {
|
||||||
|
if (!text) return text;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await ai.models.generateContent({
|
||||||
|
model: "gemini-3-flash-preview",
|
||||||
|
contents: `Translate the following text to ${targetLang}. Keep the markdown formatting and don't add any extra commentary. Just the translation: \n\n${text}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.text || text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Translation error:", error);
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function translateArticle(article: any, targetLang: string) {
|
||||||
|
try {
|
||||||
|
const prompt = `Translate the following article object to ${targetLang}.
|
||||||
|
Return a JSON object with the same structure.
|
||||||
|
Only translate the 'title', 'excerpt', 'content', 'metaTitle', and 'metaDescription' fields.
|
||||||
|
Keep the 'id', 'slug', 'category', 'publishedAt', 'image', 'readTime', 'author', 'tags', and 'lang' (set lang to ${targetLang}) fields exactly as they are.
|
||||||
|
|
||||||
|
Article to translate:
|
||||||
|
${JSON.stringify({
|
||||||
|
title: article.title,
|
||||||
|
excerpt: article.excerpt,
|
||||||
|
content: article.content,
|
||||||
|
metaTitle: article.metaTitle,
|
||||||
|
metaDescription: article.metaDescription
|
||||||
|
})}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const response = await ai.models.generateContent({
|
||||||
|
model: "gemini-3-flash-preview",
|
||||||
|
contents: prompt,
|
||||||
|
config: {
|
||||||
|
responseMimeType: "application/json",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const translatedParts = JSON.parse(response.text || "{}");
|
||||||
|
|
||||||
|
return {
|
||||||
|
...article,
|
||||||
|
...translatedParts,
|
||||||
|
lang: targetLang
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Article translation error:", error);
|
||||||
|
return article;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Template-04/src/services/geminiService.ts
Normal file
54
Template-04/src/services/geminiService.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { GoogleGenAI } from "@google/genai";
|
||||||
|
|
||||||
|
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY || '' });
|
||||||
|
|
||||||
|
export async function generateSEOArticle(topic: string) {
|
||||||
|
try {
|
||||||
|
const response = await ai.models.generateContent({
|
||||||
|
model: "gemini-3-flash-preview",
|
||||||
|
contents: `Crie um artigo de blog profissional e altamente otimizado para SEO sobre o seguinte tópico: ${topic}.
|
||||||
|
O artigo deve estar em Português do Brasil.
|
||||||
|
|
||||||
|
Retorne APENAS um JSON no seguinte formato:
|
||||||
|
{
|
||||||
|
"title": "Título otimizado com palavra-chave",
|
||||||
|
"excerpt": "Um resumo atraente do artigo",
|
||||||
|
"content": "Conteúdo completo em Markdown, com H2, H3, bullets e parágrafos curtos",
|
||||||
|
"category": "SEO para Iniciantes",
|
||||||
|
"metaTitle": "Meta title SEO",
|
||||||
|
"metaDescription": "Meta description persuasiva",
|
||||||
|
"tags": ["tag1", "tag2"],
|
||||||
|
"readTime": "X min"
|
||||||
|
}
|
||||||
|
|
||||||
|
Certifique-se de que o conteúdo seja profundo, profissional e focado em autoridade.`,
|
||||||
|
config: {
|
||||||
|
responseMimeType: "application/json"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return JSON.parse(response.text || '{}');
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao gerar artigo:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function summarizeSearchResults(query: string, results: any[]) {
|
||||||
|
try {
|
||||||
|
const titles = results.map(r => r.title).join(', ');
|
||||||
|
const response = await ai.models.generateContent({
|
||||||
|
model: "gemini-3-flash-preview",
|
||||||
|
contents: `O usuário pesquisou por "${query}" em um blog de autoridade em SEO.
|
||||||
|
Encontramos os seguintes artigos relacionados: ${titles}.
|
||||||
|
|
||||||
|
Crie um resumo muito curto (máximo 200 caracteres) explicando por que esses tópicos são importantes para a estratégia de SEO do usuário.
|
||||||
|
Seja direto, profissional e encorajador. Use Português do Brasil.`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao resumir busca:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
77
Template-04/src/types.ts
Normal file
77
Template-04/src/types.ts
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type Category =
|
||||||
|
| 'Estratégia'
|
||||||
|
| 'Técnico'
|
||||||
|
| 'Autoridade'
|
||||||
|
| 'Negócios'
|
||||||
|
| 'SEO para Iniciantes'
|
||||||
|
| 'SEO Técnico'
|
||||||
|
| 'SEO On Page'
|
||||||
|
| 'SEO Off Page'
|
||||||
|
| 'Link Building'
|
||||||
|
| 'Ferramentas de SEO'
|
||||||
|
| 'SEO para E-commerce'
|
||||||
|
| 'SEO para Blogs'
|
||||||
|
| 'Atualizações do Google';
|
||||||
|
|
||||||
|
export interface Author {
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Article {
|
||||||
|
id: string;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
excerpt: string;
|
||||||
|
content: string;
|
||||||
|
category: Category;
|
||||||
|
author: Author;
|
||||||
|
publishedAt: string;
|
||||||
|
image: string;
|
||||||
|
readTime: string;
|
||||||
|
metaTitle: string;
|
||||||
|
metaDescription: string;
|
||||||
|
tags: string[];
|
||||||
|
lang: 'pt-br' | 'en' | 'es';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Translations {
|
||||||
|
newsletter: string;
|
||||||
|
subscribe: string;
|
||||||
|
readMore: string;
|
||||||
|
latest: string;
|
||||||
|
allArticles: string;
|
||||||
|
sections: string;
|
||||||
|
publishing: string;
|
||||||
|
contact: string;
|
||||||
|
about: string;
|
||||||
|
privacy: string;
|
||||||
|
terms: string;
|
||||||
|
featured: string;
|
||||||
|
recent: string;
|
||||||
|
explore: string;
|
||||||
|
search: string;
|
||||||
|
viewAll: string;
|
||||||
|
manifesto: string;
|
||||||
|
methodology: string;
|
||||||
|
heroTitle: string;
|
||||||
|
heroSubtitle: string;
|
||||||
|
heroDescription: string;
|
||||||
|
readManifesto: string;
|
||||||
|
publishedIn: string;
|
||||||
|
newsletterTitle: string;
|
||||||
|
newsletterDescription: string;
|
||||||
|
emailPlaceholder: string;
|
||||||
|
subscribeButton: string;
|
||||||
|
trendsTitle: string;
|
||||||
|
footerNewsletterTitle: string;
|
||||||
|
footerNewsletterSubtitle: string;
|
||||||
|
footerNewsletterInput: string;
|
||||||
|
footerNewsletterDisclaimer: string;
|
||||||
|
}
|
||||||
26
Template-04/tsconfig.json
Normal file
26
Template-04/tsconfig.json
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Template-04/vite.config.ts
Normal file
24
Template-04/vite.config.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import path from 'path';
|
||||||
|
import {defineConfig, loadEnv} from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig(({mode}) => {
|
||||||
|
const env = loadEnv(mode, '.', '');
|
||||||
|
return {
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
define: {
|
||||||
|
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, '.'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
// HMR is disabled in AI Studio via DISABLE_HMR env var.
|
||||||
|
// Do not modifyâfile watching is disabled to prevent flickering during agent edits.
|
||||||
|
hmr: process.env.DISABLE_HMR !== 'true',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue