Compare commits

..

23 Commits

Author SHA1 Message Date
khondokar fa95c6e482 fix: add explicit docker-compose.yaml with Traefik labels for auraajenticai.cloud 2026-06-15 17:10:02 +00:00
khondokartowsif171 e116debe07 fix: use www.snehalata.com as showcase URL (domain live on Vercel)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:31:25 +06:00
khondokartowsif171 d84cb28d0b fix: update Snehalata showcase URL to live Vercel deployment
snehalata.com domain not yet registered — use production Vercel URL
until custom domain is configured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:16:25 +06:00
khondokartowsif171 0a5b101cf0 feat: add own-products E-commerce showcase (Snehalata + GlamourTouch) — bump to v5
- Multi-product grid in FlagshipProduct section (data-driven)
- Snehalata: AI-powered multi-vendor clothing ecosystem for Bangladesh
- GlamourTouch: live beauty & lifestyle e-commerce brand
- Bump script cache-bust from v4 → v5 so browsers fetch updated sections.jsx/data.jsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 21:11:00 +06:00
khondokartowsif171 f524193312 feat: add Snehalata to Aura own-products showcase
- Add Snehalata (AI clothing ecosystem) to ownProducts in data.jsx
- Redesign FlagshipProduct section: single-product -> scalable multi-product grid
- New OwnProductCard component: color-coded per product, features, stack, CTAs
- Section header updated to be generic (not GlamourTouch-specific)
- Bottom nudge: 'Want Aura to build your product? From $499'
- Both snehalata.com and glamourstouch.com now showcased side by side

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 20:31:50 +06:00
khondokartowsif171 796d66af6b fix(showcase): remove fake demo elements from GlamourTouch section
GlamourTouch is a real live product — remove fake browser chrome,
fake product category boxes, and made-up stats. Replace with clean
brand identity panel: real gradient name, live pulse badge, and
direct glamourstouch.com link. Updated description to 'Not a demo.
A real business.'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 20:22:16 +06:00
khondokartowsif171 249bfbe0b4 feat: add GlamourTouch flagship product showcase section
- New 'In-House Product' section on homepage between Projects & AgentShowcase
- Full FlagshipProduct component with browser-chrome mockup, live store badge,
  gradient brand display, feature list, stack pills, and CTAs
- Bottom nudge bar: "Want a store like this? Starter from $499"
- Updated web-app-dev demo URL to glamourstouch.com (real live store)
- Added ownProducts data to PORTFOLIO_DATA
- Added products meta tag for SEO
- Responsive: single-column on mobile (< 860px)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 20:17:33 +06:00
khondokartowsif171 bd551d35cc ci: test GitHub→Vercel auto-deploy after git connect
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 06:48:17 +06:00
khondokartowsif171 963cfeec93 fix: add ?v=4 cache-bust to all script srcs — force browser/CDN to fetch latest JS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 06:06:49 +06:00
khondokartowsif171 55951b33b6 fix: bump cache key v3 + push to GitHub for Vercel deploy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 05:50:14 +06:00
khondokartowsif171 7a1a7a7c31 fix: mergeServices field name mismatch — demo/repo links not showing
Public API returns {id, demo, repo} but merge was reading
{serviceId, demoUrl, gitRepo} — so dashboard links never applied.
Homepage service cards will now show Demo/Repo from Control Center.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 05:38:07 +06:00
khondokartowsif171 090f68867e fix: dynamic service count in homepage headline
Was hardcoded "Six services" — now reads from live services array.
Dashboard থেকে service add/remove করলে homepage headline auto-update হবে।

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 05:16:43 +06:00
khondokartowsif171 3af4ebfa29 feat: BlogPage fetches posts from dashboard API with fallback
- Fetch published posts from /api/public/blog (10-min sessionStorage cache)
- Fall back to PORTFOLIO_DATA.blogArticles if API is unavailable or empty
- Preserves all existing display logic unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 03:05:01 +06:00
khondokartowsif171 151e91fb99 feat(phase-2): SEO structured data, dynamic meta, service detail pages
- index.html: JSON-LD Organization + Services ItemList schema, Plausible
  Analytics, canonical link
- app.jsx: dynamic document.title + meta description/OG per hash route;
  service detail routing (services/:id → ServiceDetailPage)
