1596 lines
45 KiB
HTML
1596 lines
45 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Vibe Ads — AI Ad Creative Engine</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* ═══════════════════════════════════════════════════════
|
||
CSS RESET & VARIABLES
|
||
═══════════════════════════════════════════════════════ */
|
||
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--bg-primary: #0a0a0a;
|
||
--bg-secondary: #1a1a2e;
|
||
--glass-bg: rgba(255, 255, 255, 0.04);
|
||
--glass-border: rgba(255, 255, 255, 0.08);
|
||
--glass-hover: rgba(255, 255, 255, 0.08);
|
||
--blue: #00d4ff;
|
||
--blue-glow: rgba(0, 212, 255, 0.3);
|
||
--coral: #ff6b6b;
|
||
--coral-glow: rgba(255, 107, 107, 0.3);
|
||
--purple: #a855f7;
|
||
--green: #34d399;
|
||
--yellow: #fbbf24;
|
||
--text-primary: #f0f0f0;
|
||
--text-secondary: rgba(255, 255, 255, 0.6);
|
||
--text-muted: rgba(255, 255, 255, 0.35);
|
||
--radius: 16px;
|
||
--radius-sm: 10px;
|
||
--radius-xs: 6px;
|
||
}
|
||
|
||
html { scroll-behavior: smooth; }
|
||
|
||
body {
|
||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||
background: linear-gradient(135deg, var(--bg-primary) 0%, var(--bg-secondary) 50%, #0d1117 100%);
|
||
color: var(--text-primary);
|
||
min-height: 100vh;
|
||
overflow-x: hidden;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
AMBIENT BACKGROUND
|
||
═══════════════════════════════════════════════════════ */
|
||
.ambient-bg {
|
||
position: fixed;
|
||
top: 0; left: 0; right: 0; bottom: 0;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.ambient-orb {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
filter: blur(120px);
|
||
opacity: 0.15;
|
||
animation: orbFloat 20s ease-in-out infinite;
|
||
}
|
||
|
||
.ambient-orb:nth-child(1) {
|
||
width: 600px; height: 600px;
|
||
background: var(--blue);
|
||
top: -200px; left: -100px;
|
||
animation-delay: 0s;
|
||
}
|
||
|
||
.ambient-orb:nth-child(2) {
|
||
width: 500px; height: 500px;
|
||
background: var(--coral);
|
||
bottom: -200px; right: -100px;
|
||
animation-delay: -7s;
|
||
}
|
||
|
||
.ambient-orb:nth-child(3) {
|
||
width: 400px; height: 400px;
|
||
background: var(--purple);
|
||
top: 50%; left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
animation-delay: -14s;
|
||
opacity: 0.08;
|
||
}
|
||
|
||
@keyframes orbFloat {
|
||
0%, 100% { transform: translate(0, 0) scale(1); }
|
||
25% { transform: translate(30px, -40px) scale(1.05); }
|
||
50% { transform: translate(-20px, 30px) scale(0.95); }
|
||
75% { transform: translate(40px, 20px) scale(1.02); }
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
LAYOUT
|
||
═══════════════════════════════════════════════════════ */
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 0 24px;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
HEADER / HERO
|
||
═══════════════════════════════════════════════════════ */
|
||
.hero {
|
||
padding: 80px 0 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.hero-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 16px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: 100px;
|
||
font-size: 13px;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 24px;
|
||
backdrop-filter: blur(20px);
|
||
}
|
||
|
||
.hero-badge .dot {
|
||
width: 6px; height: 6px;
|
||
background: var(--green);
|
||
border-radius: 50%;
|
||
animation: pulse 2s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(52, 211, 153, 0.4); }
|
||
50% { opacity: 0.8; box-shadow: 0 0 0 6px rgba(52, 211, 153, 0); }
|
||
}
|
||
|
||
.hero h1 {
|
||
font-size: clamp(2.5rem, 6vw, 4.5rem);
|
||
font-weight: 900;
|
||
line-height: 1.05;
|
||
letter-spacing: -0.03em;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.hero h1 .gradient-text {
|
||
background: linear-gradient(135deg, var(--blue) 0%, var(--purple) 50%, var(--coral) 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.hero p {
|
||
font-size: clamp(1rem, 2vw, 1.25rem);
|
||
color: var(--text-secondary);
|
||
max-width: 600px;
|
||
margin: 0 auto 48px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
INPUT SECTION
|
||
═══════════════════════════════════════════════════════ */
|
||
.input-section {
|
||
max-width: 640px;
|
||
margin: 0 auto 60px;
|
||
}
|
||
|
||
.input-wrapper {
|
||
position: relative;
|
||
display: flex;
|
||
gap: 12px;
|
||
padding: 8px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: 16px;
|
||
backdrop-filter: blur(20px);
|
||
transition: border-color 0.3s, box-shadow 0.3s;
|
||
}
|
||
|
||
.input-wrapper:focus-within {
|
||
border-color: rgba(0, 212, 255, 0.3);
|
||
box-shadow: 0 0 0 4px rgba(0, 212, 255, 0.08), 0 8px 32px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.input-wrapper input {
|
||
flex: 1;
|
||
background: none;
|
||
border: none;
|
||
outline: none;
|
||
color: var(--text-primary);
|
||
font-size: 16px;
|
||
font-family: inherit;
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.input-wrapper input::placeholder {
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.btn-generate {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 12px 28px;
|
||
background: linear-gradient(135deg, var(--blue), #0099cc);
|
||
color: #000;
|
||
border: none;
|
||
border-radius: var(--radius-sm);
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.btn-generate:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 20px var(--blue-glow);
|
||
}
|
||
|
||
.btn-generate:active { transform: translateY(0); }
|
||
|
||
.btn-generate:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
.btn-generate svg {
|
||
width: 18px; height: 18px;
|
||
fill: currentColor;
|
||
}
|
||
|
||
.demo-hint {
|
||
text-align: center;
|
||
margin-top: 12px;
|
||
font-size: 13px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.demo-hint a {
|
||
color: var(--blue);
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
border-bottom: 1px solid transparent;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.demo-hint a:hover { border-bottom-color: var(--blue); }
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
PIPELINE / STEPS INDICATOR
|
||
═══════════════════════════════════════════════════════ */
|
||
.pipeline {
|
||
display: none;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
margin-bottom: 48px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.pipeline.active { display: flex; }
|
||
|
||
.pipeline-step {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 16px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: 100px;
|
||
font-size: 13px;
|
||
color: var(--text-muted);
|
||
transition: all 0.5s;
|
||
}
|
||
|
||
.pipeline-step.active {
|
||
color: var(--blue);
|
||
border-color: rgba(0, 212, 255, 0.3);
|
||
background: rgba(0, 212, 255, 0.06);
|
||
}
|
||
|
||
.pipeline-step.done {
|
||
color: var(--green);
|
||
border-color: rgba(52, 211, 153, 0.3);
|
||
background: rgba(52, 211, 153, 0.06);
|
||
}
|
||
|
||
.pipeline-step .step-icon {
|
||
width: 20px; height: 20px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 11px;
|
||
font-weight: 700;
|
||
border: 1.5px solid currentColor;
|
||
transition: all 0.5s;
|
||
}
|
||
|
||
.pipeline-step.done .step-icon {
|
||
background: var(--green);
|
||
border-color: var(--green);
|
||
color: #000;
|
||
}
|
||
|
||
.pipeline-step.active .step-icon {
|
||
border-color: var(--blue);
|
||
animation: spinStep 1.5s linear infinite;
|
||
}
|
||
|
||
@keyframes spinStep {
|
||
0% { box-shadow: 0 0 0 0 rgba(0, 212, 255, 0.4); }
|
||
50% { box-shadow: 0 0 0 4px rgba(0, 212, 255, 0); }
|
||
100% { box-shadow: 0 0 0 0 rgba(0, 212, 255, 0.4); }
|
||
}
|
||
|
||
.pipeline-connector {
|
||
display: flex;
|
||
align-items: center;
|
||
color: var(--text-muted);
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
LOADING STATE
|
||
═══════════════════════════════════════════════════════ */
|
||
.loading-section {
|
||
display: none;
|
||
text-align: center;
|
||
padding: 80px 0;
|
||
}
|
||
|
||
.loading-section.active { display: block; }
|
||
|
||
.loader-ring {
|
||
width: 64px; height: 64px;
|
||
margin: 0 auto 24px;
|
||
border: 3px solid var(--glass-border);
|
||
border-top-color: var(--blue);
|
||
border-right-color: var(--coral);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
|
||
.loading-text {
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.loading-subtext {
|
||
font-size: 14px;
|
||
color: var(--text-muted);
|
||
font-family: 'JetBrains Mono', monospace;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
ERROR STATE
|
||
═══════════════════════════════════════════════════════ */
|
||
.error-banner {
|
||
display: none;
|
||
max-width: 640px;
|
||
margin: 0 auto 32px;
|
||
padding: 16px 20px;
|
||
background: rgba(255, 107, 107, 0.08);
|
||
border: 1px solid rgba(255, 107, 107, 0.2);
|
||
border-radius: var(--radius-sm);
|
||
color: var(--coral);
|
||
font-size: 14px;
|
||
text-align: center;
|
||
}
|
||
|
||
.error-banner.active { display: block; }
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
RESULTS SECTION
|
||
═══════════════════════════════════════════════════════ */
|
||
.results-section {
|
||
display: none;
|
||
padding-bottom: 100px;
|
||
}
|
||
|
||
.results-section.active { display: block; }
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
BRAND PERSONA CARD
|
||
═══════════════════════════════════════════════════════ */
|
||
.brand-section {
|
||
margin-bottom: 64px;
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
animation: fadeUp 0.7s ease-out forwards;
|
||
}
|
||
|
||
@keyframes fadeUp {
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.section-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.1em;
|
||
color: var(--text-muted);
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.section-label::after {
|
||
content: '';
|
||
flex: 1;
|
||
height: 1px;
|
||
background: var(--glass-border);
|
||
}
|
||
|
||
.brand-card {
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: var(--radius);
|
||
backdrop-filter: blur(20px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.brand-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
padding: 32px 32px 24px;
|
||
border-bottom: 1px solid var(--glass-border);
|
||
}
|
||
|
||
.brand-avatar {
|
||
width: 72px; height: 72px;
|
||
border-radius: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28px;
|
||
font-weight: 900;
|
||
color: #000;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.brand-header-text h2 {
|
||
font-size: 24px;
|
||
font-weight: 800;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.brand-header-text .brand-tagline {
|
||
font-size: 15px;
|
||
color: var(--text-secondary);
|
||
font-style: italic;
|
||
}
|
||
|
||
.brand-body {
|
||
padding: 24px 32px 32px;
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||
gap: 24px;
|
||
}
|
||
|
||
.brand-meta {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.brand-meta-label {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.brand-meta-value {
|
||
font-size: 14px;
|
||
color: var(--text-secondary);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.brand-colors {
|
||
display: flex;
|
||
gap: 8px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.brand-color-swatch {
|
||
width: 28px; height: 28px;
|
||
border-radius: 8px;
|
||
border: 2px solid rgba(255,255,255,0.1);
|
||
}
|
||
|
||
.brand-benefits {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.brand-benefit-tag {
|
||
padding: 4px 10px;
|
||
background: rgba(0, 212, 255, 0.08);
|
||
border: 1px solid rgba(0, 212, 255, 0.15);
|
||
border-radius: 100px;
|
||
font-size: 12px;
|
||
color: var(--blue);
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
AD GALLERY
|
||
═══════════════════════════════════════════════════════ */
|
||
.ads-section {
|
||
margin-bottom: 64px;
|
||
}
|
||
|
||
.ads-section .section-label { margin-bottom: 28px; }
|
||
|
||
.ads-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
||
gap: 24px;
|
||
}
|
||
|
||
.ad-card {
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: var(--radius);
|
||
overflow: hidden;
|
||
backdrop-filter: blur(20px);
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
transition: transform 0.3s, box-shadow 0.3s;
|
||
}
|
||
|
||
.ad-card:hover {
|
||
transform: translateY(-4px) !important;
|
||
box-shadow: 0 12px 40px rgba(0,0,0,0.4);
|
||
}
|
||
|
||
.ad-card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid var(--glass-border);
|
||
}
|
||
|
||
.ad-format-label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.ad-format-label .format-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.ad-format-tag {
|
||
padding: 3px 8px;
|
||
border-radius: 4px;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.ad-card-body { padding: 0; }
|
||
|
||
/* ─── MEME FORMAT ─── */
|
||
.meme-mockup {
|
||
position: relative;
|
||
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
|
||
min-height: 280px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24px 20px;
|
||
text-align: center;
|
||
}
|
||
|
||
.meme-mockup::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E") repeat;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.meme-top, .meme-bottom {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: 22px;
|
||
font-weight: 900;
|
||
text-transform: uppercase;
|
||
text-shadow: 2px 2px 4px rgba(0,0,0,0.8), -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.meme-center-emoji {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: 64px;
|
||
margin: 16px 0;
|
||
}
|
||
|
||
/* ─── IMESSAGE FORMAT ─── */
|
||
.imessage-mockup {
|
||
background: #000;
|
||
padding: 20px 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.imessage-bubble {
|
||
max-width: 80%;
|
||
padding: 10px 14px;
|
||
border-radius: 18px;
|
||
font-size: 14px;
|
||
line-height: 1.4;
|
||
word-wrap: break-word;
|
||
}
|
||
|
||
.imessage-bubble.received {
|
||
align-self: flex-start;
|
||
background: #26252a;
|
||
color: #fff;
|
||
border-bottom-left-radius: 4px;
|
||
}
|
||
|
||
.imessage-bubble.sent {
|
||
align-self: flex-end;
|
||
background: #0b84fe;
|
||
color: #fff;
|
||
border-bottom-right-radius: 4px;
|
||
}
|
||
|
||
.imessage-header {
|
||
text-align: center;
|
||
padding: 4px 0 12px;
|
||
}
|
||
|
||
.imessage-header .contact-name {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: rgba(255,255,255,0.9);
|
||
}
|
||
|
||
.imessage-header .contact-label {
|
||
font-size: 10px;
|
||
color: rgba(255,255,255,0.35);
|
||
}
|
||
|
||
.imessage-time {
|
||
text-align: center;
|
||
font-size: 11px;
|
||
color: rgba(255,255,255,0.25);
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
/* ─── TWEET FORMAT ─── */
|
||
.tweet-mockup {
|
||
padding: 20px;
|
||
background: #000;
|
||
}
|
||
|
||
.tweet-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.tweet-avatar {
|
||
width: 44px; height: 44px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 800;
|
||
font-size: 18px;
|
||
color: #000;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tweet-names {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.tweet-display-name {
|
||
font-size: 15px;
|
||
font-weight: 700;
|
||
color: #e7e9ea;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.tweet-verified {
|
||
width: 18px; height: 18px;
|
||
background: #1d9bf0;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 10px;
|
||
color: #fff;
|
||
}
|
||
|
||
.tweet-handle {
|
||
font-size: 14px;
|
||
color: #71767b;
|
||
}
|
||
|
||
.tweet-text {
|
||
font-size: 16px;
|
||
line-height: 1.5;
|
||
color: #e7e9ea;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.tweet-time {
|
||
font-size: 13px;
|
||
color: #71767b;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid #2f3336;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.tweet-stats {
|
||
display: flex;
|
||
gap: 24px;
|
||
}
|
||
|
||
.tweet-stat {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 13px;
|
||
color: #71767b;
|
||
}
|
||
|
||
.tweet-stat .stat-icon {
|
||
font-size: 16px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* ─── STAT CARD FORMAT ─── */
|
||
.stat-mockup {
|
||
padding: 40px 24px;
|
||
text-align: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stat-mockup::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%; left: -50%;
|
||
width: 200%; height: 200%;
|
||
background: radial-gradient(circle at center, rgba(0, 212, 255, 0.08) 0%, transparent 50%);
|
||
animation: statPulse 4s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes statPulse {
|
||
0%, 100% { transform: scale(1); opacity: 0.5; }
|
||
50% { transform: scale(1.1); opacity: 1; }
|
||
}
|
||
|
||
.stat-big-number {
|
||
position: relative;
|
||
font-size: 72px;
|
||
font-weight: 900;
|
||
letter-spacing: -0.03em;
|
||
line-height: 1;
|
||
margin-bottom: 12px;
|
||
background: linear-gradient(135deg, var(--blue), var(--purple));
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
}
|
||
|
||
.stat-label {
|
||
position: relative;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.stat-subtext {
|
||
position: relative;
|
||
font-size: 14px;
|
||
color: var(--text-secondary);
|
||
max-width: 300px;
|
||
margin: 0 auto 16px;
|
||
}
|
||
|
||
.stat-source {
|
||
position: relative;
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
font-style: italic;
|
||
}
|
||
|
||
/* ─── UGC / REVIEW FORMAT ─── */
|
||
.ugc-mockup {
|
||
padding: 24px 20px;
|
||
}
|
||
|
||
.ugc-stars {
|
||
display: flex;
|
||
gap: 2px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.ugc-star {
|
||
font-size: 18px;
|
||
color: #fbbf24;
|
||
}
|
||
|
||
.ugc-star.empty { color: #333; }
|
||
|
||
.ugc-title {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
margin-bottom: 8px;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.ugc-body {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.ugc-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding-top: 12px;
|
||
border-top: 1px solid var(--glass-border);
|
||
}
|
||
|
||
.ugc-reviewer {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.ugc-reviewer-avatar {
|
||
width: 32px; height: 32px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--coral), var(--purple));
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
}
|
||
|
||
.ugc-reviewer-name {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.ugc-verified {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 11px;
|
||
color: var(--green);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.ugc-platform {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
/* ─── BILLBOARD FORMAT ─── */
|
||
.billboard-mockup {
|
||
position: relative;
|
||
min-height: 300px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
text-align: center;
|
||
padding: 40px 32px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.billboard-headline {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: clamp(28px, 4vw, 40px);
|
||
font-weight: 900;
|
||
text-transform: uppercase;
|
||
letter-spacing: -0.02em;
|
||
line-height: 1.1;
|
||
margin-bottom: 16px;
|
||
max-width: 500px;
|
||
}
|
||
|
||
.billboard-subline {
|
||
position: relative;
|
||
z-index: 1;
|
||
font-size: 16px;
|
||
color: rgba(255,255,255,0.7);
|
||
margin-bottom: 24px;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.billboard-cta {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: inline-flex;
|
||
padding: 12px 32px;
|
||
background: #fff;
|
||
color: #000;
|
||
font-size: 14px;
|
||
font-weight: 800;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
border-radius: 100px;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
FOOTER
|
||
═══════════════════════════════════════════════════════ */
|
||
.footer {
|
||
text-align: center;
|
||
padding: 40px 0 60px;
|
||
border-top: 1px solid var(--glass-border);
|
||
}
|
||
|
||
.footer-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 16px;
|
||
background: var(--glass-bg);
|
||
border: 1px solid var(--glass-border);
|
||
border-radius: 100px;
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
backdrop-filter: blur(20px);
|
||
}
|
||
|
||
.footer-badge .cc-icon {
|
||
width: 16px;
|
||
height: 16px;
|
||
background: linear-gradient(135deg, var(--blue), var(--purple));
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 9px;
|
||
font-weight: 900;
|
||
color: #fff;
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
RESPONSIVE
|
||
═══════════════════════════════════════════════════════ */
|
||
@media (max-width: 768px) {
|
||
.hero { padding: 48px 0 24px; }
|
||
.brand-header { flex-direction: column; text-align: center; }
|
||
.brand-body { grid-template-columns: 1fr; }
|
||
.ads-grid { grid-template-columns: 1fr; }
|
||
.input-wrapper { flex-direction: column; }
|
||
.btn-generate { justify-content: center; }
|
||
.pipeline-step span:not(.step-icon) { display: none; }
|
||
.brand-header { padding: 24px; }
|
||
.brand-body { padding: 20px; }
|
||
}
|
||
|
||
/* ═══════════════════════════════════════════════════════
|
||
ANIMATIONS
|
||
═══════════════════════════════════════════════════════ */
|
||
.ad-card.animate {
|
||
animation: fadeUp 0.6s ease-out forwards;
|
||
}
|
||
|
||
.ad-card.animate:nth-child(2) { animation-delay: 0.1s; }
|
||
.ad-card.animate:nth-child(3) { animation-delay: 0.2s; }
|
||
.ad-card.animate:nth-child(4) { animation-delay: 0.3s; }
|
||
.ad-card.animate:nth-child(5) { animation-delay: 0.4s; }
|
||
.ad-card.animate:nth-child(6) { animation-delay: 0.5s; }
|
||
|
||
/* Typing animation for iMessage */
|
||
.typing-indicator {
|
||
display: flex;
|
||
gap: 4px;
|
||
padding: 10px 14px;
|
||
align-self: flex-start;
|
||
background: #26252a;
|
||
border-radius: 18px;
|
||
border-bottom-left-radius: 4px;
|
||
}
|
||
|
||
.typing-dot {
|
||
width: 7px; height: 7px;
|
||
background: rgba(255,255,255,0.3);
|
||
border-radius: 50%;
|
||
animation: typingBounce 1.4s ease-in-out infinite;
|
||
}
|
||
|
||
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
|
||
.typing-dot:nth-child(3) { animation-delay: 0.4s; }
|
||
|
||
@keyframes typingBounce {
|
||
0%, 60%, 100% { transform: translateY(0); }
|
||
30% { transform: translateY(-4px); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- Ambient background -->
|
||
<div class="ambient-bg">
|
||
<div class="ambient-orb"></div>
|
||
<div class="ambient-orb"></div>
|
||
<div class="ambient-orb"></div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
|
||
<!-- Hero -->
|
||
<section class="hero">
|
||
<div class="hero-badge">
|
||
<span class="dot"></span>
|
||
AI-Powered Creative Engine
|
||
</div>
|
||
<h1>
|
||
Generate <span class="gradient-text">Scroll-Stopping</span><br>
|
||
Ad Creative in Seconds
|
||
</h1>
|
||
<p>
|
||
Paste any website URL. Our AI analyzes the brand, extracts their DNA, and generates
|
||
ad concepts across 6 high-converting formats — instantly.
|
||
</p>
|
||
</section>
|
||
|
||
<!-- Input -->
|
||
<section class="input-section">
|
||
<div class="input-wrapper">
|
||
<input
|
||
type="url"
|
||
id="urlInput"
|
||
placeholder="Paste a website URL — e.g., https://thevibemarketer.com"
|
||
autocomplete="off"
|
||
spellcheck="false"
|
||
>
|
||
<button class="btn-generate" id="generateBtn" onclick="handleGenerate()">
|
||
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
|
||
Generate
|
||
</button>
|
||
</div>
|
||
<div class="demo-hint">
|
||
or <a onclick="loadDemo()">try the demo</a> with pre-generated results
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Pipeline Steps -->
|
||
<div class="pipeline" id="pipeline">
|
||
<div class="pipeline-step" id="step-fetch">
|
||
<span class="step-icon">1</span>
|
||
<span>Fetching Site</span>
|
||
</div>
|
||
<div class="pipeline-connector">→</div>
|
||
<div class="pipeline-step" id="step-analyze">
|
||
<span class="step-icon">2</span>
|
||
<span>Analyzing Brand</span>
|
||
</div>
|
||
<div class="pipeline-connector">→</div>
|
||
<div class="pipeline-step" id="step-generate">
|
||
<span class="step-icon">3</span>
|
||
<span>Generating Ads</span>
|
||
</div>
|
||
<div class="pipeline-connector">→</div>
|
||
<div class="pipeline-step" id="step-render">
|
||
<span class="step-icon">4</span>
|
||
<span>Rendering</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Loading -->
|
||
<section class="loading-section" id="loadingSection">
|
||
<div class="loader-ring"></div>
|
||
<div class="loading-text" id="loadingText">Analyzing brand identity...</div>
|
||
<div class="loading-subtext" id="loadingSubtext">Extracting colors, voice, and positioning</div>
|
||
</section>
|
||
|
||
<!-- Error -->
|
||
<div class="error-banner" id="errorBanner"></div>
|
||
|
||
<!-- Results -->
|
||
<section class="results-section" id="resultsSection">
|
||
<!-- Brand Persona Card -->
|
||
<div class="brand-section" id="brandSection"></div>
|
||
|
||
<!-- Ad Gallery -->
|
||
<div class="ads-section" id="adsSection"></div>
|
||
</section>
|
||
|
||
<!-- Footer -->
|
||
<footer class="footer">
|
||
<div class="footer-badge">
|
||
<span class="cc-icon">⚡</span>
|
||
Powered by Claude Code × Anthropic
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
/* ═══════════════════════════════════════════════════════
|
||
MAIN APPLICATION LOGIC
|
||
═══════════════════════════════════════════════════════ */
|
||
|
||
const API_BASE = window.location.origin;
|
||
|
||
// ─── Pipeline Step Management ───
|
||
function setStep(stepId, state) {
|
||
const el = document.getElementById(stepId);
|
||
if (!el) return;
|
||
el.classList.remove('active', 'done');
|
||
if (state) el.classList.add(state);
|
||
if (state === 'done') {
|
||
el.querySelector('.step-icon').textContent = '✓';
|
||
}
|
||
}
|
||
|
||
function showPipeline() {
|
||
const p = document.getElementById('pipeline');
|
||
p.classList.add('active');
|
||
// Reset all steps
|
||
['step-fetch', 'step-analyze', 'step-generate', 'step-render'].forEach(id => {
|
||
setStep(id, null);
|
||
const el = document.getElementById(id);
|
||
if (el) {
|
||
const num = id.split('-')[1] === 'fetch' ? '1' : id.split('-')[1] === 'analyze' ? '2' : id.split('-')[1] === 'generate' ? '3' : '4';
|
||
el.querySelector('.step-icon').textContent = num;
|
||
}
|
||
});
|
||
}
|
||
|
||
function setLoading(text, subtext) {
|
||
document.getElementById('loadingText').textContent = text;
|
||
document.getElementById('loadingSubtext').textContent = subtext;
|
||
}
|
||
|
||
// ─── Main Generate Handler ───
|
||
async function handleGenerate() {
|
||
const urlInput = document.getElementById('urlInput');
|
||
let url = urlInput.value.trim();
|
||
if (!url) {
|
||
url = 'https://thevibemarketer.com';
|
||
urlInput.value = url;
|
||
}
|
||
|
||
// Ensure protocol
|
||
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
||
url = 'https://' + url;
|
||
urlInput.value = url;
|
||
}
|
||
|
||
const btn = document.getElementById('generateBtn');
|
||
btn.disabled = true;
|
||
|
||
// Reset UI
|
||
document.getElementById('errorBanner').classList.remove('active');
|
||
document.getElementById('resultsSection').classList.remove('active');
|
||
document.getElementById('loadingSection').classList.add('active');
|
||
showPipeline();
|
||
|
||
try {
|
||
// Step 1: Fetch website
|
||
setStep('step-fetch', 'active');
|
||
setLoading('Fetching website content...', `Downloading ${new URL(url).hostname}`);
|
||
|
||
let websiteContent = '';
|
||
try {
|
||
const fetchRes = await fetch(`${API_BASE}/api/fetch-url`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ url })
|
||
});
|
||
const fetchData = await fetchRes.json();
|
||
if (fetchData.success) {
|
||
websiteContent = fetchData.content;
|
||
}
|
||
} catch (e) {
|
||
console.warn('URL fetch failed, continuing with URL only:', e);
|
||
}
|
||
|
||
setStep('step-fetch', 'done');
|
||
|
||
// Step 2: Analyze with Claude
|
||
setStep('step-analyze', 'active');
|
||
setLoading('AI analyzing brand identity...', 'Extracting colors, voice, tone, and positioning');
|
||
|
||
await sleep(300); // Brief pause for visual effect
|
||
|
||
setStep('step-analyze', 'done');
|
||
setStep('step-generate', 'active');
|
||
setLoading('Generating ad creative concepts...', 'Creating 6 unique formats with tailored copy');
|
||
|
||
const genRes = await fetch(`${API_BASE}/api/generate`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ url, websiteContent: websiteContent || `Website URL: ${url}. Unable to fetch content directly, please analyze based on the URL and domain name.` })
|
||
});
|
||
const genData = await genRes.json();
|
||
|
||
if (!genData.success) {
|
||
throw new Error(genData.error || 'Generation failed');
|
||
}
|
||
|
||
setStep('step-generate', 'done');
|
||
|
||
// Step 3: Render results
|
||
setStep('step-render', 'active');
|
||
setLoading('Rendering ad mockups...', 'Building visual layouts');
|
||
await sleep(500);
|
||
setStep('step-render', 'done');
|
||
|
||
document.getElementById('loadingSection').classList.remove('active');
|
||
renderResults(genData.data);
|
||
|
||
} catch (err) {
|
||
console.error('Error:', err);
|
||
document.getElementById('loadingSection').classList.remove('active');
|
||
|
||
// Show error and fall back to demo
|
||
const errorBanner = document.getElementById('errorBanner');
|
||
errorBanner.textContent = `⚠️ ${err.message} — Loading demo results instead...`;
|
||
errorBanner.classList.add('active');
|
||
|
||
setTimeout(() => loadDemo(), 1500);
|
||
} finally {
|
||
btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
// ─── Render Results ───
|
||
function renderResults(data) {
|
||
const { brand, ads } = data;
|
||
const resultsSection = document.getElementById('resultsSection');
|
||
|
||
// Brand Persona Card
|
||
document.getElementById('brandSection').innerHTML = `
|
||
<div class="section-label">🎯 Brand Persona Analysis</div>
|
||
<div class="brand-card">
|
||
<div class="brand-header">
|
||
<div class="brand-avatar" style="background: linear-gradient(135deg, ${brand.primaryColor || '#00d4ff'}, ${brand.secondaryColor || '#a855f7'})">
|
||
${(brand.name || 'B')[0]}
|
||
</div>
|
||
<div class="brand-header-text">
|
||
<h2>${escHtml(brand.name)}</h2>
|
||
<div class="brand-tagline">"${escHtml(brand.tagline)}"</div>
|
||
</div>
|
||
</div>
|
||
<div class="brand-body">
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Brand Voice</div>
|
||
<div class="brand-meta-value">${escHtml(brand.voice)}</div>
|
||
</div>
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Positioning</div>
|
||
<div class="brand-meta-value">${escHtml(brand.positioning)}</div>
|
||
</div>
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Industry</div>
|
||
<div class="brand-meta-value">${escHtml(brand.industry)}</div>
|
||
</div>
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Target Audience</div>
|
||
<div class="brand-meta-value">${escHtml(brand.targetAudience)}</div>
|
||
</div>
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Emotional Tone</div>
|
||
<div class="brand-meta-value">${escHtml(brand.emotionalTone)}</div>
|
||
</div>
|
||
<div class="brand-meta">
|
||
<div class="brand-meta-label">Brand Colors</div>
|
||
<div class="brand-colors">
|
||
<div class="brand-color-swatch" style="background: ${brand.primaryColor}" title="${brand.primaryColor}"></div>
|
||
<div class="brand-color-swatch" style="background: ${brand.secondaryColor}" title="${brand.secondaryColor}"></div>
|
||
<div class="brand-color-swatch" style="background: ${brand.accentColor}" title="${brand.accentColor}"></div>
|
||
</div>
|
||
</div>
|
||
<div class="brand-meta" style="grid-column: 1 / -1;">
|
||
<div class="brand-meta-label">Key Benefits</div>
|
||
<div class="brand-benefits">
|
||
${(brand.keyBenefits || []).map(b => `<span class="brand-benefit-tag">${escHtml(b)}</span>`).join('')}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Ad Gallery
|
||
const adsHtml = `
|
||
<div class="section-label">🎨 Generated Ad Concepts</div>
|
||
<div class="ads-grid">
|
||
${renderMemeCard(ads.meme, brand)}
|
||
${renderImessageCard(ads.iMessage, brand)}
|
||
${renderTweetCard(ads.tweet, brand)}
|
||
${renderStatCard(ads.statCard)}
|
||
${renderUgcCard(ads.ugc)}
|
||
${renderBillboardCard(ads.billboard, brand)}
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('adsSection').innerHTML = adsHtml;
|
||
|
||
resultsSection.classList.add('active');
|
||
|
||
// Animate cards in
|
||
setTimeout(() => {
|
||
document.querySelectorAll('.ad-card').forEach(card => card.classList.add('animate'));
|
||
}, 100);
|
||
|
||
// Scroll to results
|
||
document.getElementById('brandSection').scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
|
||
// ─── Ad Card Renderers ───
|
||
function renderMemeCard(meme, brand) {
|
||
const emojis = ['😤', '💀', '🤯', '😂', '🔥', '👀', '💪', '🧠'];
|
||
const emoji = emojis[Math.floor(Math.random() * emojis.length)];
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">🖼️</span> Meme</div>
|
||
<span class="ad-format-tag" style="background: rgba(251,191,36,0.15); color: #fbbf24;">Social</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="meme-mockup">
|
||
<div class="meme-top">${escHtml(meme.topText)}</div>
|
||
<div class="meme-center-emoji">${emoji}</div>
|
||
<div class="meme-bottom">${escHtml(meme.bottomText)}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderImessageCard(im, brand) {
|
||
const messages = im.messages || [];
|
||
const bubbles = messages.map(m => {
|
||
const cls = m.sender === 'user' ? 'sent' : 'received';
|
||
return `<div class="imessage-bubble ${cls}">${escHtml(m.text)}</div>`;
|
||
}).join('');
|
||
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">💬</span> iMessage</div>
|
||
<span class="ad-format-tag" style="background: rgba(11,132,254,0.15); color: #0b84fe;">Conversational</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="imessage-mockup">
|
||
<div class="imessage-header">
|
||
<div class="contact-name">bestie 🫶</div>
|
||
<div class="contact-label">iMessage</div>
|
||
</div>
|
||
<div class="imessage-time">Today ${new Date().getHours() % 12 || 12}:${String(new Date().getMinutes()).padStart(2, '0')} ${new Date().getHours() >= 12 ? 'PM' : 'AM'}</div>
|
||
${bubbles}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderTweetCard(tweet, brand) {
|
||
const now = new Date();
|
||
const timeStr = `${now.getHours() % 12 || 12}:${String(now.getMinutes()).padStart(2, '0')} ${now.getHours() >= 12 ? 'PM' : 'AM'} · ${now.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`;
|
||
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">𝕏</span> Tweet</div>
|
||
<span class="ad-format-tag" style="background: rgba(29,155,240,0.15); color: #1d9bf0;">Viral</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="tweet-mockup">
|
||
<div class="tweet-header">
|
||
<div class="tweet-avatar" style="background: linear-gradient(135deg, ${brand.primaryColor || '#00d4ff'}, ${brand.secondaryColor || '#a855f7'})">${(tweet.displayName || 'B')[0]}</div>
|
||
<div class="tweet-names">
|
||
<div class="tweet-display-name">
|
||
${escHtml(tweet.displayName)}
|
||
<div class="tweet-verified">✓</div>
|
||
</div>
|
||
<div class="tweet-handle">${escHtml(tweet.handle)}</div>
|
||
</div>
|
||
</div>
|
||
<div class="tweet-text">${escHtml(tweet.text)}</div>
|
||
<div class="tweet-time">${timeStr}</div>
|
||
<div class="tweet-stats">
|
||
<div class="tweet-stat"><span class="stat-icon">💬</span> ${escHtml(tweet.replies)}</div>
|
||
<div class="tweet-stat"><span class="stat-icon">🔁</span> ${escHtml(tweet.retweets)}</div>
|
||
<div class="tweet-stat"><span class="stat-icon">❤️</span> ${escHtml(tweet.likes)}</div>
|
||
<div class="tweet-stat"><span class="stat-icon">📊</span> ${escHtml(tweet.views)}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderStatCard(stat) {
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">📊</span> Stat Card</div>
|
||
<span class="ad-format-tag" style="background: rgba(0,212,255,0.15); color: #00d4ff;">Data</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="stat-mockup">
|
||
<div class="stat-big-number">${escHtml(stat.bigNumber)}</div>
|
||
<div class="stat-label">${escHtml(stat.label)}</div>
|
||
<div class="stat-subtext">${escHtml(stat.subtext)}</div>
|
||
<div class="stat-source">${escHtml(stat.source)}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderUgcCard(ugc) {
|
||
const stars = Array.from({ length: 5 }, (_, i) =>
|
||
`<span class="ugc-star ${i < ugc.rating ? '' : 'empty'}">★</span>`
|
||
).join('');
|
||
const initials = (ugc.reviewerName || 'U').split(' ').map(n => n[0]).join('');
|
||
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">⭐</span> UGC Review</div>
|
||
<span class="ad-format-tag" style="background: rgba(255,107,107,0.15); color: #ff6b6b;">Social Proof</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="ugc-mockup">
|
||
<div class="ugc-stars">${stars}</div>
|
||
<div class="ugc-title">${escHtml(ugc.title)}</div>
|
||
<div class="ugc-body">${escHtml(ugc.body)}</div>
|
||
<div class="ugc-meta">
|
||
<div class="ugc-reviewer">
|
||
<div class="ugc-reviewer-avatar">${initials}</div>
|
||
<div>
|
||
<div class="ugc-reviewer-name">${escHtml(ugc.reviewerName)}</div>
|
||
<div class="ugc-platform">${escHtml(ugc.platform)}</div>
|
||
</div>
|
||
</div>
|
||
${ugc.verified ? '<div class="ugc-verified">✓ Verified Purchase</div>' : ''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function renderBillboardCard(billboard, brand) {
|
||
return `
|
||
<div class="ad-card">
|
||
<div class="ad-card-header">
|
||
<div class="ad-format-label"><span class="format-icon">🏗️</span> Billboard</div>
|
||
<span class="ad-format-tag" style="background: rgba(168,85,247,0.15); color: #a855f7;">Impact</span>
|
||
</div>
|
||
<div class="ad-card-body">
|
||
<div class="billboard-mockup" style="background: linear-gradient(135deg, ${brand.primaryColor || '#1a1a2e'}, ${brand.secondaryColor || '#0a0a0a'}, ${brand.accentColor || '#1a1a2e'})">
|
||
<div class="billboard-headline">${escHtml(billboard.headline)}</div>
|
||
<div class="billboard-subline">${escHtml(billboard.subline)}</div>
|
||
<div class="billboard-cta">${escHtml(billboard.cta)}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// ─── Utility ───
|
||
function escHtml(str) {
|
||
if (!str) return '';
|
||
const div = document.createElement('div');
|
||
div.textContent = str;
|
||
return div.innerHTML;
|
||
}
|
||
|
||
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
||
|
||
// ─── Demo / Fallback Data ───
|
||
function loadDemo() {
|
||
document.getElementById('urlInput').value = 'https://thevibemarketer.com';
|
||
document.getElementById('errorBanner').classList.remove('active');
|
||
document.getElementById('loadingSection').classList.remove('active');
|
||
document.getElementById('pipeline').classList.remove('active');
|
||
|
||
renderResults(DEMO_DATA);
|
||
}
|
||
|
||
const DEMO_DATA = {
|
||
brand: {
|
||
name: "The Vibe Marketer",
|
||
tagline: "AI-powered marketing that actually vibes with your audience",
|
||
voice: "Confident yet approachable. Speaks in the language of modern creators — casual but backed by serious strategy. Avoids corporate jargon, embraces cultural fluency.",
|
||
positioning: "The AI marketing platform for brands that want to feel native on every platform, not just present. Where data meets cultural relevance.",
|
||
primaryColor: "#6C5CE7",
|
||
secondaryColor: "#00CEC9",
|
||
accentColor: "#FD79A8",
|
||
industry: "AI Marketing / AdTech",
|
||
targetAudience: "DTC founders, growth marketers, and creative directors at brands doing $1M-50M who need scroll-stopping creative at scale",
|
||
keyBenefits: ["AI-generated ad creative in seconds", "Platform-native formats", "Brand voice consistency", "10x content velocity", "Data-driven creative decisions"],
|
||
emotionalTone: "Energetic, empowering, and slightly irreverent — makes you feel like you're in on the future"
|
||
},
|
||
ads: {
|
||
meme: {
|
||
topText: "MY DESIGNER WHEN I ASK FOR 47 AD VARIATIONS BY TOMORROW",
|
||
bottomText: "THE VIBE MARKETER AI: 'SAY LESS FAM'",
|
||
context: "Stressed person transforming into calm person meme format"
|
||
},
|
||
iMessage: {
|
||
messages: [
|
||
{ sender: "friend", text: "how are you pumping out so much content lately?? your ads are everywhere 👀" },
|
||
{ sender: "user", text: "lol you're gonna think I'm lying" },
|
||
{ sender: "friend", text: "try me" },
|
||
{ sender: "user", text: "AI generates all my ad creative now. thevibemarketer.com — it literally analyzes my brand and creates scroll-stoppers in like 30 seconds" },
|
||
{ sender: "friend", text: "BRB canceling my agency retainer 😭" }
|
||
]
|
||
},
|
||
tweet: {
|
||
handle: "@thevibemarketer",
|
||
displayName: "The Vibe Marketer",
|
||
text: "Hot take: your brand doesn't have a content problem.\n\nIt has a creative velocity problem. 🚀\n\nWe just helped a DTC brand go from 4 ad variants/week to 200+.\n\nSame brand voice. Same quality. 50x the output.\n\nThe future of marketing isn't more people. It's better AI.",
|
||
likes: "4.2K",
|
||
retweets: "1.1K",
|
||
replies: "847",
|
||
views: "892K"
|
||
},
|
||
statCard: {
|
||
bigNumber: "50×",
|
||
label: "Creative Output Increase",
|
||
subtext: "Average increase in ad variants produced per week after switching to AI-powered creative generation",
|
||
source: "The Vibe Marketer — 2024 Customer Data"
|
||
},
|
||
ugc: {
|
||
reviewerName: "Sarah M.",
|
||
rating: 5,
|
||
title: "Replaced our entire creative team's bottleneck",
|
||
body: "We were spending $15K/month on a creative agency and waiting 2 weeks for ad variants. Now I paste our landing page URL, and in 30 seconds I have more creative options than my agency delivered in a month. The brand voice matching is scary accurate — my team couldn't tell the difference.",
|
||
platform: "G2 Reviews",
|
||
verified: true
|
||
},
|
||
billboard: {
|
||
headline: "YOUR BRAND DESERVES BETTER THAN GENERIC ADS",
|
||
subline: "AI creative that actually understands your vibe",
|
||
cta: "Start Creating Free →"
|
||
}
|
||
}
|
||
};
|
||
|
||
// ─── Keyboard shortcut ───
|
||
document.getElementById('urlInput').addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter') handleGenerate();
|
||
});
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|