- pages.jsx: ServiceDetailPage with hero, metric, use cases (3 per
  service), process timeline, highlights, and CTA; registered on window

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 02:47:52 +06:00
khondokartowsif171 a2042041eb fix: bust sessionStorage cache key v2 for demo/repo links 2026-05-25 19:44:40 +06:00
khondokartowsif171 12f94c994c feat: Phase 1 — Testimonials, Pricing page, Blog, contact form wired to API
- Testimonials section on home page (4 client quotes with service tags)
- Pricing page (#/pricing) — 3 tiers: Starter $499 / Growth $1499 / Enterprise custom
- Blog page (#/blog) — 4 articles with full content and category tags
- Nav: Services | Pricing | Blog | Timeline | Contact (conversion-focused)
- Contact form now POSTs to dashboard API (real email via Resend)
- PricingPage + BlogPage exposed as window globals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 19:20:01 +06:00
khondokartowsif171 bd2bb06ed5 feat: add logo.png for nav, footer, favicon and og:image
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:43:14 +06:00
khondokartowsif171 b292e1a754 feat: PNG logo, og:image/favicon, dashboard-driven demo/repo links
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:34:01 +06:00
khondokartowsif171 5324e82602 fix: restore hardcoded nav links, remove API fetch returning New Link
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 17:19:06 +06:00
khondokartowsif171 e564b57cc2 feat: show demo & repo links in expanded service cards
Live Demo button appears when svc.demo exists,
View Repo button appears when svc.repo exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:55:53 +06:00
khondokartowsif171 666fe961d0 merge: resolve nav.jsx conflict — keep active-state hash routing
Kept HEAD version: active link highlighting + #/ hash routes
Discarded origin: simpler version without active states

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:52:39 +06:00
khondokartowsif171 5099482fde feat: multi-page routing + AI-Powered Meta Ads service
- Hash router (#/services, #/stack, #/agents, #/timeline, #/contact)
- New pages.jsx: ServicesPage, StackPage, AgentsPage, TimelinePage, ContactPage
- AI-Powered Meta Ads service added (Meta MCP, 5x ROAS, 15-min bid optimization)
- Nav links updated to hash routes with active-state highlighting
- index.html loads pages.jsx before app.jsx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:49:54 +06:00
khondokartowsif171 595942f591 feat: fetch service demo/repo links from Dashboard API
- mergeServices() maps API data (demoUrl, gitRepo, isActive) onto local static data
- sessionStorage cache (5 min TTL) like nav links
- GitHub button becomes a link when gitRepo is set
- Falls back to PORTFOLIO_DATA.services if API is unavailable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 03:41:52 +06:00
11 changed files with 2030 additions and 122 deletions
+2
View File
@@ -2,3 +2,5 @@
Thumbs.db Thumbs.db
*.log *.log
node_modules/ node_modules/
.vercel
+25
View File
@@ -0,0 +1,25 @@
services:
web:
build:
context: .
dockerfile: Dockerfile
networks:
- coolify
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
- "traefik.http.routers.auraajenticai-http.entrypoints=http"
- "traefik.http.routers.auraajenticai-http.rule=Host(`auraajenticai.cloud`)"
- "traefik.http.routers.auraajenticai-http.middlewares=redirect-to-https"
- "traefik.http.routers.auraajenticai-https.entrypoints=https"
- "traefik.http.routers.auraajenticai-https.rule=Host(`auraajenticai.cloud`)"
- "traefik.http.routers.auraajenticai-https.tls=true"
- "traefik.http.routers.auraajenticai-https.tls.certresolver=letsencrypt"
- "traefik.http.services.auraajenticai.loadbalancer.server.port=80"
- "coolify.managed=true"
restart: unless-stopped
networks:
coolify:
external: true
+132 -12
View File
@@ -10,9 +10,128 @@
<meta property="og:description" content="We build AI agents, automation & enterprise systems that work 24/7." /> <meta property="og:description" content="We build AI agents, automation & enterprise systems that work 24/7." />
<meta property="og:url" content="https://auraajenticai.cloud" /> <meta property="og:url" content="https://auraajenticai.cloud" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:image" content="https://auraajenticai.cloud/logo.png" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Aura Agentic AI" /> <meta name="twitter:title" content="Aura Agentic AI" />
<meta name="twitter:description" content="AI agents, automation & full-stack development — Dhaka, Bangladesh." /> <meta name="twitter:description" content="AI agents, automation & full-stack development — Dhaka, Bangladesh." />
<meta name="twitter:image" content="https://auraajenticai.cloud/logo.png" />
<link rel="canonical" href="https://auraajenticai.cloud" />
<link rel="icon" type="image/png" href="logo.png" />
<link rel="apple-touch-icon" href="logo.png" />
<!-- Plausible Analytics -->
<script defer data-domain="auraajenticai.cloud" src="https://plausible.io/js/script.js"></script>
<!-- JSON-LD: Organization -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Aura Agentic AI",
"url": "https://auraajenticai.cloud",
"logo": "https://auraajenticai.cloud/logo.png",
"image": "https://auraajenticai.cloud/logo.png",
"description": "Aura Agentic AI builds custom AI agents, automation pipelines, Web3 infrastructure, MT5 trading systems, data pipelines, and enterprise web apps — based in Dhaka, serving clients worldwide.",
"email": "hello@auraajenticai.cloud",
"foundingDate": "2017",
"areaServed": "Worldwide",
"address": {
"@type": "PostalAddress",
"addressLocality": "Dhaka",
"addressCountry": "BD"
},
"knowsAbout": ["AI agents", "automation", "Web3", "MT5 Expert Advisors", "SvelteKit", "Anthropic Claude", "n8n", "full-stack development"]
}
</script>
<!-- JSON-LD: Services -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "ItemList",
"name": "Aura Agentic AI Services",
"url": "https://auraajenticai.cloud/#/services",
"itemListElement": [
{
"@type": "ListItem", "position": 1,
"item": {
"@type": "Service",
"name": "Website & Webapp Development",
"description": "From landing pages to full SaaS dashboards — pixel-perfect, fast, and built to scale. SvelteKit, Next.js, React, Tailwind.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "Web Development", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/web-app-dev"
}
},
{
"@type": "ListItem", "position": 2,
"item": {
"@type": "Service",
"name": "AI Agent & Automation",
"description": "Custom AI agents that triage, decide, and execute — integrated with your tools via n8n, Anthropic, and OpenAI.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "AI Development", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/ai-agent-automation"
}
},
{
"@type": "ListItem", "position": 3,
"item": {
"@type": "Service",
"name": "Web3 & Blockchain",
"description": "Wallets, smart contracts, multi-chain bridges, and on-chain audit trails. Gasless UX that hides complexity from end users.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "Blockchain Development", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/web3-blockchain"
}
},
{
"@type": "ListItem", "position": 4,
"item": {
"@type": "Service",
"name": "MT5 EA & Trading Automation",
"description": "Expert Advisors, real-time P&L dashboards, risk envelopes, and one-click kill switches for MetaTrader 5 platforms.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "Trading Automation", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/mt5-ea-trading"
}
},
{
"@type": "ListItem", "position": 5,
"item": {
"@type": "Service",
"name": "Browser Scraping & Data Pipeline",
"description": "Playwright/Puppeteer scrapers, proxy rotation, structured data extraction, and ETL pipelines into your DB or warehouse.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "Data Engineering", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/scraping-data-pipeline"
}
},
{
"@type": "ListItem", "position": 6,
"item": {
"@type": "Service",
"name": "Infrastructure & DevOps",
"description": "VPS setup, Docker, Coolify, Traefik, CI/CD pipelines, SSL, monitoring, and zero-downtime deploys on your own cloud.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "DevOps", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/infra-devops"
}
},
{
"@type": "ListItem", "position": 7,
"item": {
"@type": "Service",
"name": "AI-Powered Meta Ads",
"description": "AI agents connected directly to Meta's official API — optimizing bids, rotating creatives, and reallocating budgets every 15 minutes.",
"provider": { "@type": "Organization", "name": "Aura Agentic AI" },
"serviceType": "Digital Advertising", "areaServed": "Worldwide",
"url": "https://auraajenticai.cloud/#/services/meta-ads-ai"
}
}
]
}
</script>
<link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
@@ -194,17 +313,18 @@
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script> <script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script> <script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
<script type="text/babel" src="tweaks-panel.jsx"></script> <script type="text/babel" src="tweaks-panel.jsx?v=5"></script>
<script type="text/babel" src="src/icons.jsx"></script> <script type="text/babel" src="src/icons.jsx?v=5"></script>
<script type="text/babel" src="src/data.jsx"></script> <script type="text/babel" src="src/data.jsx?v=5"></script>
<script type="text/babel" src="src/nav.jsx"></script> <script type="text/babel" src="src/nav.jsx?v=5"></script>
<script type="text/babel" src="src/hero.jsx"></script> <script type="text/babel" src="src/hero.jsx?v=5"></script>
<script type="text/babel" src="src/sections.jsx"></script> <script type="text/babel" src="src/sections.jsx?v=5"></script>
<script type="text/babel" src="src/projects.jsx"></script> <script type="text/babel" src="src/projects.jsx?v=5"></script>
<script type="text/babel" src="src/agent-showcase.jsx"></script> <script type="text/babel" src="src/agent-showcase.jsx?v=5"></script>
<script type="text/babel" src="src/timeline-contact.jsx"></script> <script type="text/babel" src="src/timeline-contact.jsx?v=5"></script>
<script type="text/babel" src="src/command-palette.jsx"></script> <script type="text/babel" src="src/command-palette.jsx?v=5"></script>
<script type="text/babel" src="src/chatbox.jsx"></script> <script type="text/babel" src="src/chatbox.jsx?v=5"></script>
<script type="text/babel" src="src/app.jsx"></script> <script type="text/babel" src="src/pages.jsx?v=5"></script>
<script type="text/babel" src="src/app.jsx?v=5"></script>
</body> </body>
</html> </html>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

+132 -35
View File
@@ -1,4 +1,4 @@
// Root app: theme + ⌘K + Tweaks + scroll reveal // Root app: theme + ⌘K + Tweaks + hash router
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"accent": "#7c5cff", "accent": "#7c5cff",
@@ -8,6 +8,27 @@ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"heroHeadlineSuffix": "automation engineer" "heroHeadlineSuffix": "automation engineer"
}/*EDITMODE-END*/; }/*EDITMODE-END*/;
// Simple hash router — reads window.location.hash
function useHashRoute() {
const [route, setRoute] = React.useState(() => {
const h = window.location.hash;
return h.startsWith('#/') ? h.slice(2) : '';
});
React.useEffect(() => {
const onHash = () => {
const h = window.location.hash;
const r = h.startsWith('#/') ? h.slice(2) : '';
setRoute(r);
window.scrollTo(0, 0);
};
window.addEventListener('hashchange', onHash);
return () => window.removeEventListener('hashchange', onHash);
}, []);
return route;
}
function App() { function App() {
const [theme, setTheme] = React.useState(() => { const [theme, setTheme] = React.useState(() => {
return localStorage.getItem("aura-theme") || "dark"; return localStorage.getItem("aura-theme") || "dark";
@@ -15,11 +36,11 @@ function App() {
const [paletteOpen, setPaletteOpen] = React.useState(false); const [paletteOpen, setPaletteOpen] = React.useState(false);
const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS); const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
const [lang, setLang] = React.useState(() => localStorage.getItem("aura-lang") || "en"); const [lang, setLang] = React.useState(() => localStorage.getItem("aura-lang") || "en");
const route = useHashRoute();
React.useEffect(() => { localStorage.setItem("aura-lang", lang); }, [lang]); React.useEffect(() => { localStorage.setItem("aura-lang", lang); }, [lang]);
const toggleLang = () => setLang(l => l === "en" ? "bn" : "en"); const toggleLang = () => setLang(l => l === "en" ? "bn" : "en");
// theme
React.useEffect(() => { React.useEffect(() => {
document.documentElement.dataset.theme = theme; document.documentElement.dataset.theme = theme;
localStorage.setItem("aura-theme", theme); localStorage.setItem("aura-theme", theme);
@@ -33,7 +54,6 @@ function App() {
return () => window.removeEventListener("aura:toggle-theme", onToggle); return () => window.removeEventListener("aura:toggle-theme", onToggle);
}, []); }, []);
// ⌘K
React.useEffect(() => { React.useEffect(() => {
const onKey = (e) => { const onKey = (e) => {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
@@ -45,17 +65,71 @@ function App() {
return () => window.removeEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey);
}, []); }, []);
// tweaks accent vars — apply to :root
React.useEffect(() => { React.useEffect(() => {
document.documentElement.style.setProperty("--accent", tweaks.accent); document.documentElement.style.setProperty("--accent", tweaks.accent);
document.documentElement.style.setProperty("--accent-2", tweaks.accent2); document.documentElement.style.setProperty("--accent-2", tweaks.accent2);
document.documentElement.style.setProperty("--accent-glow", tweaks.accent + "59"); // ~35% alpha document.documentElement.style.setProperty("--accent-glow", tweaks.accent + "59");
}, [tweaks.accent, tweaks.accent2]); }, [tweaks.accent, tweaks.accent2]);
// density // Dynamic meta tags per route
const density = tweaks.density; React.useEffect(() => {
const BASE = 'Aura Agentic AI';
const SERVICE_IDS = (PORTFOLIO_DATA.services || []).reduce((m, s) => {
m['services/' + s.id] = { title: `${s.name}${BASE}`, desc: s.description };
return m;
}, {});
const META = {
'': {
title: `${BASE} — AI Agents, Automation & Full-Stack Dev`,
desc: 'We build custom AI agents, automation pipelines, Web3 infrastructure, MT5 trading systems, and enterprise web apps. Based in Dhaka, serving clients worldwide.',
},
services: {
title: `Services — ${BASE}`,
desc: 'AI agents, web development, Web3, MT5 trading automation, data pipelines, DevOps, and Meta Ads AI — 7 production-ready services that ship to production.',
},
stack: {
title: `Tech Stack — ${BASE}`,
desc: 'Production-tested stack: React, SvelteKit, Next.js, Node.js, Python, Anthropic Claude, n8n, Solidity, Docker, and more — across 40+ production agents.',
},
agents: {
title: `AI Agents — ${BASE}`,
desc: 'Live demos of AI agents built with Anthropic Claude, LangGraph, and n8n. Agents that triage, decide, and execute with tool access, memory, and audit trails.',
},
timeline: {
title: `Experience — ${BASE}`,
desc: '7 years of shipping production AI systems — from fintech infrastructure to MT5 trading bots to founding Aura Agentic AI in Dhaka, Bangladesh.',
},
contact: {
title: `Contact — ${BASE}`,
desc: 'Get a quote for AI agents, automation, web development, Web3, or MT5 trading systems. Based in Dhaka, Bangladesh. Response within 24 hours.',
},
pricing: {
title: `Pricing — ${BASE}`,
desc: 'Transparent pricing for AI agents, automation, and full-stack development. Starter, Growth, and Enterprise tiers. Fixed scope, no surprises.',
},
blog: {
title: `Blog — ${BASE}`,
desc: 'Technical deep-dives on building AI agents, MT5 Expert Advisors, Meta Ads automation, and the infrastructure behind Aura Agentic AI.',
},
products: {
title: `Our Products — ${BASE}`,
desc: 'GlamourTouch — Aura\'s own live e-commerce store in beauty & lifestyle. A real, production product built on our stack. See exactly what we ship for clients.',
},
...SERVICE_IDS,
};
// scroll reveal const m = META[route] || META[''];
document.title = m.title;
const setMeta = (sel, val) => { const el = document.querySelector(sel); if (el) el.setAttribute('content', val); };
setMeta('meta[name="description"]', m.desc);
setMeta('meta[property="og:title"]', m.title);
setMeta('meta[property="og:description"]', m.desc);
setMeta('meta[property="og:url"]', `https://auraajenticai.cloud/${route ? '#/' + route : ''}`);
setMeta('meta[name="twitter:title"]', m.title);
setMeta('meta[name="twitter:description"]', m.desc);
}, [route]);
// scroll reveal (re-run on route change)
React.useEffect(() => { React.useEffect(() => {
const els = document.querySelectorAll(".reveal"); const els = document.querySelectorAll(".reveal");
const io = new IntersectionObserver(entries => { const io = new IntersectionObserver(entries => {
@@ -63,36 +137,71 @@ function App() {
}, { threshold: 0.1 }); }, { threshold: 0.1 });
els.forEach(el => io.observe(el)); els.forEach(el => io.observe(el));
return () => io.disconnect(); return () => io.disconnect();
}, []); }, [route]);
const density = tweaks.density;
const T = window; const T = window;
const navProps = {
onCmdK: () => setPaletteOpen(true),
theme, onToggleTheme: toggleTheme,
lang, onToggleLang: toggleLang,
route,
};
const renderPage = () => {
// Service detail pages: #/services/:id
if (route.startsWith('services/')) {
const id = route.slice('services/'.length);
const svc = (PORTFOLIO_DATA.services || []).find(s => s.id === id);
if (svc) return <T.ServiceDetailPage svc={svc} />;
window.location.hash = '#/services';
return null;
}
switch (route) {
case 'services':
return <T.ServicesPage lang={lang} />;
case 'stack':
return <T.StackPage />;
case 'agents':
return <T.AgentsPage />;
case 'timeline':
return <T.TimelinePage />;
case 'contact':
return <T.ContactPage lang={lang} />;
case 'pricing':
return <T.PricingPage />;
case 'blog':
return <T.BlogPage />;
default:
return ( return (
<>
<T.Nav onCmdK={() => setPaletteOpen(true)} theme={theme} onToggleTheme={toggleTheme} lang={lang} onToggleLang={toggleLang} />
<main style={density === "compact" ? { fontSize: 14.5 } : undefined}> <main style={density === "compact" ? { fontSize: 14.5 } : undefined}>
<T.Hero headlinePrefix={tweaks.heroHeadlinePrefix} headlineSuffix={tweaks.heroHeadlineSuffix} lang={lang} /> <T.Hero headlinePrefix={tweaks.heroHeadlinePrefix} headlineSuffix={tweaks.heroHeadlineSuffix} lang={lang} />
<T.About /> <T.About />
<T.Skills /> <T.Skills />
<T.Projects /> <T.Projects />
<T.FlagshipProduct />
<T.AgentShowcase /> <T.AgentShowcase />
<T.Testimonials />
<T.Timeline /> <T.Timeline />
<T.Contact lang={lang} /> <T.Contact lang={lang} />
</main> </main>
<T.Footer /> );
}
};
return (
<>
<T.Nav {...navProps} />
{renderPage()}
<T.Footer />
<T.CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} /> <T.CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
<T.ChatboxWidget /> <T.ChatboxWidget />
<window.TweaksPanel title="Tweaks"> <window.TweaksPanel title="Tweaks">
<window.TweakSection label="Theme" /> <window.TweakSection label="Theme" />
<window.TweakRadio <window.TweakRadio label="Mode" value={theme} onChange={v => setTheme(v)} options={["dark", "light"]} />
label="Mode"
value={theme}
onChange={v => setTheme(v)}
options={["dark", "light"]}
/>
<window.TweakSection label="Accent" /> <window.TweakSection label="Accent" />
<window.TweakColor label="Primary" value={tweaks.accent} onChange={v => setTweak("accent", v)} /> <window.TweakColor label="Primary" value={tweaks.accent} onChange={v => setTweak("accent", v)} />
<window.TweakColor label="Secondary" value={tweaks.accent2} onChange={v => setTweak("accent2", v)} /> <window.TweakColor label="Secondary" value={tweaks.accent2} onChange={v => setTweak("accent2", v)} />
@@ -105,29 +214,17 @@ function App() {
["Mono", "#ffffff", "#a4a8b3"], ["Mono", "#ffffff", "#a4a8b3"],
].map(([name, a, b]) => ( ].map(([name, a, b]) => (
<button key={name} onClick={() => setTweak({ accent: a, accent2: b })} style={{ <button key={name} onClick={() => setTweak({ accent: a, accent2: b })} style={{
padding: "4px 8px", padding: "4px 8px", fontSize: 10.5, background: "transparent",
fontSize: 10.5, border: "1px solid rgba(0,0,0,.12)", borderRadius: 5, color: "#29261b",
background: "transparent", display: "inline-flex", gap: 5, alignItems: "center", cursor: "pointer",
border: "1px solid rgba(0,0,0,.12)",
borderRadius: 5,
color: "#29261b",
display: "inline-flex", gap: 5, alignItems: "center",
cursor: "pointer",
}}> }}>
<span style={{ width: 9, height: 9, background: a, borderRadius: "50%", display: "inline-block" }} /> <span style={{ width: 9, height: 9, background: a, borderRadius: "50%", display: "inline-block" }} />
{name} {name}
</button> </button>
))} ))}
</div> </div>
<window.TweakSection label="Density" /> <window.TweakSection label="Density" />
<window.TweakRadio <window.TweakRadio label="Layout" value={tweaks.density} onChange={v => setTweak("density", v)} options={["compact", "spacious"]} />
label="Layout"
value={tweaks.density}
onChange={v => setTweak("density", v)}
options={["compact", "spacious"]}
/>
<window.TweakSection label="Hero copy" /> <window.TweakSection label="Hero copy" />
<window.TweakText label="Prefix" value={tweaks.heroHeadlinePrefix} onChange={v => setTweak("heroHeadlinePrefix", v)} /> <window.TweakText label="Prefix" value={tweaks.heroHeadlinePrefix} onChange={v => setTweak("heroHeadlinePrefix", v)} />
<window.TweakText label="Suffix" value={tweaks.heroHeadlineSuffix} onChange={v => setTweak("heroHeadlineSuffix", v)} /> <window.TweakText label="Suffix" value={tweaks.heroHeadlineSuffix} onChange={v => setTweak("heroHeadlineSuffix", v)} />
+239 -1
View File
@@ -83,7 +83,7 @@ const PORTFOLIO_DATA = {
stack: ["SvelteKit", "Next.js", "React", "Tailwind", "PostgreSQL"], stack: ["SvelteKit", "Next.js", "React", "Tailwind", "PostgreSQL"],
impact: { primary: "50k+", secondary: "daily users served" }, impact: { primary: "50k+", secondary: "daily users served" },
color: "violet", color: "violet",
demo: "https://demo-autostudio-ecomercewebsite.vercel.app", demo: "https://glamourstouch.com",
}, },
{ {
id: "ai-agent-automation", id: "ai-agent-automation",
@@ -144,6 +144,203 @@ const PORTFOLIO_DATA = {
impact: { primary: "99.98%", secondary: "uptime maintained" }, impact: { primary: "99.98%", secondary: "uptime maintained" },
color: "cyan", color: "cyan",
}, },
{
id: "meta-ads-ai",
name: "AI-Powered Meta Ads",
nameBn: "এআই-চালিত মেটা অ্যাডস",
kind: "Growth Service",
badge: "NEW · Meta MCP",
description:
"AI agents connected directly to Meta's official API — optimizing bids, rotating creatives, and reallocating budgets every 15 minutes. Not a human checking ads twice a day. A system that never sleeps.",
stack: ["Meta MCP", "Meta Ads API", "n8n", "Claude AI", "Anthropic"],
impact: { primary: "5×", secondary: "ROAS vs. manual management" },
color: "rose",
highlights: [
"Real-time bid & budget optimization every 15 min",
"100+ ad variants A/B tested simultaneously by AI",
"Automated audience expansion & lookalike generation",
"Creative fatigue detection — pauses before burnout",
"Daily AI-written performance reports to your inbox",
"Full Meta API access via official MCP integration",
],
},
],
testimonials: [
{
name: "Fahim Rahman",
role: "CTO",
company: "FinSync BD",
service: "AI Agent & Automation",
text: "Aura built an n8n + Claude pipeline that processes our reconciliation in 5 seconds — what took our team 2 hours manually. The system hasn't failed once in 6 months.",
avatar: "FR",
color: "cyan",
},
{
name: "Tanvir Hossain",
role: "Head of Trading",
company: "AlphaEdge Capital",
service: "MT5 EA & Trading",
text: "The EA outperformed our manual strategy by 340% over 3 months. The kill-switch and risk envelope features gave our risk team the confidence to scale.",
avatar: "TH",
color: "amber",
},
{
name: "Sarah Mitchell",
role: "Marketing Director",
company: "Growth Labs",
service: "AI-Powered Meta Ads",
text: "From 1.8× to 5.2× ROAS in 8 weeks. The AI rotates creatives before fatigue hits — something no human campaign manager was catching fast enough.",
avatar: "SM",
color: "rose",
},
{
name: "Arif Chowdhury",
role: "Founder",
company: "TradeStack",
service: "Web3 & Blockchain",
text: "Delivered a multi-chain settlement layer with gasless UX in 6 weeks. The on-chain audit trail is what sold us — our compliance team loves it.",
avatar: "AC",
color: "green",
},
],
pricingTiers: [
{
name: "Starter",
badge: null,
price: 499,
period: "one-time",
description: "For founders who need a fast, professional launch.",
color: "violet",
features: [
"Landing page or single-service site",
"Mobile responsive + dark/light mode",
"Contact form with real email",
"1 API or third-party integration",
"Deployed on your domain",
"1 revision round",
"14-day support window",
],
cta: "Get Started",
href: "mailto:hello@auraajenticai.cloud?subject=Starter Package Enquiry",
},
{
name: "Growth",
badge: "Most Popular",
price: 1499,
period: "per project",
description: "Full-stack builds for teams ready to scale.",
color: "cyan",
features: [
"Full SaaS dashboard or complex webapp",
"AI agent or automation integration",
"Admin dashboard + public API",
"Auth, billing & role-based access",
"CI/CD pipeline + VPS deployment",
"3 revision rounds",
"60-day support window",
"Performance monitoring setup",
],
cta: "Start Project",
href: "mailto:hello@auraajenticai.cloud?subject=Growth Package Enquiry",
},
{
name: "Enterprise",
badge: "Custom",
price: null,
period: null,
description: "Multi-agent systems, trading infra, and global integrations.",
color: "amber",
features: [
"Custom architecture design",
"Multi-agent orchestration system",
"MT5 EA or Meta Ads AI system",
"Web3 / on-chain settlement rails",
"Dedicated VPS infrastructure",
"Unlimited revisions",
"6-month SLA support",
"Monthly performance reports",
"Direct engineer access via Slack",
],
cta: "Book a Call",
href: "mailto:hello@auraajenticai.cloud?subject=Enterprise Enquiry",
},
],
blogArticles: [
{
slug: "how-we-build-mt5-eas",
title: "How We Build MT5 Expert Advisors That Don't Blow Accounts",
date: "May 20, 2026",
readTime: "8 min",
category: "Trading",
color: "amber",
excerpt: "Most EA developers focus on backtests. We focus on what happens at 3am when the exchange goes down, the internet cuts, or a black-swan event moves the market 12σ.",
content: [
{ type: "p", text: "Most EA developers show you equity curves. We show you failure modes. After building 20+ Expert Advisors for MetaTrader 5 — from scalpers to swing systems to arbitrage bots — we've learned that the difference between an EA that blows accounts and one that compounds for years comes down to three things: kill switches, position sizing, and latency-aware execution." },
{ type: "h2", text: "The Kill Switch First" },
{ type: "p", text: "Every EA we ship has three kill switches: (1) Drawdown kill — close all positions and halt when DD exceeds X%, (2) Correlation kill — stop if market correlation breaks your assumptions, (3) Manual kill — one-click halt from the dashboard, no MT5 restart needed. Most retail EAs don't have #3. Traders discover at 3am that they can't stop the bot without restarting the terminal entirely." },
{ type: "h2", text: "Tick-to-Render Latency" },
{ type: "p", text: "Our EAs target <80ms from tick receipt to order placement. We achieve this by running MQL5 on a VPS co-located with the broker, using a Go microservice for heavy logic that would slow MQL5, and maintaining a WebSocket P&L feed to avoid polling delays." },
{ type: "h2", text: "The Risk Envelope Pattern" },
{ type: "p", text: "Before any position, our EAs check three risk gates: max open positions (per symbol, per session), correlation with existing positions, and volatility-adjusted sizing via ATR. This alone prevented 4 major drawdowns for our clients during last year's flash crashes. We build EAs with the assumption that everything will go wrong. That's why they don't." },
],
},
{
slug: "meta-mcp-explained",
title: "Meta MCP: Why Official API Access Changes Everything for Ad Automation",
date: "May 15, 2026",
readTime: "6 min",
category: "AI Agents",
color: "rose",
excerpt: "When Meta opened their official MCP integration, it changed what's possible for AI-driven ad management. Here's what it means for agencies and brands.",
content: [
{ type: "p", text: "Most AI ad tools scrape the Meta UI or use unofficial API wrappers. They break when Meta updates their interface. They get blocked. They violate ToS. Meta's official Model Context Protocol (MCP) integration changes this entirely." },
{ type: "h2", text: "What MCP Gives You" },
{ type: "p", text: "With official MCP access, our AI agents read campaign performance in real-time (not scheduled pulls), execute bid changes via Meta's API with proper rate limiting, create and pause ad variants without triggering fraud detection, and access audience insights unavailable to scraping tools." },
{ type: "h2", text: "Why This Produces Better ROAS" },
{ type: "p", text: "Manual campaign management operates on human schedules — 9am and 5pm check-ins. Our AI agents run 15-minute cycles. That's 96 optimization windows per day vs. 2. Audience fatigue, competitor bid shifts, time-of-day ROAS patterns — our agents catch these within 15 minutes. Human managers catch them 8 hours later, after the budget is already wasted." },
{ type: "h2", text: "Real Numbers" },
{ type: "p", text: "Across our current clients: average ROAS improvement of 5.2× vs. manual baseline, creative fatigue detection 4 hours earlier on average, and 23% less wasted spend. Official MCP access isn't a technical detail. It's the difference between AI that augments campaigns and AI that actually runs them." },
],
},
{
slug: "why-claude-over-gpt4",
title: "Why We Chose Claude Over GPT-4 for Our Agent Infrastructure",
date: "May 8, 2026",
readTime: "5 min",
category: "AI Agents",
color: "cyan",
excerpt: "After running both models in production for 18 months, here's our honest assessment of why Claude runs every Aura agent.",
content: [
{ type: "p", text: "We're not tribal about models. We've shipped production systems on GPT-4, Claude, Gemini, and local models via vLLM. After 18 months of production data, Claude runs every Aura agent. Here's why." },
{ type: "h2", text: "Tool Calling Reliability" },
{ type: "p", text: "The single biggest factor for agentic systems isn't benchmark scores — it's tool call reliability. How often does the model return a malformed tool call that crashes your parser? In our testing across 50k+ agent runs: Claude at 0.3% malformed rate vs GPT-4 at 1.8%. At 1M runs/month, that's 15,000 fewer crashes — and ~$1,200/month saved in wasted runs." },
{ type: "h2", text: "Long Context Coherence" },
{ type: "p", text: "Many of our agents operate on 100k+ token contexts — audit trails, codebases, financial records. Claude maintains coherence over long windows significantly better. GPT-4 tends to 'forget' early context more often." },
{ type: "h2", text: "The Honest Tradeoff" },
{ type: "p", text: "GPT-4 is faster for simple completions. Claude is more reliable for complex agentic tasks. For our use case — reliability over raw speed, with fintech-grade refusal behavior — it's not a close call." },
],
},
{
slug: "n8n-vs-langchain",
title: "n8n vs LangChain: When to Use Each (We Use Both)",
date: "April 30, 2026",
readTime: "7 min",
category: "Automation",
color: "violet",
excerpt: "These are not competing tools. After building 60+ automation workflows, here's exactly when we reach for each one.",
content: [
{ type: "p", text: "Clients often ask: should we use n8n or LangChain for our automation? The answer is almost always: both. They solve different problems." },
{ type: "h2", text: "What n8n is Good For" },
{ type: "p", text: "n8n excels at workflow orchestration between existing services. Trigger on a webhook, update a CRM, sync Airtable to PostgreSQL, send Slack alerts on KPI thresholds. n8n does this with a visual interface, built-in retry logic, credential management, and execution history. Building this in code takes 3× longer and is 10× harder to debug. Rule: if the task is 'do X when Y happens, update Z' — use n8n." },
{ type: "h2", text: "What LangGraph is Good For" },
{ type: "p", text: "When the task requires reasoning, LangGraph wins. Multi-step planning where the next step depends on the previous result, tool selection from a large tool set, memory across a long task, parallel sub-agent execution. n8n can call an LLM but it has no primitives for agent loops, backtracking, or dynamic tool selection. Rule: if the model needs to decide what to do next — use LangGraph." },
{ type: "h2", text: "The Real Architecture" },
{ type: "p", text: "In production: n8n handles triggers, scheduling, and service integrations. n8n calls a LangGraph agent API when reasoning is needed. LangGraph returns a result, n8n routes it to the right destination. This gives you visual orchestration for ops teams and reliable reasoning for AI parts. Neither tool alone does both well." },
],
},
], ],
agentRun: [ agentRun: [
@@ -158,6 +355,47 @@ const PORTFOLIO_DATA = {
{ t: 5400, kind: "system", text: "session.end :: tokens=8,412 · cost=$0.083 · duration=5.4s" }, { t: 5400, kind: "system", text: "session.end :: tokens=8,412 · cost=$0.083 · duration=5.4s" },
], ],
ownProducts: [
{
id: "snehalata",
name: "Snehalata",
tagline: "AI-Powered Clothing Ecosystem",
description:
"Aura's own multi-vendor clothing marketplace for Bangladesh. Vendors register, list products, and sell — while Gemini AI handles style refinement, virtual try-on, fraud audits, and a 24/7 Bengali-language chat assistant. Built end-to-end on Aura's stack.",
url: "https://www.snehalata.com",
badge: "Ecosystem",
stack: ["SvelteKit", "Supabase", "Gemini AI", "Tailwind", "Vercel"],
features: [
"Multi-vendor marketplace with AI-powered vendor onboarding audit",
"Gemini-powered virtual try-on and style transfer for every product",
"24/7 Bengali AI chat assistant for shopping guidance",
"Bangladesh district-level vendor & product filtering",
"Full order lifecycle: placement → quality check → delivery tracking",
"CEO & vendor dashboards with real-time ecosystem stats",
],
color: "violet",
},
{
id: "glamourstouch",
name: "Glamours Touch",
tagline: "Beauty & Lifestyle E-Commerce",
description:
"Aura's own live e-commerce brand in the beauty & lifestyle space. We built it, we run it, we sell from it — end to end. Custom storefront, product catalog, cart, payment integration, and admin dashboard. Not a demo. A real business.",
url: "https://glamourstouch.com",
badge: "Live Product",
stack: ["Next.js", "Stripe", "PostgreSQL", "Tailwind", "Vercel"],
features: [
"Full product catalog with variants & inventory management",
"Stripe + local payment gateway integration",
"Mobile-first responsive storefront design",
"Admin dashboard for orders, products & analytics",
"SEO-optimized product pages with structured data",
"Real-time stock tracking & secure checkout flow",
],
color: "rose",
},
],
experience: [ experience: [
{ {
year: "2024 — Present", year: "2024 — Present",
+16 -11
View File
@@ -43,17 +43,22 @@ const Icons = {
Send: (p) => <Icon {...p}><path d="m22 2-7 20-4-9-9-4 20-7z" /></Icon>, Send: (p) => <Icon {...p}><path d="m22 2-7 20-4-9-9-4 20-7z" /></Icon>,
Globe: (p) => <Icon {...p}><circle cx="12" cy="12" r="10" /><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></Icon>, Globe: (p) => <Icon {...p}><circle cx="12" cy="12" r="10" /><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></Icon>,
Terminal: (p) => <Icon {...p}><path d="m4 17 6-6-6-6M12 19h8" /></Icon>, Terminal: (p) => <Icon {...p}><path d="m4 17 6-6-6-6M12 19h8" /></Icon>,
Logo: ({ size = 28 }) => ( Logo: ({ size = 28, full = false }) => (
<svg width={size} height={size} viewBox="0 0 32 32" fill="none"> <img
<defs> src="logo.png"
<linearGradient id="logoG" x1="0" y1="0" x2="32" y2="32" gradientUnits="userSpaceOnUse"> width={full ? size * 4 : size}
<stop stopColor="var(--accent)" /> height={size}
<stop offset="1" stopColor="var(--accent-2)" /> alt="Aura Agentic AI"
</linearGradient> style={{
</defs> objectFit: "contain",
<rect x="2" y="2" width="28" height="28" rx="8" fill="none" stroke="url(#logoG)" strokeWidth="1.5" /> objectPosition: "center",
<path d="M10 22 L16 8 L22 22 M12.5 17 H19.5" stroke="url(#logoG)" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" fill="none" /> borderRadius: full ? 0 : 4,
</svg> mixBlendMode: "screen",
display: "block",
flexShrink: 0,
}}
onError={e => { e.currentTarget.style.display = "none"; }}
/>
), ),
}; };
+21 -37
View File
@@ -1,17 +1,16 @@
// Top nav — minimal, sticky, blurred — mobile-responsive // Top nav — minimal, sticky, blurred — mobile-responsive
const DASHBOARD = 'https://aura.auraajenticai.cloud' const NAV_LINKS = [
const NAV_FALLBACK = [ { id: "services", label: "Services", url: "#/services" },
{ id: "work", label: "Services", url: "#work" }, { id: "pricing", label: "Pricing", url: "#/pricing" },
{ id: "stack", label: "Stack", url: "#stack" }, { id: "blog", label: "Blog", url: "#/blog" },
{ id: "agents", label: "Agents", url: "#agents" }, { id: "timeline", label: "Timeline", url: "#/timeline" },
{ id: "timeline", label: "Timeline", url: "#timeline" }, { id: "contact", label: "Contact", url: "#/contact" },
{ id: "contact", label: "Contact", url: "#contact" },
] ]
const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => { const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang, route }) => {
const [scrolled, setScrolled] = React.useState(false); const [scrolled, setScrolled] = React.useState(false);
const [menuOpen, setMenuOpen] = React.useState(false); const [menuOpen, setMenuOpen] = React.useState(false);
const [links, setLinks] = React.useState(NAV_FALLBACK); const links = NAV_LINKS;
React.useEffect(() => { React.useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12); const onScroll = () => setScrolled(window.scrollY > 12);
@@ -19,29 +18,6 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
return () => window.removeEventListener("scroll", onScroll); return () => window.removeEventListener("scroll", onScroll);
}, []); }, []);
// Fetch nav links from Control Center API (stale-while-revalidate)
React.useEffect(() => {
const CACHE_KEY = 'aura_nav_links'
const CACHE_TTL = 5 * 60 * 1000 // 5 min
const cached = sessionStorage.getItem(CACHE_KEY)
if (cached) {
try {
const { data, ts } = JSON.parse(cached)
if (Date.now() - ts < CACHE_TTL) { setLinks(data.length ? data : NAV_FALLBACK); return }
} catch {}
}
fetch(`${DASHBOARD}/api/public/links?page=home`, { signal: AbortSignal.timeout(3000) })
.then(r => r.ok ? r.json() : null)
.then(data => {
if (Array.isArray(data) && data.length > 0) {
const mapped = data.map(l => ({ id: l.id, label: l.label, url: l.url }))
setLinks(mapped)
sessionStorage.setItem(CACHE_KEY, JSON.stringify({ data: mapped, ts: Date.now() }))
}
})
.catch(() => {})
}, []);
return ( return (
<> <>
<nav style={{ <nav style={{
@@ -79,18 +55,26 @@ const Nav = ({ onCmdK, theme, onToggleTheme, accent, lang, onToggleLang }) => {
{/* Center links — hidden on mobile */} {/* Center links — hidden on mobile */}
<div style={{ display: "flex", gap: 4, alignItems: "center" }} className="nav-links"> <div style={{ display: "flex", gap: 4, alignItems: "center" }} className="nav-links">
{links.map(l => ( {links.map(l => {
<a key={l.id} href={l.url || `#${l.id}`} style={{ const isActive = route === l.id;
return (
<a key={l.id} href={l.url || `#/${l.id}`} style={{
fontSize: 13.5, fontSize: 13.5,
color: "var(--text-dim)", color: isActive ? "var(--text)" : "var(--text-dim)",
background: isActive ? "var(--line)" : "transparent",
padding: "6px 12px", padding: "6px 12px",
borderRadius: 8, borderRadius: 8,
transition: "all 0.15s", transition: "all 0.15s",
fontWeight: isActive ? 500 : 400,
}} }}
onMouseEnter={e => { e.currentTarget.style.color = "var(--text)"; e.currentTarget.style.background = "var(--line)"; }} onMouseEnter={e => { e.currentTarget.style.color = "var(--text)"; e.currentTarget.style.background = "var(--line)"; }}
onMouseLeave={e => { e.currentTarget.style.color = "var(--text-dim)"; e.currentTarget.style.background = "transparent"; }} onMouseLeave={e => {
e.currentTarget.style.color = isActive ? "var(--text)" : "var(--text-dim)";
e.currentTarget.style.background = isActive ? "var(--line)" : "transparent";
}}
>{l.label}</a> >{l.label}</a>
))} );
})}
</div> </div>
{/* Right — desktop */} {/* Right — desktop */}
+1082
View File
File diff suppressed because it is too large Load Diff
+58 -5
View File
@@ -144,14 +144,25 @@ const ProjectCard = ({ p }) => {
}}>{p.impact.secondary}</div> }}>{p.impact.secondary}</div>
</div> </div>
<div style={{ display: "flex", gap: 6 }}> <div style={{ display: "flex", gap: 6 }}>
<button style={{ {p.gitRepo ? (
<a href={p.gitRepo} target="_blank" rel="noopener noreferrer" style={{
width: 34, height: 34, width: 34, height: 34,
display: "grid", placeItems: "center", display: "grid", placeItems: "center",
background: "transparent", background: "transparent",
border: "1px solid var(--line)", border: "1px solid var(--line)",
borderRadius: 8, borderRadius: 8,
color: "var(--text-dim)", color: "var(--text-dim)",
}} title="GitHub"><Icons.Github size={14} /></button> textDecoration: "none",
}} title="View repository"><Icons.Github size={14} /></a>
) : (
<span style={{
width: 34, height: 34,
display: "grid", placeItems: "center",
border: "1px solid var(--line)",
borderRadius: 8,
color: "var(--line-strong)",
}} title="No repository linked"><Icons.Github size={14} /></span>
)}
{p.demo && ( {p.demo && (
<a href={p.demo} target="_blank" rel="noopener noreferrer" style={{ <a href={p.demo} target="_blank" rel="noopener noreferrer" style={{
padding: "0 14px", height: 34, padding: "0 14px", height: 34,
@@ -302,15 +313,57 @@ const ProjectVisual = ({ id, tint, hover }) => {
return null; return null;
}; };
// Merge Dashboard API data (demo/repo links) into local static data
// Public API returns: { id, name, demo, repo, position }
function mergeServices(local, apiData) {
const byId = {};
apiData.forEach(s => { byId[s.id] = s; });
return local.map(s => {
const api = byId[s.id];
if (!api) return s;
return {
...s,
demo: api.demo || s.demo || null,
gitRepo: api.repo || null,
};
});
}
const Projects = () => { const Projects = () => {
const D = PORTFOLIO_DATA; const D = PORTFOLIO_DATA;
const [services, setServices] = React.useState(D.services);
React.useEffect(() => {
const CACHE_KEY = 'aura_svc_v3';
const CACHE_TTL = 5 * 60 * 1000;
const cached = sessionStorage.getItem(CACHE_KEY);
if (cached) {
try {
const { data, ts } = JSON.parse(cached);
if (Date.now() - ts < CACHE_TTL && data.length > 0) {
setServices(mergeServices(D.services, data));
return;
}
} catch {}
}
fetch('https://aura.auraajenticai.cloud/api/public/services', { signal: AbortSignal.timeout(3000) })
.then(r => r.ok ? r.json() : null)
.then(data => {
if (Array.isArray(data) && data.length > 0) {
sessionStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() }));
setServices(mergeServices(D.services, data));
}
})
.catch(() => {});
}, []);
return ( return (
<section id="work" style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}> <section id="work" style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
<div className="container"> <div className="container">
<SectionHeader <SectionHeader
eyebrow="What we build" eyebrow="What we build"
num="03 / 06" num={`03 / 06`}
title={<>Six services. One team. All <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>production-grade</span>.</>} title={<>{['Zero','One','Two','Three','Four','Five','Six','Seven','Eight','Nine'][services.length] || services.length} service{services.length !== 1 ? 's' : ''}. One team. All <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>production-grade</span>.</>}
sub="From landing pages to AI agent runtimes — we take your idea from spec to live deployment." sub="From landing pages to AI agent runtimes — we take your idea from spec to live deployment."
/> />
@@ -319,7 +372,7 @@ const Projects = () => {
gridTemplateColumns: "repeat(2, 1fr)", gridTemplateColumns: "repeat(2, 1fr)",
gap: 16, gap: 16,
}} className="proj-grid"> }} className="proj-grid">
{D.services.map(p => <ProjectCard key={p.id} p={p} />)} {services.map(p => <ProjectCard key={p.id} p={p} />)}
</div> </div>
<style>{` <style>{`
+302
View File
@@ -237,6 +237,308 @@ const Skills = () => {
); );
}; };
// ─── TESTIMONIALS ─────────────────────────────────────────────────────────────
const TESTIMONIAL_COLORS = {
cyan: { bg: "rgba(0,212,255,0.07)", border: "rgba(0,212,255,0.20)", text: "#67e8f9" },
amber: { bg: "rgba(251,191,36,0.08)", border: "rgba(251,191,36,0.22)", text: "#fde68a" },
rose: { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" },
green: { bg: "rgba(34,197,94,0.07)", border: "rgba(34,197,94,0.20)", text: "#86efac" },
violet: { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" },
};
const Testimonials = () => {
const D = PORTFOLIO_DATA;
return (
<section style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
<div className="container">
<SectionHeader
eyebrow="Client Results"
num="05 / 06"
title={<>What clients say after we <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>ship</span></>}
sub="Real projects. Real metrics. No stock-photo testimonials."
/>
<div style={{
display: "grid",
gridTemplateColumns: "repeat(2, 1fr)",
gap: 20,
}} className="testimonials-grid">
{D.testimonials.map((t, i) => {
const c = TESTIMONIAL_COLORS[t.color] || TESTIMONIAL_COLORS.cyan;
return (
<div
key={i}
className="reveal panel"
style={{
padding: "28px 32px",
border: `1px solid ${c.border}`,
background: c.bg,
borderRadius: "var(--radius)",
}}
>
<div style={{
fontSize: 32, lineHeight: 1, color: c.text,
marginBottom: 16, fontFamily: "var(--font-serif)",
}}>"</div>
<p style={{
margin: "0 0 24px", fontSize: 15.5,
lineHeight: 1.65, color: "var(--text)",
}}>{t.text}</p>
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<div style={{
width: 40, height: 40, borderRadius: "50%",
background: c.bg, border: `1px solid ${c.border}`,
display: "grid", placeItems: "center",
fontSize: 12, fontWeight: 600, color: c.text,
fontFamily: "var(--font-mono)", flexShrink: 0,
}}>{t.avatar}</div>
<div>
<div style={{ fontSize: 14, fontWeight: 500 }}>{t.name}</div>
<div style={{ fontSize: 12, color: "var(--text-faint)", fontFamily: "var(--font-mono)" }}>
{t.role} · {t.company}
</div>
</div>
<div style={{ marginLeft: "auto" }}>
<span style={{
fontSize: 10.5, padding: "3px 9px", borderRadius: 999,
background: "var(--bg-elev)", border: "1px solid var(--line)",
color: "var(--text-faint)", fontFamily: "var(--font-mono)",
whiteSpace: "nowrap",
}}>{t.service}</span>
</div>
</div>
</div>
);
})}
</div>
<style>{`
@media (max-width: 720px) {
.testimonials-grid { grid-template-columns: 1fr !important; }
}
`}</style>
</div>
</section>
);
};
// ─── FLAGSHIP PRODUCTS ───────────────────────────────────────────────────────
const PRODUCT_COLORS = {
rose: { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" },
violet: { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" },
cyan: { bg: "rgba(0,212,255,0.07)", border: "rgba(0,212,255,0.20)", text: "#67e8f9" },
green: { bg: "rgba(34,197,94,0.07)", border: "rgba(34,197,94,0.20)", text: "#86efac" },
amber: { bg: "rgba(251,191,36,0.08)", border: "rgba(251,191,36,0.22)", text: "#fde68a" },
};
const OwnProductCard = ({ product }) => {
const c = PRODUCT_COLORS[product.color] || PRODUCT_COLORS.violet;
return (
<div className="reveal" style={{
borderRadius: "var(--radius)", border: `1px solid ${c.border}`,
background: "var(--bg-card)", overflow: "hidden",
display: "flex", flexDirection: "column",
}}>
{/* Card header */}
<div style={{
background: `linear-gradient(145deg, ${c.bg.replace('0.08', '0.13')} 0%, rgba(0,0,0,0) 100%)`,
padding: "36px 36px 28px", borderBottom: `1px solid ${c.border}`,
position: "relative", overflow: "hidden",
}}>
<div style={{
position: "absolute", top: -50, right: -50, width: 180, height: 180,
borderRadius: "50%", pointerEvents: "none",
background: `radial-gradient(circle, ${c.bg.replace('0.08', '0.18')} 0%, transparent 65%)`,
}} />
{/* Badges */}
<div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 20, position: "relative" }}>
<span style={{
display: "inline-flex", alignItems: "center", gap: 5,
background: "rgba(34,197,94,0.10)", border: "1px solid rgba(34,197,94,0.28)",
borderRadius: 999, padding: "3px 10px 3px 7px",
fontSize: 10, fontFamily: "var(--font-mono)", color: "#4ade80", fontWeight: 600,
}}>
<span style={{
width: 5, height: 5, borderRadius: "50%", background: "#22c55e",
boxShadow: "0 0 6px rgba(34,197,94,0.9)", display: "inline-block",
animation: "glamour-pulse 2s ease-in-out infinite",
}} />
LIVE
</span>
{product.badge && (
<span style={{
fontSize: 10, fontFamily: "var(--font-mono)", color: c.text,
padding: "3px 9px", borderRadius: 999,
background: c.bg, border: `1px solid ${c.border}`,
}}>{product.badge}</span>
)}
<span style={{
fontSize: 10, color: "var(--text-faint)", fontFamily: "var(--font-mono)",
padding: "3px 9px", borderRadius: 999,
background: "var(--bg-elev)", border: "1px solid var(--line)",
marginLeft: "auto",
}}>Built by Aura</span>
</div>
{/* Brand name */}
<div style={{ position: "relative" }}>
<h3 style={{
margin: "0 0 6px",
fontSize: "clamp(22px, 2.5vw, 32px)",
fontWeight: 700, fontFamily: "var(--font-serif)", fontStyle: "italic",
letterSpacing: "-0.01em",
background: `linear-gradient(135deg, ${c.text} 0%, #fff 80%)`,
WebkitBackgroundClip: "text", WebkitTextFillColor: "transparent",
backgroundClip: "text",
}}>{product.name}</h3>
<p style={{
margin: 0, fontSize: 11.5, color: "var(--text-faint)",
fontFamily: "var(--font-mono)", letterSpacing: "0.1em",
textTransform: "uppercase",
}}>{product.tagline}</p>
</div>
</div>
{/* Card body */}
<div style={{ padding: "28px 36px 32px", display: "flex", flexDirection: "column", gap: 20, flex: 1 }}>
<p style={{ margin: 0, fontSize: 14.5, lineHeight: 1.65, color: "var(--text-dim)" }}>
{product.description}
</p>
{/* Top 3 features */}
<div style={{ display: "grid", gap: 8 }}>
{product.features.slice(0, 3).map((f, i) => (
<div key={i} style={{ display: "flex", gap: 10, fontSize: 13, color: "var(--text)" }}>
<span style={{ color: c.text, flexShrink: 0, fontSize: 11, marginTop: 2 }}>✓</span>
{f}
</div>
))}
{product.features.length > 3 && (
<div style={{ fontSize: 12, color: "var(--text-faint)", fontFamily: "var(--font-mono)", paddingLeft: 21 }}>
+{product.features.length - 3} more capabilities
</div>
)}
</div>
{/* Stack */}
<div style={{ display: "flex", flexWrap: "wrap", gap: 5 }}>
{product.stack.map(s => (
<span key={s} style={{
fontSize: 11, padding: "3px 9px", borderRadius: 5,
background: c.bg, border: `1px solid ${c.border}`,
color: c.text, fontFamily: "var(--font-mono)",
}}>{s}</span>
))}
</div>
{/* CTAs */}
<div style={{ display: "flex", gap: 10, marginTop: "auto", paddingTop: 4 }}>
<a
href={product.url} target="_blank" rel="noopener noreferrer"
style={{
display: "inline-flex", alignItems: "center", gap: 7,
padding: "10px 20px",
background: `linear-gradient(135deg, ${c.bg.replace('0.08','0.6')}, ${c.bg.replace('0.08','0.4')})`,
color: "#fff", borderRadius: 9, fontSize: 13, fontWeight: 600,
textDecoration: "none", border: `1px solid ${c.border}`,
transition: "opacity 0.15s",
}}
onMouseEnter={e => e.currentTarget.style.opacity = "0.82"}
onMouseLeave={e => e.currentTarget.style.opacity = "1"}
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
<polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/>
</svg>
Visit →
</a>
<a href="#/services/web-app-dev" style={{
display: "inline-flex", alignItems: "center", gap: 7,
padding: "10px 16px",
background: "transparent", border: `1px solid ${c.border}`,
color: c.text, borderRadius: 9, fontSize: 13,
textDecoration: "none", transition: "background 0.15s",
}}
onMouseEnter={e => e.currentTarget.style.background = c.bg}
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
>Build Mine →</a>
</div>
</div>
</div>
);
};
const FlagshipProduct = () => {
const D = PORTFOLIO_DATA;
const products = D.ownProducts || [];
if (!products.length) return null;
const rose = { bg: "rgba(244,63,94,0.08)", border: "rgba(244,63,94,0.22)", text: "#fda4af" };
const violet = { bg: "rgba(124,92,255,0.08)", border: "rgba(124,92,255,0.22)", text: "#a78bfa" };
return (
<section style={{ padding: "120px 0", borderTop: "1px solid var(--line)" }}>
<div className="container">
<SectionHeader
eyebrow="Our Own Products"
num="03.5 / 06"
title={<>We don't just build for clients — we build for <span style={{ fontFamily: "var(--font-serif)", fontStyle: "italic", fontWeight: 400 }}>ourselves.</span></>}
sub="These are Aura's own live products — real businesses we built, run, and grow. Visit them. That's exactly what we ship for you."
/>
{/* Products grid */}
<div className="own-products-grid" style={{
display: "grid",
gridTemplateColumns: `repeat(${Math.min(products.length, 2)}, 1fr)`,
gap: 20,
}}>
{products.map(p => <OwnProductCard key={p.id} product={p} />)}
</div>
{/* Bottom nudge */}
<div className="reveal" style={{
marginTop: 20, padding: "22px 32px",
background: "var(--bg-card)", border: "1px solid var(--line)",
borderRadius: "var(--radius)",
display: "flex", alignItems: "center", justifyContent: "space-between",
flexWrap: "wrap", gap: 16,
}}>
<div>
<span style={{ fontSize: 15, fontWeight: 500, color: "var(--text)" }}>
Want Aura to build your product?
</span>
<span style={{
display: "block", fontSize: 13, color: "var(--text-faint)",
marginTop: 3, fontFamily: "var(--font-mono)",
}}>
E-commerce · AI marketplace · SaaS — from $499 · Delivered in 24 weeks
</span>
</div>
<a href="#/contact" style={{
padding: "11px 24px",
background: "var(--text)", color: "var(--bg)",
borderRadius: 9, fontSize: 13.5, fontWeight: 500,
textDecoration: "none", whiteSpace: "nowrap", flexShrink: 0,
}}>Start a Project </a>
</div>
</div>
<style>{`
@keyframes glamour-pulse {
0%, 100% { opacity: 1; box-shadow: 0 0 7px rgba(34,197,94,0.9); }
50% { opacity: 0.55; box-shadow: 0 0 2px rgba(34,197,94,0.3); }
}
@media (max-width: 860px) {
.own-products-grid { grid-template-columns: 1fr !important; }
}
`}</style>
</section>
);
};
window.About = About; window.About = About;
window.Skills = Skills; window.Skills = Skills;
window.SectionHeader = SectionHeader; window.SectionHeader = SectionHeader;
window.Testimonials = Testimonials;
window.FlagshipProduct = FlagshipProduct;