chore: clean up root directory, move docs to /docs and legacy plans to /docs_archive
This commit is contained in:
@@ -1,61 +0,0 @@
|
|||||||
# Continue Configuration for Product OS
|
|
||||||
# https://docs.continue.dev/reference/config
|
|
||||||
|
|
||||||
name: Product OS
|
|
||||||
|
|
||||||
# Models - using Gemini via your Control Plane
|
|
||||||
models:
|
|
||||||
- name: Gemini (Product OS)
|
|
||||||
provider: openai # Continue uses OpenAI-compatible API format
|
|
||||||
model: gemini-1.5-flash
|
|
||||||
apiBase: http://localhost:8080 # Your Control Plane
|
|
||||||
apiKey: not-needed # Auth handled by Control Plane
|
|
||||||
|
|
||||||
# Default model for chat
|
|
||||||
model: Gemini (Product OS)
|
|
||||||
|
|
||||||
# MCP Servers - your Product OS tools
|
|
||||||
experimental:
|
|
||||||
modelContextProtocolServers:
|
|
||||||
- name: productos
|
|
||||||
command: npx
|
|
||||||
args:
|
|
||||||
- tsx
|
|
||||||
- /Users/markhenderson/Cursor Projects/Master Biz AI/platform/backend/mcp-adapter/src/index.ts
|
|
||||||
env:
|
|
||||||
CONTROL_PLANE_URL: http://localhost:8080
|
|
||||||
TENANT_ID: t_continue
|
|
||||||
|
|
||||||
# Context providers
|
|
||||||
contextProviders:
|
|
||||||
- name: code
|
|
||||||
params:
|
|
||||||
nFinal: 5
|
|
||||||
nRetrieve: 10
|
|
||||||
- name: docs
|
|
||||||
- name: terminal
|
|
||||||
- name: problems
|
|
||||||
|
|
||||||
# Slash commands
|
|
||||||
slashCommands:
|
|
||||||
- name: deploy
|
|
||||||
description: Deploy a service to Cloud Run
|
|
||||||
- name: analytics
|
|
||||||
description: Get funnel analytics
|
|
||||||
- name: marketing
|
|
||||||
description: Generate marketing content
|
|
||||||
|
|
||||||
# Custom instructions for the AI
|
|
||||||
systemMessage: |
|
|
||||||
You are Product OS, an AI assistant for building and operating SaaS products on Google Cloud.
|
|
||||||
|
|
||||||
You have access to these tools via MCP:
|
|
||||||
- deploy_service: Deploy Cloud Run services
|
|
||||||
- get_service_status: Check deployment health
|
|
||||||
- get_funnel_analytics: Analyze conversion funnels
|
|
||||||
- get_top_drivers: Understand what drives metrics
|
|
||||||
- generate_marketing_posts: Create social media content
|
|
||||||
- chat_with_gemini: General AI conversation
|
|
||||||
|
|
||||||
When users ask to deploy, analyze, or generate content, use the appropriate tool.
|
|
||||||
Always confirm before deploying to production.
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,33 +0,0 @@
|
|||||||
# Coolify White Label — Vibn Branding
|
|
||||||
|
|
||||||
Users never see the Coolify UI directly (it's backend infrastructure), but if you
|
|
||||||
want the admin panel to match, add these to the Coolify `.env` on the server.
|
|
||||||
|
|
||||||
## Steps
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# SSH into the server
|
|
||||||
gcloud compute ssh coolify-server-mtl --zone=northamerica-northeast1-a
|
|
||||||
|
|
||||||
# Edit the Coolify env file
|
|
||||||
sudo nano /data/coolify/source/.env
|
|
||||||
```
|
|
||||||
|
|
||||||
## Add These Lines
|
|
||||||
|
|
||||||
```env
|
|
||||||
COOLIFY_WHITE_LABELED=true
|
|
||||||
COOLIFY_WHITE_LABELED_ICON=https://vibnai.com/vibn-icon.png
|
|
||||||
```
|
|
||||||
|
|
||||||
The icon URL must be publicly accessible. Once your logo is live on vibnai.com,
|
|
||||||
point this to the 180×180 PNG version.
|
|
||||||
|
|
||||||
## Apply Changes
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /data/coolify/source
|
|
||||||
docker compose down && docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: These settings are lost on Coolify upgrades — re-apply if you update Coolify.
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
; Vibn branding overrides for Gitea
|
|
||||||
; These settings go in $GITEA_CUSTOM/conf/app.ini
|
|
||||||
; On the server this is at: /data/gitea/data/gitea/custom/conf/app.ini
|
|
||||||
|
|
||||||
[ui]
|
|
||||||
SITE_TITLE = Vibn
|
|
||||||
|
|
||||||
[server]
|
|
||||||
; Optional: change the landing page description meta tag
|
|
||||||
LANDING_PAGE = home
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Gitea Brand Assets — Drop Files Here
|
|
||||||
|
|
||||||
Place the following files in this directory, then copy them to the server at:
|
|
||||||
`/data/gitea/data/gitea/custom/public/assets/img/`
|
|
||||||
|
|
||||||
Then restart Gitea for changes to take effect.
|
|
||||||
|
|
||||||
## Required Files
|
|
||||||
|
|
||||||
| File | Format | Dimensions | Purpose |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `logo.svg` | SVG | Vector | Main nav logo |
|
|
||||||
| `logo.png` | PNG | 512×512 | Open Graph previews |
|
|
||||||
| `favicon.svg` | SVG | Vector | Browser tab (primary) |
|
|
||||||
| `favicon.png` | PNG | 180×180 | Browser tab (fallback) |
|
|
||||||
| `apple-touch-icon.png` | PNG | 180×180 | iOS bookmark icon |
|
|
||||||
| `avatar_default.png` | PNG | 200×200 | Default user avatar |
|
|
||||||
|
|
||||||
## How to Deploy
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# From local machine:
|
|
||||||
gcloud compute scp branding/gitea/public/assets/img/* \
|
|
||||||
coolify-server-mtl:/data/gitea/data/gitea/custom/public/assets/img/ \
|
|
||||||
--zone=northamerica-northeast1-a
|
|
||||||
|
|
||||||
gcloud compute scp branding/gitea/conf/app.ini \
|
|
||||||
coolify-server-mtl:/data/gitea/data/gitea/custom/conf/app.ini \
|
|
||||||
--zone=northamerica-northeast1-a
|
|
||||||
|
|
||||||
# Then restart Gitea:
|
|
||||||
gcloud compute ssh coolify-server-mtl --zone=northamerica-northeast1-a \
|
|
||||||
--command="sudo docker restart gitea-bcc4k0kog0w4ckkskg8gwggc"
|
|
||||||
```
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>vibn — Homepage</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#444441;--mid:#6B7280;--muted:#9CA3AF;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#FFFFFF;--border:#E5E7EB;--serif:'Lora',Georgia,serif;--sans:'Inter',sans-serif;}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFA,#F3F7F5);min-height:100vh;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
nav{background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 52px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;}
|
|
||||||
.logo-box{width:30px;height:30px;background:var(--ink);border-radius:7px;display:flex;align-items:center;justify-content:center;}
|
|
||||||
.btn-ink{background:linear-gradient(135deg,#1F3D2B,#2F6F4F);color:#FFFFFF;border:none;border-radius:8px;padding:9px 22px;font-family:var(--sans);font-size:13.5px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(31,61,43,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink:hover{box-shadow:0 10px 25px rgba(31,61,43,0.15),0 0 0 6px rgba(47,111,79,0.2);transform:translateY(-1px);}
|
|
||||||
.btn-ink-lg{background:linear-gradient(135deg,#1F3D2B,#2F6F4F);color:#FFFFFF;border:none;border-radius:10px;padding:15px 36px;font-family:var(--sans);font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(31,61,43,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink-lg:hover{box-shadow:0 10px 25px rgba(31,61,43,0.15),0 0 0 6px rgba(47,111,79,0.2);transform:translateY(-1px);}
|
|
||||||
.gradient-em{background:linear-gradient(to right,#2F6F4F,#6FAF8F);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:italic;}
|
|
||||||
.gradient-text{background:linear-gradient(to right,#2F6F4F,#6FAF8F);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.gradient-num{background:linear-gradient(135deg,#1F3D2B,#2F6F4F);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.empathy-card{background:var(--white);border:1px solid var(--border);border-left:3px solid rgba(47,111,79,0.8);border-radius:12px;padding:18px 20px;display:flex;gap:14px;align-items:flex-start;box-shadow:0 10px 30px rgba(31,61,43,0.05);transition:border-color 0.2s ease,background 0.2s ease;}
|
|
||||||
.empathy-card:hover{border-color:#2F6F4F;background:#F3F7F5;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;">
|
|
||||||
<div class="logo-box"><span class="f" style="font-size:15px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:32px;align-items:center;">
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Product</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Stories</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Blog</a>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:12px;">
|
|
||||||
<a href="02_signup.html" style="font-size:14px;color:var(--muted);text-decoration:none;">Log in</a>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
|
||||||
<section style="max-width:980px;margin:0 auto;padding:88px 52px 72px;">
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:96px;align-items:center;">
|
|
||||||
|
|
||||||
<!-- Left: copy -->
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:22px;">For non-technical founders</div>
|
|
||||||
<h1 class="f" style="font-size:58px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.06;margin-bottom:28px;">
|
|
||||||
You have the idea.<br>We handle<br><em class="gradient-em">everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p style="font-size:17px;color:var(--mid);line-height:1.75;">You describe it. Vibn builds it, launches it, and markets it. From idea to <strong style="color:var(--ink);">live</strong> product in <strong style="color:var(--ink);">72 hours</strong> — no code, no agencies, no waiting.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: product moment card -->
|
|
||||||
<div style="flex-shrink:0;">
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:0 20px 60px rgba(31,61,43,0.05);">
|
|
||||||
|
|
||||||
<!-- Input area -->
|
|
||||||
<div style="padding:24px 26px 20px;background:#FAFAFA;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:12px;">Your idea</div>
|
|
||||||
<p class="f" style="font-size:15px;font-style:italic;color:var(--ink);line-height:1.65;margin-bottom:14px;">"I want to build a booking tool for independent personal trainers."</p>
|
|
||||||
<div style="display:flex;justify-content:flex-end;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);background:var(--white);border:1px solid var(--border);border-radius:5px;padding:3px 9px;letter-spacing:0.04em;">↵ Enter</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Output area -->
|
|
||||||
<div style="padding:20px 26px 24px;background:var(--white);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">vibn generated</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:0;">
|
|
||||||
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Pages</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Landing, Dashboard, Booking, Payments</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Stack</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Auth, database, payments — handled</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Revenue</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Subscription · $29 / mo</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Status</span>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:#2F6F4F;">⬤ Ready to build</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CTA row -->
|
|
||||||
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;margin-top:52px;">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg">Start free — no code needed</button></a>
|
|
||||||
<span style="font-size:13.5px;color:#6FAF8F;">★★★★★</span><span style="font-size:13.5px;color:var(--stone);"> 280 founders launched</span>
|
|
||||||
<p style="font-size:12px;color:#9CA3AF;">No credit card required · Free forever plan</p>
|
|
||||||
<a href="#how-it-works" style="font-size:13.5px;color:#2F6F4F;text-decoration:none;font-weight:500;margin-top:4px;">See how it works →</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- EMPATHY -->
|
|
||||||
<section style="border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:80px 52px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:72px;align-items:center;">
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:18px;">Sound familiar?</div>
|
|
||||||
<h2 class="f" style="font-size:36px;font-weight:700;color:#1A1A1A;line-height:1.18;margin-bottom:24px;letter-spacing:-0.02em;">The idea is the hard part. <span class="gradient-text">Everything else shouldn't be.</span></h2>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;margin-bottom:20px;">You know exactly what you want to build and who it's for. But the moment you think about servers, databases, deployment pipelines, SEO — the whole thing stalls.</p>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;">vibn exists to remove all of that. Not abstract it — <em class="f" style="font-style:italic;">remove it entirely.</em></p>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:14px;">
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(47,111,79,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6FAF8F;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I need to hire a developer first"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">vibn is your developer. Start building the moment you have an idea.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(47,111,79,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6FAF8F;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more staring at a blank marketing calendar</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">AI generates and publishes your content every single week.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(47,111,79,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6FAF8F;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I'll launch when it's ready"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">Most founders ship their first version in under 72 hours.</div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- HOW IT WORKS -->
|
|
||||||
<section id="how-it-works" style="max-width:980px;margin:0 auto;padding:84px 52px;">
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">How it works</div>
|
|
||||||
<h2 class="f" style="font-size:42px;font-weight:700;color:#1A1A1A;letter-spacing:-0.02em;margin-bottom:54px;max-width:480px;line-height:1.15;">Four phases. One <span class="gradient-text">complete</span> product.</h2>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;border:1px solid rgba(47,111,79,0.2);border-radius:14px;overflow:hidden;">
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(47,111,79,0.2);border-bottom:1px solid rgba(47,111,79,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(47,111,79,0.7);margin-bottom:14px;">01 — Discover</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Define your idea</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-bottom:1px solid rgba(47,111,79,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(47,111,79,0.7);margin-bottom:14px;">02 — Design</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Choose your style</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Pick a visual style and see your exact site and emails live before a single line of code is written.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(47,111,79,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(47,111,79,0.7);margin-bottom:14px;">03 — Build</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Your app, live</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(47,111,79,0.7);margin-bottom:14px;">04 — Grow</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Market & automate</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on users.</p></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- WHAT YOU GET -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div style="max-width:980px;margin:0 auto;padding:0 52px;display:grid;grid-template-columns:1fr 1fr 1fr;">
|
|
||||||
|
|
||||||
<div style="padding:44px 40px 44px 0;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#2F6F4F;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A live, working product</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Not a prototype. Real auth, real payments, real database — on your own URL from day one.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 40px;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#2F6F4F;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A full marketing engine</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Blog posts, onboarding emails, and social content — written and published automatically every week.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 0 44px 40px;">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#2F6F4F;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A product that evolves</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Describe changes in plain English. Vibn handles the code so your product grows as fast as your ideas.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- QUOTE BAND -->
|
|
||||||
<section style="background:#1A1A1A;padding:32px 52px 28px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
|
|
||||||
<!-- Carousel track -->
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1.6fr 1fr;gap:28px;align-items:center;margin-bottom:20px;">
|
|
||||||
|
|
||||||
<!-- Left: supporting quote -->
|
|
||||||
<div style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6FAF8F;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Alex K., founder of Taskly</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Center: dominant quote -->
|
|
||||||
<div style="background:rgba(255,255,255,0.05);border-radius:12px;padding:22px 26px;">
|
|
||||||
<div style="width:3px;height:16px;background:#6FAF8F;border-radius:2px;margin-bottom:12px;opacity:0.7;"></div>
|
|
||||||
<p class="f" style="font-size:16px;color:#FFFFFF;line-height:1.7;font-style:italic;margin-bottom:12px;">"I have zero coding experience. Three weeks in, I have 300 paying users. That's entirely because of vibn."</p>
|
|
||||||
<span style="font-size:11px;color:var(--muted);font-weight:600;">— Marcus L., founder of Flowmatic</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: supporting quote -->
|
|
||||||
<div style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6FAF8F;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"The marketing autopilot saved me ten hours a week. My blog runs itself. I just focus on product."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Sara R., founder of Nudge</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination dots -->
|
|
||||||
<div style="display:flex;justify-content:center;gap:7px;">
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
<div style="width:16px;height:5px;border-radius:3px;background:#FFFFFF;"></div>
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- STATS -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div style="max-width:980px;margin:0 auto;padding:0 52px;display:grid;grid-template-columns:1fr 1fr 1fr 1fr;">
|
|
||||||
<div style="padding:40px 0;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">280+</div><div style="font-size:13px;color:var(--muted);">founders launched</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">72h</div><div style="font-size:13px;color:var(--muted);">average time to first version</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">4.9★</div><div style="font-size:13px;color:var(--muted);">average rating</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">3×</div><div style="font-size:13px;color:var(--muted);">faster than hiring a developer</div></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- CTA -->
|
|
||||||
<section style="padding:80px 52px;text-align:center;">
|
|
||||||
<div style="max-width:680px;margin:0 auto;background:#FFFFFF;border-radius:20px;padding:64px 52px;box-shadow:0 0 0 1px rgba(47,111,79,0.15),0 20px 60px rgba(31,61,43,0.08);">
|
|
||||||
<h2 class="f" style="font-size:48px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.1;margin-bottom:20px;">Your idea deserves to exist.</h2>
|
|
||||||
<p style="font-size:16px;color:var(--mid);line-height:1.75;margin-bottom:38px;">Thousands of ideas never make it past a spreadsheet. Yours doesn't have to be one of them.</p>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="margin-bottom:16px;">Build my product — free</button></a>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">Joins 280+ non-technical founders already live</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FOOTER -->
|
|
||||||
<footer style="background:rgba(250,250,250,0.95);border-top:1px solid var(--border);padding:32px 52px;display:flex;align-items:center;justify-content:space-between;">
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:var(--ink);">vibn</span>
|
|
||||||
<div style="display:flex;gap:28px;">
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Product</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Privacy</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Terms</a>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:12.5px;color:var(--muted);">© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>vibn — Homepage</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#444441;--mid:#6B7280;--muted:#9CA3AF;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#FFFFFF;--border:#E5E7EB;--serif:'Lora',Georgia,serif;--sans:'Inter',sans-serif;}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFE,#F0EEFF);min-height:100vh;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
nav{background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 52px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;}
|
|
||||||
.logo-box{width:30px;height:30px;background:var(--ink);border-radius:7px;display:flex;align-items:center;justify-content:center;}
|
|
||||||
.btn-ink{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:8px;padding:9px 22px;font-family:var(--sans);font-size:13.5px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.btn-ink-lg{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:10px;padding:15px 36px;font-family:var(--sans);font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink-lg:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.gradient-em{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:italic;}
|
|
||||||
.gradient-text{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.gradient-num{background:linear-gradient(135deg,#2E2A5E,#4338CA);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.empathy-card{background:var(--white);border:1px solid var(--border);border-left:3px solid rgba(99,102,241,0.8);border-radius:12px;padding:18px 20px;display:flex;gap:14px;align-items:flex-start;box-shadow:0 10px 30px rgba(30,27,75,0.05);transition:border-color 0.2s ease,background 0.2s ease;}
|
|
||||||
.empathy-card:hover{border-color:#6366F1;background:#FAFAFF;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;">
|
|
||||||
<div class="logo-box"><span class="f" style="font-size:15px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:32px;align-items:center;">
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Product</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Stories</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Blog</a>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:12px;">
|
|
||||||
<a href="02_signup.html" style="font-size:14px;color:var(--muted);text-decoration:none;">Log in</a>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
|
||||||
<section style="max-width:980px;margin:0 auto;padding:88px 52px 72px;">
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:96px;align-items:center;">
|
|
||||||
|
|
||||||
<!-- Left: copy -->
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:22px;">For non-technical founders</div>
|
|
||||||
<h1 class="f" style="font-size:58px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.06;margin-bottom:28px;">
|
|
||||||
You have the idea.<br>We handle<br><em class="gradient-em">everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p style="font-size:17px;color:var(--mid);line-height:1.75;">You describe it. Vibn builds it, launches it, and markets it. From idea to <strong style="color:var(--ink);">live</strong> product in <strong style="color:var(--ink);">72 hours</strong> — no code, no agencies, no waiting.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: product moment card -->
|
|
||||||
<div style="flex-shrink:0;">
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:0 20px 60px rgba(30,27,75,0.05);">
|
|
||||||
|
|
||||||
<!-- Input area -->
|
|
||||||
<div style="padding:24px 26px 20px;background:#FCFCFF;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:12px;">Your idea</div>
|
|
||||||
<p class="f" style="font-size:15px;font-style:italic;color:var(--ink);line-height:1.65;margin-bottom:14px;">"I want to build a booking tool for independent personal trainers."</p>
|
|
||||||
<div style="display:flex;justify-content:flex-end;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);background:var(--white);border:1px solid var(--border);border-radius:5px;padding:3px 9px;letter-spacing:0.04em;">↵ Enter</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Output area -->
|
|
||||||
<div style="padding:20px 26px 24px;background:var(--white);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">vibn generated</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:0;">
|
|
||||||
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Pages</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Landing, Dashboard, Booking, Payments</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Stack</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Auth, database, payments — handled</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Revenue</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Subscription · $29 / mo</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Status</span>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:#6366F1;">⬤ Ready to build</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CTA row -->
|
|
||||||
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;margin-top:52px;">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg">Start free — no code needed</button></a>
|
|
||||||
<span style="font-size:13.5px;color:#818CF8;">★★★★★</span><span style="font-size:13.5px;color:var(--stone);"> 280 founders launched</span>
|
|
||||||
<p style="font-size:12px;color:#9CA3AF;">No credit card required · Free forever plan</p>
|
|
||||||
<a href="#how-it-works" style="font-size:13.5px;color:#6366F1;text-decoration:none;font-weight:500;margin-top:4px;">See how it works →</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- EMPATHY -->
|
|
||||||
<section style="border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:80px 52px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;display:grid;grid-template-columns:1fr 1fr;gap:72px;align-items:center;">
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:18px;">Sound familiar?</div>
|
|
||||||
<h2 class="f" style="font-size:36px;font-weight:700;color:#1A1A1A;line-height:1.18;margin-bottom:24px;letter-spacing:-0.02em;">The idea is the hard part. <span class="gradient-text">Everything else shouldn't be.</span></h2>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;margin-bottom:20px;">You know exactly what you want to build and who it's for. But the moment you think about servers, databases, deployment pipelines, SEO — the whole thing stalls.</p>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;">vibn exists to remove all of that. Not abstract it — <em class="f" style="font-style:italic;">remove it entirely.</em></p>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:14px;">
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I need to hire a developer first"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">vibn is your developer. Start building the moment you have an idea.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more staring at a blank marketing calendar</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">AI generates and publishes your content every single week.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I'll launch when it's ready"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">Most founders ship their first version in under 72 hours.</div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- HOW IT WORKS -->
|
|
||||||
<section id="how-it-works" style="max-width:980px;margin:0 auto;padding:84px 52px;">
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">How it works</div>
|
|
||||||
<h2 class="f" style="font-size:42px;font-weight:700;color:#1A1A1A;letter-spacing:-0.02em;margin-bottom:54px;max-width:480px;line-height:1.15;">Four phases. One <span class="gradient-text">complete</span> product.</h2>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;border:1px solid rgba(99,102,241,0.2);border-radius:14px;overflow:hidden;">
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">01 — Discover</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Define your idea</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">02 — Design</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Choose your style</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Pick a visual style and see your exact site and emails live before a single line of code is written.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">03 — Build</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Your app, live</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">04 — Grow</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Market & automate</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on users.</p></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- WHAT YOU GET -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div style="max-width:980px;margin:0 auto;padding:0 52px;display:grid;grid-template-columns:1fr 1fr 1fr;">
|
|
||||||
|
|
||||||
<div style="padding:44px 40px 44px 0;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A live, working product</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Not a prototype. Real auth, real payments, real database — on your own URL from day one.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 40px;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A full marketing engine</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Blog posts, onboarding emails, and social content — written and published automatically every week.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 0 44px 40px;">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A product that evolves</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Describe changes in plain English. Vibn handles the code so your product grows as fast as your ideas.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- QUOTE BAND -->
|
|
||||||
<section style="background:#1A1A1A;padding:32px 52px 28px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
|
|
||||||
<!-- Carousel track -->
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1.6fr 1fr;gap:28px;align-items:center;margin-bottom:20px;">
|
|
||||||
|
|
||||||
<!-- Left: supporting quote -->
|
|
||||||
<div style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Alex K., founder of Taskly</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Center: dominant quote -->
|
|
||||||
<div style="background:rgba(255,255,255,0.05);border-radius:12px;padding:22px 26px;">
|
|
||||||
<div style="width:3px;height:16px;background:#6366F1;border-radius:2px;margin-bottom:12px;opacity:0.7;"></div>
|
|
||||||
<p class="f" style="font-size:16px;color:#FFFFFF;line-height:1.7;font-style:italic;margin-bottom:12px;">"I have zero coding experience. Three weeks in, I have 300 paying users. That's entirely because of vibn."</p>
|
|
||||||
<span style="font-size:11px;color:var(--muted);font-weight:600;">— Marcus L., founder of Flowmatic</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: supporting quote -->
|
|
||||||
<div style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"The marketing autopilot saved me ten hours a week. My blog runs itself. I just focus on product."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Sara R., founder of Nudge</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination dots -->
|
|
||||||
<div style="display:flex;justify-content:center;gap:7px;">
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
<div style="width:16px;height:5px;border-radius:3px;background:#FFFFFF;"></div>
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- STATS -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div style="max-width:980px;margin:0 auto;padding:0 52px;display:grid;grid-template-columns:1fr 1fr 1fr 1fr;">
|
|
||||||
<div style="padding:40px 0;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">280+</div><div style="font-size:13px;color:var(--muted);">founders launched</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">72h</div><div style="font-size:13px;color:var(--muted);">average time to first version</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">4.9★</div><div style="font-size:13px;color:var(--muted);">average rating</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">3×</div><div style="font-size:13px;color:var(--muted);">faster than hiring a developer</div></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- CTA -->
|
|
||||||
<section style="padding:80px 52px;text-align:center;">
|
|
||||||
<div style="max-width:680px;margin:0 auto;background:#FFFFFF;border-radius:20px;padding:64px 52px;box-shadow:0 0 0 1px rgba(99,102,241,0.15),0 20px 60px rgba(30,27,75,0.08);">
|
|
||||||
<h2 class="f" style="font-size:48px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.1;margin-bottom:20px;">Your idea deserves to exist.</h2>
|
|
||||||
<p style="font-size:16px;color:var(--mid);line-height:1.75;margin-bottom:38px;">Thousands of ideas never make it past a spreadsheet. Yours doesn't have to be one of them.</p>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="margin-bottom:16px;">Build my product — free</button></a>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">Joins 280+ non-technical founders already live</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FOOTER -->
|
|
||||||
<footer style="background:rgba(250,250,250,0.95);border-top:1px solid var(--border);padding:32px 52px;display:flex;align-items:center;justify-content:space-between;">
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:var(--ink);">vibn</span>
|
|
||||||
<div style="display:flex;gap:28px;">
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Product</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Privacy</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Terms</a>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:12.5px;color:var(--muted);">© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
1
dyad
1
dyad
Submodule dyad deleted from f86b78bba3
@@ -1,26 +0,0 @@
|
|||||||
/* vibn Design Tokens — Ink & Parchment */
|
|
||||||
/* Import this in any HTML file or reference these values */
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--ink: #1a1510;
|
|
||||||
--ink2: #2c2c2a;
|
|
||||||
--ink3: #444441;
|
|
||||||
--mid: #5f5e5a;
|
|
||||||
--muted: #888780;
|
|
||||||
--stone: #b4b2a9;
|
|
||||||
--parch: #d3d1c7;
|
|
||||||
--cream: #f1efe8;
|
|
||||||
--paper: #f7f4ee;
|
|
||||||
--white: #fdfcfa;
|
|
||||||
--border: #e8e2d9;
|
|
||||||
|
|
||||||
--serif: 'Lora', Georgia, serif;
|
|
||||||
--sans: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body { font-family: var(--sans); background: var(--paper); color: var(--ink); }
|
|
||||||
.f { font-family: var(--serif); }
|
|
||||||
.s { font-family: var(--sans); }
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>vibn — Homepage</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#444441;--mid:#6B7280;--muted:#9CA3AF;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#FFFFFF;--border:#E5E7EB;--serif:'Plus Jakarta Sans',sans-serif;--sans:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFE,#F0EEFF);min-height:100vh;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
nav{background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 52px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;}
|
|
||||||
.nav-links{display:flex;gap:32px;align-items:center;}
|
|
||||||
.btn-ink{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:8px;padding:9px 22px;font-family:var(--sans);font-size:13.5px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.btn-ink-lg{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:10px;padding:15px 36px;font-family:var(--sans);font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink-lg:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.gradient-em{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:italic;}
|
|
||||||
.gradient-text{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.gradient-num{background:linear-gradient(135deg,#2E2A5E,#4338CA);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.empathy-card{background:var(--white);border:1px solid var(--border);border-left:3px solid rgba(99,102,241,0.8);border-radius:12px;padding:18px 20px;display:flex;gap:14px;align-items:flex-start;box-shadow:0 10px 30px rgba(30,27,75,0.05);transition:border-color 0.2s ease,background 0.2s ease;}
|
|
||||||
.empathy-card:hover{border-color:#6366F1;background:#FAFAFF;}
|
|
||||||
|
|
||||||
/* ── Layout grid classes (for responsive overrides) ── */
|
|
||||||
.hero-grid{display:grid;grid-template-columns:1fr 1fr;gap:96px;align-items:center;}
|
|
||||||
.empathy-grid{display:grid;grid-template-columns:1fr 1fr;gap:72px;align-items:center;}
|
|
||||||
.phase-grid{display:grid;grid-template-columns:1fr 1fr;border:1px solid rgba(99,102,241,0.2);border-radius:14px;overflow:hidden;}
|
|
||||||
.wyg-grid{display:grid;grid-template-columns:1fr 1fr 1fr;}
|
|
||||||
.quote-grid{display:grid;grid-template-columns:1fr 1.6fr 1fr;gap:28px;align-items:center;margin-bottom:20px;}
|
|
||||||
.stats-grid{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;}
|
|
||||||
.footer-tagline{display:block;font-size:12px;color:var(--muted);margin-top:4px;font-family:var(--sans);}
|
|
||||||
|
|
||||||
/* ── Hamburger ── */
|
|
||||||
.hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:6px;}
|
|
||||||
.hamburger span{display:block;width:22px;height:2px;background:var(--ink);border-radius:2px;transition:transform 0.25s ease,opacity 0.25s ease;}
|
|
||||||
.hamburger.open span:nth-child(1){transform:translateY(7px) rotate(45deg);}
|
|
||||||
.hamburger.open span:nth-child(2){opacity:0;}
|
|
||||||
.hamburger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}
|
|
||||||
|
|
||||||
/* Mobile drawer */
|
|
||||||
.mobile-menu{display:none;position:fixed;top:62px;left:0;right:0;background:rgba(250,250,250,0.98);border-bottom:1px solid var(--border);padding:20px 24px 28px;z-index:49;flex-direction:column;gap:0;box-shadow:0 8px 24px rgba(30,27,75,0.08);}
|
|
||||||
.mobile-menu.open{display:flex;}
|
|
||||||
.mobile-menu a{font-size:15px;color:var(--ink);text-decoration:none;padding:13px 0;border-bottom:1px solid var(--border);font-weight:500;}
|
|
||||||
.mobile-menu a:last-of-type{border-bottom:none;}
|
|
||||||
.mobile-menu .mobile-menu-cta{margin-top:18px;}
|
|
||||||
|
|
||||||
/* ── Mobile ── */
|
|
||||||
@media (max-width:768px){
|
|
||||||
nav{padding:0 20px;}
|
|
||||||
.nav-links{display:none;}
|
|
||||||
.hamburger{display:flex;}
|
|
||||||
.nav-right-btns{display:none;}
|
|
||||||
.hero-grid{grid-template-columns:1fr;gap:44px;}
|
|
||||||
.hero-section{padding:52px 24px 48px !important;}
|
|
||||||
.empathy-section{padding:56px 24px !important;}
|
|
||||||
.empathy-grid{grid-template-columns:1fr;gap:36px;}
|
|
||||||
.how-section{padding:64px 24px !important;}
|
|
||||||
.phase-grid{grid-template-columns:1fr;}
|
|
||||||
.phase-grid > div{border-right:none !important;padding:28px 24px !important;}
|
|
||||||
.wyg-grid{grid-template-columns:1fr;}
|
|
||||||
.wyg-grid > div{border-right:none !important;border-bottom:1px solid var(--border);padding:32px 24px !important;}
|
|
||||||
.wyg-grid > div:last-child{border-bottom:none;}
|
|
||||||
.wyg-section{padding:0 24px !important;}
|
|
||||||
.quote-grid{grid-template-columns:1fr;}
|
|
||||||
.quote-side{display:none !important;}
|
|
||||||
.quote-section{padding:32px 24px 28px !important;}
|
|
||||||
.stats-grid{grid-template-columns:1fr 1fr;}
|
|
||||||
.stats-grid > div{padding:28px 16px !important;}
|
|
||||||
.stats-grid > div:nth-child(odd){padding-left:0 !important;}
|
|
||||||
.stats-grid > div:nth-child(3),.stats-grid > div:nth-child(4){border-top:1px solid var(--border);}
|
|
||||||
.stats-grid > div:nth-child(even){border-right:none !important;}
|
|
||||||
.stats-section{padding:0 24px !important;}
|
|
||||||
.cta-section{padding:56px 20px !important;}
|
|
||||||
.cta-card{padding:44px 28px !important;}
|
|
||||||
.hero-h1{font-size:40px !important;line-height:1.1 !important;}
|
|
||||||
.hero-sub{font-size:15px !important;}
|
|
||||||
footer{flex-direction:column;gap:20px;text-align:center;padding:32px 24px !important;}
|
|
||||||
.footer-links{flex-wrap:wrap;justify-content:center;}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;">
|
|
||||||
<div class="logo-box" style="width:30px;height:30px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:7px;display:flex;align-items:center;justify-content:center;"><span class="f" style="font-size:15px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="#how-it-works" style="font-size:14px;color:var(--muted);text-decoration:none;">How it works</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Stories</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Blog</a>
|
|
||||||
</div>
|
|
||||||
<div class="nav-right-btns" style="display:flex;align-items:center;gap:12px;">
|
|
||||||
<a href="03_dashboard.html" style="font-size:14px;color:#6366F1;font-weight:600;text-decoration:none;">Log in</a>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
<button class="hamburger" id="hamburger" aria-label="Open menu" onclick="toggleMenu()">
|
|
||||||
<span></span><span></span><span></span>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Mobile drawer -->
|
|
||||||
<div class="mobile-menu" id="mobile-menu">
|
|
||||||
<a href="#how-it-works" onclick="closeMenu()">How it works</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Pricing</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Stories</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Blog</a>
|
|
||||||
<a href="03_dashboard.html" style="color:#6366F1;font-weight:600;" onclick="closeMenu()">Log in</a>
|
|
||||||
<div class="mobile-menu-cta">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="width:100%;">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
|
||||||
<section class="hero-section" style="max-width:980px;margin:0 auto;padding:88px 52px 72px;">
|
|
||||||
<div class="hero-grid">
|
|
||||||
|
|
||||||
<!-- Left: copy -->
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:22px;">For non-technical founders</div>
|
|
||||||
<h1 class="f hero-h1" style="font-size:58px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.06;margin-bottom:28px;">
|
|
||||||
You have the idea.<br>We handle<br><em class="gradient-em">everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p class="hero-sub" style="font-size:17px;color:var(--mid);line-height:1.75;">You describe it. Vibn builds it, launches it, and markets it. From idea to <strong style="color:var(--ink);">live</strong> product in <strong style="color:var(--ink);">72 hours</strong> — no code, no agencies, no waiting.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: product moment card -->
|
|
||||||
<div style="flex-shrink:0;">
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:0 20px 60px rgba(30,27,75,0.05);">
|
|
||||||
|
|
||||||
<!-- Input area -->
|
|
||||||
<div style="padding:24px 26px 20px;background:#FCFCFF;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:12px;">Your idea</div>
|
|
||||||
<p class="f" style="font-size:15px;font-style:italic;color:var(--ink);line-height:1.65;margin-bottom:14px;">"I want to build a booking tool for independent personal trainers."</p>
|
|
||||||
<div style="display:flex;justify-content:flex-end;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);background:var(--white);border:1px solid var(--border);border-radius:5px;padding:3px 9px;letter-spacing:0.04em;">↵ Enter</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Output area -->
|
|
||||||
<div style="padding:20px 26px 24px;background:var(--white);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">vibn generated</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:0;">
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Pages</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Landing, Dashboard, Booking, Payments</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Stack</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Auth, database, payments — handled</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Revenue</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Subscription · $29 / mo</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Status</span>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:#6366F1;">⬤ Ready to build</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CTA row -->
|
|
||||||
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;margin-top:52px;">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg">Start free — no code needed</button></a>
|
|
||||||
<div><span style="font-size:13.5px;color:#818CF8;">★★★★★</span><span style="font-size:13.5px;color:var(--stone);"> 280 founders launched</span></div>
|
|
||||||
<p style="font-size:12px;color:#9CA3AF;">No credit card required · Free forever plan</p>
|
|
||||||
<a href="#how-it-works" style="font-size:13.5px;color:#6366F1;text-decoration:none;font-weight:500;margin-top:4px;">See how it works →</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- EMPATHY -->
|
|
||||||
<section class="empathy-section" style="border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:80px 52px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
<div class="empathy-grid">
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:18px;">Sound familiar?</div>
|
|
||||||
<h2 class="f" style="font-size:36px;font-weight:700;color:#1A1A1A;line-height:1.18;margin-bottom:24px;letter-spacing:-0.02em;">The idea is the hard part. <span class="gradient-text">Everything else shouldn't be.</span></h2>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;margin-bottom:20px;">You know exactly what you want to build and who it's for. But the moment you think about servers, databases, deployment pipelines, SEO — the whole thing stalls.</p>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;">vibn exists to remove all of that. Not abstract it — <em class="f" style="font-style:italic;">remove it entirely.</em></p>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:14px;">
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I need to hire a developer first"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">vibn is your developer. Start building the moment you have an idea.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more staring at a blank marketing calendar</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">AI generates and publishes your content every single week.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I'll launch when it's ready"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">Most founders ship their first version in under 72 hours.</div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- HOW IT WORKS -->
|
|
||||||
<section id="how-it-works" class="how-section" style="max-width:980px;margin:0 auto;padding:84px 52px;">
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">How it works</div>
|
|
||||||
<h2 class="f" style="font-size:42px;font-weight:700;color:#1A1A1A;letter-spacing:-0.02em;margin-bottom:54px;max-width:480px;line-height:1.15;">Four phases. One <span class="gradient-text">complete</span> product.</h2>
|
|
||||||
<div class="phase-grid">
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">01 — Discover</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Define your idea</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">02 — Design</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Choose your style</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Pick a visual style and see your exact site and emails live before a single line of code is written.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">03 — Build</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Your app, live</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">04 — Grow</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Market & automate</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on users.</p></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- WHAT YOU GET -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div class="wyg-grid wyg-section" style="max-width:980px;margin:0 auto;padding:0 52px;">
|
|
||||||
|
|
||||||
<div style="padding:44px 40px 44px 0;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A live, working product</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Not a prototype. Real auth, real payments, real database — on your own URL from day one.</p>
|
|
||||||
<p style="font-size:12px;color:var(--muted);line-height:1.6;text-align:center;margin-top:10px;">Runs on your own servers — your data, your infrastructure, no lock-in.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 40px;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A full marketing engine</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Blog posts, onboarding emails, and social content — written and published automatically every week.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 0 44px 40px;">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A product that evolves</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Describe changes in plain English. Vibn handles the code so your product grows as fast as your ideas.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- QUOTE BAND -->
|
|
||||||
<section class="quote-section" style="background:#1A1A1A;padding:32px 52px 28px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
|
|
||||||
<div class="quote-grid">
|
|
||||||
|
|
||||||
<!-- Left: supporting quote -->
|
|
||||||
<div class="quote-side" style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Alex K., founder of Taskly</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Center: dominant quote -->
|
|
||||||
<div style="background:rgba(255,255,255,0.05);border-radius:12px;padding:22px 26px;">
|
|
||||||
<div style="width:3px;height:16px;background:#6366F1;border-radius:2px;margin-bottom:12px;opacity:0.7;"></div>
|
|
||||||
<p class="f" style="font-size:16px;color:#FFFFFF;line-height:1.7;font-style:italic;margin-bottom:12px;">"I have zero coding experience. Three weeks in, I have 300 paying users. That's entirely because of vibn."</p>
|
|
||||||
<span style="font-size:11px;color:var(--muted);font-weight:600;">— Marcus L., founder of Flowmatic</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: supporting quote -->
|
|
||||||
<div class="quote-side" style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"The marketing autopilot saved me ten hours a week. My blog runs itself. I just focus on product."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Sara R., founder of Nudge</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination dots -->
|
|
||||||
<div style="display:flex;justify-content:center;gap:7px;">
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
<div style="width:16px;height:5px;border-radius:3px;background:#FFFFFF;"></div>
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- STATS -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div class="stats-grid stats-section" style="max-width:980px;margin:0 auto;padding:0 52px;">
|
|
||||||
<div style="padding:40px 0;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">280+</div><div style="font-size:13px;color:var(--muted);">founders launched</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">72h</div><div style="font-size:13px;color:var(--muted);">average time to first version</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">4.9★</div><div style="font-size:13px;color:var(--muted);">average rating</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">3×</div><div style="font-size:13px;color:var(--muted);">faster than hiring a developer</div></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- CTA -->
|
|
||||||
<section class="cta-section" style="padding:80px 52px;text-align:center;">
|
|
||||||
<div class="cta-card" style="max-width:680px;margin:0 auto;background:#FFFFFF;border-radius:20px;padding:64px 52px;box-shadow:0 0 0 1px rgba(99,102,241,0.15),0 20px 60px rgba(30,27,75,0.08);">
|
|
||||||
<h2 class="f" style="font-size:48px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.1;margin-bottom:20px;">Your idea deserves to exist.</h2>
|
|
||||||
<p style="font-size:16px;color:var(--mid);line-height:1.75;margin-bottom:38px;">Thousands of ideas never make it past a spreadsheet. Yours doesn't have to be one of them.</p>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="margin-bottom:16px;">Build my product — free</button></a>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">Joins 280+ non-technical founders already live</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FOOTER -->
|
|
||||||
<footer style="background:rgba(250,250,250,0.95);border-top:1px solid var(--border);padding:32px 52px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;">
|
|
||||||
<div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:var(--ink);">vibn</span>
|
|
||||||
<span class="footer-tagline">The fastest way from idea to product.</span>
|
|
||||||
</div>
|
|
||||||
<div class="footer-links" style="display:flex;gap:28px;">
|
|
||||||
<a href="#how-it-works" style="font-size:13px;color:var(--muted);text-decoration:none;">How it works</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Privacy</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Terms</a>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:12.5px;color:var(--muted);text-align:right;display:block;">© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function toggleMenu(){
|
|
||||||
var btn=document.getElementById('hamburger');
|
|
||||||
var menu=document.getElementById('mobile-menu');
|
|
||||||
var open=menu.classList.toggle('open');
|
|
||||||
btn.classList.toggle('open',open);
|
|
||||||
document.body.style.overflow=open?'hidden':'';
|
|
||||||
}
|
|
||||||
function closeMenu(){
|
|
||||||
document.getElementById('hamburger').classList.remove('open');
|
|
||||||
document.getElementById('mobile-menu').classList.remove('open');
|
|
||||||
document.body.style.overflow='';
|
|
||||||
}
|
|
||||||
// Close on anchor click (for same-page links like #how-it-works)
|
|
||||||
document.querySelectorAll('.mobile-menu a[href^="#"]').forEach(function(a){
|
|
||||||
a.addEventListener('click',closeMenu);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>vibn — Sign up</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{
|
|
||||||
--ink:#1A1A1A;
|
|
||||||
--mid:#6B7280;
|
|
||||||
--muted:#9CA3AF;
|
|
||||||
--border:#E5E7EB;
|
|
||||||
--white:#FFFFFF;
|
|
||||||
--soft:#F5F3FF;
|
|
||||||
--hover:#FAFAFF;
|
|
||||||
--serif:'Plus Jakarta Sans',sans-serif;
|
|
||||||
--sans:'Plus Jakarta Sans',sans-serif;
|
|
||||||
}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);min-height:100vh;display:flex;flex-direction:column;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
|
|
||||||
/* Inputs */
|
|
||||||
input::placeholder{color:var(--muted);}
|
|
||||||
input{width:100%;border:1px solid var(--border);border-radius:8px;padding:10px 13px;font-family:var(--sans);font-size:14px;color:var(--ink);background:#FAFAFA;outline:none;transition:border-color 0.15s,box-shadow 0.15s;}
|
|
||||||
input:focus{border-color:#6366F1;box-shadow:0 0 0 3px rgba(99,102,241,0.12);}
|
|
||||||
input.error{border-color:#F87171;}
|
|
||||||
|
|
||||||
/* Primary button */
|
|
||||||
.btn{width:100%;background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:10px;padding:13px;font-family:var(--sans);font-size:14px;font-weight:600;cursor:pointer;margin-top:4px;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.btn:disabled{opacity:0.4;cursor:default;transform:none;box-shadow:0 10px 25px rgba(30,27,75,0.15);}
|
|
||||||
|
|
||||||
/* Mode option cards */
|
|
||||||
.mode-opt{border:1px solid var(--border);background:transparent;border-radius:10px;padding:16px;cursor:pointer;margin-bottom:10px;display:flex;align-items:center;gap:12px;transition:all 0.15s;}
|
|
||||||
.mode-opt:hover{border-color:#6366F1;background:var(--hover);}
|
|
||||||
.mode-opt.selected{border-color:#6366F1;background:var(--hover);box-shadow:0 0 0 3px rgba(99,102,241,0.1);}
|
|
||||||
|
|
||||||
/* Password strength */
|
|
||||||
.strength-bar{display:flex;gap:4px;margin-top:8px;}
|
|
||||||
.strength-seg{flex:1;height:3px;border-radius:2px;background:var(--border);transition:background 0.2s ease;}
|
|
||||||
.strength-label{font-size:11px;color:var(--muted);margin-top:5px;min-height:16px;}
|
|
||||||
|
|
||||||
/* Password toggle */
|
|
||||||
.pwd-wrap{position:relative;}
|
|
||||||
.pwd-wrap input{padding-right:40px;}
|
|
||||||
.pwd-toggle{position:absolute;right:11px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--muted);padding:4px;display:flex;align-items:center;transition:color 0.15s;}
|
|
||||||
.pwd-toggle:hover{color:var(--ink);}
|
|
||||||
|
|
||||||
/* Google button */
|
|
||||||
.btn-google{width:100%;background:transparent;border:1px solid var(--border);color:var(--ink);border-radius:10px;padding:11px;font-family:var(--sans);font-size:13.5px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:9px;transition:border-color 0.15s,background 0.15s;}
|
|
||||||
.btn-google:hover{border-color:#6366F1;background:var(--hover);}
|
|
||||||
|
|
||||||
/* Billing notice — animated */
|
|
||||||
.billing-notice{overflow:hidden;max-height:0;opacity:0;transition:max-height 0.3s ease,opacity 0.25s ease,margin-bottom 0.3s ease;margin-bottom:0;}
|
|
||||||
.billing-notice.visible{max-height:140px;opacity:1;margin-bottom:20px;}
|
|
||||||
|
|
||||||
/* Experience hint */
|
|
||||||
.mode-hint{text-align:center;font-size:12px;color:var(--muted);margin-top:10px;min-height:18px;transition:opacity 0.2s ease;}
|
|
||||||
.exp-feedback{overflow:hidden;max-height:0;opacity:0;transition:max-height 0.3s ease,opacity 0.25s ease,margin-bottom 0.3s ease;margin-bottom:0;border-radius:10px;padding:0 16px;}
|
|
||||||
.exp-feedback.visible{max-height:80px;opacity:1;margin-bottom:4px;padding:13px 16px;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav style="background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 40px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;">
|
|
||||||
<div style="display:flex;align-items:center;gap:9px;">
|
|
||||||
<div style="width:28px;height:28px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:7px;display:flex;align-items:center;justify-content:center;"><span class="f" style="font-size:14px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:13.5px;color:var(--muted);">Already have an account? <a href="03_dashboard.html" style="color:#6366F1;font-weight:600;text-decoration:none;">Log in</a></span>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div style="flex:1;display:flex;align-items:center;justify-content:center;padding:40px 24px;">
|
|
||||||
<div style="width:100%;max-width:440px;">
|
|
||||||
|
|
||||||
<!-- Step indicator -->
|
|
||||||
<div id="steps" style="display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:32px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;" id="s1">
|
|
||||||
<div id="s1c" style="width:24px;height:24px;border-radius:50%;background:#6366F1;display:flex;align-items:center;justify-content:center;font-size:11px;color:#FFFFFF;font-weight:700;">1</div>
|
|
||||||
<span style="font-size:12.5px;font-weight:600;color:var(--ink);">Account</span>
|
|
||||||
</div>
|
|
||||||
<div style="width:28px;height:1px;background:var(--border);"></div>
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;opacity:0.35;" id="s2">
|
|
||||||
<div id="s2c" style="width:24px;height:24px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--muted);font-weight:700;">2</div>
|
|
||||||
<span id="s2l" style="font-size:12.5px;color:var(--muted);">Your experience</span>
|
|
||||||
</div>
|
|
||||||
<div style="width:28px;height:1px;background:var(--border);"></div>
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;opacity:0.35;" id="s3">
|
|
||||||
<div id="s3c" style="width:24px;height:24px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--muted);font-weight:700;">3</div>
|
|
||||||
<span id="s3l" style="font-size:12.5px;color:var(--muted);">Ready</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 1 -->
|
|
||||||
<div id="step1" style="background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<h2 class="f" style="font-size:23px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;margin-bottom:6px;">Let's build your first product.</h2>
|
|
||||||
<p style="font-size:14px;color:var(--muted);margin-bottom:22px;">Free to start · No credit card needed</p>
|
|
||||||
|
|
||||||
<!-- Google first -->
|
|
||||||
<button onclick="openGoogleAuth()" class="btn-google" style="margin-bottom:20px;">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
|
||||||
Continue with Google
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div style="margin-bottom:20px;display:flex;align-items:center;gap:12px;">
|
|
||||||
<div style="flex:1;height:1px;background:var(--border);"></div>
|
|
||||||
<span style="font-size:12px;color:var(--muted);">or continue with email</span>
|
|
||||||
<div style="flex:1;height:1px;background:var(--border);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email form -->
|
|
||||||
<div style="display:flex;flex-direction:column;gap:15px;">
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Full name</label>
|
|
||||||
<input type="text" id="inp-name" placeholder="Jane Smith" oninput="validateStep1()"/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Email</label>
|
|
||||||
<input type="email" id="inp-email" placeholder="jane@studio.com" oninput="validateStep1()"/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Password</label>
|
|
||||||
<div class="pwd-wrap">
|
|
||||||
<input type="password" id="pwd" placeholder="8+ characters" oninput="checkStrength(this.value);validateStep1()"/>
|
|
||||||
<button type="button" class="pwd-toggle" onclick="togglePwd()" id="pwd-toggle-btn" aria-label="Show password">
|
|
||||||
<svg id="eye-open" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
||||||
<svg id="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none;"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="strength-bar"><div class="strength-seg" id="seg1"></div><div class="strength-seg" id="seg2"></div><div class="strength-seg" id="seg3"></div></div>
|
|
||||||
<div class="strength-label" id="strength-label"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Social proof above button -->
|
|
||||||
<p style="text-align:center;font-size:12px;color:var(--muted);margin-bottom:-4px;">Joining 280+ founders already building</p>
|
|
||||||
|
|
||||||
<button id="step1btn" class="btn" onclick="goStep(2)" disabled>Continue →</button>
|
|
||||||
<p style="text-align:center;font-size:11.5px;color:var(--muted);margin-top:2px;">By continuing you agree to our <a href="#" style="color:var(--muted);text-decoration:underline;">Terms</a> and <a href="#" style="color:var(--muted);text-decoration:underline;">Privacy Policy</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 2 -->
|
|
||||||
<div id="step2" style="display:none;background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<h2 class="f" style="font-size:23px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;margin-bottom:6px;">How experienced are you?</h2>
|
|
||||||
<p style="font-size:14px;color:var(--muted);margin-bottom:24px;">Just so we know who we're building with</p>
|
|
||||||
<div id="modes">
|
|
||||||
<div class="mode-opt" onclick="selectExperience('beginner',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◎</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">First time</div><div style="font-size:12.5px;color:var(--muted);">I've never shipped a product before</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="mode-opt" onclick="selectExperience('some',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◇</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">Some experience</div><div style="font-size:12.5px;color:var(--muted);">I've built things before</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="mode-opt" onclick="selectExperience('experienced',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◈</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">Experienced</div><div style="font-size:12.5px;color:var(--muted);">I ship products regularly</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Contextual feedback — animated -->
|
|
||||||
<div id="exp-feedback" class="exp-feedback" style="background:var(--soft);border:1px solid rgba(99,102,241,0.2);">
|
|
||||||
<div id="exp-feedback-text" style="font-size:13px;color:#4338CA;line-height:1.6;"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="step2btn" class="btn" style="margin-top:20px;" onclick="goStep(3)" disabled>Set up my workspace →</button>
|
|
||||||
<p class="mode-hint" id="mode-hint">Select an option above to continue</p>
|
|
||||||
<p style="text-align:center;margin-top:6px;"><a onclick="goStep(1)" style="font-size:13px;color:var(--muted);text-decoration:none;cursor:pointer;">← Back</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 3 -->
|
|
||||||
<div id="step3" style="display:none;background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:6px;">
|
|
||||||
<div style="width:36px;height:36px;background:var(--soft);border:1px solid rgba(99,102,241,0.25);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;color:#6366F1;">✓</div>
|
|
||||||
<h2 class="f" style="font-size:22px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">You're in. Got an idea?</h2>
|
|
||||||
</div>
|
|
||||||
<p id="done-msg" style="font-size:14px;color:var(--muted);line-height:1.7;margin-bottom:20px;padding-left:48px;">Describe it in one sentence and we'll carry it straight into your workspace.</p>
|
|
||||||
|
|
||||||
<!-- Seed input -->
|
|
||||||
<textarea id="seed-idea" placeholder="e.g. A booking tool for independent personal trainers." style="width:100%;border:1px solid var(--border);border-radius:8px;padding:11px 13px;font-family:var(--sans);font-size:14px;color:var(--ink);background:#FAFAFA;outline:none;resize:none;height:88px;line-height:1.6;transition:border-color 0.15s,box-shadow 0.15s;margin-bottom:16px;" onfocus="this.style.borderColor='#6366F1';this.style.boxShadow='0 0 0 3px rgba(99,102,241,0.12)';" onblur="this.style.borderColor='#E5E7EB';this.style.boxShadow='none';"></textarea>
|
|
||||||
|
|
||||||
<button id="dash-btn" class="btn" style="margin-top:0;" onclick="openDashboard()">Start building →</button>
|
|
||||||
<p style="text-align:center;margin-top:14px;">
|
|
||||||
<a onclick="goToDashboard()" style="font-size:13px;color:var(--muted);text-decoration:none;cursor:pointer;">I'll do this later — take me to the dashboard</a>
|
|
||||||
</p>
|
|
||||||
<p style="text-align:center;margin-top:10px;"><a onclick="goStep(2)" style="font-size:12px;color:var(--muted);text-decoration:none;cursor:pointer;opacity:0.6;">← Back</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/* Google auth popup */
|
|
||||||
function openGoogleAuth(){
|
|
||||||
var w=500,h=600;
|
|
||||||
var left=(screen.width/2)-(w/2);
|
|
||||||
var top=(screen.height/2)-(h/2);
|
|
||||||
window.open('google-auth-popup.html','google-auth','width='+w+',height='+h+',left='+left+',top='+top+',toolbar=no,menubar=no,scrollbars=no');
|
|
||||||
window.addEventListener('message',function(e){
|
|
||||||
if(e.data&&e.data.type==='google-auth-success'){
|
|
||||||
goStep(2);
|
|
||||||
}
|
|
||||||
},{once:true});
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode=null;
|
|
||||||
var EXP_FEEDBACK={
|
|
||||||
beginner:"We've got you — we'll explain every step clearly, no jargon, no assumptions. You'll have a product live before you know it.",
|
|
||||||
some:"Great! You know the ropes — let's move fast and make something great.",
|
|
||||||
experienced:"You know the process. We'll keep things efficient and get straight to the point."
|
|
||||||
};
|
|
||||||
var EXP_DONE={
|
|
||||||
beginner:"Everything is in place. We'll guide you every step of the way.",
|
|
||||||
some:"Everything is in place. Let's get straight to building.",
|
|
||||||
experienced:"Everything is in place. Let's move fast."
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Step navigation */
|
|
||||||
function goStep(n){
|
|
||||||
[1,2,3].forEach(function(i){
|
|
||||||
document.getElementById('step'+i).style.display=i===n?'block':'none';
|
|
||||||
var s=document.getElementById('s'+i);
|
|
||||||
if(s) s.style.opacity=i<=n?'1':'0.35';
|
|
||||||
var c=document.getElementById('s'+i+'c');
|
|
||||||
if(c){
|
|
||||||
c.style.background=i<n?'#4338CA':i===n?'#6366F1':'#E5E7EB';
|
|
||||||
c.style.color=i<=n?'#FFFFFF':'#9CA3AF';
|
|
||||||
c.textContent=i<n?'✓':String(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 1 — form validation */
|
|
||||||
function validateStep1(){
|
|
||||||
var name=document.getElementById('inp-name').value.trim();
|
|
||||||
var email=document.getElementById('inp-email').value.trim();
|
|
||||||
var pwd=document.getElementById('pwd').value;
|
|
||||||
var emailOk=/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
||||||
var pwdOk=pwd.length>=8;
|
|
||||||
document.getElementById('step1btn').disabled=!(name.length>0 && emailOk && pwdOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Password show / hide */
|
|
||||||
function togglePwd(){
|
|
||||||
var input=document.getElementById('pwd');
|
|
||||||
var eyeOpen=document.getElementById('eye-open');
|
|
||||||
var eyeClosed=document.getElementById('eye-closed');
|
|
||||||
var isHidden=input.type==='password';
|
|
||||||
input.type=isHidden?'text':'password';
|
|
||||||
eyeOpen.style.display=isHidden?'none':'block';
|
|
||||||
eyeClosed.style.display=isHidden?'block':'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Password strength */
|
|
||||||
function checkStrength(v){
|
|
||||||
var segs=['seg1','seg2','seg3'];
|
|
||||||
var labelEl=document.getElementById('strength-label');
|
|
||||||
|
|
||||||
if(v.length===0){
|
|
||||||
segs.forEach(function(id){document.getElementById(id).style.background='#E5E7EB';});
|
|
||||||
labelEl.textContent='';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(v.length<8){
|
|
||||||
segs.forEach(function(id){document.getElementById(id).style.background='#E5E7EB';});
|
|
||||||
document.getElementById('seg1').style.background='#F87171';
|
|
||||||
labelEl.textContent='Not enough characters — 8 minimum';
|
|
||||||
labelEl.style.color='#F87171';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var score=1;
|
|
||||||
if(/[A-Z]/.test(v)&&/[0-9]/.test(v)) score++;
|
|
||||||
if(/[^A-Za-z0-9]/.test(v)||v.length>=12) score++;
|
|
||||||
|
|
||||||
var colors=['#E5E7EB','#E5E7EB','#E5E7EB'];
|
|
||||||
var label='';
|
|
||||||
if(score===1){colors[0]='#F87171';label='Weak';}
|
|
||||||
else if(score===2){colors[0]='#FBBF24';colors[1]='#FBBF24';label='Fair';}
|
|
||||||
else{colors[0]='#4338CA';colors[1]='#6366F1';colors[2]='#818CF8';label='Strong';}
|
|
||||||
|
|
||||||
segs.forEach(function(id,i){document.getElementById(id).style.background=colors[i];});
|
|
||||||
labelEl.textContent=label;
|
|
||||||
labelEl.style.color=score===1?'#F87171':score===2?'#FBBF24':'#6366F1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 2 — experience selection */
|
|
||||||
function selectExperience(level,el){
|
|
||||||
mode=level;
|
|
||||||
document.querySelectorAll('.mode-opt').forEach(function(d){d.classList.remove('selected');});
|
|
||||||
el.classList.add('selected');
|
|
||||||
|
|
||||||
// Show contextual feedback
|
|
||||||
var feedback=document.getElementById('exp-feedback');
|
|
||||||
var feedbackText=document.getElementById('exp-feedback-text');
|
|
||||||
feedbackText.textContent=EXP_FEEDBACK[level];
|
|
||||||
feedback.classList.add('visible');
|
|
||||||
|
|
||||||
// Enable button + hide hint
|
|
||||||
document.getElementById('step2btn').disabled=false;
|
|
||||||
document.getElementById('mode-hint').style.opacity='0';
|
|
||||||
|
|
||||||
document.getElementById('done-msg').textContent=EXP_DONE[level];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 3 — start building (carries seed idea to Describe) */
|
|
||||||
function openDashboard(){
|
|
||||||
var btn=document.getElementById('dash-btn');
|
|
||||||
btn.textContent='Setting up…';
|
|
||||||
btn.disabled=true;
|
|
||||||
var idea=(document.getElementById('seed-idea').value||'').trim();
|
|
||||||
setTimeout(function(){
|
|
||||||
try {
|
|
||||||
sessionStorage.setItem('vibn_new_project','1');
|
|
||||||
if(idea) sessionStorage.setItem('vibn_seed_idea', idea);
|
|
||||||
} catch(e){}
|
|
||||||
window.location.href='05_describe.html';
|
|
||||||
},800);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 3 — skip to dashboard */
|
|
||||||
function goToDashboard(){
|
|
||||||
window.location.href='03_dashboard.html';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,589 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>vibn — Architect</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{
|
|
||||||
--ink:#1A1A1A;--mid:#6B7280;--muted:#9CA3AF;
|
|
||||||
--border:#E5E7EB;--cream:#FAFAFF;--paper:#F5F3FF;--white:#FFFFFF;
|
|
||||||
--indigo:#6366F1;--indigo-dark:#4338CA;--indigo-deep:#2E2A5E;
|
|
||||||
--indigo-soft:rgba(99,102,241,0.08);--indigo-ring:rgba(99,102,241,0.12);
|
|
||||||
}
|
|
||||||
body{font-family:'Plus Jakarta Sans',sans-serif;background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);display:flex;flex-direction:column;height:100vh;overflow:hidden;}
|
|
||||||
.f{font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
|
|
||||||
/* ── Dark mode — exact match to Describe ── */
|
|
||||||
[data-theme="dark"]{
|
|
||||||
--ink:#ECE9F5;--ink2:#C8C4D8;--ink3:#A0A0B8;
|
|
||||||
--mid:#9AA3BC;--muted:#6A7490;--border:#3A4260;
|
|
||||||
--cream:#2A3250;--paper:#2A3250;--white:#2A3250;
|
|
||||||
--indigo:#818CF8;--indigo-dark:#A5B4FC;--indigo-deep:#6366F1;
|
|
||||||
--indigo-soft:rgba(99,102,241,0.12);--indigo-ring:rgba(99,102,241,0.2);
|
|
||||||
}
|
|
||||||
[data-theme="dark"] body{background:#1A1F2E;}
|
|
||||||
/* Structural panels — same hierarchy as Describe: sidebar & main = mid, right panel = darkest */
|
|
||||||
[data-theme="dark"] .arch-sidebar{background:#2A3250!important;border-right-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] .arch-main{background:#212840!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#f5f3ff"]{background:#1A1F2E!important;}
|
|
||||||
/* White & near-white backgrounds (cards float above panels) */
|
|
||||||
[data-theme="dark"] [style*="background:var(--white)"]{background:#2A3250!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#fafaff"]{background:#212840!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#FAFAFA"]{background:#212840!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#f0f4ff"]{background:#2A3250!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#eef2ff"]{background:rgba(99,102,241,0.18)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#F3F4F6"]{background:#3A4260!important;}
|
|
||||||
/* Borders */
|
|
||||||
[data-theme="dark"] [style*="border-bottom:1px solid #c7d2fe"]{border-bottom-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] [style*="border:1px solid #e0e7ff"]{border-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] [style*="border:1px solid rgba(99,102,241"]{border-color:rgba(99,102,241,0.06)!important;}
|
|
||||||
/* Sidebar-specific */
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="border-top:1px solid #e5e7eb"]{border-top-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="background:#e5e7eb"]{background:#3A4260!important;}
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="color:#1a1a1a"]{color:#ECE9F5!important;}
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="color:#6b7280"]{color:#9AA3BC!important;}
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="color:#444441"]{color:#6A7490!important;}
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="color:#9ca3af"]{color:#6A7490!important;}
|
|
||||||
/* Phase & progress dots: filled indigo → light lavender (matching Describe's --accent-primary in dark) */
|
|
||||||
[data-theme="dark"] .arch-sidebar [style*="background:#6366F1"]{background:#A5B4FC!important;color:#1A1F2E!important;}
|
|
||||||
[data-theme="dark"] [style*="color:#4338ca"]{color:#A5B4FC!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase.active{background:rgba(165,180,252,0.12)!important;}
|
|
||||||
/* Blueprint rows */
|
|
||||||
[data-theme="dark"] .blueprint-row:hover{background:#323C5E!important;}
|
|
||||||
[data-theme="dark"] .blueprint-row.locked{background:#242B48!important;}
|
|
||||||
[data-theme="dark"] .blueprint-row.locked:hover{background:#2E3A5A!important;}
|
|
||||||
/* Option buttons */
|
|
||||||
[data-theme="dark"] .opt-btns{border-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] .opt-btn{color:#C8D0E8!important;border-right-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] .opt-btn:hover{background:#2E3A5A!important;color:#A5B4FC!important;}
|
|
||||||
[data-theme="dark"] .opt-btn.selected{background:rgba(99,102,241,0.15)!important;color:#A5B4FC!important;}
|
|
||||||
[data-theme="dark"] .opt-btn.why-btn{color:#A5B4FC!important;}
|
|
||||||
[data-theme="dark"] .why-btn{color:#9AA3BC!important;border-color:#3A4260!important;}
|
|
||||||
/* Right panel */
|
|
||||||
[data-theme="dark"] .deliverable-row{color:#9AA3BC!important;}
|
|
||||||
[data-theme="dark"] .deliverable-row:hover{background:#2E3A5A!important;}
|
|
||||||
/* Popups & modals */
|
|
||||||
[data-theme="dark"] #why-popup>div{background:#242B48!important;box-shadow:0 0 0 1px rgba(165,180,252,0.18),0 24px 64px rgba(0,0,0,0.55),0 0 48px rgba(99,102,241,0.14)!important;}
|
|
||||||
[data-theme="dark"] #save-exit-box{background:#242B48!important;box-shadow:0 0 0 1px rgba(165,180,252,0.18),0 24px 64px rgba(0,0,0,0.55),0 0 48px rgba(99,102,241,0.14)!important;}
|
|
||||||
[data-theme="dark"] .modal-card{background:#242B48!important;box-shadow:0 0 0 1px rgba(165,180,252,0.18),0 24px 64px rgba(0,0,0,0.55),0 0 48px rgba(99,102,241,0.14)!important;}
|
|
||||||
/* Buttons */
|
|
||||||
[data-theme="dark"] #sidebar-project-name{color:#6A7490!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle{background:#2A3250!important;border-color:#3A4260!important;color:var(--mid)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]{background:#1E2640!important;border-color:#3A4260!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"] span{color:#A5B4FC!important;}
|
|
||||||
/* Next button — same gradient as Describe's next button */
|
|
||||||
[data-theme="dark"] button[onclick="openWhy()"]{color:#A5B4FC!important;}
|
|
||||||
[data-theme="dark"] .btn-primary{background:linear-gradient(135deg,#4338CA,#6366F1)!important;color:#FFFFFF!important;box-shadow:0 4px 14px rgba(99,102,241,0.25)!important;}
|
|
||||||
/* Scrollbar */
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar{width:6px;height:6px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-track{background:#1A1F2E;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb{background:#3A4260;border-radius:3px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:#5865A0;}
|
|
||||||
[data-theme="dark"] *{scrollbar-color:#3A4260 #1A1F2E;scrollbar-width:thin;}
|
|
||||||
[data-theme="dark"] .vibn-avatar{background:#6366F1!important;}
|
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
.sidebar-phase{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;}
|
|
||||||
.sidebar-phase.active{background:#fafaff;}
|
|
||||||
.phase-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;}
|
|
||||||
|
|
||||||
/* Blueprint row */
|
|
||||||
.blueprint-row{display:grid;grid-template-columns:36px 1fr 248px;align-items:center;gap:16px;padding:15px 22px;border-bottom:1px solid var(--border);transition:background 0.15s;}
|
|
||||||
.blueprint-row:last-child{border-bottom:none;border-radius:0 0 14px 14px;}
|
|
||||||
.blueprint-row:hover{background:#FAFAFF;}
|
|
||||||
.blueprint-row.locked{background:#FAFAFA;}
|
|
||||||
.blueprint-row.locked:hover{background:#F5F5F5;}
|
|
||||||
|
|
||||||
/* Option toggle buttons */
|
|
||||||
.opt-btns{display:flex;gap:0;flex-shrink:0;border:1.5px solid var(--border);border-radius:8px;overflow:visible;}
|
|
||||||
.opt-btn{flex:1;text-align:center;font-size:12px;font-weight:500;color:var(--mid);background:transparent;border:none;border-right:1.5px solid var(--border);border-radius:0;padding:6px 13px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;white-space:nowrap;position:relative;}
|
|
||||||
.opt-btn:first-child{border-radius:7px 0 0 7px;}
|
|
||||||
.opt-btn:last-child{border-right:none;border-radius:0 7px 7px 0;}
|
|
||||||
.opt-btn:hover{background:#F5F5FF;color:var(--indigo);}
|
|
||||||
.opt-btn.selected{background:var(--indigo-soft);color:var(--indigo-dark);font-weight:600;}
|
|
||||||
|
|
||||||
/* Why button (hosting) */
|
|
||||||
.why-btn{font-size:11.5px;font-weight:600;color:var(--mid);background:transparent;border:1px solid var(--border);border-radius:6px;padding:5px 11px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:border-color 0.15s,color 0.15s;white-space:nowrap;}
|
|
||||||
.why-btn:hover{border-color:var(--indigo);color:var(--indigo);}
|
|
||||||
|
|
||||||
/* Primary button */
|
|
||||||
.btn-primary{background:linear-gradient(135deg,var(--indigo-deep),var(--indigo-dark));color:#FFFFFF;border:none;border-radius:8px;padding:10px 22px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s,transform 0.2s;}
|
|
||||||
.btn-primary:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px var(--indigo-ring);transform:translateY(-1px);}
|
|
||||||
|
|
||||||
/* What you're getting panel */
|
|
||||||
.deliverable-row{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:7px;font-size:12px;color:var(--mid);transition:background 0.12s;}
|
|
||||||
.deliverable-row:hover{background:var(--cream);}
|
|
||||||
|
|
||||||
/* Modal */
|
|
||||||
.modal-bg{display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:100;align-items:center;justify-content:center;}
|
|
||||||
.modal-bg.open{display:flex;}
|
|
||||||
.modal-card{background:var(--white);border-radius:16px;width:400px;overflow:hidden;box-shadow:0 24px 64px rgba(30,27,75,0.18);}
|
|
||||||
|
|
||||||
/* Option buttons in modal */
|
|
||||||
.option-btn{display:flex;align-items:center;gap:12px;padding:12px 16px;border-radius:10px;border:1px solid var(--border);background:var(--white);cursor:pointer;text-align:left;margin-bottom:8px;transition:all 0.15s;}
|
|
||||||
.option-btn:hover{border-color:var(--indigo);background:var(--cream);}
|
|
||||||
.option-btn.selected{border-color:var(--indigo);background:var(--cream);box-shadow:0 0 0 3px var(--indigo-ring);}
|
|
||||||
|
|
||||||
/* Save popup */
|
|
||||||
#save-exit-popup{display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:700;align-items:center;justify-content:center;padding:24px;}
|
|
||||||
#save-exit-popup.visible{display:flex;}
|
|
||||||
#save-exit-box{background:#FFFFFF;border-radius:16px;box-shadow:0 24px 64px rgba(30,27,75,0.18);padding:32px;width:100%;max-width:380px;text-align:center;}
|
|
||||||
#save-exit-box .save-icon{width:48px;height:48px;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:20px;margin:0 auto 16px;}
|
|
||||||
#save-exit-box h3{font-size:18px;font-weight:700;color:var(--ink);margin-bottom:8px;}
|
|
||||||
#save-exit-box p{font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:20px;}
|
|
||||||
#save-exit-box .save-cancel{font-size:12px;color:var(--muted);cursor:pointer;text-decoration:underline;background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
#save-exit-box .save-cancel:hover{color:var(--ink);}
|
|
||||||
|
|
||||||
/* Why accordion */
|
|
||||||
|
|
||||||
/* ── Mobile tab bar (hidden on desktop) ── */
|
|
||||||
.mob-tabs{display:none;flex-shrink:0;background:#EEF0FF;border-top:1px solid #D4D8FA;padding-bottom:env(safe-area-inset-bottom);}
|
|
||||||
[data-theme="dark"] .mob-tabs{background:#212840!important;}
|
|
||||||
.mob-tab-btn{flex:1;padding:11px 8px;border:none;background:transparent;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;border-top:2px solid transparent;transition:color 0.15s,border-color 0.15s;}
|
|
||||||
.mob-tab-btn.active{color:#6366F1;border-top-color:#6366F1;font-weight:600;}
|
|
||||||
.mob-dash-btn{background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;font-size:11.5px;font-weight:500;color:var(--muted);cursor:pointer;padding:11px 12px;white-space:nowrap;flex:none;transition:color 0.15s;}
|
|
||||||
.mob-dash-btn:hover{color:var(--ink);}
|
|
||||||
[data-theme="dark"] .mob-tab-btn{color:#6A7490!important;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{color:#A5B4FC!important;border-top-color:#A5B4FC!important;}
|
|
||||||
|
|
||||||
/* ── Responsive ── */
|
|
||||||
|
|
||||||
/* Tablet (641px – 1024px): top tab bar, full-height panels */
|
|
||||||
@media (min-width:641px) and (max-width:1024px){
|
|
||||||
body{height:100dvh;overflow:hidden;}
|
|
||||||
.arch-layout{flex-direction:column!important;height:100dvh!important;overflow:hidden!important;}
|
|
||||||
.arch-sidebar{display:none!important;}
|
|
||||||
.mob-tabs{display:flex!important;order:0;border-top:none!important;border-bottom:1px solid #D4D8FA!important;padding-bottom:0!important;}
|
|
||||||
.mob-tab-btn{border-top:none;border-bottom:2px solid transparent;}
|
|
||||||
.mob-tab-btn.active{border-top-color:transparent;border-bottom-color:#6366F1;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{border-bottom-color:#A5B4FC!important;border-top-color:transparent!important;}
|
|
||||||
.arch-main{display:none!important;order:1;overflow:hidden!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-main.tab-active{display:flex!important;}
|
|
||||||
.arch-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right{display:none!important;order:1;width:100%!important;border-left:none!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-right.tab-active{display:flex!important;}
|
|
||||||
.arch-right-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right-footer{padding:12px 16px 20px!important;}
|
|
||||||
.arch-right-footer a{width:80%;display:block;}
|
|
||||||
.blueprint-row{grid-template-columns:36px 1fr!important;row-gap:10px;padding:13px 18px!important;}
|
|
||||||
.blueprint-row .opt-btns{grid-column:1 / -1;width:100%;}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile (≤ 640px): tabbed layout, tabs at bottom */
|
|
||||||
@media (max-width:640px){
|
|
||||||
body{height:100dvh;overflow:hidden;}
|
|
||||||
.arch-layout{flex-direction:column!important;height:100dvh!important;overflow:hidden!important;}
|
|
||||||
.arch-sidebar{display:none!important;}
|
|
||||||
.mob-tabs{display:flex!important;order:2;}
|
|
||||||
.arch-main{display:none!important;order:1;overflow:hidden!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-main.tab-active{display:flex!important;}
|
|
||||||
.arch-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right{display:none!important;order:1;width:100%!important;border-left:none!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-right.tab-active{display:flex!important;}
|
|
||||||
.arch-right-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.blueprint-row{grid-template-columns:36px 1fr!important;row-gap:10px;padding:13px 16px!important;}
|
|
||||||
.blueprint-row .opt-btns{grid-column:1 / -1;width:100%;}
|
|
||||||
.opt-btn{flex:1;padding:7px 8px!important;font-size:11.5px!important;}
|
|
||||||
.arch-topbar{padding:14px 16px 12px!important;}
|
|
||||||
.arch-right-footer{padding:12px 16px 20px!important;}
|
|
||||||
.arch-right-footer a{width:100%!important;display:block!important;}
|
|
||||||
.modal-card{width:calc(100vw - 32px)!important;}
|
|
||||||
#why-popup>div{max-width:calc(100vw - 32px)!important;}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile with trackpad (laptop narrow): tabs at top */
|
|
||||||
@media (max-width:640px) and (hover:hover) and (pointer:fine){
|
|
||||||
.mob-tabs{order:0!important;border-top:none!important;border-bottom:1px solid #D4D8FA!important;padding-bottom:0!important;}
|
|
||||||
.mob-tab-btn{border-top:none;border-bottom:2px solid transparent;}
|
|
||||||
.mob-tab-btn.active{border-top-color:transparent;border-bottom-color:#6366F1;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{border-bottom-color:#A5B4FC!important;}
|
|
||||||
.arch-main{order:1!important;}
|
|
||||||
.arch-right{order:1!important;}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="arch-layout" style="display:flex;height:100%;overflow:hidden;">
|
|
||||||
|
|
||||||
<!-- Mobile tab bar (hidden on desktop, shown on ≤ 640px) -->
|
|
||||||
<div class="mob-tabs" id="mob-tabs">
|
|
||||||
<button onclick="saveAndExit()" class="mob-dash-btn">Dashboard</button>
|
|
||||||
<div style="width:1px;background:var(--border);margin:8px 0;flex-shrink:0;"></div>
|
|
||||||
<button class="mob-tab-btn active" id="tab-blueprint" onclick="switchArchTab('blueprint')">Blueprint</button>
|
|
||||||
<button class="mob-tab-btn" id="tab-scope" onclick="switchArchTab('scope')">Scope</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── SIDEBAR ── -->
|
|
||||||
<div class="arch-sidebar" style="width:200px;background:#ffffff;border-right:1px solid #e5e7eb;display:flex;flex-direction:column;padding:18px 12px;flex-shrink:0;">
|
|
||||||
<div style="padding:0 6px;margin-bottom:26px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;"><span class="f" style="font-size:13px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:#1a1a1a;letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div id="sidebar-project-name" style="font-size:11px;font-weight:500;color:#9ca3af;padding-left:34px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="font-size:9.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;padding:0 6px;margin-bottom:8px;">MVP Setup</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Describe</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase active">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">⬡</div>
|
|
||||||
<div><div style="font-size:12.5px;font-weight:600;color:#1a1a1a;">Architect</div><div style="font-size:10px;color:#9ca3af;">What gets built</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">◈</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Design</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">✦</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Market</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">▲</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Build MVP</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="border-top:1px solid #e5e7eb;margin-top:14px;padding-top:12px;">
|
|
||||||
<button onclick="saveAndExit()" style="display:flex;align-items:center;justify-content:center;gap:7px;width:100%;background:#eef2ff;border:1px solid #e0e7ff;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:background 0.15s;" onmouseover="this.style.background='#e0e7ff'" onmouseout="this.style.background='#eef2ff'">
|
|
||||||
<span style="font-size:12px;font-weight:600;color:#6366F1;">Save & go to dashboard</span>
|
|
||||||
</button>
|
|
||||||
<button id="dark-toggle" onclick="toggleTheme()" style="margin-top:8px;display:flex;align-items:center;justify-content:center;width:100%;background:transparent;border:1px solid var(--border);border-radius:8px;padding:8px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--mid);transition:background 0.15s,border-color 0.15s;" onmouseover="this.style.borderColor='#6366F1';this.style.color='#6366F1';" onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--mid)';">🌙 Dark mode</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── MAIN ── -->
|
|
||||||
<div class="arch-main" style="flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;position:relative;z-index:1;">
|
|
||||||
|
|
||||||
<!-- Top bar -->
|
|
||||||
<div class="arch-topbar" style="padding:18px 28px 14px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">
|
|
||||||
<div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:var(--ink);margin-bottom:3px;">Your product blueprint</div>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">We set everything up — review and confirm to continue.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scrollable body -->
|
|
||||||
<div class="arch-scroll" style="flex:1;overflow-y:auto;padding:24px 28px;display:flex;flex-direction:column;gap:12px;">
|
|
||||||
|
|
||||||
<!-- Intro message -->
|
|
||||||
<div style="display:flex;align-items:flex-start;gap:10px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;"><span style="font-size:12px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<div style="max-width:84%;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:4px 12px 12px 12px;padding:11px 14px;font-size:13px;color:var(--ink);line-height:1.65;">
|
|
||||||
Here's the technical stack we've set up for <strong id="intro-project-name" style="font-weight:600;">your product</strong>. These are the best defaults for an idea like yours — review each decision below and change anything that doesn't feel right.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Blueprint card -->
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:14px;overflow:visible;box-shadow:0 4px 20px rgba(30,27,75,0.05);">
|
|
||||||
|
|
||||||
<!-- Card header -->
|
|
||||||
<div style="padding:14px 22px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:8px;height:8px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:var(--ink);">How vibn will build it</span>
|
|
||||||
<button onclick="openWhy()" style="margin-left:auto;background:none;border:none;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;color:#6366F1;font-weight:600;padding:0;display:flex;align-items:center;gap:5px;">💡 Why these choices?</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Frontend -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">◇</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Frontend</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Where will your users mostly be when they use your product — at a desk, or on the go? This shapes every screen we design.</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="frontend" data-tip="Runs in any browser, desktop & mobile" onclick="selectOpt('frontend',this)">Web app</button>
|
|
||||||
<button class="opt-btn" data-group="frontend" data-tip="Optimised for phones, still works on desktop" onclick="selectOpt('frontend',this)">Mobile-first</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Backend -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">⬡</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Backend & Database</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">The invisible part that stores your users' data and makes everything work behind the scenes</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="backend" data-tip="Standard setup, works for almost everything" onclick="selectOpt('backend',this)">API + database</button>
|
|
||||||
<button class="opt-btn" data-group="backend" data-tip="Live updates between users — great for collaboration" onclick="selectOpt('backend',this)">Real-time</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Auth -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">◎</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Sign up & Login</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">How people create an account and get back in — fewer steps means more people actually sign up</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="auth" data-tip="Google & GitHub sign-in — less friction, more sign-ups" onclick="selectOpt('auth',this)">Email + social</button>
|
|
||||||
<button class="opt-btn" data-group="auth" data-tip="Simpler setup, no third-party login" onclick="selectOpt('auth',this)">Email only</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Payments -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">◈</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Payments</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">How you get paid — Stripe handles the card processing so you never touch sensitive data</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="payments" data-tip="Monthly or annual recurring payments" onclick="selectOpt('payments',this)">Subscription</button>
|
|
||||||
<button class="opt-btn" data-group="payments" data-tip="Pay once, own forever" onclick="selectOpt('payments',this)">One-time</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">✦</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Email</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Automated messages sent to your users — from welcome emails on day one to newsletters later</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="email" data-tip="Welcome emails, resets & marketing newsletters" onclick="selectOpt('email',this)">Full suite</button>
|
|
||||||
<button class="opt-btn" data-group="email" data-tip="Just transactional emails — resets and confirmations" onclick="selectOpt('email',this)">Essentials only</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hosting — locked -->
|
|
||||||
<div class="blueprint-row locked">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:#F3F4F6;display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--muted);">▲</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Hosting</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Where your product lives — on your own servers, so no one else controls your data or your costs</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" style="cursor:default;">Your servers 🔒</button>
|
|
||||||
<button class="opt-btn why-btn" onclick="openHostingWhy()" style="border-radius:0 7px 7px 0;border-right:none;color:#6366F1;font-weight:600;">Why?</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reassurance note -->
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:11px 16px;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:10px;">
|
|
||||||
<span style="font-size:16px;flex-shrink:0;">💬</span>
|
|
||||||
<p style="font-size:12px;color:var(--mid);line-height:1.55;margin:0;">Not sure about any of these? Don't worry — you can change them anytime before we start building.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── RIGHT PANEL ── -->
|
|
||||||
<div class="arch-right" style="width:384px;border-left:1px solid var(--border);background:#f5f3ff;display:flex;flex-direction:column;flex-shrink:0;">
|
|
||||||
|
|
||||||
<!-- Panel header — matches Describe PRD style -->
|
|
||||||
<div style="flex-shrink:0;padding:18px 0 0;">
|
|
||||||
<div style="margin:0 16px;padding-bottom:14px;border-bottom:1px solid #c7d2fe;">
|
|
||||||
<div style="font-size:15px;font-weight:800;letter-spacing:0.04em;text-transform:uppercase;color:#4338ca;margin-bottom:5px;">What your users will be able to do</div>
|
|
||||||
<div style="font-size:12px;color:var(--ink3);line-height:1.5;">10 screens covering the full user journey, ready to design.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scrollable content -->
|
|
||||||
<div class="arch-right-scroll" style="flex:1;overflow-y:auto;padding:16px;">
|
|
||||||
|
|
||||||
<!-- Pages -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Pages</div>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:16px;">
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Public</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Discover your product</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>See your pricing</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Learn about you</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Auth</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Create an account</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Sign back in</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Reset their password</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">App</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Use the dashboard</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Manage their settings</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Payments</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Subscribe and pay</div>
|
|
||||||
<div class="deliverable-row" style="border-bottom:none;"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Manage their plan</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Infrastructure -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Infrastructure</div>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;">
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🖥</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Your own servers</div><div style="font-size:11px;color:var(--muted);">No platform lock-in, ever</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🔁</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Auto-deploy via Coolify</div><div style="font-size:11px;color:var(--muted);">Every push goes live instantly</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🔒</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Code stored in Gitea</div><div style="font-size:11px;color:var(--muted);">Private repo, yours alone</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Timeline / cost signal -->
|
|
||||||
<div style="display:flex;align-items:center;justify-content:center;gap:16px;padding:10px 0 2px;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);display:flex;align-items:center;gap:5px;">⏱ <span>~3–4 weeks to build</span></span>
|
|
||||||
<span style="font-size:11px;color:var(--muted);">·</span>
|
|
||||||
<span style="font-size:11px;color:var(--muted);display:flex;align-items:center;gap:5px;">💰 <span>No platform fees</span></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<div class="arch-right-footer" style="border-top:1px solid var(--border);padding:9px 0 13px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;">
|
|
||||||
<p style="font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 10px;line-height:1.5;">All set — let's decide how it looks.</p>
|
|
||||||
<a href="07_design.html" style="text-decoration:none;display:block;width:80%;">
|
|
||||||
<button class="btn-primary" style="width:100%;padding:12px 14px;border-radius:8px;">Next: Design</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── WHY POPUP ── -->
|
|
||||||
<div id="why-popup" style="display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:200;align-items:center;justify-content:center;padding:24px;" onclick="closeWhy()">
|
|
||||||
<div style="background:var(--white);border-radius:16px;width:100%;max-width:480px;box-shadow:0 24px 64px rgba(30,27,75,0.18);padding:32px;" onclick="event.stopPropagation()">
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;">
|
|
||||||
<span style="font-size:20px;">💡</span>
|
|
||||||
<h3 style="font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">Why we chose this for your product</h3>
|
|
||||||
</div>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;">Based on your idea, vibn picked a <strong style="color:var(--ink);font-weight:600;">web app with subscription billing</strong> — the fastest path to recurring revenue for a SaaS product.</p>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink);font-weight:600;">Email + social login</strong> keeps sign-up friction low. Most people won't create a password for something they haven't tried yet — one click with Google removes that barrier.</p>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:24px;"><strong style="color:var(--ink);font-weight:600;">Your own hosting</strong> means you own the infrastructure outright. No platform lock-in, no surprise price hikes. Coolify + Gitea are already configured to your account.</p>
|
|
||||||
<button onclick="closeWhy()" class="btn-primary" style="width:100%;padding:12px;">Got it</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── SAVE POPUP ── -->
|
|
||||||
<div id="save-exit-popup">
|
|
||||||
<div id="save-exit-box">
|
|
||||||
<div class="save-icon">✓</div>
|
|
||||||
<h3>Your progress is saved.</h3>
|
|
||||||
<p>You can come back to this project anytime from your dashboard — everything will be exactly where you left it.</p>
|
|
||||||
<button onclick="window.location.href='03_dashboard.html'" style="width:100%;background:linear-gradient(135deg,#2e2a5e,#4338ca);color:#fff;border:none;border-radius:10px;padding:12px;font-family:'Plus Jakarta Sans',sans-serif;font-size:14px;font-weight:600;cursor:pointer;margin-bottom:10px;">Got it, go to dashboard</button>
|
|
||||||
<button class="save-cancel" onclick="cancelSaveExit()">Stay on this page</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── MODAL ── -->
|
|
||||||
<div id="modal" class="modal-bg" onclick="closeModal()">
|
|
||||||
<div class="modal-card" onclick="event.stopPropagation()">
|
|
||||||
<div style="padding:18px 22px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;">
|
|
||||||
<span id="modal-title" class="f" style="font-size:15px;font-weight:700;color:var(--ink);"></span>
|
|
||||||
<button onclick="closeModal()" style="background:transparent;border:none;color:var(--muted);cursor:pointer;font-size:18px;line-height:1;">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:16px 22px;" id="modal-options"></div>
|
|
||||||
<div style="padding:10px 22px 18px;">
|
|
||||||
<button onclick="closeModal()" class="btn-primary" style="width:100%;padding:12px;border-radius:9px;">Got it</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
try {
|
|
||||||
var name = localStorage.getItem('vibn_project_name') || 'My project';
|
|
||||||
var el = document.getElementById('sidebar-project-name');
|
|
||||||
el.textContent = name;
|
|
||||||
el.style.display = 'block';
|
|
||||||
var intro = document.getElementById('intro-project-name');
|
|
||||||
if(intro) intro.textContent = name;
|
|
||||||
} catch(e){}
|
|
||||||
// Init Blueprint tab on tablet + mobile
|
|
||||||
if(window.innerWidth <= 1024){
|
|
||||||
document.querySelector('.arch-main').classList.add('tab-active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function switchArchTab(tab){
|
|
||||||
var main = document.querySelector('.arch-main');
|
|
||||||
var right = document.querySelector('.arch-right');
|
|
||||||
var btnBlueprint = document.getElementById('tab-blueprint');
|
|
||||||
var btnScope = document.getElementById('tab-scope');
|
|
||||||
if(tab === 'blueprint'){
|
|
||||||
main.classList.add('tab-active');
|
|
||||||
right.classList.remove('tab-active');
|
|
||||||
btnBlueprint.classList.add('active');
|
|
||||||
btnScope.classList.remove('active');
|
|
||||||
} else {
|
|
||||||
right.classList.add('tab-active');
|
|
||||||
main.classList.remove('tab-active');
|
|
||||||
btnScope.classList.add('active');
|
|
||||||
btnBlueprint.classList.remove('active');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveAndExit(){ document.getElementById('save-exit-popup').classList.add('visible'); }
|
|
||||||
function cancelSaveExit(){ document.getElementById('save-exit-popup').classList.remove('visible'); }
|
|
||||||
|
|
||||||
function openWhy(){ document.getElementById('why-popup').style.display='flex'; }
|
|
||||||
function closeWhy(){ document.getElementById('why-popup').style.display='none'; }
|
|
||||||
|
|
||||||
function selectOpt(group, el){
|
|
||||||
document.querySelectorAll('[data-group="'+group+'"]').forEach(function(b){ b.classList.remove('selected'); });
|
|
||||||
el.classList.add('selected');
|
|
||||||
}
|
|
||||||
function openHostingWhy(){
|
|
||||||
document.getElementById('modal-title').textContent = 'Why your own servers?';
|
|
||||||
document.getElementById('modal-options').innerHTML =
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink)">Most platforms rent you server space</strong> — which means they control your data, your uptime, and your bill. The day they raise prices or shut down, your product goes with it.</p>' +
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink)">With vibn, your product lives on your own servers.</strong> You own the infrastructure outright. Nobody can lock you out, hike your costs, or access your users\u2019 data without your permission.</p>' +
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;">We handle the hard part: <a href="https://coolify.io" target="_blank" style="color:var(--indigo);font-weight:600;text-decoration:none;">Coolify</a> auto-deploys your code every time you push an update, and <a href="https://gitea.com" target="_blank" style="color:var(--indigo);font-weight:600;text-decoration:none;">Gitea</a> stores your codebase securely. You get full control of your infrastructure \u2014 without needing to know how any of it works.</p>';
|
|
||||||
document.getElementById('modal').classList.add('open');
|
|
||||||
}
|
|
||||||
function openModal(title, opt1, desc1, opt2, desc2){
|
|
||||||
document.getElementById('modal-title').textContent = title;
|
|
||||||
document.getElementById('modal-options').innerHTML = '<p style="font-size:13px;color:var(--mid);line-height:1.6;">'+desc1+'</p>';
|
|
||||||
document.getElementById('modal').classList.add('open');
|
|
||||||
}
|
|
||||||
function closeModal(){ document.getElementById('modal').classList.remove('open'); }
|
|
||||||
function showTip(el){
|
|
||||||
hideTip();
|
|
||||||
var tip=document.createElement('div');
|
|
||||||
tip.id='vibn-tip';
|
|
||||||
tip.textContent=el.dataset.tip;
|
|
||||||
tip.style.cssText='position:fixed;background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#fff;font-size:11px;font-weight:500;line-height:1.5;padding:6px 10px;border-radius:6px;white-space:nowrap;z-index:9999;pointer-events:none;box-shadow:0 4px 16px rgba(67,56,202,0.35);font-family:Plus Jakarta Sans,sans-serif;transition:opacity 0.1s;';
|
|
||||||
document.body.appendChild(tip);
|
|
||||||
var r=el.getBoundingClientRect();
|
|
||||||
tip.style.left=(r.left+r.width/2-tip.offsetWidth/2)+'px';
|
|
||||||
tip.style.top=(r.top-tip.offsetHeight-9)+'px';
|
|
||||||
var arrow=document.createElement('div');
|
|
||||||
arrow.id='vibn-tip-arrow';
|
|
||||||
arrow.style.cssText='position:fixed;border:5px solid transparent;border-top-color:#4338CA;z-index:9999;pointer-events:none;';
|
|
||||||
document.body.appendChild(arrow);
|
|
||||||
arrow.style.left=(r.left+r.width/2-5)+'px';
|
|
||||||
arrow.style.top=(r.top-9)+'px';
|
|
||||||
}
|
|
||||||
function hideTip(){
|
|
||||||
var t=document.getElementById('vibn-tip');
|
|
||||||
var a=document.getElementById('vibn-tip-arrow');
|
|
||||||
if(t)t.remove();
|
|
||||||
if(a)a.remove();
|
|
||||||
}
|
|
||||||
document.addEventListener('mouseover',function(e){var b=e.target.closest('.opt-btn[data-tip]');if(b)showTip(b);});
|
|
||||||
document.addEventListener('mouseout',function(e){var b=e.target.closest('.opt-btn[data-tip]');if(b)hideTip();});
|
|
||||||
function toggleTheme(){const html=document.documentElement;const isDark=html.dataset.theme==='dark';html.dataset.theme=isDark?'':'dark';document.getElementById('dark-toggle').textContent=isDark?'🌙 Dark mode':'☀️ Light mode';localStorage.setItem('vibn-theme',isDark?'':'dark');}
|
|
||||||
(function(){const saved=localStorage.getItem('vibn-theme');if(saved==='dark'){document.documentElement.dataset.theme='dark';document.addEventListener('DOMContentLoaded',function(){const btn=document.getElementById('dark-toggle');if(btn)btn.textContent='☀️ Light mode';});}})();
|
|
||||||
</script>
|
|
||||||
</body></html>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# vibn — UX Screen Pack
|
|
||||||
|
|
||||||
Design system: **Ink & parchment** — Lora serif + Inter sans, no colour accent.
|
|
||||||
All HTML files open directly in any browser — no build step required.
|
|
||||||
|
|
||||||
## Complete screen inventory
|
|
||||||
|
|
||||||
| File | Screen | Interactive? |
|
|
||||||
|------|--------|-------------|
|
|
||||||
| `01_homepage.html` | Marketing homepage | Scroll |
|
|
||||||
| `02_signup.html` | Sign up (3 steps) | ✓ Click through steps, mode selection |
|
|
||||||
| `03_dashboard.html` | Projects dashboard | ✓ Projects / Clients / Invoices / Costs nav |
|
|
||||||
| `04_welcome.html` | Welcome phase | Static |
|
|
||||||
| `05_discover.html` | Discover phase | ✓ Live chat, PRD fills in |
|
|
||||||
| `06_architect.html` | Architect phase | ✓ Change modal per block |
|
|
||||||
| `07_design.html` | Design phase | ✓ Live style preview switching |
|
|
||||||
| `08_market.html` | Market phase | ✓ Voice sliders, Topics, Website style |
|
|
||||||
| `09_build.html` | Build phase | ✓ Review + animated 12-step pipeline |
|
|
||||||
| `10_vibe_editor.html` | Vibe editor (post-launch) | ✓ Chat, preview updates, deploy status |
|
|
||||||
| `vibn-website.jsx` | Marketing site | React component |
|
|
||||||
| `vibn-dashboard.jsx` | Dashboard + billing | React component |
|
|
||||||
| `00_design-tokens.css` | Design tokens | Reference |
|
|
||||||
|
|
||||||
## Full user flow
|
|
||||||
01 Homepage → 02 Sign up → 03 Dashboard → 04 Welcome →
|
|
||||||
05 Discover → 06 Architect → 07 Design → 08 Market →
|
|
||||||
09 Build → 10 Vibe editor
|
|
||||||
|
|
||||||
## Design tokens
|
|
||||||
|
|
||||||
| Token | Value | Usage |
|
|
||||||
|-------|-------|-------|
|
|
||||||
| `--ink` | `#1a1510` | Primary text, buttons |
|
|
||||||
| `--ink3` | `#444441` | Secondary text |
|
|
||||||
| `--mid` | `#5f5e5a` | Body text |
|
|
||||||
| `--muted` | `#888780` | Labels, captions |
|
|
||||||
| `--stone` | `#b4b2a9` | Disabled, hints |
|
|
||||||
| `--cream` | `#f1efe8` | Surface tint, hover |
|
|
||||||
| `--paper` | `#f7f4ee` | Page background |
|
|
||||||
| `--white` | `#fdfcfa` | Card backgrounds |
|
|
||||||
| `--border` | `#e8e2d9` | All borders |
|
|
||||||
|
|
||||||
Heading font: **Lora** (serif)
|
|
||||||
Body font: **Inter** (sans-serif)
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
@@ -1,119 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>Sign in – Google Accounts</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
body{font-family:'Roboto',sans-serif;background:#FFFFFF;display:flex;flex-direction:column;align-items:center;min-height:100vh;padding:40px 24px;}
|
|
||||||
|
|
||||||
.card{width:100%;max-width:400px;border:1px solid #DADCE0;border-radius:8px;padding:40px 40px 28px;display:flex;flex-direction:column;align-items:center;}
|
|
||||||
|
|
||||||
.google-logo{margin-bottom:24px;}
|
|
||||||
|
|
||||||
h1{font-family:'Google Sans',sans-serif;font-size:24px;font-weight:400;color:#202124;margin-bottom:8px;text-align:center;}
|
|
||||||
.subtitle{font-size:16px;color:#202124;margin-bottom:24px;text-align:center;}
|
|
||||||
|
|
||||||
/* Account tile */
|
|
||||||
.account-tile{width:100%;border:1px solid #DADCE0;border-radius:8px;padding:12px 16px;display:flex;align-items:center;gap:16px;cursor:pointer;transition:background 0.15s;margin-bottom:12px;}
|
|
||||||
.account-tile:hover{background:#F8F9FA;}
|
|
||||||
.avatar{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#4285F4,#34A853);display:flex;align-items:center;justify-content:center;font-family:'Google Sans',sans-serif;font-size:16px;font-weight:500;color:#FFFFFF;flex-shrink:0;}
|
|
||||||
.account-info{flex:1;text-align:left;}
|
|
||||||
.account-name{font-family:'Google Sans',sans-serif;font-size:14px;font-weight:500;color:#202124;margin-bottom:2px;}
|
|
||||||
.account-email{font-size:13px;color:#5F6368;}
|
|
||||||
.chevron{color:#5F6368;}
|
|
||||||
|
|
||||||
/* Add account */
|
|
||||||
.add-account{width:100%;border:1px solid #DADCE0;border-radius:8px;padding:12px 16px;display:flex;align-items:center;gap:16px;cursor:pointer;transition:background 0.15s;margin-bottom:24px;}
|
|
||||||
.add-account:hover{background:#F8F9FA;}
|
|
||||||
.add-icon{width:40px;height:40px;border-radius:50%;background:#F1F3F4;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:#5F6368;}
|
|
||||||
.add-label{font-size:14px;color:#202124;}
|
|
||||||
|
|
||||||
.divider{width:100%;height:1px;background:#E8EAED;margin-bottom:20px;}
|
|
||||||
|
|
||||||
/* Continue button */
|
|
||||||
.btn-continue{background:#1A73E8;color:#FFFFFF;border:none;border-radius:4px;padding:10px 24px;font-family:'Google Sans',sans-serif;font-size:14px;font-weight:500;cursor:pointer;transition:background 0.15s,box-shadow 0.15s;box-shadow:0 1px 2px rgba(0,0,0,0.2);}
|
|
||||||
.btn-continue:hover{background:#1765CC;box-shadow:0 2px 6px rgba(0,0,0,0.2);}
|
|
||||||
|
|
||||||
.footer{margin-top:auto;padding-top:24px;display:flex;gap:24px;justify-content:center;}
|
|
||||||
.footer a{font-size:12px;color:#5F6368;text-decoration:none;}
|
|
||||||
.footer a:hover{text-decoration:underline;}
|
|
||||||
|
|
||||||
/* Loading state */
|
|
||||||
.loading{display:none;flex-direction:column;align-items:center;gap:16px;margin-top:20px;}
|
|
||||||
.spinner{width:32px;height:32px;border:3px solid #E8EAED;border-top-color:#1A73E8;border-radius:50%;animation:spin 0.8s linear infinite;}
|
|
||||||
@keyframes spin{to{transform:rotate(360deg);}}
|
|
||||||
.loading-text{font-size:14px;color:#5F6368;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
|
|
||||||
<!-- Google logo -->
|
|
||||||
<div class="google-logo">
|
|
||||||
<svg width="75" height="24" viewBox="0 0 75 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M30.5 12.3c0-.7-.1-1.4-.2-2h-9.6v3.8h5.5c-.2 1.3-1 2.4-2.1 3.1v2.6h3.4c2-1.8 3-4.5 3-7.5z" fill="#4285F4"/>
|
|
||||||
<path d="M20.7 19.9c2.7 0 5-.9 6.7-2.4l-3.4-2.6c-.9.6-2 1-3.3 1-2.6 0-4.7-1.7-5.5-4.1H11.7v2.7c1.7 3.4 5.2 5.4 9 5.4z" fill="#34A853"/>
|
|
||||||
<path d="M15.2 11.8c-.2-.6-.3-1.2-.3-1.8s.1-1.2.3-1.8V7.5H11.7C11 8.8 10.7 10.3 10.7 12s.3 3.2 1 4.5l3.5-2.7z" fill="#FBBC05"/>
|
|
||||||
<path d="M20.7 5.9c1.4 0 2.7.5 3.7 1.5l2.8-2.8C25.7 3 23.4 2 20.7 2c-3.8 0-7.3 2-9 5.4l3.5 2.7c.8-2.4 2.9-4.2 5.5-4.2z" fill="#EA4335"/>
|
|
||||||
<text x="36" y="17" font-family="'Product Sans',Roboto,sans-serif" font-size="16" fill="#5F6368">Google</text>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>Sign in</h1>
|
|
||||||
<p class="subtitle">to continue to vibn</p>
|
|
||||||
|
|
||||||
<!-- Account selector (shown by default) -->
|
|
||||||
<div id="account-select" style="width:100%;">
|
|
||||||
<div class="account-tile" onclick="selectAccount()">
|
|
||||||
<div class="avatar">J</div>
|
|
||||||
<div class="account-info">
|
|
||||||
<div class="account-name">Jane Smith</div>
|
|
||||||
<div class="account-email">jane@gmail.com</div>
|
|
||||||
</div>
|
|
||||||
<svg class="chevron" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="add-account" onclick="selectAccount()">
|
|
||||||
<div class="add-icon">+</div>
|
|
||||||
<div class="add-label">Use another account</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="divider"></div>
|
|
||||||
|
|
||||||
<p style="font-size:12px;color:#5F6368;text-align:center;line-height:1.6;">To continue, Google will share your name, email address, and profile picture with vibn.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Loading state (shown after selection) -->
|
|
||||||
<div class="loading" id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<div class="loading-text">Signing you in…</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<a href="#">Help</a>
|
|
||||||
<a href="#">Privacy</a>
|
|
||||||
<a href="#">Terms</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function selectAccount(){
|
|
||||||
// Show loading
|
|
||||||
document.getElementById('account-select').style.display='none';
|
|
||||||
document.getElementById('loading').style.display='flex';
|
|
||||||
|
|
||||||
// After brief delay, notify parent and close
|
|
||||||
setTimeout(function(){
|
|
||||||
if(window.opener){
|
|
||||||
window.opener.postMessage({type:'google-auth-success',name:'Jane Smith',email:'jane@gmail.com'},'*');
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>vibn — UX screen pack (local)</title>
|
|
||||||
<link rel="icon" href="favicon_clean.ico">
|
|
||||||
<style>
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body {
|
|
||||||
font-family: system-ui, sans-serif;
|
|
||||||
background: #f7f4ee;
|
|
||||||
color: #1a1510;
|
|
||||||
padding: 48px 24px;
|
|
||||||
max-width: 42rem;
|
|
||||||
margin: 0 auto;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
h1 { font-size: 1.35rem; margin-bottom: 0.5rem; }
|
|
||||||
p { color: #5f5e5a; font-size: 0.95rem; margin-bottom: 1.5rem; }
|
|
||||||
ol { padding-left: 1.25rem; }
|
|
||||||
li { margin: 0.5rem 0; }
|
|
||||||
a { color: #1a1510; font-weight: 600; }
|
|
||||||
a:hover { text-decoration: underline; }
|
|
||||||
.note { font-size: 0.85rem; color: #888780; margin-top: 2rem; padding-top: 1rem; border-top: 1px solid #e8e2d9; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>vibn — UX screen pack</h1>
|
|
||||||
<p>Static HTML prototypes. Suggested flow:</p>
|
|
||||||
<ol>
|
|
||||||
<li><a href="01_homepage.html">01 — Homepage</a></li>
|
|
||||||
<li><a href="02_signup.html">02 — Sign up</a></li>
|
|
||||||
<li><a href="03_dashboard.html">03 — Dashboard</a></li>
|
|
||||||
<li><a href="05_describe.html">05 — Describe / discover</a></li>
|
|
||||||
<li><a href="06_architect.html">06 — Architect</a></li>
|
|
||||||
</ol>
|
|
||||||
<p style="margin-top:1.25rem;"><a href="google-auth-popup.html">Google auth popup</a> · <a href="00_design-tokens.css">Design tokens (CSS)</a></p>
|
|
||||||
<p class="note"><code>vibn-website.jsx</code> and <code>vibn-dashboard.jsx</code> are React source references — use the HTML screens here, or wire those into a React app separately.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": ".."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {}
|
|
||||||
}
|
|
||||||
1055
justine/package-lock.json
generated
1055
justine/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "justine-vibn-ux",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"dev": "serve -l 3040",
|
|
||||||
"start": "serve -l 3040"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"serve": "^14.2.4"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
/* vibn Design Tokens — Ink & Parchment */
|
|
||||||
/* Import this in any HTML file or reference these values */
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--ink: #1a1510;
|
|
||||||
--ink2: #2c2c2a;
|
|
||||||
--ink3: #444441;
|
|
||||||
--mid: #5f5e5a;
|
|
||||||
--muted: #888780;
|
|
||||||
--stone: #b4b2a9;
|
|
||||||
--parch: #d3d1c7;
|
|
||||||
--cream: #f1efe8;
|
|
||||||
--paper: #f7f4ee;
|
|
||||||
--white: #fdfcfa;
|
|
||||||
--border: #e8e2d9;
|
|
||||||
|
|
||||||
--serif: 'Lora', Georgia, serif;
|
|
||||||
--sans: 'Inter', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body { font-family: var(--sans); background: var(--paper); color: var(--ink); }
|
|
||||||
.f { font-family: var(--serif); }
|
|
||||||
.s { font-family: var(--sans); }
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>vibn — Homepage</title>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#444441;--mid:#6B7280;--muted:#9CA3AF;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#FFFFFF;--border:#E5E7EB;--serif:'Plus Jakarta Sans',sans-serif;--sans:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFE,#F0EEFF);min-height:100vh;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
nav{background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 52px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;}
|
|
||||||
.nav-links{display:flex;gap:32px;align-items:center;}
|
|
||||||
.btn-ink{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:8px;padding:9px 22px;font-family:var(--sans);font-size:13.5px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.btn-ink-lg{background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:10px;padding:15px 36px;font-family:var(--sans);font-size:15px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn-ink-lg:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.gradient-em{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;font-style:italic;}
|
|
||||||
.gradient-text{background:linear-gradient(to right,#6366F1,#8B5CF6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.gradient-num{background:linear-gradient(135deg,#2E2A5E,#4338CA);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
.empathy-card{background:var(--white);border:1px solid var(--border);border-left:3px solid rgba(99,102,241,0.8);border-radius:12px;padding:18px 20px;display:flex;gap:14px;align-items:flex-start;box-shadow:0 10px 30px rgba(30,27,75,0.05);transition:border-color 0.2s ease,background 0.2s ease;}
|
|
||||||
.empathy-card:hover{border-color:#6366F1;background:#FAFAFF;}
|
|
||||||
|
|
||||||
/* ── Layout grid classes (for responsive overrides) ── */
|
|
||||||
.hero-grid{display:grid;grid-template-columns:1fr 1fr;gap:96px;align-items:center;}
|
|
||||||
.empathy-grid{display:grid;grid-template-columns:1fr 1fr;gap:72px;align-items:center;}
|
|
||||||
.phase-grid{display:grid;grid-template-columns:1fr 1fr;border:1px solid rgba(99,102,241,0.2);border-radius:14px;overflow:hidden;}
|
|
||||||
.wyg-grid{display:grid;grid-template-columns:1fr 1fr 1fr;}
|
|
||||||
.quote-grid{display:grid;grid-template-columns:1fr 1.6fr 1fr;gap:28px;align-items:center;margin-bottom:20px;}
|
|
||||||
.stats-grid{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;}
|
|
||||||
.footer-tagline{display:block;font-size:12px;color:var(--muted);margin-top:4px;font-family:var(--sans);}
|
|
||||||
|
|
||||||
/* ── Hamburger ── */
|
|
||||||
.hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:6px;}
|
|
||||||
.hamburger span{display:block;width:22px;height:2px;background:var(--ink);border-radius:2px;transition:transform 0.25s ease,opacity 0.25s ease;}
|
|
||||||
.hamburger.open span:nth-child(1){transform:translateY(7px) rotate(45deg);}
|
|
||||||
.hamburger.open span:nth-child(2){opacity:0;}
|
|
||||||
.hamburger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}
|
|
||||||
|
|
||||||
/* Mobile drawer */
|
|
||||||
.mobile-menu{display:none;position:fixed;top:62px;left:0;right:0;background:rgba(250,250,250,0.98);border-bottom:1px solid var(--border);padding:20px 24px 28px;z-index:49;flex-direction:column;gap:0;box-shadow:0 8px 24px rgba(30,27,75,0.08);}
|
|
||||||
.mobile-menu.open{display:flex;}
|
|
||||||
.mobile-menu a{font-size:15px;color:var(--ink);text-decoration:none;padding:13px 0;border-bottom:1px solid var(--border);font-weight:500;}
|
|
||||||
.mobile-menu a:last-of-type{border-bottom:none;}
|
|
||||||
.mobile-menu .mobile-menu-cta{margin-top:18px;}
|
|
||||||
|
|
||||||
/* ── Mobile ── */
|
|
||||||
@media (max-width:768px){
|
|
||||||
nav{padding:0 20px;}
|
|
||||||
.nav-links{display:none;}
|
|
||||||
.hamburger{display:flex;}
|
|
||||||
.nav-right-btns{display:none;}
|
|
||||||
.hero-grid{grid-template-columns:1fr;gap:44px;}
|
|
||||||
.hero-section{padding:52px 24px 48px !important;}
|
|
||||||
.empathy-section{padding:56px 24px !important;}
|
|
||||||
.empathy-grid{grid-template-columns:1fr;gap:36px;}
|
|
||||||
.how-section{padding:64px 24px !important;}
|
|
||||||
.phase-grid{grid-template-columns:1fr;}
|
|
||||||
.phase-grid > div{border-right:none !important;padding:28px 24px !important;}
|
|
||||||
.wyg-grid{grid-template-columns:1fr;}
|
|
||||||
.wyg-grid > div{border-right:none !important;border-bottom:1px solid var(--border);padding:32px 24px !important;}
|
|
||||||
.wyg-grid > div:last-child{border-bottom:none;}
|
|
||||||
.wyg-section{padding:0 24px !important;}
|
|
||||||
.quote-grid{grid-template-columns:1fr;}
|
|
||||||
.quote-side{display:none !important;}
|
|
||||||
.quote-section{padding:32px 24px 28px !important;}
|
|
||||||
.stats-grid{grid-template-columns:1fr 1fr;}
|
|
||||||
.stats-grid > div{padding:28px 16px !important;}
|
|
||||||
.stats-grid > div:nth-child(odd){padding-left:0 !important;}
|
|
||||||
.stats-grid > div:nth-child(3),.stats-grid > div:nth-child(4){border-top:1px solid var(--border);}
|
|
||||||
.stats-grid > div:nth-child(even){border-right:none !important;}
|
|
||||||
.stats-section{padding:0 24px !important;}
|
|
||||||
.cta-section{padding:56px 20px !important;}
|
|
||||||
.cta-card{padding:44px 28px !important;}
|
|
||||||
.hero-h1{font-size:40px !important;line-height:1.1 !important;}
|
|
||||||
.hero-sub{font-size:15px !important;}
|
|
||||||
footer{flex-direction:column;gap:20px;text-align:center;padding:32px 24px !important;}
|
|
||||||
.footer-links{flex-wrap:wrap;justify-content:center;}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;">
|
|
||||||
<div class="logo-box" style="width:30px;height:30px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:7px;display:flex;align-items:center;justify-content:center;"><span class="f" style="font-size:15px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:19px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="#how-it-works" style="font-size:14px;color:var(--muted);text-decoration:none;">How it works</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Stories</a>
|
|
||||||
<a href="#" style="font-size:14px;color:var(--muted);text-decoration:none;">Blog</a>
|
|
||||||
</div>
|
|
||||||
<div class="nav-right-btns" style="display:flex;align-items:center;gap:12px;">
|
|
||||||
<a href="03_dashboard.html" style="font-size:14px;color:#6366F1;font-weight:600;text-decoration:none;">Log in</a>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
<button class="hamburger" id="hamburger" aria-label="Open menu" onclick="toggleMenu()">
|
|
||||||
<span></span><span></span><span></span>
|
|
||||||
</button>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Mobile drawer -->
|
|
||||||
<div class="mobile-menu" id="mobile-menu">
|
|
||||||
<a href="#how-it-works" onclick="closeMenu()">How it works</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Pricing</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Stories</a>
|
|
||||||
<a href="#" onclick="closeMenu()">Blog</a>
|
|
||||||
<a href="03_dashboard.html" style="color:#6366F1;font-weight:600;" onclick="closeMenu()">Log in</a>
|
|
||||||
<div class="mobile-menu-cta">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="width:100%;">Get started free</button></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- HERO -->
|
|
||||||
<section class="hero-section" style="max-width:980px;margin:0 auto;padding:88px 52px 72px;">
|
|
||||||
<div class="hero-grid">
|
|
||||||
|
|
||||||
<!-- Left: copy -->
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:22px;">For non-technical founders</div>
|
|
||||||
<h1 class="f hero-h1" style="font-size:58px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.06;margin-bottom:28px;">
|
|
||||||
You have the idea.<br>We handle<br><em class="gradient-em">everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p class="hero-sub" style="font-size:17px;color:var(--mid);line-height:1.75;">You describe it. Vibn builds it, launches it, and markets it. From idea to <strong style="color:var(--ink);">live</strong> product in <strong style="color:var(--ink);">72 hours</strong> — no code, no agencies, no waiting.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: product moment card -->
|
|
||||||
<div style="flex-shrink:0;">
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:16px;overflow:hidden;box-shadow:0 20px 60px rgba(30,27,75,0.05);">
|
|
||||||
|
|
||||||
<!-- Input area -->
|
|
||||||
<div style="padding:24px 26px 20px;background:#FCFCFF;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:12px;">Your idea</div>
|
|
||||||
<p class="f" style="font-size:15px;font-style:italic;color:var(--ink);line-height:1.65;margin-bottom:14px;">"I want to build a booking tool for independent personal trainers."</p>
|
|
||||||
<div style="display:flex;justify-content:flex-end;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);background:var(--white);border:1px solid var(--border);border-radius:5px;padding:3px 9px;letter-spacing:0.04em;">↵ Enter</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Output area -->
|
|
||||||
<div style="padding:20px 26px 24px;background:var(--white);">
|
|
||||||
<div style="font-size:10px;font-weight:600;letter-spacing:0.12em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">vibn generated</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:0;">
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Pages</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Landing, Dashboard, Booking, Payments</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Stack</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Auth, database, payments — handled</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Revenue</span>
|
|
||||||
<span style="font-size:13px;color:var(--ink);font-weight:600;">Subscription · $29 / mo</span>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;justify-content:space-between;align-items:baseline;padding:10px 0;">
|
|
||||||
<span style="font-size:12px;color:var(--muted);font-weight:500;">Status</span>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:#6366F1;">⬤ Ready to build</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CTA row -->
|
|
||||||
<div style="display:flex;flex-direction:column;align-items:center;text-align:center;gap:10px;margin-top:52px;">
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg">Start free — no code needed</button></a>
|
|
||||||
<div><span style="font-size:13.5px;color:#818CF8;">★★★★★</span><span style="font-size:13.5px;color:var(--stone);"> 280 founders launched</span></div>
|
|
||||||
<p style="font-size:12px;color:#9CA3AF;">No credit card required · Free forever plan</p>
|
|
||||||
<a href="#how-it-works" style="font-size:13.5px;color:#6366F1;text-decoration:none;font-weight:500;margin-top:4px;">See how it works →</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- EMPATHY -->
|
|
||||||
<section class="empathy-section" style="border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:80px 52px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
<div class="empathy-grid">
|
|
||||||
<div>
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:18px;">Sound familiar?</div>
|
|
||||||
<h2 class="f" style="font-size:36px;font-weight:700;color:#1A1A1A;line-height:1.18;margin-bottom:24px;letter-spacing:-0.02em;">The idea is the hard part. <span class="gradient-text">Everything else shouldn't be.</span></h2>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;margin-bottom:20px;">You know exactly what you want to build and who it's for. But the moment you think about servers, databases, deployment pipelines, SEO — the whole thing stalls.</p>
|
|
||||||
<p style="font-size:15px;color:var(--mid);line-height:1.82;">vibn exists to remove all of that. Not abstract it — <em class="f" style="font-style:italic;">remove it entirely.</em></p>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:14px;">
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I need to hire a developer first"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">vibn is your developer. Start building the moment you have an idea.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more staring at a blank marketing calendar</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">AI generates and publishes your content every single week.</div></div></div>
|
|
||||||
<div class="empathy-card"><div style="width:20px;height:20px;border-radius:50%;border:1.5px solid rgba(99,102,241,0.4);display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:2px;"><div style="width:7px;height:7px;border-radius:50%;background:#6366F1;"></div></div><div><div class="f" style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:4px;">No more "I'll launch when it's ready"</div><div style="font-size:13px;color:var(--mid);line-height:1.7;">Most founders ship their first version in under 72 hours.</div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- HOW IT WORKS -->
|
|
||||||
<section id="how-it-works" class="how-section" style="max-width:980px;margin:0 auto;padding:84px 52px;">
|
|
||||||
<div style="font-size:11px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--muted);margin-bottom:16px;">How it works</div>
|
|
||||||
<h2 class="f" style="font-size:42px;font-weight:700;color:#1A1A1A;letter-spacing:-0.02em;margin-bottom:54px;max-width:480px;line-height:1.15;">Four phases. One <span class="gradient-text">complete</span> product.</h2>
|
|
||||||
<div class="phase-grid">
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">01 — Discover</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Define your idea</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-bottom:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">02 — Design</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Choose your style</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">Pick a visual style and see your exact site and emails live before a single line of code is written.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);border-right:1px solid rgba(99,102,241,0.2);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">03 — Build</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Your app, live</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English.</p></div>
|
|
||||||
<div style="padding:40px 44px;background:var(--white);"><div style="font-size:11px;font-weight:600;letter-spacing:0.1em;text-transform:uppercase;color:rgba(99,102,241,0.6);margin-bottom:14px;">04 — Grow</div><div class="f" style="font-size:22px;font-weight:700;color:#1A1A1A;margin-bottom:10px;">Market & automate</div><p style="font-size:13.5px;color:var(--mid);line-height:1.72;">AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on users.</p></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- WHAT YOU GET -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div class="wyg-grid wyg-section" style="max-width:980px;margin:0 auto;padding:0 52px;">
|
|
||||||
|
|
||||||
<div style="padding:44px 40px 44px 0;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A live, working product</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Not a prototype. Real auth, real payments, real database — on your own URL from day one.</p>
|
|
||||||
<p style="font-size:12px;color:var(--muted);line-height:1.6;text-align:center;margin-top:10px;">Runs on your own servers — your data, your infrastructure, no lock-in.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 40px;border-right:1px solid var(--border);">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A full marketing engine</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Blog posts, onboarding emails, and social content — written and published automatically every week.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="padding:44px 0 44px 40px;">
|
|
||||||
<div style="font-size:13px;font-weight:700;color:#6366F1;margin-bottom:12px;text-align:center;">✦</div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:#1A1A1A;margin-bottom:8px;text-align:center;">A product that evolves</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--mid);line-height:1.7;text-align:center;">Describe changes in plain English. Vibn handles the code so your product grows as fast as your ideas.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- QUOTE BAND -->
|
|
||||||
<section class="quote-section" style="background:#1A1A1A;padding:32px 52px 28px;">
|
|
||||||
<div style="max-width:980px;margin:0 auto;">
|
|
||||||
|
|
||||||
<div class="quote-grid">
|
|
||||||
|
|
||||||
<!-- Left: supporting quote -->
|
|
||||||
<div class="quote-side" style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Alex K., founder of Taskly</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Center: dominant quote -->
|
|
||||||
<div style="background:rgba(255,255,255,0.05);border-radius:12px;padding:22px 26px;">
|
|
||||||
<div style="width:3px;height:16px;background:#6366F1;border-radius:2px;margin-bottom:12px;opacity:0.7;"></div>
|
|
||||||
<p class="f" style="font-size:16px;color:#FFFFFF;line-height:1.7;font-style:italic;margin-bottom:12px;">"I have zero coding experience. Three weeks in, I have 300 paying users. That's entirely because of vibn."</p>
|
|
||||||
<span style="font-size:11px;color:var(--muted);font-weight:600;">— Marcus L., founder of Flowmatic</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: supporting quote -->
|
|
||||||
<div class="quote-side" style="display:flex;gap:14px;opacity:0.85;">
|
|
||||||
<div style="width:2px;background:#6366F1;border-radius:2px;flex-shrink:0;"></div>
|
|
||||||
<div>
|
|
||||||
<p class="f" style="font-size:12.5px;color:#FFFFFF;line-height:1.65;font-style:italic;margin-bottom:8px;">"The marketing autopilot saved me ten hours a week. My blog runs itself. I just focus on product."</p>
|
|
||||||
<span style="font-size:10.5px;color:var(--muted);font-weight:600;">— Sara R., founder of Nudge</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Pagination dots -->
|
|
||||||
<div style="display:flex;justify-content:center;gap:7px;">
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
<div style="width:16px;height:5px;border-radius:3px;background:#FFFFFF;"></div>
|
|
||||||
<div style="width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,0.3);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- STATS -->
|
|
||||||
<section style="background:var(--white);border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<div class="stats-grid stats-section" style="max-width:980px;margin:0 auto;padding:0 52px;">
|
|
||||||
<div style="padding:40px 0;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">280+</div><div style="font-size:13px;color:var(--muted);">founders launched</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">72h</div><div style="font-size:13px;color:var(--muted);">average time to first version</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;border-right:1px solid var(--border);"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">4.9★</div><div style="font-size:13px;color:var(--muted);">average rating</div></div>
|
|
||||||
<div style="padding:40px 0 40px 36px;"><div class="f gradient-num" style="font-size:40px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">3×</div><div style="font-size:13px;color:var(--muted);">faster than hiring a developer</div></div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- CTA -->
|
|
||||||
<section class="cta-section" style="padding:80px 52px;text-align:center;">
|
|
||||||
<div class="cta-card" style="max-width:680px;margin:0 auto;background:#FFFFFF;border-radius:20px;padding:64px 52px;box-shadow:0 0 0 1px rgba(99,102,241,0.15),0 20px 60px rgba(30,27,75,0.08);">
|
|
||||||
<h2 class="f" style="font-size:48px;font-weight:700;color:var(--ink);letter-spacing:-0.03em;line-height:1.1;margin-bottom:20px;">Your idea deserves to exist.</h2>
|
|
||||||
<p style="font-size:16px;color:var(--mid);line-height:1.75;margin-bottom:38px;">Thousands of ideas never make it past a spreadsheet. Yours doesn't have to be one of them.</p>
|
|
||||||
<a href="02_signup.html"><button class="btn-ink-lg" style="margin-bottom:16px;">Build my product — free</button></a>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">Joins 280+ non-technical founders already live</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FOOTER -->
|
|
||||||
<footer style="background:rgba(250,250,250,0.95);border-top:1px solid var(--border);padding:32px 52px;display:grid;grid-template-columns:1fr auto 1fr;align-items:center;">
|
|
||||||
<div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:var(--ink);">vibn</span>
|
|
||||||
<span class="footer-tagline">The fastest way from idea to product.</span>
|
|
||||||
</div>
|
|
||||||
<div class="footer-links" style="display:flex;gap:28px;">
|
|
||||||
<a href="#how-it-works" style="font-size:13px;color:var(--muted);text-decoration:none;">How it works</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Pricing</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Privacy</a>
|
|
||||||
<a href="#" style="font-size:13px;color:var(--muted);text-decoration:none;">Terms</a>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:12.5px;color:var(--muted);text-align:right;display:block;">© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function toggleMenu(){
|
|
||||||
var btn=document.getElementById('hamburger');
|
|
||||||
var menu=document.getElementById('mobile-menu');
|
|
||||||
var open=menu.classList.toggle('open');
|
|
||||||
btn.classList.toggle('open',open);
|
|
||||||
document.body.style.overflow=open?'hidden':'';
|
|
||||||
}
|
|
||||||
function closeMenu(){
|
|
||||||
document.getElementById('hamburger').classList.remove('open');
|
|
||||||
document.getElementById('mobile-menu').classList.remove('open');
|
|
||||||
document.body.style.overflow='';
|
|
||||||
}
|
|
||||||
// Close on anchor click (for same-page links like #how-it-works)
|
|
||||||
document.querySelectorAll('.mobile-menu a[href^="#"]').forEach(function(a){
|
|
||||||
a.addEventListener('click',closeMenu);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>vibn — Sign up</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{
|
|
||||||
--ink:#1A1A1A;
|
|
||||||
--mid:#6B7280;
|
|
||||||
--muted:#9CA3AF;
|
|
||||||
--border:#E5E7EB;
|
|
||||||
--white:#FFFFFF;
|
|
||||||
--soft:#F5F3FF;
|
|
||||||
--hover:#FAFAFF;
|
|
||||||
--serif:'Plus Jakarta Sans',sans-serif;
|
|
||||||
--sans:'Plus Jakarta Sans',sans-serif;
|
|
||||||
}
|
|
||||||
body{font-family:var(--sans);background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);min-height:100vh;display:flex;flex-direction:column;color:var(--ink);}
|
|
||||||
.f{font-family:var(--serif);}
|
|
||||||
|
|
||||||
/* Inputs */
|
|
||||||
input::placeholder{color:var(--muted);}
|
|
||||||
input{width:100%;border:1px solid var(--border);border-radius:8px;padding:10px 13px;font-family:var(--sans);font-size:14px;color:var(--ink);background:#FAFAFA;outline:none;transition:border-color 0.15s,box-shadow 0.15s;}
|
|
||||||
input:focus{border-color:#6366F1;box-shadow:0 0 0 3px rgba(99,102,241,0.12);}
|
|
||||||
input.error{border-color:#F87171;}
|
|
||||||
|
|
||||||
/* Primary button */
|
|
||||||
.btn{width:100%;background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:10px;padding:13px;font-family:var(--sans);font-size:14px;font-weight:600;cursor:pointer;margin-top:4px;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:box-shadow 0.2s ease,transform 0.2s ease;}
|
|
||||||
.btn:hover{box-shadow:0 10px 25px rgba(30,27,75,0.15),0 0 0 6px rgba(99,102,241,0.15);transform:translateY(-1px);}
|
|
||||||
.btn:disabled{opacity:0.4;cursor:default;transform:none;box-shadow:0 10px 25px rgba(30,27,75,0.15);}
|
|
||||||
|
|
||||||
/* Mode option cards */
|
|
||||||
.mode-opt{border:1px solid var(--border);background:transparent;border-radius:10px;padding:16px;cursor:pointer;margin-bottom:10px;display:flex;align-items:center;gap:12px;transition:all 0.15s;}
|
|
||||||
.mode-opt:hover{border-color:#6366F1;background:var(--hover);}
|
|
||||||
.mode-opt.selected{border-color:#6366F1;background:var(--hover);box-shadow:0 0 0 3px rgba(99,102,241,0.1);}
|
|
||||||
|
|
||||||
/* Password strength */
|
|
||||||
.strength-bar{display:flex;gap:4px;margin-top:8px;}
|
|
||||||
.strength-seg{flex:1;height:3px;border-radius:2px;background:var(--border);transition:background 0.2s ease;}
|
|
||||||
.strength-label{font-size:11px;color:var(--muted);margin-top:5px;min-height:16px;}
|
|
||||||
|
|
||||||
/* Password toggle */
|
|
||||||
.pwd-wrap{position:relative;}
|
|
||||||
.pwd-wrap input{padding-right:40px;}
|
|
||||||
.pwd-toggle{position:absolute;right:11px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;color:var(--muted);padding:4px;display:flex;align-items:center;transition:color 0.15s;}
|
|
||||||
.pwd-toggle:hover{color:var(--ink);}
|
|
||||||
|
|
||||||
/* Google button */
|
|
||||||
.btn-google{width:100%;background:transparent;border:1px solid var(--border);color:var(--ink);border-radius:10px;padding:11px;font-family:var(--sans);font-size:13.5px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:9px;transition:border-color 0.15s,background 0.15s;}
|
|
||||||
.btn-google:hover{border-color:#6366F1;background:var(--hover);}
|
|
||||||
|
|
||||||
/* Billing notice — animated */
|
|
||||||
.billing-notice{overflow:hidden;max-height:0;opacity:0;transition:max-height 0.3s ease,opacity 0.25s ease,margin-bottom 0.3s ease;margin-bottom:0;}
|
|
||||||
.billing-notice.visible{max-height:140px;opacity:1;margin-bottom:20px;}
|
|
||||||
|
|
||||||
/* Experience hint */
|
|
||||||
.mode-hint{text-align:center;font-size:12px;color:var(--muted);margin-top:10px;min-height:18px;transition:opacity 0.2s ease;}
|
|
||||||
.exp-feedback{overflow:hidden;max-height:0;opacity:0;transition:max-height 0.3s ease,opacity 0.25s ease,margin-bottom 0.3s ease;margin-bottom:0;border-radius:10px;padding:0 16px;}
|
|
||||||
.exp-feedback.visible{max-height:80px;opacity:1;margin-bottom:4px;padding:13px 16px;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav style="background:rgba(250,250,250,0.95);border-bottom:1px solid var(--border);padding:0 40px;height:62px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50;">
|
|
||||||
<div style="display:flex;align-items:center;gap:9px;">
|
|
||||||
<div style="width:28px;height:28px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:7px;display:flex;align-items:center;justify-content:center;"><span class="f" style="font-size:14px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<span style="font-size:13.5px;color:var(--muted);">Already have an account? <a href="03_dashboard.html" style="color:#6366F1;font-weight:600;text-decoration:none;">Log in</a></span>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div style="flex:1;display:flex;align-items:center;justify-content:center;padding:40px 24px;">
|
|
||||||
<div style="width:100%;max-width:440px;">
|
|
||||||
|
|
||||||
<!-- Step indicator -->
|
|
||||||
<div id="steps" style="display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:32px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;" id="s1">
|
|
||||||
<div id="s1c" style="width:24px;height:24px;border-radius:50%;background:#6366F1;display:flex;align-items:center;justify-content:center;font-size:11px;color:#FFFFFF;font-weight:700;">1</div>
|
|
||||||
<span style="font-size:12.5px;font-weight:600;color:var(--ink);">Account</span>
|
|
||||||
</div>
|
|
||||||
<div style="width:28px;height:1px;background:var(--border);"></div>
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;opacity:0.35;" id="s2">
|
|
||||||
<div id="s2c" style="width:24px;height:24px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--muted);font-weight:700;">2</div>
|
|
||||||
<span id="s2l" style="font-size:12.5px;color:var(--muted);">Your experience</span>
|
|
||||||
</div>
|
|
||||||
<div style="width:28px;height:1px;background:var(--border);"></div>
|
|
||||||
<div style="display:flex;align-items:center;gap:6px;opacity:0.35;" id="s3">
|
|
||||||
<div id="s3c" style="width:24px;height:24px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--muted);font-weight:700;">3</div>
|
|
||||||
<span id="s3l" style="font-size:12.5px;color:var(--muted);">Ready</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 1 -->
|
|
||||||
<div id="step1" style="background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<h2 class="f" style="font-size:23px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;margin-bottom:6px;">Let's build your first product.</h2>
|
|
||||||
<p style="font-size:14px;color:var(--muted);margin-bottom:22px;">Free to start · No credit card needed</p>
|
|
||||||
|
|
||||||
<!-- Google first -->
|
|
||||||
<button onclick="openGoogleAuth()" class="btn-google" style="margin-bottom:20px;">
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l3.66-2.84z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
|
||||||
Continue with Google
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div style="margin-bottom:20px;display:flex;align-items:center;gap:12px;">
|
|
||||||
<div style="flex:1;height:1px;background:var(--border);"></div>
|
|
||||||
<span style="font-size:12px;color:var(--muted);">or continue with email</span>
|
|
||||||
<div style="flex:1;height:1px;background:var(--border);"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email form -->
|
|
||||||
<div style="display:flex;flex-direction:column;gap:15px;">
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Full name</label>
|
|
||||||
<input type="text" id="inp-name" placeholder="Jane Smith" oninput="validateStep1()"/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Email</label>
|
|
||||||
<input type="email" id="inp-email" placeholder="jane@studio.com" oninput="validateStep1()"/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label style="display:block;font-size:11px;font-weight:600;color:var(--mid);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;">Password</label>
|
|
||||||
<div class="pwd-wrap">
|
|
||||||
<input type="password" id="pwd" placeholder="8+ characters" oninput="checkStrength(this.value);validateStep1()"/>
|
|
||||||
<button type="button" class="pwd-toggle" onclick="togglePwd()" id="pwd-toggle-btn" aria-label="Show password">
|
|
||||||
<svg id="eye-open" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
|
||||||
<svg id="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none;"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94"/><path d="M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/><line x1="1" y1="1" x2="23" y2="23"/></svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="strength-bar"><div class="strength-seg" id="seg1"></div><div class="strength-seg" id="seg2"></div><div class="strength-seg" id="seg3"></div></div>
|
|
||||||
<div class="strength-label" id="strength-label"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Social proof above button -->
|
|
||||||
<p style="text-align:center;font-size:12px;color:var(--muted);margin-bottom:-4px;">Joining 280+ founders already building</p>
|
|
||||||
|
|
||||||
<button id="step1btn" class="btn" onclick="goStep(2)" disabled>Continue →</button>
|
|
||||||
<p style="text-align:center;font-size:11.5px;color:var(--muted);margin-top:2px;">By continuing you agree to our <a href="#" style="color:var(--muted);text-decoration:underline;">Terms</a> and <a href="#" style="color:var(--muted);text-decoration:underline;">Privacy Policy</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 2 -->
|
|
||||||
<div id="step2" style="display:none;background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<h2 class="f" style="font-size:23px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;margin-bottom:6px;">How experienced are you?</h2>
|
|
||||||
<p style="font-size:14px;color:var(--muted);margin-bottom:24px;">Just so we know who we're building with</p>
|
|
||||||
<div id="modes">
|
|
||||||
<div class="mode-opt" onclick="selectExperience('beginner',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◎</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">First time</div><div style="font-size:12.5px;color:var(--muted);">I've never shipped a product before</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="mode-opt" onclick="selectExperience('some',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◇</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">Some experience</div><div style="font-size:12.5px;color:var(--muted);">I've built things before</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="mode-opt" onclick="selectExperience('experienced',this)">
|
|
||||||
<div style="width:36px;height:36px;border-radius:9px;background:var(--soft);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:#6366F1;">◈</div>
|
|
||||||
<div><div class="f" style="font-size:14px;font-weight:600;color:var(--ink);margin-bottom:3px;">Experienced</div><div style="font-size:12.5px;color:var(--muted);">I ship products regularly</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Contextual feedback — animated -->
|
|
||||||
<div id="exp-feedback" class="exp-feedback" style="background:var(--soft);border:1px solid rgba(99,102,241,0.2);">
|
|
||||||
<div id="exp-feedback-text" style="font-size:13px;color:#4338CA;line-height:1.6;"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button id="step2btn" class="btn" style="margin-top:20px;" onclick="goStep(3)" disabled>Set up my workspace →</button>
|
|
||||||
<p class="mode-hint" id="mode-hint">Select an option above to continue</p>
|
|
||||||
<p style="text-align:center;margin-top:6px;"><a onclick="goStep(1)" style="font-size:13px;color:var(--muted);text-decoration:none;cursor:pointer;">← Back</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- STEP 3 -->
|
|
||||||
<div id="step3" style="display:none;background:var(--white);border:1px solid var(--border);border-radius:16px;padding:32px;box-shadow:0 10px 30px rgba(30,27,75,0.05);">
|
|
||||||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:6px;">
|
|
||||||
<div style="width:36px;height:36px;background:var(--soft);border:1px solid rgba(99,102,241,0.25);border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0;color:#6366F1;">✓</div>
|
|
||||||
<h2 class="f" style="font-size:22px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">You're in. Got an idea?</h2>
|
|
||||||
</div>
|
|
||||||
<p id="done-msg" style="font-size:14px;color:var(--muted);line-height:1.7;margin-bottom:20px;padding-left:48px;">Describe it in one sentence and we'll carry it straight into your workspace.</p>
|
|
||||||
|
|
||||||
<!-- Seed input -->
|
|
||||||
<textarea id="seed-idea" placeholder="e.g. A booking tool for independent personal trainers." style="width:100%;border:1px solid var(--border);border-radius:8px;padding:11px 13px;font-family:var(--sans);font-size:14px;color:var(--ink);background:#FAFAFA;outline:none;resize:none;height:88px;line-height:1.6;transition:border-color 0.15s,box-shadow 0.15s;margin-bottom:16px;" onfocus="this.style.borderColor='#6366F1';this.style.boxShadow='0 0 0 3px rgba(99,102,241,0.12)';" onblur="this.style.borderColor='#E5E7EB';this.style.boxShadow='none';"></textarea>
|
|
||||||
|
|
||||||
<button id="dash-btn" class="btn" style="margin-top:0;" onclick="openDashboard()">Start building →</button>
|
|
||||||
<p style="text-align:center;margin-top:14px;">
|
|
||||||
<a onclick="goToDashboard()" style="font-size:13px;color:var(--muted);text-decoration:none;cursor:pointer;">I'll do this later — take me to the dashboard</a>
|
|
||||||
</p>
|
|
||||||
<p style="text-align:center;margin-top:10px;"><a onclick="goStep(2)" style="font-size:12px;color:var(--muted);text-decoration:none;cursor:pointer;opacity:0.6;">← Back</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/* Google auth popup */
|
|
||||||
function openGoogleAuth(){
|
|
||||||
var w=500,h=600;
|
|
||||||
var left=(screen.width/2)-(w/2);
|
|
||||||
var top=(screen.height/2)-(h/2);
|
|
||||||
window.open('google-auth-popup.html','google-auth','width='+w+',height='+h+',left='+left+',top='+top+',toolbar=no,menubar=no,scrollbars=no');
|
|
||||||
window.addEventListener('message',function(e){
|
|
||||||
if(e.data&&e.data.type==='google-auth-success'){
|
|
||||||
goStep(2);
|
|
||||||
}
|
|
||||||
},{once:true});
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode=null;
|
|
||||||
var EXP_FEEDBACK={
|
|
||||||
beginner:"We've got you — we'll explain every step clearly, no jargon, no assumptions. You'll have a product live before you know it.",
|
|
||||||
some:"Great! You know the ropes — let's move fast and make something great.",
|
|
||||||
experienced:"You know the process. We'll keep things efficient and get straight to the point."
|
|
||||||
};
|
|
||||||
var EXP_DONE={
|
|
||||||
beginner:"Everything is in place. We'll guide you every step of the way.",
|
|
||||||
some:"Everything is in place. Let's get straight to building.",
|
|
||||||
experienced:"Everything is in place. Let's move fast."
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Step navigation */
|
|
||||||
function goStep(n){
|
|
||||||
[1,2,3].forEach(function(i){
|
|
||||||
document.getElementById('step'+i).style.display=i===n?'block':'none';
|
|
||||||
var s=document.getElementById('s'+i);
|
|
||||||
if(s) s.style.opacity=i<=n?'1':'0.35';
|
|
||||||
var c=document.getElementById('s'+i+'c');
|
|
||||||
if(c){
|
|
||||||
c.style.background=i<n?'#4338CA':i===n?'#6366F1':'#E5E7EB';
|
|
||||||
c.style.color=i<=n?'#FFFFFF':'#9CA3AF';
|
|
||||||
c.textContent=i<n?'✓':String(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 1 — form validation */
|
|
||||||
function validateStep1(){
|
|
||||||
var name=document.getElementById('inp-name').value.trim();
|
|
||||||
var email=document.getElementById('inp-email').value.trim();
|
|
||||||
var pwd=document.getElementById('pwd').value;
|
|
||||||
var emailOk=/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
||||||
var pwdOk=pwd.length>=8;
|
|
||||||
document.getElementById('step1btn').disabled=!(name.length>0 && emailOk && pwdOk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Password show / hide */
|
|
||||||
function togglePwd(){
|
|
||||||
var input=document.getElementById('pwd');
|
|
||||||
var eyeOpen=document.getElementById('eye-open');
|
|
||||||
var eyeClosed=document.getElementById('eye-closed');
|
|
||||||
var isHidden=input.type==='password';
|
|
||||||
input.type=isHidden?'text':'password';
|
|
||||||
eyeOpen.style.display=isHidden?'none':'block';
|
|
||||||
eyeClosed.style.display=isHidden?'block':'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Password strength */
|
|
||||||
function checkStrength(v){
|
|
||||||
var segs=['seg1','seg2','seg3'];
|
|
||||||
var labelEl=document.getElementById('strength-label');
|
|
||||||
|
|
||||||
if(v.length===0){
|
|
||||||
segs.forEach(function(id){document.getElementById(id).style.background='#E5E7EB';});
|
|
||||||
labelEl.textContent='';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(v.length<8){
|
|
||||||
segs.forEach(function(id){document.getElementById(id).style.background='#E5E7EB';});
|
|
||||||
document.getElementById('seg1').style.background='#F87171';
|
|
||||||
labelEl.textContent='Not enough characters — 8 minimum';
|
|
||||||
labelEl.style.color='#F87171';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var score=1;
|
|
||||||
if(/[A-Z]/.test(v)&&/[0-9]/.test(v)) score++;
|
|
||||||
if(/[^A-Za-z0-9]/.test(v)||v.length>=12) score++;
|
|
||||||
|
|
||||||
var colors=['#E5E7EB','#E5E7EB','#E5E7EB'];
|
|
||||||
var label='';
|
|
||||||
if(score===1){colors[0]='#F87171';label='Weak';}
|
|
||||||
else if(score===2){colors[0]='#FBBF24';colors[1]='#FBBF24';label='Fair';}
|
|
||||||
else{colors[0]='#4338CA';colors[1]='#6366F1';colors[2]='#818CF8';label='Strong';}
|
|
||||||
|
|
||||||
segs.forEach(function(id,i){document.getElementById(id).style.background=colors[i];});
|
|
||||||
labelEl.textContent=label;
|
|
||||||
labelEl.style.color=score===1?'#F87171':score===2?'#FBBF24':'#6366F1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 2 — experience selection */
|
|
||||||
function selectExperience(level,el){
|
|
||||||
mode=level;
|
|
||||||
document.querySelectorAll('.mode-opt').forEach(function(d){d.classList.remove('selected');});
|
|
||||||
el.classList.add('selected');
|
|
||||||
|
|
||||||
// Show contextual feedback
|
|
||||||
var feedback=document.getElementById('exp-feedback');
|
|
||||||
var feedbackText=document.getElementById('exp-feedback-text');
|
|
||||||
feedbackText.textContent=EXP_FEEDBACK[level];
|
|
||||||
feedback.classList.add('visible');
|
|
||||||
|
|
||||||
// Enable button + hide hint
|
|
||||||
document.getElementById('step2btn').disabled=false;
|
|
||||||
document.getElementById('mode-hint').style.opacity='0';
|
|
||||||
|
|
||||||
document.getElementById('done-msg').textContent=EXP_DONE[level];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 3 — start building (carries seed idea to Describe) */
|
|
||||||
function openDashboard(){
|
|
||||||
var btn=document.getElementById('dash-btn');
|
|
||||||
btn.textContent='Setting up…';
|
|
||||||
btn.disabled=true;
|
|
||||||
var idea=(document.getElementById('seed-idea').value||'').trim();
|
|
||||||
setTimeout(function(){
|
|
||||||
try {
|
|
||||||
sessionStorage.setItem('vibn_new_project','1');
|
|
||||||
if(idea) sessionStorage.setItem('vibn_seed_idea', idea);
|
|
||||||
} catch(e){}
|
|
||||||
window.location.href='05_describe.html';
|
|
||||||
},800);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 3 — skip to dashboard */
|
|
||||||
function goToDashboard(){
|
|
||||||
window.location.href='03_dashboard.html';
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,638 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<script>if(localStorage.getItem('vibn-theme')==='dark')document.documentElement.dataset.theme='dark';</script>
|
|
||||||
<title>vibn — Architect</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{
|
|
||||||
--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#444441;--mid:#6B7280;--muted:#9CA3AF;
|
|
||||||
--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#F5F3FF;--white:#FFFFFF;--border:#E5E7EB;
|
|
||||||
--indigo:#6366F1;--indigo-dark:#4338CA;--indigo-deep:#2E2A5E;
|
|
||||||
--indigo-soft:rgba(99,102,241,0.08);--indigo-ring:rgba(99,102,241,0.12);
|
|
||||||
}
|
|
||||||
body{font-family:'Plus Jakarta Sans',sans-serif;background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);display:flex;flex-direction:column;height:100vh;overflow:hidden;}
|
|
||||||
.f{font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
|
|
||||||
/* ── Dark mode tokens — exact match with design.html ── */
|
|
||||||
[data-theme="dark"]{--ink:#EEEEFF;--ink2:#B8B8D0;--ink3:#8484A8;--mid:#9898B8;--muted:#c2c2ee;--border:rgba(255,255,255,0.08);
|
|
||||||
--cream:rgba(108,124,255,0.14);--paper:#0A1120;--white:rgba(255,255,255,0.05);--stone:rgba(255,255,255,0.08);
|
|
||||||
--parch:rgba(255,255,255,0.06);--section:rgba(108,124,255,0.55);--accent-primary:#6C7CFF;--indigo:#6C7CFF;--indigo-dark:#6C7CFF;
|
|
||||||
--indigo-deep:#4B42D8;--indigo-soft:rgba(108,124,255,0.14);--indigo-ring:rgba(108,124,255,0.22);--dm-surf-sidebar:rgba(12,18,34,0.72);
|
|
||||||
--dm-surf-topbar:rgba(12,18,34,0.58);--dm-surf-panel:rgba(12,18,34,0.58);--dm-surf-right:rgba(12,18,34,0.58);
|
|
||||||
--dm-surf-card:rgba(255,255,255,0.05);--dm-surf-refine:rgba(8,12,22,0.60);--dm-border:rgba(255,255,255,0.08);
|
|
||||||
--dm-border-strong:rgba(255,255,255,0.14);--dm-border-hero:rgba(255,255,255,0.18);--dm-accent:#6C7CFF;
|
|
||||||
--dm-accent-fill:rgba(108,124,255,0.14);--dm-accent-fill-mid:rgba(108,124,255,0.20);--dm-accent-border:rgba(108,124,255,0.55);
|
|
||||||
--dm-text-1:#EEEEFF;--dm-text-2:#B4B4CC;--dm-text-3:#d2d2ef;--dm-shadow-panel:0 4px 36px rgba(0,0,0,0.55);
|
|
||||||
--dm-shadow-hero:0 20px 70px rgba(0,0,0,0.65),0 6px 24px rgba(0,0,0,0.40),inset 0 1px 0 rgba(255,255,255,0.10);}
|
|
||||||
[data-theme="dark"] body{background:linear-gradient(to bottom,rgba(108,80,255,0.06) 0%,rgba(60,120,255,0.10) 38%,transparent 62%),linear-gradient(to bottom,rgba(108,124,255,0.10),transparent 180px),radial-gradient(900px 520px at 14% -8%,rgba(108,124,255,0.24),transparent 62%),radial-gradient(760px 420px at 88% 0%,rgba(72,145,255,0.16),transparent 60%),linear-gradient(180deg,#18213B 0%,#101726 48%,#0A1120 100%);}[data-theme="dark"] #mock {background: #fff !important;}
|
|
||||||
/* Sidebar */
|
|
||||||
[data-theme="dark"] .sidebar-col{background:var(--dm-surf-sidebar)!important;border-right:1px solid var(--dm-border)!important;box-shadow:2px 0 28px rgba(0,0,0,0.60)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="border-top:1px solid #e5e7eb"]{border-top-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#e5e7eb"]{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#1a1a1a"]{color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#6b7280"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#444441"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#9ca3af"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#6366F1"]{background:var(--dm-accent)!important;color:#0F1424!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase.active{background:var(--dm-accent-fill)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase:not(.active):hover{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] #sidebar-project-name{color:var(--dm-text-3)!important;}
|
|
||||||
/*Arch-scroll*/
|
|
||||||
[data-theme="dark"] .arch-scroll{background:var(--dm-surf-panel)!important;border-right:1px solid var(--dm-border)!important;box-shadow:4px 0 0px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
|
|
||||||
/* Main content */
|
|
||||||
[data-theme="dark"] .arch-main{background:transparent!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#f5f3ff"]{background:rgba(108,124,255,0.04)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:var(--white)"]{background:var(--dm-surf-card)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#fafaff"],[data-theme="dark"] [style*="background:#FAFAFA"]{background:rgba(255,255,255,0.03)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#f0f4ff"]{background:var(--dm-surf-card)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#F3F4F6"]{background:rgba(255,255,255,0.05)!important;}
|
|
||||||
/* Borders */
|
|
||||||
[data-theme="dark"] [style*="border-bottom:1px solid #c7d2fe"]{border-bottom-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] [style*="border:1px solid #e0e7ff"]{border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] [style*="border:1px solid rgba(99,102,241"]{border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] [style*="color:#4338ca"]{color:var(--dm-accent)!important;}
|
|
||||||
/* Blueprint rows */
|
|
||||||
[data-theme="dark"] .blueprint-row:hover{background:rgba(108,124,255,0.07)!important;}
|
|
||||||
[data-theme="dark"] .blueprint-row.locked{background:rgba(255,255,255,0.02)!important;}
|
|
||||||
[data-theme="dark"] .blueprint-row.locked:hover{background:rgba(255,255,255,0.04)!important;}
|
|
||||||
/* Option buttons */
|
|
||||||
[data-theme="dark"] .opt-btns{border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn{color:var(--dm-text-2)!important;border-right-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn:hover{background:rgba(108,124,255,0.09)!important;color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn.selected{background:var(--dm-accent-fill)!important;color:var(--dm-accent)!important;font-weight:600!important;box-shadow:inset 0 0 0 1px rgba(108,124,255,0.32),0 2px 16px rgba(108,124,255,0.22)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn.why-btn{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .why-btn{color:var(--dm-text-3)!important;border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .why-btn:hover{color:var(--dm-accent)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
/* Right panel / deliverables */
|
|
||||||
[data-theme="dark"] .arch-right{background:var(--dm-surf-right)!important;border-left:1px solid var(--dm-border)!important;box-shadow:-4px 0 36px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
|
|
||||||
[data-theme="dark"] .deliverable-row{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .deliverable-row:hover{background:rgba(255,255,255,0.04)!important;}
|
|
||||||
/* Popups & modals */
|
|
||||||
[data-theme="dark"] #why-popup>div{background:#111828!important;box-shadow:0 0 0 1px rgba(108,124,255,0.50),0 0 0 4px rgba(108,124,255,0.14),0 0 60px rgba(108,124,255,0.28),0 24px 64px rgba(0,0,0,0.72)!important;}
|
|
||||||
[data-theme="dark"] #save-exit-box{background:#111828!important;box-shadow:0 0 0 1px rgba(108,124,255,0.50),0 0 0 4px rgba(108,124,255,0.14),0 0 60px rgba(108,124,255,0.28),0 24px 64px rgba(0,0,0,0.72)!important;}
|
|
||||||
[data-theme="dark"] .modal-card{background:#111828!important;box-shadow:0 0 0 1px rgba(108,124,255,0.50),0 0 0 4px rgba(108,124,255,0.14),0 0 60px rgba(108,124,255,0.28),0 24px 64px rgba(0,0,0,0.72)!important;}
|
|
||||||
[data-theme="dark"] .arch-blueprint-card{box-shadow:0 0 0 1px rgba(108,124,255,0.22),0 2px 24px rgba(108,124,255,0.14),0 0 48px rgba(108,124,255,0.08)!important;}
|
|
||||||
/* Buttons */
|
|
||||||
[data-theme="dark"] #dark-toggle{background:rgba(255,255,255,0.05)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle:hover{background:rgba(255,255,255,0.10)!important;color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover{background:var(--dm-accent-fill-mid)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover span{color:#fff!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="openWhy()"]{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .btn-primary{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;color:#fff!important;box-shadow:0 4px 22px rgba(108,124,255,0.38),inset 0 1px 0 rgba(255,255,255,0.14)!important;}
|
|
||||||
[data-theme="dark"] .btn-primary:hover{box-shadow:0 6px 32px rgba(108,124,255,0.50),inset 0 1px 0 rgba(255,255,255,0.18)!important;}
|
|
||||||
[data-theme="dark"] .vibn-avatar{background:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .arch-intro-bubble{box-shadow:0 0 0 1px rgba(108,124,255,0.12),0 2px 18px rgba(108,124,255,0.10),0 0 36px rgba(108,124,255,0.06)!important;}
|
|
||||||
[data-theme="dark"] .arch-deliverable-card{box-shadow:0 0 0 1px rgba(108,124,255,0.10),0 2px 14px rgba(108,124,255,0.08)!important;}
|
|
||||||
/* Topbar */
|
|
||||||
[data-theme="dark"] .arch-topbar{
|
|
||||||
background: var(--dm-surf-topbar) !important;
|
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.08) !important;
|
|
||||||
|
|
||||||
box-shadow: none !important;
|
|
||||||
-webkit-backdrop-filter: none !important;
|
|
||||||
backdrop-filter: none !important;
|
|
||||||
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .arch-topbar::after{
|
|
||||||
content: none !important;
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .arch-topbar .f{
|
|
||||||
color: var(--dm-text-1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .arch-topbar [style*="color:#9ca3af"]{
|
|
||||||
color: var(--dm-text-3) !important;
|
|
||||||
}
|
|
||||||
/* Scrollbar */
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar{width:5px;height:5px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-track{background:transparent;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.12);border-radius:3px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.22);}
|
|
||||||
[data-theme="dark"] *{scrollbar-color:rgba(255,255,255,0.12) transparent;scrollbar-width:thin;}
|
|
||||||
|
|
||||||
/* Sidebar */
|
|
||||||
.sidebar-phase{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;}
|
|
||||||
.sidebar-phase.active{background:#fafaff;}
|
|
||||||
.phase-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;}
|
|
||||||
|
|
||||||
/* Blueprint row */
|
|
||||||
.blueprint-row{display:grid;grid-template-columns:36px 1fr 248px;align-items:center;gap:16px;padding:15px 22px;border-bottom:1px solid var(--border);transition:background 0.15s;}
|
|
||||||
.blueprint-row:last-child{border-bottom:none;border-radius:0 0 14px 14px;}
|
|
||||||
.blueprint-row:hover{background:#FAFAFF;}
|
|
||||||
.blueprint-row.locked{background:#FAFAFA;}
|
|
||||||
.blueprint-row.locked:hover{background:#F5F5F5;}
|
|
||||||
|
|
||||||
/* Option toggle buttons */
|
|
||||||
.opt-btns{display:flex;gap:0;flex-shrink:0;border:1.5px solid var(--border);border-radius:8px;overflow:visible;}
|
|
||||||
.opt-btn{flex:1;text-align:center;font-size:12px;font-weight:500;color:var(--mid);background:transparent;border:none;border-right:1.5px solid var(--border);border-radius:0;padding:6px 13px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;white-space:nowrap;position:relative;}
|
|
||||||
.opt-btn:first-child{border-radius:7px 0 0 7px;}
|
|
||||||
.opt-btn:last-child{border-right:none;border-radius:0 7px 7px 0;}
|
|
||||||
.opt-btn:hover{background:#F5F5FF;color:var(--indigo);}
|
|
||||||
.opt-btn.selected{background:var(--indigo-soft);color:var(--indigo-dark);font-weight:600;box-shadow:0 2px 10px rgba(99,102,241,0.12);}
|
|
||||||
|
|
||||||
/* Intro AI bubble */
|
|
||||||
.arch-intro-bubble{box-shadow:0 2px 12px rgba(99,102,241,0.08);}
|
|
||||||
|
|
||||||
/* Deliverable cards (Pages + Infrastructure) */
|
|
||||||
.arch-deliverable-card{box-shadow:0 2px 12px rgba(99,102,241,0.08);}
|
|
||||||
|
|
||||||
/* Why button (hosting) */
|
|
||||||
.why-btn{font-size:11.5px;font-weight:600;color:var(--mid);background:transparent;border:1px solid var(--border);border-radius:6px;padding:5px 11px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:border-color 0.15s,color 0.15s;white-space:nowrap;}
|
|
||||||
.why-btn:hover{border-color:var(--indigo);color:var(--indigo);}
|
|
||||||
|
|
||||||
/* Primary button */
|
|
||||||
.btn-primary{background:linear-gradient(135deg,var(--indigo-deep),var(--indigo-dark));color:#FFFFFF;border:none;border-radius:8px;padding:10px 22px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 4px 18px rgba(99,102,241,0.22),0 1px 4px rgba(99,102,241,0.12);transition:box-shadow 0.2s,transform 0.2s;}
|
|
||||||
.btn-primary:hover{box-shadow:0 6px 24px rgba(99,102,241,0.30),0 1px 4px rgba(99,102,241,0.14),0 0 0 6px var(--indigo-ring);transform:translateY(-1px);}
|
|
||||||
|
|
||||||
/* What you're getting panel */
|
|
||||||
.deliverable-row{display:flex;align-items:center;gap:8px;padding:7px 10px;border-radius:7px;font-size:12px;color:var(--mid);transition:background 0.12s;}
|
|
||||||
.deliverable-row:hover{background:var(--cream);}
|
|
||||||
|
|
||||||
/* Modal */
|
|
||||||
.modal-bg{display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:100;align-items:center;justify-content:center;}
|
|
||||||
.modal-bg.open{display:flex;}
|
|
||||||
.modal-card{background:var(--white);border-radius:16px;width:400px;overflow:hidden;box-shadow:0 24px 64px rgba(30,27,75,0.18);}
|
|
||||||
|
|
||||||
/* Option buttons in modal */
|
|
||||||
.option-btn{display:flex;align-items:center;gap:12px;padding:12px 16px;border-radius:10px;border:1px solid var(--border);background:var(--white);cursor:pointer;text-align:left;margin-bottom:8px;transition:all 0.15s;}
|
|
||||||
.option-btn:hover{border-color:var(--indigo);background:var(--cream);}
|
|
||||||
.option-btn.selected{border-color:var(--indigo);background:var(--cream);box-shadow:0 0 0 3px var(--indigo-ring);}
|
|
||||||
|
|
||||||
/* Save popup */
|
|
||||||
#save-exit-popup{display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:700;align-items:center;justify-content:center;padding:24px;}
|
|
||||||
#save-exit-popup.visible{display:flex;}
|
|
||||||
#save-exit-box{background:#FFFFFF;border-radius:16px;box-shadow:0 24px 64px rgba(30,27,75,0.18);padding:32px;width:100%;max-width:380px;text-align:center;}
|
|
||||||
#save-exit-box .save-icon{width:48px;height:48px;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:20px;margin:0 auto 16px;}
|
|
||||||
#save-exit-box h3{font-size:18px;font-weight:700;color:var(--ink);margin-bottom:8px;}
|
|
||||||
#save-exit-box p{font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:20px;}
|
|
||||||
#save-exit-box .save-cancel{font-size:12px;color:var(--muted);cursor:pointer;text-decoration:underline;background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
#save-exit-box .save-cancel:hover{color:var(--ink);}
|
|
||||||
|
|
||||||
/* Why accordion */
|
|
||||||
|
|
||||||
/* ── Mobile tab bar (hidden on desktop) ── */
|
|
||||||
.mob-tabs{display:none;flex-shrink:0;background:#EEF0FF;border-top:1px solid #D4D8FA;padding-bottom:env(safe-area-inset-bottom);}
|
|
||||||
[data-theme="dark"] .mob-tabs{background:rgba(8,12,24,0.90)!important;border-top-color:var(--dm-border)!important;}
|
|
||||||
.mob-tab-btn{flex:1;padding:11px 8px;border:none;background:transparent;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;border-top:2px solid transparent;transition:color 0.15s,border-color 0.15s;}
|
|
||||||
.mob-tab-btn.active{color:#6366F1;border-top-color:#6366F1;font-weight:600;}
|
|
||||||
.mob-dash-btn{background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;font-size:11.5px;font-weight:500;color:var(--muted);cursor:pointer;padding:11px 12px;white-space:nowrap;flex:none;transition:color 0.15s;}
|
|
||||||
.mob-dash-btn:hover{color:var(--ink);}
|
|
||||||
[data-theme="dark"] .mob-tab-btn{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{color:var(--dm-accent)!important;border-top-color:var(--dm-accent)!important;}
|
|
||||||
|
|
||||||
/* ── Responsive ── */
|
|
||||||
|
|
||||||
/* Tablet (641px – 1024px): top tab bar, full-height panels */
|
|
||||||
@media (min-width:641px) and (max-width:1024px){
|
|
||||||
body{height:100dvh;overflow:hidden;}
|
|
||||||
.arch-layout{flex-direction:column!important;height:100dvh!important;overflow:hidden!important;}
|
|
||||||
.sidebar-col{display:none!important;}
|
|
||||||
.mob-tabs{display:flex!important;order:0;border-top:none!important;border-bottom:1px solid #D4D8FA!important;padding-bottom:0!important;}
|
|
||||||
.mob-tab-btn{border-top:none;border-bottom:2px solid transparent;}
|
|
||||||
.mob-tab-btn.active{border-top-color:transparent;border-bottom-color:#6366F1;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{border-bottom-color:var(--dm-accent)!important;border-top-color:transparent!important;}
|
|
||||||
.arch-main{display:none!important;order:1;overflow:hidden!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-main.tab-active{display:flex!important;}
|
|
||||||
.arch-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right{display:none!important;order:1;width:100%!important;border-left:none!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-right.tab-active{display:flex!important;}
|
|
||||||
.arch-right-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right-footer{padding:12px 16px 20px!important;}
|
|
||||||
.arch-right-footer a{width:80%;display:block;}
|
|
||||||
.blueprint-row{grid-template-columns:36px 1fr!important;row-gap:10px;padding:13px 18px!important;}
|
|
||||||
.blueprint-row .opt-btns{grid-column:1 / -1;width:100%;}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile (≤ 640px): tabbed layout, tabs at bottom */
|
|
||||||
@media (max-width:640px){
|
|
||||||
body{height:100dvh;overflow:hidden;}
|
|
||||||
.arch-layout{flex-direction:column!important;height:100dvh!important;overflow:hidden!important;}
|
|
||||||
.sidebar-col{display:none!important;}
|
|
||||||
.mob-tabs{display:flex!important;order:2;}
|
|
||||||
.arch-main{display:none!important;order:1;overflow:hidden!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-main.tab-active{display:flex!important;}
|
|
||||||
.arch-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.arch-right{display:none!important;order:1;width:100%!important;border-left:none!important;flex:1 1 0%!important;height:0!important;min-height:0!important;}
|
|
||||||
.arch-right.tab-active{display:flex!important;}
|
|
||||||
.arch-right-scroll{flex:1 1 0%!important;height:0!important;min-height:0!important;overflow-y:auto!important;-webkit-overflow-scrolling:touch;}
|
|
||||||
.blueprint-row{grid-template-columns:36px 1fr!important;row-gap:10px;padding:13px 16px!important;}
|
|
||||||
.blueprint-row .opt-btns{grid-column:1 / -1;width:100%;}
|
|
||||||
.opt-btn{flex:1;padding:7px 8px!important;font-size:11.5px!important;}
|
|
||||||
.arch-topbar{padding:14px 16px 12px!important;}
|
|
||||||
.arch-right-footer{padding:12px 16px 20px!important;}
|
|
||||||
.arch-right-footer a{width:100%!important;display:block!important;}
|
|
||||||
.modal-card{width:calc(100vw - 32px)!important;}
|
|
||||||
#why-popup>div{max-width:calc(100vw - 32px)!important;}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile with trackpad (laptop narrow): tabs at top */
|
|
||||||
@media (max-width:640px) and (hover:hover) and (pointer:fine){
|
|
||||||
.mob-tabs{order:0!important;border-top:none!important;border-bottom:1px solid #D4D8FA!important;padding-bottom:0!important;}
|
|
||||||
.mob-tab-btn{border-top:none;border-bottom:2px solid transparent;}
|
|
||||||
.mob-tab-btn.active{border-top-color:transparent;border-bottom-color:#6366F1;}
|
|
||||||
[data-theme="dark"] .mob-tab-btn.active{border-bottom-color:var(--dm-accent)!important;}
|
|
||||||
.arch-main{order:1!important;}
|
|
||||||
.arch-right{order:1!important;}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="arch-layout" style="display:flex;height:100%;overflow:hidden;">
|
|
||||||
|
|
||||||
<!-- Mobile tab bar (hidden on desktop, shown on ≤ 640px) -->
|
|
||||||
<div class="mob-tabs" id="mob-tabs">
|
|
||||||
<button onclick="saveAndExit()" class="mob-dash-btn">Dashboard</button>
|
|
||||||
<div style="width:1px;background:var(--border);margin:8px 0;flex-shrink:0;"></div>
|
|
||||||
<button class="mob-tab-btn active" id="tab-blueprint" onclick="switchArchTab('blueprint')">Blueprint</button>
|
|
||||||
<button class="mob-tab-btn" id="tab-scope" onclick="switchArchTab('scope')">Scope</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── SIDEBAR ── -->
|
|
||||||
<div class="sidebar-col" style="width:200px;background:#ffffff;border-right:1px solid #e5e7eb;display:flex;flex-direction:column;padding:18px 12px;flex-shrink:0;">
|
|
||||||
<div style="padding:0 6px;margin-bottom:26px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;"><span class="f" style="font-size:13px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:#1a1a1a;letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div id="sidebar-project-name" style="font-size:11px;font-weight:500;color:#9ca3af;padding-left:34px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="font-size:9.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;padding:0 6px;margin-bottom:8px;">MVP Setup</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='05_describe.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Describe</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase active">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">⛁</div>
|
|
||||||
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);">Architect</div><div style="font-size:10px;color:#9ca3af;">What gets built</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">◧</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Design</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">◉</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Website</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">▲</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">Build MVP</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="border-top:1px solid #e5e7eb;margin-top:14px;padding-top:12px;">
|
|
||||||
<button onclick="saveAndExit()" style="display:flex;align-items:center;justify-content:center;gap:7px;width:100%;background:#eef2ff;border:1px solid #e0e7ff;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:background 0.15s;" onmouseover="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#e0e7ff'" onmouseout="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#eef2ff'">
|
|
||||||
<span style="font-size:12px;font-weight:600;color:#6366F1;">Save & go to dashboard</span>
|
|
||||||
</button>
|
|
||||||
<button id="dark-toggle" onclick="toggleTheme()" style="margin-top:8px;display:flex;align-items:center;justify-content:center;width:100%;background:transparent;border:1px solid var(--border);border-radius:8px;padding:8px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--mid);transition:background 0.15s,border-color 0.15s;" onmouseover="this.style.borderColor='#6366F1';this.style.color='#6366F1';" onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--mid)';">🌙 Dark mode</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── MAIN ── -->
|
|
||||||
<div class="arch-main" style="flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;position:relative;z-index:1;">
|
|
||||||
|
|
||||||
<!-- Top bar -->
|
|
||||||
<div class="arch-topbar" style="padding:18px 28px 14px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">
|
|
||||||
<div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:var(--ink);margin-bottom:3px;">Your product blueprint</div>
|
|
||||||
<div style="font-size:12.5px;color:var(--muted);">We’ve translated your idea into a complete system — how it’s built, how it runs, and how users interact with it. Review and confirm to continue</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scrollable body -->
|
|
||||||
<div class="arch-scroll" style="flex:1;overflow-y:auto;padding:24px 28px;display:flex;flex-direction:column;gap:12px;">
|
|
||||||
|
|
||||||
<!-- Intro message -->
|
|
||||||
<div style="display:flex;align-items:flex-start;gap:10px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;"><span style="font-size:12px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<div class="arch-intro-bubble" style="max-width:84%;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:4px 12px 12px 12px;padding:11px 14px;font-size:13px;color:var(--ink);line-height:1.65;">
|
|
||||||
Here's the technical stack we've set up for <strong id="intro-project-name" style="font-weight:600;">your product</strong>. These are the best defaults for an idea like yours — review each decision below and change anything that doesn't feel right.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Blueprint card -->
|
|
||||||
<div class="arch-blueprint-card" style="background:var(--white);border:1px solid var(--border);border-radius:14px;overflow:visible;box-shadow:0 4px 20px rgba(30,27,75,0.05);">
|
|
||||||
|
|
||||||
<!-- Card header -->
|
|
||||||
<div style="padding:14px 22px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:8px;height:8px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>
|
|
||||||
<span style="font-size:13px;font-weight:600;color:var(--ink);">How vibn will build it</span>
|
|
||||||
<button onclick="openWhy()" style="margin-left:auto;background:none;border:none;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;color:#6366F1;font-weight:600;padding:0;display:flex;align-items:center;gap:5px;">💡 Why these choices?</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Frontend -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">◧</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Frontend</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Where will your users mostly be when they use your product — at a desk, or on the go? This shapes every screen we design.</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="frontend" data-tip="Runs in any browser, desktop & mobile" onclick="selectOpt('frontend',this)">Web app</button>
|
|
||||||
<button class="opt-btn" data-group="frontend" data-tip="Optimised for phones, still works on desktop" onclick="selectOpt('frontend',this)">Mobile-first</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Backend -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">⛁</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Backend & Database</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">The invisible part that stores your users' data and makes everything work behind the scenes</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="backend" data-tip="Standard setup, works for almost everything" onclick="selectOpt('backend',this)">API + database</button>
|
|
||||||
<button class="opt-btn" data-group="backend" data-tip="Live updates between users — great for collaboration" onclick="selectOpt('backend',this)">Real-time</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Auth -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">⛓</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Sign up & Login</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">How people create an account and get back in — fewer steps means more people actually sign up</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="auth" data-tip="Google & GitHub sign-in — less friction, more sign-ups" onclick="selectOpt('auth',this)">Email + social</button>
|
|
||||||
<button class="opt-btn" data-group="auth" data-tip="Simpler setup, no third-party login" onclick="selectOpt('auth',this)">Email only</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Payments -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">$</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Payments</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">How you get paid — Stripe handles the card processing so you never touch sensitive data</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="payments" data-tip="Monthly or annual recurring payments" onclick="selectOpt('payments',this)">Subscription</button>
|
|
||||||
<button class="opt-btn" data-group="payments" data-tip="Pay once, own forever" onclick="selectOpt('payments',this)">One-time</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email -->
|
|
||||||
<div class="blueprint-row">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--indigo);">✉</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Email</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Automated messages sent to your users — from welcome emails on day one to newsletters later</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="email" data-tip="Welcome emails, resets & marketing newsletters" onclick="selectOpt('email',this)">Full suite</button>
|
|
||||||
<button class="opt-btn" data-group="email" data-tip="Just transactional emails — resets and confirmations" onclick="selectOpt('email',this)">Essentials only</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hosting — locked -->
|
|
||||||
<div class="blueprint-row locked">
|
|
||||||
<div style="width:32px;height:32px;border-radius:9px;background:#F3F4F6;display:flex;align-items:center;justify-content:center;font-size:15px;color:var(--muted);">▲</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Hosting</div>
|
|
||||||
<div style="font-size:11.5px;color:var(--muted);">Where your product lives — on your own servers, so no one else controls your data or your costs</div>
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" style="cursor:default;">Your servers 🔒</button>
|
|
||||||
<button class="opt-btn why-btn" onclick="openHostingWhy()" style="border-radius:0 7px 7px 0;border-right:none;color:#6366F1;font-weight:600;">Why?</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Reassurance note -->
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:11px 16px;background:#f0f4ff;border:1px solid #e0e7ff;border-radius:10px;">
|
|
||||||
<span style="font-size:16px;flex-shrink:0;">💬</span>
|
|
||||||
<p style="font-size:12px;color:var(--mid);line-height:1.55;margin:0;">Not sure about any of these? Don't worry — you can change them anytime before we start building.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── RIGHT PANEL ── -->
|
|
||||||
<div class="arch-right" style="width:384px;border-left:1px solid var(--border);background:#f5f3ff;display:flex;flex-direction:column;flex-shrink:0;">
|
|
||||||
|
|
||||||
<!-- Panel header — matches Describe PRD style -->
|
|
||||||
<div style="flex-shrink:0;padding:18px 0 0;">
|
|
||||||
<div style="margin:0 16px;padding-bottom:14px;border-bottom:1px solid #c7d2fe;">
|
|
||||||
<div style="font-size:15px;font-weight:800;letter-spacing:0.04em;text-transform:uppercase;color:#4338ca;margin-bottom:5px;">What your users will be able to do</div>
|
|
||||||
<div style="font-size:12px;color:#A0A0B8;line-height:1.5;">10 screens covering the full user journey, ready to design.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Scrollable content -->
|
|
||||||
<div class="arch-right-scroll" style="flex:1;overflow-y:auto;padding:16px;">
|
|
||||||
|
|
||||||
<!-- Pages -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Pages</div>
|
|
||||||
<div class="arch-deliverable-card" style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:16px;">
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Public</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Discover your product</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>See your pricing</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Learn about you</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Auth</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Create an account</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Sign back in</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Reset their password</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">App</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Use the dashboard</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Manage their settings</div>
|
|
||||||
|
|
||||||
<div style="padding:5px 12px;background:#fafaff;border-top:1px solid var(--border);border-bottom:1px solid var(--border);">
|
|
||||||
<span style="font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);">Payments</span>
|
|
||||||
</div>
|
|
||||||
<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Subscribe and pay</div>
|
|
||||||
<div class="deliverable-row" style="border-bottom:none;"><div style="width:5px;height:5px;border-radius:50%;background:#10B981;flex-shrink:0;"></div>Manage their plan</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Infrastructure -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Infrastructure</div>
|
|
||||||
<div class="arch-deliverable-card" style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;">
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🖥</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Your own servers</div><div style="font-size:11px;color:var(--muted);">No platform lock-in, ever</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid var(--border);">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🔁</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Auto-deploy via Coolify</div><div style="font-size:11px;color:var(--muted);">Every push goes live instantly</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;padding:10px 12px;">
|
|
||||||
<div style="width:28px;height:28px;border-radius:7px;background:var(--indigo-soft);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;">🔒</div>
|
|
||||||
<div><div style="font-size:12px;font-weight:600;color:var(--ink);">Code stored in Gitea</div><div style="font-size:11px;color:var(--muted);">Private repo, yours alone</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Timeline / cost signal -->
|
|
||||||
<div style="display:flex;align-items:center;justify-content:center;gap:16px;padding:10px 0 2px;">
|
|
||||||
<span style="font-size:11px;color:var(--muted);display:flex;align-items:center;gap:5px;">⏱ <span>~3–4 weeks to build</span></span>
|
|
||||||
<span style="font-size:11px;color:var(--muted);">·</span>
|
|
||||||
<span style="font-size:11px;color:var(--muted);display:flex;align-items:center;gap:5px;">💰 <span>No platform fees</span></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<div class="arch-right-footer" style="border-top:1px solid var(--border);padding:9px 0 13px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;">
|
|
||||||
<p style="font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 10px;line-height:1.5;">All set — let's decide how it looks.</p>
|
|
||||||
<a href="07_design.html" style="text-decoration:none;display:block;width:80%;">
|
|
||||||
<button class="btn-primary" style="width:100%;padding:12px 14px;border-radius:8px;">Next: Design</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── WHY POPUP ── -->
|
|
||||||
<div id="why-popup" style="display:none;position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:200;align-items:center;justify-content:center;padding:24px;" onclick="closeWhy()">
|
|
||||||
<div style="background:var(--white);border-radius:16px;width:100%;max-width:480px;box-shadow:0 24px 64px rgba(30,27,75,0.18);padding:32px;" onclick="event.stopPropagation()">
|
|
||||||
<div style="display:flex;align-items:center;gap:10px;margin-bottom:20px;">
|
|
||||||
<span style="font-size:20px;">💡</span>
|
|
||||||
<h3 style="font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">Why we chose this for your product</h3>
|
|
||||||
</div>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;">Based on your idea, vibn picked a <strong style="color:var(--ink);font-weight:600;">web app with subscription billing</strong> — the fastest path to recurring revenue for a SaaS product.</p>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink);font-weight:600;">Email + social login</strong> keeps sign-up friction low. Most people won't create a password for something they haven't tried yet — one click with Google removes that barrier.</p>
|
|
||||||
<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:24px;"><strong style="color:var(--ink);font-weight:600;">Your own hosting</strong> means you own the infrastructure outright. No platform lock-in, no surprise price hikes. Coolify + Gitea are already configured to your account.</p>
|
|
||||||
<button onclick="closeWhy()" class="btn-primary" style="width:100%;padding:12px;">Got it</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── SAVE POPUP ── -->
|
|
||||||
<div id="save-exit-popup">
|
|
||||||
<div id="save-exit-box">
|
|
||||||
<div class="save-icon">✓</div>
|
|
||||||
<h3>Your progress is saved.</h3>
|
|
||||||
<p>You can come back to this project anytime from your dashboard — everything will be exactly where you left it.</p>
|
|
||||||
<button onclick="window.location.href='03_dashboard.html'" style="width:100%;background:linear-gradient(135deg,#2e2a5e,#4338ca);color:#fff;border:none;border-radius:10px;padding:12px;font-family:'Plus Jakarta Sans',sans-serif;font-size:14px;font-weight:600;cursor:pointer;margin-bottom:10px;">Got it, go to dashboard</button>
|
|
||||||
<button class="save-cancel" onclick="cancelSaveExit()">Stay on this page</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- ── MODAL ── -->
|
|
||||||
<div id="modal" class="modal-bg" onclick="closeModal()">
|
|
||||||
<div class="modal-card" onclick="event.stopPropagation()">
|
|
||||||
<div style="padding:18px 22px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;">
|
|
||||||
<span id="modal-title" class="f" style="font-size:15px;font-weight:700;color:var(--ink);"></span>
|
|
||||||
<button onclick="closeModal()" style="background:transparent;border:none;color:var(--muted);cursor:pointer;font-size:18px;line-height:1;">×</button>
|
|
||||||
</div>
|
|
||||||
<div style="padding:16px 22px;" id="modal-options"></div>
|
|
||||||
<div style="padding:10px 22px 18px;">
|
|
||||||
<button onclick="closeModal()" class="btn-primary" style="width:100%;padding:12px;border-radius:9px;">Got it</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
try {
|
|
||||||
var name = localStorage.getItem('vibn_project_name') || 'My project';
|
|
||||||
var el = document.getElementById('sidebar-project-name');
|
|
||||||
el.textContent = name;
|
|
||||||
el.style.display = 'block';
|
|
||||||
var intro = document.getElementById('intro-project-name');
|
|
||||||
if(intro) intro.textContent = name;
|
|
||||||
} catch(e){}
|
|
||||||
// Init Blueprint tab on tablet + mobile
|
|
||||||
if(window.innerWidth <= 1024){
|
|
||||||
document.querySelector('.arch-main').classList.add('tab-active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function switchArchTab(tab){
|
|
||||||
var main = document.querySelector('.arch-main');
|
|
||||||
var right = document.querySelector('.arch-right');
|
|
||||||
var btnBlueprint = document.getElementById('tab-blueprint');
|
|
||||||
var btnScope = document.getElementById('tab-scope');
|
|
||||||
if(tab === 'blueprint'){
|
|
||||||
main.classList.add('tab-active');
|
|
||||||
right.classList.remove('tab-active');
|
|
||||||
btnBlueprint.classList.add('active');
|
|
||||||
btnScope.classList.remove('active');
|
|
||||||
} else {
|
|
||||||
right.classList.add('tab-active');
|
|
||||||
main.classList.remove('tab-active');
|
|
||||||
btnScope.classList.add('active');
|
|
||||||
btnBlueprint.classList.remove('active');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveAndExit(){ document.getElementById('save-exit-popup').classList.add('visible'); }
|
|
||||||
function cancelSaveExit(){ document.getElementById('save-exit-popup').classList.remove('visible'); }
|
|
||||||
|
|
||||||
function openWhy(){ document.getElementById('why-popup').style.display='flex'; }
|
|
||||||
function closeWhy(){ document.getElementById('why-popup').style.display='none'; }
|
|
||||||
|
|
||||||
function selectOpt(group, el){
|
|
||||||
document.querySelectorAll('[data-group="'+group+'"]').forEach(function(b){ b.classList.remove('selected'); });
|
|
||||||
el.classList.add('selected');
|
|
||||||
if (group === 'frontend') {
|
|
||||||
localStorage.setItem('vibn_frontend', el.getAttribute('data-tip'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function openHostingWhy(){
|
|
||||||
document.getElementById('modal-title').textContent = 'Why your own servers?';
|
|
||||||
document.getElementById('modal-options').innerHTML =
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink)">Most platforms rent you server space</strong> — which means they control your data, your uptime, and your bill. The day they raise prices or shut down, your product goes with it.</p>' +
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;margin-bottom:12px;"><strong style="color:var(--ink)">With vibn, your product lives on your own servers.</strong> You own the infrastructure outright. Nobody can lock you out, hike your costs, or access your users\u2019 data without your permission.</p>' +
|
|
||||||
'<p style="font-size:13px;color:var(--mid);line-height:1.8;">We handle the hard part: <a href="https://coolify.io" target="_blank" style="color:var(--indigo);font-weight:600;text-decoration:none;">Coolify</a> auto-deploys your code every time you push an update, and <a href="https://gitea.com" target="_blank" style="color:var(--indigo);font-weight:600;text-decoration:none;">Gitea</a> stores your codebase securely. You get full control of your infrastructure \u2014 without needing to know how any of it works.</p>';
|
|
||||||
document.getElementById('modal').classList.add('open');
|
|
||||||
}
|
|
||||||
function openModal(title, opt1, desc1, opt2, desc2){
|
|
||||||
document.getElementById('modal-title').textContent = title;
|
|
||||||
document.getElementById('modal-options').innerHTML = '<p style="font-size:13px;color:var(--mid);line-height:1.6;">'+desc1+'</p>';
|
|
||||||
document.getElementById('modal').classList.add('open');
|
|
||||||
}
|
|
||||||
function closeModal(){ document.getElementById('modal').classList.remove('open'); }
|
|
||||||
function showTip(el){
|
|
||||||
hideTip();
|
|
||||||
var tip=document.createElement('div');
|
|
||||||
tip.id='vibn-tip';
|
|
||||||
tip.textContent=el.dataset.tip;
|
|
||||||
tip.style.cssText='position:fixed;background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#fff;font-size:11px;font-weight:500;line-height:1.5;padding:6px 10px;border-radius:6px;white-space:nowrap;z-index:9999;pointer-events:none;box-shadow:0 4px 16px rgba(67,56,202,0.35);font-family:Plus Jakarta Sans,sans-serif;transition:opacity 0.1s;';
|
|
||||||
document.body.appendChild(tip);
|
|
||||||
var r=el.getBoundingClientRect();
|
|
||||||
tip.style.left=(r.left+r.width/2-tip.offsetWidth/2)+'px';
|
|
||||||
tip.style.top=(r.top-tip.offsetHeight-9)+'px';
|
|
||||||
var arrow=document.createElement('div');
|
|
||||||
arrow.id='vibn-tip-arrow';
|
|
||||||
arrow.style.cssText='position:fixed;border:5px solid transparent;border-top-color:#4338CA;z-index:9999;pointer-events:none;';
|
|
||||||
document.body.appendChild(arrow);
|
|
||||||
arrow.style.left=(r.left+r.width/2-5)+'px';
|
|
||||||
arrow.style.top=(r.top-9)+'px';
|
|
||||||
}
|
|
||||||
function hideTip(){
|
|
||||||
var t=document.getElementById('vibn-tip');
|
|
||||||
var a=document.getElementById('vibn-tip-arrow');
|
|
||||||
if(t)t.remove();
|
|
||||||
if(a)a.remove();
|
|
||||||
}
|
|
||||||
document.addEventListener('mouseover',function(e){var b=e.target.closest('.opt-btn[data-tip]');if(b)showTip(b);});
|
|
||||||
document.addEventListener('mouseout',function(e){var b=e.target.closest('.opt-btn[data-tip]');if(b)hideTip();});
|
|
||||||
function toggleTheme(){const html=document.documentElement;const isDark=html.dataset.theme==='dark';html.dataset.theme=isDark?'':'dark';document.getElementById('dark-toggle').textContent=isDark?'🌙 Dark mode':'☀️ Light mode';localStorage.setItem('vibn-theme',isDark?'':'dark');}
|
|
||||||
(function(){const saved=localStorage.getItem('vibn-theme');if(saved==='dark'){document.documentElement.dataset.theme='dark';document.addEventListener('DOMContentLoaded',function(){const btn=document.getElementById('dark-toggle');if(btn)btn.textContent='☀️ Light mode';});}})();
|
|
||||||
</script>
|
|
||||||
</body></html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,805 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<script>if(localStorage.getItem('vibn-theme')==='dark')document.documentElement.dataset.theme='dark';</script>
|
|
||||||
<title>vibn — Website</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{--ink:#1a1510;--ink2:#2c2c2a;--ink3:#444441;--mid:#5f5e5a;--muted:#888780;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#fdfcfa;--border:#e8e2d9; --section: #b8a9e93f;}
|
|
||||||
/* ── Dark mode tokens — exact match with design.html ── */
|
|
||||||
[data-theme="dark"]{--ink:#EEEEFF;--ink2:#B8B8D0;--ink3:#8484A8;--mid:#9898B8;--muted:#c2c2ee;--border:rgba(255,255,255,0.08);--cream:rgba(108,124,255,0.14);--paper:#0A1120;--white:rgba(255,255,255,0.05);--stone:rgba(255,255,255,0.08);--parch:rgba(255,255,255,0.06);--section:rgba(108,124,255,0.55);--accent-primary:#6C7CFF;--dm-surf-sidebar:rgba(12,18,34,0.72);--dm-surf-topbar:rgba(12,18,34,0.58);--dm-surf-panel:rgba(12,18,34,0.58);--dm-surf-right:rgba(12,18,34,0.58);--dm-surf-card:rgba(255,255,255,0.05);--dm-surf-refine:rgba(8,12,22,0.60);--dm-border:rgba(255,255,255,0.08);--dm-border-strong:rgba(255,255,255,0.14);--dm-border-hero:rgba(255,255,255,0.18);--dm-accent:#6C7CFF;--dm-accent-fill:rgba(108,124,255,0.14);--dm-accent-fill-mid:rgba(108,124,255,0.20);--dm-accent-border:rgba(108,124,255,0.55);--dm-text-1:#EEEEFF;--dm-text-2:#B4B4CC;--dm-text-3:#d2d2ef;--dm-shadow-panel:0 4px 36px rgba(0,0,0,0.55);--dm-shadow-hero:0 20px 70px rgba(0,0,0,0.65),0 6px 24px rgba(0,0,0,0.40),inset 0 1px 0 rgba(255,255,255,0.10);}
|
|
||||||
html:not([data-theme="dark"]) .preview-box {background: linear-gradient(to bottom, #FAFAFA, #F5F3FF);}
|
|
||||||
[data-theme="dark"] body{background:linear-gradient(to bottom,rgba(108,80,255,0.06) 0%,rgba(60,120,255,0.10) 38%,transparent 62%),linear-gradient(to bottom,rgba(108,124,255,0.10),transparent 180px),radial-gradient(900px 520px at 14% -8%,rgba(108,124,255,0.24),transparent 62%),radial-gradient(760px 420px at 88% 0%,rgba(72,145,255,0.16),transparent 60%),linear-gradient(180deg,#18213B 0%,#101726 48%,#0A1120 100%);}
|
|
||||||
[data-theme="dark"] .sidebar-col{background:var(--dm-surf-sidebar)!important;border-right:1px solid var(--dm-border)!important;box-shadow:2px 0 28px rgba(0,0,0,0.60)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#1a1a1a"],[data-theme="dark"] .sidebar-col [style*="color: #1a1a1a"]{color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#6b7280"],[data-theme="dark"] .sidebar-col [style*="color: #6b7280"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color: #9ca3af"]{color:var(--dm-text-3)!important;}
|
|
||||||
.ph-name{color:#9ca3af;}
|
|
||||||
[data-theme="dark"] .ph-name{color:var(--dm-text-3);}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#444441"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#6366F1"]{background:var(--dm-accent)!important;color:#0F1424!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#e5e7eb"]{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase.active{background:var(--dm-accent-fill)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase:not(.active):hover{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#fafaff"]{background:rgba(108,124,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="border:1px solid rgba(99,102,241"]{border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="border-top:1px solid #e5e7eb"]{border-top-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover{background:var(--dm-accent-fill-mid)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover span{color:#fff!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] #sidebar-project-name{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle{background:rgba(255,255,255,0.05)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle:hover{background:rgba(255,255,255,0.10)!important;color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .vibn-avatar{background:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .arch-topbar{background:var(--dm-surf-topbar)!important;border-bottom:1px solid rgba(255,255,255,0.04)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .arch-topbar .f{color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .arch-topbar [style*="color:#9ca3af"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .website-left{background:var(--dm-surf-panel)!important;border-right:1px solid var(--dm-border)!important;box-shadow:4px 0 36px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .website-left [style*="color:#9ca3af"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .website-left [style*="background:#f0f0f8"]{background:rgba(108,124,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .tab{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .tab.on{color:var(--dm-accent)!important;border-bottom-color:var(--dm-accent)!important;}
|
|
||||||
body{font-family:'Plus Jakarta Sans',sans-serif;background:var(--paper);display:flex;flex-direction:column;height:100vh;overflow:hidden;}
|
|
||||||
.f{font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
.sidebar-phase{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;}
|
|
||||||
.sidebar-phase.active{background:#fafaff;}
|
|
||||||
.phase-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;}
|
|
||||||
.tab{padding:11px 22px;border:none;background:transparent;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:13.5px;color:var(--muted);border-bottom:2px solid transparent;}
|
|
||||||
.tab.on{color:#6366F1;border-bottom-color:#6366F1;font-weight:600;}
|
|
||||||
input[type=range]{-webkit-appearance:none;width:100%;height:4px;border-radius:2px;background:#c7d2fe;outline:none;cursor:pointer;}
|
|
||||||
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:16px;height:16px;border-radius:50%;background:#6366F1;cursor:pointer;}
|
|
||||||
[data-theme="dark"] input[type=range]{background:rgba(255,255,255,0.12);}
|
|
||||||
[data-theme="dark"] input[type=range]::-webkit-slider-thumb{background:var(--dm-accent);}
|
|
||||||
.section{display:none;}
|
|
||||||
.section.active{display:block;}
|
|
||||||
#sec-style.active{display:flex;}
|
|
||||||
.ws-btn{border-radius:9px;border:1px solid var(--border);padding:11px 13px;cursor:pointer;text-align:left;margin-bottom:7px;width:100%;transition:all 0.15s;}
|
|
||||||
.ws-btn.selected{border:2px solid var(--ink);}
|
|
||||||
.deliverable-row{display:flex;align-items:center;gap:8px;padding:7px 10px;font-size:12px;color:var(--mid);border-bottom:1px solid var(--border);}
|
|
||||||
.deliverable-row:last-child{border-bottom:none;}
|
|
||||||
.vpill{border:1.5px solid #e0e7ff;border-radius:20px;padding:4px 11px;font-size:11px;font-weight:500;color:#6b7280;background:#fff;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;white-space:nowrap;}
|
|
||||||
.vpill:hover{border-color:#6366F1;color:#6366F1;}
|
|
||||||
.vpill.active{background:#6366F1;border-color:#6366F1;color:#fff;font-weight:600;box-shadow:0 2px 10px rgba(99,102,241,0.12);}
|
|
||||||
.tchip{border:1.5px solid #e0e7ff;border-radius:20px;padding:4px 12px;font-size:11.5px;font-weight:500;color:#6b7280;background:#fff;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;}
|
|
||||||
.tchip:hover{border-color:#6366F1;color:#6366F1;}
|
|
||||||
.tchip.active{background:#6366F1;border-color:#6366F1;color:#fff;font-weight:600;box-shadow:0 1px 6px rgba(99,102,241,0.10);}
|
|
||||||
.scard{border:2px solid #e0e7ff;border-radius:8px;cursor:pointer;text-align:left;background:#fff;overflow:hidden;transition:all 0.15s;padding:0;}
|
|
||||||
.scard:hover{border-color:#a5b4fc;}
|
|
||||||
.scard.selected{border-color:#6366F1;box-shadow:0 0 0 3px rgba(99,102,241,0.1),0 2px 10px rgba(99,102,241,0.12);}
|
|
||||||
.ws-rp-card{box-shadow:0 2px 12px rgba(99,102,241,0.08);}
|
|
||||||
.next-btn{width:100%;background:linear-gradient(135deg,#4338CA,#6366F1);color:#FFFFFF;border:none;border-radius:8px;padding:12px 14px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 4px 18px rgba(99,102,241,0.22),0 1px 4px rgba(99,102,241,0.12);transition:box-shadow 0.2s;}
|
|
||||||
.scard-name{font-size:11px;font-weight:700;color:#1a1a1a;}
|
|
||||||
.scard-desc{font-size:9.5px;color:#6b7280;margin-top:1px;}
|
|
||||||
[data-theme="dark"] .vpill{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;color:#fff)!important;}
|
|
||||||
[data-theme="dark"] .vpill:hover{border-color:rgba(108,124,255,0.45)!important;color:var(--dm-text-2)!important;}
|
|
||||||
[data-theme="dark"] .vpill.active{background:var(--dm-accent)!important;border-color:var(--dm-accent)!important;color:#fff!important;}
|
|
||||||
[data-theme="dark"] .tchip{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .tchip.active{background:var(--dm-accent)!important;border-color:var(--dm-accent)!important;color:#fff!important;}
|
|
||||||
[data-theme="dark"] .scard{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .scard:hover{border-color:var(--dm-border-strong)!important;}
|
|
||||||
[data-theme="dark"] .scard.selected{border-color:var(--dm-accent)!important;box-shadow:0 0 0 2px rgba(108,124,255,0.22)!important;}
|
|
||||||
[data-theme="dark"] .scard-name{color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .scard-desc{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .main-content-area{background:transparent!important;}
|
|
||||||
[data-theme="dark"] .main-content-area [style*="background:#f0f0f8"],[data-theme="dark"] .main-content-area [style*="background:#f8f9ff"]{background:rgba(255,255,255,0.03)!important;}
|
|
||||||
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:0.35}}
|
|
||||||
.opt-btns{display:flex;gap:0;flex-shrink:0;border:1.5px solid var(--border);border-radius:8px;overflow:visible;}
|
|
||||||
.opt-btn{flex:1;text-align:center;font-size:12px;font-weight:500;color:var(--mid);background:transparent;border:none;border-right:1.5px solid var(--border);border-radius:0;padding:6px 13px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;white-space:nowrap;position:relative;}
|
|
||||||
.opt-btn:first-child{border-radius:7px 0 0 7px;}
|
|
||||||
.opt-btn:last-child{border-right:none;border-radius:0 7px 7px 0;}
|
|
||||||
.opt-btn:hover{background:#f5f5ff;color:#6366F1;}
|
|
||||||
.opt-btn.selected{background:rgba(99,102,241,0.08);color:#4338CA;font-weight:600;}
|
|
||||||
[data-theme="dark"] .opt-btns{border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn{color:var(--dm-text-2)!important;border-right-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn:hover{background:rgba(108,124,255,0.09)!important;color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .opt-btn.selected{background:var(--dm-accent-fill)!important;color:var(--dm-accent)!important;font-weight:600!important;box-shadow:inset 0 0 0 1px rgba(108,124,255,0.32),0 2px 16px rgba(108,124,255,0.22)!important;}
|
|
||||||
[data-theme="dark"] .website-right{background:var(--dm-surf-right)!important;border-left:1px solid var(--dm-border)!important;box-shadow:-4px 0 36px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .website-right [style*="border-bottom:1px solid #c7d2fe"]{border-bottom-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .website-right [style*="color:#4338ca"]{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .website-right [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;color:var(--dm-accent)!important;}
|
|
||||||
.sec-div{height:1px;background: var(--section);}
|
|
||||||
[data-theme="dark"] .website-left .sec-div{background:rgba(108,124,255,0.20)!important;}
|
|
||||||
|
|
||||||
.surprise-btn{width:100%;padding:10px;border:1.5px solid #e0e7ff;border-radius:9px;background:#6366F1;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:600;color:#ffffff;cursor:pointer;transition:all 0.15s;text-align:center;display:block;box-shadow:0 2px 14px rgba(99,102,241,0.22);}
|
|
||||||
.surprise-btn:hover{background:#5045c8;border-color:#c7d2fe;}
|
|
||||||
[data-theme="dark"] .preview-box{background:rgba(6,10,20,0.32)!important;}
|
|
||||||
[data-theme="dark"] .preview-chrome-box{border:1.5px solid var(--dm-border-hero)!important;box-shadow:var(--dm-shadow-hero),0 0 0 1px rgba(108,124,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] [style*="background:#eef2ff;border-radius:11px"]{background:var(--dm-surf-topbar)!important;border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] span[style*="color:#6366F1;background:#eef2ff"]{color:var(--dm-accent)!important;background:var(--dm-accent-fill)!important;}
|
|
||||||
[data-theme="dark"] [style*="color:#6366F1"][style*="text-transform:uppercase"]{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] .surprise-btn{background:linear-gradient(135deg,#5450CC,var(--dm-accent))!important;border-color:rgba(108,124,255,0.35)!important;color:#fff!important;box-shadow:0 4px 18px rgba(108,124,255,0.22)!important;}
|
|
||||||
[data-theme="dark"] .surprise-btn:hover{background:linear-gradient(135deg,#4840BE,#6070F8)!important;box-shadow:0 6px 26px rgba(108,124,255,0.30)!important;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar{width:5px;height:5px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-track{background:transparent;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.12);border-radius:3px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:rgba(255,255,255,0.22);}
|
|
||||||
[data-theme="dark"] *{scrollbar-color:rgba(255,255,255,0.12) transparent;scrollbar-width:thin;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div style="display:flex;height:100%;overflow:hidden;">
|
|
||||||
|
|
||||||
<!-- SIDEBAR -->
|
|
||||||
<div class="sidebar-col" style="width:200px;background:#ffffff;border-right:1px solid #e5e7eb;display:flex;flex-direction:column;padding:18px 12px;flex-shrink:0;">
|
|
||||||
<div style="padding:0 6px;margin-bottom:26px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;"><span class="f" style="font-size:13px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:var(--ink);letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div id="sidebar-project-name" style="font-size:11px;font-weight:500;color:#9ca3af;padding-left:34px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none;"></div>
|
|
||||||
</div>
|
|
||||||
<div class="ph-name" style="font-size:9.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;padding:0 6px;margin-bottom:8px;">MVP Setup</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='05_describe.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div class="ph-name" style="font-size:12.5px;">Describe</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='06_architect.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div class="ph-name" style="font-size:12.5px;">Architect</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='07_design.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div class="ph-name" style="font-size:12.5px;">Design</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase active">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">◉</div>
|
|
||||||
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);">Website</div><div class="ph-name" style="font-size:10px;">How you'll grow</div></div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase">
|
|
||||||
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;">▲</div>
|
|
||||||
<div class="ph-name" style="font-size:12.5px;">Build MVP</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="border-top:1px solid #e5e7eb;margin-top:14px;padding-top:12px;">
|
|
||||||
<button onclick="saveAndExit()" style="display:flex;align-items:center;justify-content:center;gap:7px;width:100%;background:#eef2ff;border:1px solid #e0e7ff;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:background 0.15s;" onmouseover="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#e0e7ff'" onmouseout="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#eef2ff'">
|
|
||||||
<span style="font-size:12px;font-weight:600;color:#6366F1;">Save & go to dashboard</span>
|
|
||||||
</button>
|
|
||||||
<button id="dark-toggle" onclick="toggleTheme()" style="margin-top:8px;display:flex;align-items:center;justify-content:center;width:100%;background:transparent;border:1px solid var(--border);border-radius:8px;padding:8px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--mid);transition:background 0.15s,border-color 0.15s;" onmouseover="this.style.borderColor='#6366F1';this.style.color='#6366F1';" onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--mid)';">🌙 Dark mode</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- MAIN -->
|
|
||||||
<!-- TOP BAR -->
|
|
||||||
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;">
|
|
||||||
<div class="arch-topbar" style="padding:18px 28px 14px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">
|
|
||||||
<div>
|
|
||||||
<div class="f" style="font-size:17px;font-weight:700;color:var(--ink);margin-bottom:3px;">Website</div>
|
|
||||||
<div style="font-size:12.5px;color:#9ca3af;">This is what people see before signing up. Set your voice, topics, and website style</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="main-content-area" style="flex:1;overflow:hidden;display:flex;background:#f8f9ff;">
|
|
||||||
|
|
||||||
<!-- LEFT: Controls — single panel, no individual cards -->
|
|
||||||
<div class="website-left" style="width:272px;border-right:1px solid #e0e7ff;overflow-y:auto;padding:0;display:flex;flex-direction:column;flex-shrink:0;background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);">
|
|
||||||
|
|
||||||
<!-- Voice section -->
|
|
||||||
<div style="padding:16px 16px 14px;">
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.09em;text-transform:uppercase;color:#6366F1;margin-bottom:12px;">Voice</div>
|
|
||||||
<div style="margin-bottom:10px;">
|
|
||||||
<div style="font-size:10.5px;color:#9ca3af;margin-bottom:5px;">Tone</div>
|
|
||||||
<div style="display:flex;gap:5px;" id="pills-tone">
|
|
||||||
<button class="vpill active" onclick="setPill(this,'tone',0)">Friendly</button>
|
|
||||||
<button class="vpill" onclick="setPill(this,'tone',50)">Balanced</button>
|
|
||||||
<button class="vpill" onclick="setPill(this,'tone',100)">Professional</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="margin-bottom:10px;">
|
|
||||||
<div style="font-size:10.5px;color:#9ca3af;margin-bottom:5px;">Style</div>
|
|
||||||
<div style="display:flex;gap:5px;" id="pills-style">
|
|
||||||
<button class="vpill" onclick="setPill(this,'style',0)">Casual</button>
|
|
||||||
<button class="vpill active" onclick="setPill(this,'style',50)">Balanced</button>
|
|
||||||
<button class="vpill" onclick="setPill(this,'style',100)">Precise</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style="font-size:10.5px;color:#9ca3af;margin-bottom:5px;">Personality</div>
|
|
||||||
<div style="display:flex;gap:5px;" id="pills-pers">
|
|
||||||
<button class="vpill active" onclick="setPill(this,'pers',0)">Warm</button>
|
|
||||||
<button class="vpill" onclick="setPill(this,'pers',50)">Steady</button>
|
|
||||||
<button class="vpill" onclick="setPill(this,'pers',100)">Direct</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sec-div" style="height:1px;background: var(--section);margin:0 16px;"></div>
|
|
||||||
|
|
||||||
<!-- Topics section -->
|
|
||||||
<div style="padding:14px 16px;">
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.09em;text-transform:uppercase;color:#6366F1;margin-bottom:10px;">Topics</div>
|
|
||||||
<div style="display:flex;flex-wrap:wrap;gap:6px;">
|
|
||||||
<button class="tchip active" onclick="toggleTopic(this,'problem')">The problem</button>
|
|
||||||
<button class="tchip active" onclick="toggleTopic(this,'audience')">Who it's for</button>
|
|
||||||
<button class="tchip active" onclick="toggleTopic(this,'timing')">Why now</button>
|
|
||||||
<button class="tchip" onclick="toggleTopic(this,'benefits')">Key benefits</button>
|
|
||||||
<button class="tchip" onclick="toggleTopic(this,'comparison')">vs. alternatives</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="sec-div" style="height:1px;background: var(--section);margin:0 16px;"></div>
|
|
||||||
|
|
||||||
<!-- Style section -->
|
|
||||||
<div style="padding:14px 16px;">
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.09em;text-transform:uppercase;color:#6366F1;margin-bottom:10px;">Website style</div>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:7px;">
|
|
||||||
<button class="scard selected" id="sc-editorial" onclick="setStyle('editorial','#ffffff','#ef4444','Editorial','Bold headlines, strong opinions')">
|
|
||||||
<div style="height:28px;background:#fff;border-radius:5px 5px 0 0;padding:5px 8px;display:flex;flex-direction:column;gap:3px;border:1px solid rgba(0,0,0,0.07);border-bottom:none;">
|
|
||||||
<div style="width:50%;height:3px;border-radius:2px;background:#111;"></div>
|
|
||||||
<div style="width:26%;height:6px;border-radius:3px;background:#ef4444;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:4px 8px 6px;"><div class="scard-name">Editorial</div><div class="scard-desc" style="color:#ef4444;">Bold & opinionated</div></div>
|
|
||||||
</button>
|
|
||||||
<button class="scard" id="sc-startup" onclick="setStyle('startup','#f8fafc','#0ea5e9','Startup energy','Clear, conversion-focused')">
|
|
||||||
<div style="height:28px;background:#f8fafc;border-radius:5px 5px 0 0;padding:5px 8px;display:flex;flex-direction:column;gap:3px;border:1px solid rgba(0,0,0,0.05);border-bottom:none;">
|
|
||||||
<div style="width:50%;height:3px;border-radius:2px;background:#0f172a;opacity:0.65;"></div>
|
|
||||||
<div style="width:26%;height:6px;border-radius:3px;background:#0ea5e9;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:4px 8px 6px;"><div class="scard-name">Startup</div><div class="scard-desc" style="color:#0ea5e9;">Clean & focused</div></div>
|
|
||||||
</button>
|
|
||||||
<button class="scard" id="sc-minimal" onclick="setStyle('minimal','#ffffff','#111111','Ultra minimal','Let the product speak')">
|
|
||||||
<div style="height:28px;background:#fff;border-radius:6px 6px 0 0;padding:5px 8px;display:flex;flex-direction:column;gap:3px;border:1px solid rgba(0,0,0,0.06);border-bottom:none;">
|
|
||||||
<div style="width:50%;height:3px;border-radius:2px;background:#111;opacity:0.75;"></div>
|
|
||||||
<div style="width:26%;height:6px;border-radius:3px;background:#111;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:4px 8px 6px;"><div class="scard-name">Minimal</div><div class="scard-desc" style="color:#6b6b6b;">Less is more</div></div>
|
|
||||||
</button>
|
|
||||||
<button class="scard" id="sc-soft" onclick="setStyle('soft','#FAFCFA','#7FA58A','Soft UI','Smooth, friendly, and polished')">
|
|
||||||
<div style="height:28px;background:#FAFCFA;border-radius:5px 5px 0 0;padding:5px 8px;display:flex;flex-direction:column;gap:3px;border:1px solid rgba(0,0,0,0.06);border-bottom:none;">
|
|
||||||
<div style="width:50%;height:3px;border-radius:2px;background:#1f2937;opacity:0.45;"></div>
|
|
||||||
<div style="width:26%;height:6px;border-radius:3px;background:#7FA58A;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:4px 8px 6px;">
|
|
||||||
<div class="scard-name">Soft UI</div>
|
|
||||||
<div class="scard-desc" style="color:#6B8A78;">Smooth & approachable</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- AI decide — flush to bottom -->
|
|
||||||
<div style="margin-top:auto;padding:14px 16px 18px;">
|
|
||||||
<div class="sec-div" style="margin:0 0 14px;"></div>
|
|
||||||
<button class="surprise-btn" onclick="aiDecide()">✨ Surprise me</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- RIGHT: Preview — fills all available space -->
|
|
||||||
<div class = "preview-box"style="flex:1;display:flex;flex-direction:column;overflow:hidden;padding:4px 6px 6px;min-width:0;">
|
|
||||||
|
|
||||||
<!-- Label + device toggle -->
|
|
||||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;padding:0 2px;">
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:#9ca3af;display:flex;align-items:center;gap:7px;">
|
|
||||||
<span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;animation:pulse-dot 2s infinite;"></span>
|
|
||||||
Live preview
|
|
||||||
</div>
|
|
||||||
<div class="opt-btns">
|
|
||||||
<button class="opt-btn selected" data-group="device" data-tip="Runs in any browser, desktop & mobile" onclick="setDevice(this)">Web app</button>
|
|
||||||
<button class="opt-btn" data-group="device" data-tip="Optimised for phones, still works on desktop" onclick="setDevice(this)">Mobile-first</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Browser chrome -->
|
|
||||||
<div class="preview-chrome-box" style="flex:1;border-radius:12px;overflow:hidden;border:1.5px solid #e0e7ff;box-shadow:0 4px 24px rgba(99,102,241,0.08);display:flex;flex-direction:column;min-height:0;">
|
|
||||||
<div style="background:#f5f3ff;padding:7px 12px;display:flex;align-items:center;gap:8px;border-bottom:1px solid #e0e7ff;flex-shrink:0;">
|
|
||||||
<div style="display:flex;gap:4px;"><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div></div>
|
|
||||||
<div style="flex:1;background:#fff;border:1px solid #e0e7ff;border-radius:4px;padding:2px 9px;font-family:monospace;font-size:10px;color:#6366F1;">yourproduct.com</div>
|
|
||||||
</div>
|
|
||||||
<div id="preview-scroll" style="flex:1;overflow-y:auto;transition:background 0.3s;">
|
|
||||||
<div id="live-preview"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hidden: copy IDs kept for JS compatibility -->
|
|
||||||
<div style="display:none;">
|
|
||||||
<div id="copy-headline"></div>
|
|
||||||
<div id="copy-sub"></div>
|
|
||||||
<div id="copy-cta"></div>
|
|
||||||
<div id="copy-bullets"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- RIGHT PANEL -->
|
|
||||||
<div class="website-right" style="width:384px;border-left:1px solid var(--border);background:#f5f3ff;display:flex;flex-direction:column;flex-shrink:0;">
|
|
||||||
<div style="flex-shrink:0;padding:18px 0 0;">
|
|
||||||
<div style="margin:0 16px;padding-bottom:14px;border-bottom:1px solid #c7d2fe;">
|
|
||||||
<div style="font-size:15px;font-weight:800;letter-spacing:0.04em;text-transform:uppercase;color:#4338ca;margin-bottom:5px;">Your brand at a glance</div>
|
|
||||||
<div style="font-size:12px;color:#A0A0B8;line-height:1.5;">A summary of how your brand will look and sound to the world.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="flex:1;overflow-y:auto;padding:16px;">
|
|
||||||
|
|
||||||
<!-- 1. Your brand voice -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Your brand voice</div>
|
|
||||||
<div class="ws-rp-card" id="rp-voice-sentence" style="background:var(--white);border:1px solid var(--border);border-radius:10px;padding:11px 13px;margin-bottom:16px;font-size:12.5px;color:var(--ink);line-height:1.55;"></div>
|
|
||||||
|
|
||||||
<!-- 2. Website experience -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Website experience</div>
|
|
||||||
<div class="ws-rp-card" id="rp-experience" style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:16px;"></div>
|
|
||||||
|
|
||||||
<!-- 3. How users will perceive it -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">How users will perceive it</div>
|
|
||||||
<div class="ws-rp-card" id="rp-perception" style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:16px;"></div>
|
|
||||||
|
|
||||||
<!-- 4. Optimized for -->
|
|
||||||
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;">Optimized for</div>
|
|
||||||
<div class="ws-rp-card" id="rp-topics-list" style="background:var(--white);border:1px solid var(--border);border-radius:10px;overflow:hidden;margin-bottom:16px;"></div>
|
|
||||||
|
|
||||||
<!-- Hidden: kept for JS compat -->
|
|
||||||
<span id="rp-tone" style="display:none;">Friendly</span>
|
|
||||||
<span id="rp-style" style="display:none;">Balanced</span>
|
|
||||||
<span id="rp-pers" style="display:none;">Warm</span>
|
|
||||||
<span id="rp-ws-name" style="display:none;">Editorial</span>
|
|
||||||
<span id="rp-ws-desc" style="display:none;">Bold headlines, strong opinions</span>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div style="border-top:1px solid var(--border);padding:9px 0 13px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;">
|
|
||||||
<p style="font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 10px;line-height:1.5;">All set — let's build it.</p>
|
|
||||||
<a href="09_build.html" style="text-decoration:none;display:block;width:80%;"><button class="next-btn">Next : Build my product</button></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/* ── State ── */
|
|
||||||
var VOICE = {tone:'Friendly', style:'Balanced', pers:'Warm'};
|
|
||||||
var TOPICS = {problem:true, audience:true, timing:true, benefits:false, comparison:false};
|
|
||||||
var STYLE = {id:'editorial', bg:'#ffffff', accent:'#ef4444', label:'Editorial', desc:'Bold headlines, strong opinions'};
|
|
||||||
var DEVICE = 'desktop';
|
|
||||||
|
|
||||||
var PILL_LABELS = {
|
|
||||||
tone: {0:'Friendly', 50:'Balanced', 100:'Professional'},
|
|
||||||
style: {0:'Casual', 50:'Balanced', 100:'Precise'},
|
|
||||||
pers: {0:'Warm', 50:'Steady', 100:'Direct'}
|
|
||||||
};
|
|
||||||
|
|
||||||
var COPY_MATRIX = {
|
|
||||||
/* Friendly ─────────────────────────────────────────────────────────────── */
|
|
||||||
'Friendly-Casual-Warm': {h:'Build something people actually love', s:"We make it super easy to go from idea to product — no stress, just results. You'll wonder why you waited.", cta:"Let's build together"},
|
|
||||||
'Friendly-Casual-Steady': {h:'From idea to launch, one step at a time', s:"We walk with you every step — from your first spark to your first 1,000 users. No rush, just progress.", cta:'Start your journey'},
|
|
||||||
'Friendly-Casual-Direct': {h:'Stop waiting. Start building.', s:"Your idea is good. Let's make it real — it's way simpler than you think.", cta:'Get started now'},
|
|
||||||
'Friendly-Balanced-Warm': {h:'Your idea deserves to exist', s:'A thoughtful platform for founders who want to move fast without cutting corners.', cta:'Build something real'},
|
|
||||||
'Friendly-Balanced-Steady': {h:'Build, launch, and grow — all in one place', s:'Everything you need to turn a product idea into a growing business.', cta:'Start building'},
|
|
||||||
'Friendly-Balanced-Direct': {h:'From zero to product in days', s:'The fastest path from idea to market — no fluff, no wasted time.', cta:'Launch your product'},
|
|
||||||
'Friendly-Precise-Warm': {h:'From rough idea to live product in 5 clear steps', s:'A guided, step-by-step process that covers every decision — from architecture to your first customer — with care.', cta:'See how it works'},
|
|
||||||
'Friendly-Precise-Steady': {h:'A proven 5-step path to your first 1,000 users', s:'Our structured process covers architecture, design, copy, and launch — every stage defined, nothing left to chance.', cta:'Follow the process'},
|
|
||||||
'Friendly-Precise-Direct': {h:"Ship in days. Here's exactly how.", s:'5 defined steps. One platform. From idea to production-ready product — zero guesswork, zero wasted time.', cta:'Start now'},
|
|
||||||
/* Balanced ─────────────────────────────────────────────────────────────── */
|
|
||||||
'Balanced-Casual-Warm': {h:'Turn your idea into something real', s:"We're here to help you build the thing you've been thinking about — no overthinking required.", cta:'Build something real'},
|
|
||||||
'Balanced-Casual-Steady': {h:'Build it. Ship it. Grow it.', s:'We take the guesswork out of building a product. Just follow the process and watch it come together.', cta:'Start building'},
|
|
||||||
'Balanced-Casual-Direct': {h:'Idea to product. Fast.', s:'Stop planning, start shipping. We give you everything you need to move fast and get to market.', cta:'Get moving'},
|
|
||||||
'Balanced-Balanced-Warm': {h:'Your idea deserves to exist', s:'A thoughtful platform for founders who want to move fast without cutting corners.', cta:'Build something real'},
|
|
||||||
'Balanced-Balanced-Steady': {h:'Build, launch, and grow — all in one place', s:'Everything you need to turn a product idea into a growing business.', cta:'Start building'},
|
|
||||||
'Balanced-Balanced-Direct': {h:'From zero to product in days', s:'The fastest path from idea to market. No fluff, no wasted time.', cta:'Launch your product'},
|
|
||||||
'Balanced-Precise-Warm': {h:'From concept to customers in one structured flow', s:'Our platform guides you through every decision — from architecture to launch copy — with precision and care.', cta:'Explore the workflow'},
|
|
||||||
'Balanced-Precise-Steady': {h:'A structured path from idea to market', s:'Every step is defined, every deliverable is clear. Move confidently from concept to launch.', cta:'See the process'},
|
|
||||||
'Balanced-Precise-Direct': {h:'Zero to launched in 5 defined steps', s:'Architecture, design, copy, and deployment — all mapped upfront. No surprises, no wasted cycles.', cta:'Start now'},
|
|
||||||
/* Professional ─────────────────────────────────────────────────────────── */
|
|
||||||
'Professional-Casual-Warm': {h:'Sophisticated tools for ambitious builders', s:'We combine enterprise-grade infrastructure with a process that feels surprisingly human and approachable.', cta:'Explore the platform'},
|
|
||||||
'Professional-Casual-Steady': {h:'The reliable way to go from idea to product', s:'A proven approach that serious founders use to ship faster — without the complexity or the headaches.', cta:'Get started'},
|
|
||||||
'Professional-Casual-Direct': {h:'Build fast. Ship confident.', s:'Skip the setup headaches. We handle the complexity so you can focus on what actually matters — shipping.', cta:'Start now'},
|
|
||||||
'Professional-Balanced-Warm': {h:'Enterprise-grade tools, founder-friendly experience',s:'Sophisticated infrastructure wrapped in an experience designed for ambitious builders.', cta:'Explore the platform'},
|
|
||||||
'Professional-Balanced-Steady': {h:'The reliable path from idea to market', s:'A structured, proven approach to building and launching software products at speed.', cta:'Get started'},
|
|
||||||
'Professional-Balanced-Direct': {h:'Build fast. Ship confident.', s:'Production-ready infrastructure and AI-assisted development. Ship in days, not months.', cta:'Start now'},
|
|
||||||
'Professional-Precise-Warm': {h:'Production-grade infrastructure with a human touch',s:'From scalable architecture to conversion-optimised copy — every component built to specification, every decision explained.',cta:'Review the specs'},
|
|
||||||
'Professional-Precise-Steady': {h:'A methodical approach to product development at speed',s:'Defined workflows, documented architecture, and measurable milestones — from initial concept through to market launch.',cta:'See the methodology'},
|
|
||||||
'Professional-Precise-Direct': {h:'Ship production-ready in days.', s:'Automated architecture decisions, AI-generated copy, one-click deployment. Measurable output at every stage.', cta:'Get started'}
|
|
||||||
};
|
|
||||||
|
|
||||||
var TOPIC_TEXT = {
|
|
||||||
problem: 'Solves a real, painful problem',
|
|
||||||
audience: 'Built for a specific, well-defined audience',
|
|
||||||
timing: 'The market is ready right now',
|
|
||||||
benefits: 'Clear advantages over the status quo',
|
|
||||||
comparison: 'Better than existing alternatives'
|
|
||||||
};
|
|
||||||
|
|
||||||
var TOPIC_RP_LABELS = {
|
|
||||||
problem: 'Highlighting a clear pain point',
|
|
||||||
audience: 'Targeting a specific audience',
|
|
||||||
timing: 'Creating urgency',
|
|
||||||
benefits: 'Showing value quickly',
|
|
||||||
comparison: 'Differentiating from competitors'
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ── Device ── */
|
|
||||||
function setDevice(btn) {
|
|
||||||
document.querySelectorAll('[data-group="device"]').forEach(function(b){ b.classList.remove('selected'); });
|
|
||||||
btn.classList.add('selected');
|
|
||||||
DEVICE = btn.getAttribute('data-tip') === 'Optimised for phones, still works on desktop' ? 'mobile' : 'desktop';
|
|
||||||
renderPreview();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Controls ── */
|
|
||||||
function saveWebsiteState(){
|
|
||||||
try {
|
|
||||||
var topicLabels = {problem:'The problem', audience:"Who it's for", timing:'Why now', benefits:'Key benefits', comparison:'vs. alternatives'};
|
|
||||||
var activeTopics = Object.keys(TOPICS).filter(function(k){ return TOPICS[k]; }).map(function(k){ return topicLabels[k]; });
|
|
||||||
localStorage.setItem('vibn_website', JSON.stringify({
|
|
||||||
voice: VOICE,
|
|
||||||
styleLabel: STYLE.label,
|
|
||||||
topics: activeTopics
|
|
||||||
}));
|
|
||||||
} catch(e){}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setPill(el, key, val) {
|
|
||||||
document.getElementById('pills-' + key).querySelectorAll('.vpill').forEach(function(p){ p.classList.remove('active'); });
|
|
||||||
el.classList.add('active');
|
|
||||||
VOICE[key] = PILL_LABELS[key][val];
|
|
||||||
document.getElementById('rp-' + key).textContent = VOICE[key];
|
|
||||||
renderAll();
|
|
||||||
saveWebsiteState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTopic(el, key) {
|
|
||||||
var on = !el.classList.contains('active');
|
|
||||||
el.classList.toggle('active', on);
|
|
||||||
TOPICS[key] = on;
|
|
||||||
renderAll();
|
|
||||||
saveWebsiteState();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setStyle(id, bg, accent, label, desc) {
|
|
||||||
document.querySelectorAll('.scard').forEach(function(c){ c.classList.remove('selected'); });
|
|
||||||
document.getElementById('sc-' + id).classList.add('selected');
|
|
||||||
STYLE = {id:id, bg:bg, accent:accent, label:label, desc:desc};
|
|
||||||
document.getElementById('rp-ws-name').textContent = label;
|
|
||||||
document.getElementById('rp-ws-desc').textContent = desc;
|
|
||||||
renderAll();
|
|
||||||
saveWebsiteState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Copy generation ── */
|
|
||||||
function getCopy() {
|
|
||||||
return COPY_MATRIX[VOICE.tone + '-' + VOICE.style + '-' + VOICE.pers] || COPY_MATRIX['Balanced-Balanced-Steady'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBullets() {
|
|
||||||
return Object.keys(TOPICS).filter(function(k){ return TOPICS[k]; }).map(function(k){ return TOPIC_TEXT[k]; });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Render ── */
|
|
||||||
function renderPreview() {
|
|
||||||
var copy = getCopy();
|
|
||||||
var bullets = getBullets();
|
|
||||||
var bg = STYLE.bg;
|
|
||||||
var accent = STYLE.accent;
|
|
||||||
var isDark = ['#0f172a','#0c0a09','#1a1a1a'].indexOf(bg) !== -1;
|
|
||||||
var tc = isDark ? '#ffffff' : '#111111';
|
|
||||||
var sub = isDark ? 'rgba(255,255,255,0.55)' : '#6b7280';
|
|
||||||
var border = isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.06)';
|
|
||||||
|
|
||||||
/* ── Per-style theme variables ── */
|
|
||||||
function hexRgbaW(h,a){ var r=parseInt(h.slice(1,3),16),g=parseInt(h.slice(3,5),16),b=parseInt(h.slice(5,7),16); return 'rgba('+r+','+g+','+b+','+a+')'; }
|
|
||||||
var S = {
|
|
||||||
editorial: {
|
|
||||||
r: 4, cardR: 4, navR: 4,
|
|
||||||
shadow: 'none', cardShadow: 'none',
|
|
||||||
headingWeight: 800, headingTracking: '-0.03em',
|
|
||||||
border: isDark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.12)',
|
|
||||||
navBorder: isDark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.12)',
|
|
||||||
cardBg: isDark ? 'rgba(255,255,255,0.05)' : '#fff',
|
|
||||||
sectionBg: isDark ? 'rgba(255,255,255,0.03)' : 'rgba(0,0,0,0.02)',
|
|
||||||
pillBg: isDark ? 'rgba(255,255,255,0.08)' : hexRgbaW(accent,0.10),
|
|
||||||
ctaR: '4px', secPad: '22px 24px', heroPad: '24px 24px 14px',
|
|
||||||
subColor: isDark ? 'rgba(255,255,255,0.50)' : '#374151'
|
|
||||||
},
|
|
||||||
startup: {
|
|
||||||
r: 8, cardR: 8, navR: 6,
|
|
||||||
shadow: '0 1px 4px rgba(0,0,0,0.07)', cardShadow: '0 1px 4px rgba(0,0,0,0.07)',
|
|
||||||
headingWeight: 700, headingTracking: '-0.02em',
|
|
||||||
border: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)',
|
|
||||||
navBorder: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.07)',
|
|
||||||
cardBg: isDark ? 'rgba(255,255,255,0.05)' : '#fff',
|
|
||||||
sectionBg: isDark ? 'rgba(255,255,255,0.03)' : 'rgba(99,102,241,0.03)',
|
|
||||||
pillBg: isDark ? 'rgba(255,255,255,0.08)' : hexRgbaW(accent,0.07),
|
|
||||||
ctaR: '8px', secPad: '28px 24px', heroPad: '26px 24px 16px',
|
|
||||||
subColor: sub
|
|
||||||
},
|
|
||||||
minimal: {
|
|
||||||
r: 4, cardR: 4, navR: 4,
|
|
||||||
shadow: 'none', cardShadow: 'none',
|
|
||||||
headingWeight: 600, headingTracking: '-0.01em',
|
|
||||||
border: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)',
|
|
||||||
navBorder: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)',
|
|
||||||
cardBg: isDark ? 'rgba(255,255,255,0.03)' : '#fafafa',
|
|
||||||
sectionBg: isDark ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.01)',
|
|
||||||
pillBg: isDark ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.04)',
|
|
||||||
ctaR: '4px', secPad: '36px 24px', heroPad: '36px 24px 20px',
|
|
||||||
subColor: isDark ? 'rgba(255,255,255,0.40)' : '#9ca3af'
|
|
||||||
},
|
|
||||||
warm: {
|
|
||||||
r: 14, cardR: 14, navR: 8,
|
|
||||||
shadow: '0 2px 12px rgba(0,0,0,0.08)', cardShadow: '0 2px 8px rgba(0,0,0,0.06)',
|
|
||||||
headingWeight: 700, headingTracking: '-0.01em',
|
|
||||||
border: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.05)',
|
|
||||||
navBorder: isDark ? 'rgba(255,255,255,0.07)' : 'rgba(0,0,0,0.06)',
|
|
||||||
cardBg: isDark ? 'rgba(255,255,255,0.05)' : hexRgbaW(accent,0.04),
|
|
||||||
sectionBg: isDark ? 'rgba(255,255,255,0.03)' : hexRgbaW(accent,0.05),
|
|
||||||
pillBg: isDark ? 'rgba(255,255,255,0.08)' : hexRgbaW(accent,0.08),
|
|
||||||
ctaR: '14px', secPad: '28px 24px', heroPad: '30px 24px 18px',
|
|
||||||
subColor: sub
|
|
||||||
}
|
|
||||||
}[STYLE.id] || {
|
|
||||||
r:8, cardR:8, navR:6, shadow:'none', cardShadow:'none',
|
|
||||||
headingWeight:700, headingTracking:'-0.02em',
|
|
||||||
border: border, navBorder: border,
|
|
||||||
cardBg: isDark?'rgba(255,255,255,0.05)':'#fff',
|
|
||||||
sectionBg: isDark?'rgba(255,255,255,0.03)':'rgba(99,102,241,0.03)',
|
|
||||||
pillBg: isDark?'rgba(255,255,255,0.08)':hexRgbaW(accent,0.07),
|
|
||||||
ctaR:'8px', secPad:'28px 24px', heroPad:'26px 24px 16px',
|
|
||||||
subColor: sub
|
|
||||||
};
|
|
||||||
|
|
||||||
/* override shared vars with per-style ones */
|
|
||||||
border = S.border;
|
|
||||||
sub = S.subColor;
|
|
||||||
|
|
||||||
var pillsHtml = '';
|
|
||||||
if (bullets.length) {
|
|
||||||
pillsHtml = '<div style="padding:0 24px 18px;display:flex;flex-wrap:wrap;gap:6px;justify-content:center;">';
|
|
||||||
bullets.forEach(function(b) {
|
|
||||||
pillsHtml += '<div style="background:' + S.pillBg + ';border-radius:20px;padding:3px 11px;font-size:10px;color:' + (isDark ? 'rgba(255,255,255,0.65)' : accent) + ';">' + b + '</div>';
|
|
||||||
});
|
|
||||||
pillsHtml += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Problem section
|
|
||||||
var problemHtml = (TOPICS.problem || TOPICS.audience)
|
|
||||||
? '<div style="padding:'+S.secPad+';background:' + S.sectionBg + ';border-top:1px solid ' + S.border + ';text-align:center;">'
|
|
||||||
+ '<div style="font-size:8.5px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:' + accent + ';margin-bottom:10px;">The Problem</div>'
|
|
||||||
+ '<div style="font-size:15px;font-weight:'+S.headingWeight+';color:' + tc + ';letter-spacing:'+S.headingTracking+';line-height:1.3;margin-bottom:8px;">Most founders waste months on the wrong things</div>'
|
|
||||||
+ '<div style="font-size:11px;color:' + sub + ';line-height:1.65;max-width:340px;margin-left:auto;margin-right:auto;">Building a product is hard. Knowing what to build, how to position it, and who to build it for shouldn\'t be. That\'s what we fix.</div>'
|
|
||||||
+ '</div>'
|
|
||||||
: '';
|
|
||||||
|
|
||||||
// Benefits section
|
|
||||||
var benefitItems = [
|
|
||||||
{icon:'⚡', title:'10× faster setup', body:'Skip the research rabbit hole — vibn structures everything for you.'},
|
|
||||||
{icon:'🎯', title:'Laser-focused', body:'Every decision is tied to your specific audience and problem.'},
|
|
||||||
{icon:'🚀', title:'Launch-ready output', body:'Get copy, architecture, and a website in one session.'}
|
|
||||||
];
|
|
||||||
var benefitCols = DEVICE === 'mobile' ? '1fr' : '1fr 1fr 1fr';
|
|
||||||
var benefitsHtml = '<div style="padding:'+S.secPad+';border-top:1px solid ' + S.border + ';">'
|
|
||||||
+ '<div style="font-size:8.5px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:' + accent + ';margin-bottom:14px;">Why it works</div>'
|
|
||||||
+ '<div style="display:grid;grid-template-columns:' + benefitCols + ';gap:10px;">';
|
|
||||||
benefitItems.forEach(function(b) {
|
|
||||||
benefitsHtml += '<div style="background:' + S.cardBg + ';border:1px solid ' + S.border + ';border-radius:' + S.cardR + 'px;padding:12px;box-shadow:' + S.cardShadow + ';">'
|
|
||||||
+ '<div style="font-size:16px;margin-bottom:6px;">' + b.icon + '</div>'
|
|
||||||
+ '<div style="font-size:10.5px;font-weight:'+S.headingWeight+';color:' + tc + ';margin-bottom:4px;">' + b.title + '</div>'
|
|
||||||
+ '<div style="font-size:9.5px;color:' + sub + ';line-height:1.5;">' + b.body + '</div>'
|
|
||||||
+ '</div>';
|
|
||||||
});
|
|
||||||
benefitsHtml += '</div></div>';
|
|
||||||
|
|
||||||
// Final CTA section
|
|
||||||
var ctaSectionHtml = '<div style="padding:36px 24px;text-align:center;background:' + accent + ';margin-top:0;">'
|
|
||||||
+ '<div style="font-size:17px;font-weight:'+S.headingWeight+';color:#fff;letter-spacing:'+S.headingTracking+';line-height:1.25;margin-bottom:8px;">Ready to stop planning<br>and start building?</div>'
|
|
||||||
+ '<div style="font-size:10.5px;color:rgba(255,255,255,0.75);margin-bottom:16px;">Join founders who shipped their MVP in days, not months.</div>'
|
|
||||||
+ '<div style="display:inline-block;background:#fff;color:' + accent + ';font-size:11px;font-weight:700;padding:9px 22px;border-radius:' + S.ctaR + ';">' + copy.cta + '</div>'
|
|
||||||
+ '</div>';
|
|
||||||
|
|
||||||
var isAppDark = document.documentElement.dataset.theme === 'dark';
|
|
||||||
var phoneGlow = isAppDark
|
|
||||||
? '0 8px 40px rgba(0,0,0,0.65),0 0 0 1px rgba(255,255,255,0.18),0 0 32px rgba(255,255,255,0.12),0 0 64px rgba(255,255,255,0.06)'
|
|
||||||
: '0 8px 32px rgba(0,0,0,0.15)';
|
|
||||||
var mobileWrapOpen = DEVICE === 'mobile'
|
|
||||||
? '<div style="background:' + (isDark ? '#111827' : '#f0f0f8') + ';padding:20px 0;min-height:100%;"><div style="background:' + bg + ';max-width:375px;margin:0 auto;border-radius:' + S.r*2 + 'px;overflow:hidden;box-shadow:' + phoneGlow + ';font-family:\'Plus Jakarta Sans\',sans-serif;">'
|
|
||||||
: '';
|
|
||||||
var mobileWrapClose = DEVICE === 'mobile' ? '</div></div>' : '';
|
|
||||||
|
|
||||||
var html = (DEVICE === 'mobile' ? mobileWrapOpen : '<div style="background:' + bg + ';font-family:\'Plus Jakarta Sans\',sans-serif;">')
|
|
||||||
+ '<div style="padding:9px 16px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid ' + S.navBorder + ';box-shadow:' + S.shadow + ';">'
|
|
||||||
+ '<span style="font-size:12px;font-weight:'+S.headingWeight+';color:' + tc + ';">YourApp</span>'
|
|
||||||
+ '<div style="display:flex;gap:12px;"><span style="font-size:9.5px;color:' + sub + ';">Product</span><span style="font-size:9.5px;color:' + sub + ';">Pricing</span></div>'
|
|
||||||
+ '<div style="background:' + accent + ';color:#fff;font-size:9.5px;font-weight:700;padding:4px 10px;border-radius:' + S.ctaR + ';">' + copy.cta + '</div>'
|
|
||||||
+ '</div>'
|
|
||||||
+ '<div style="padding:'+S.heroPad+';text-align:center;">'
|
|
||||||
+ '<div style="font-size:20px;font-weight:'+S.headingWeight+';color:' + tc + ';letter-spacing:'+S.headingTracking+';line-height:1.2;margin-bottom:9px;">' + copy.h + '</div>'
|
|
||||||
+ '<div style="font-size:11.5px;color:' + sub + ';line-height:1.65;margin-bottom:16px;max-width:300px;margin-left:auto;margin-right:auto;">' + copy.s + '</div>'
|
|
||||||
+ '<div style="display:inline-flex;gap:8px;justify-content:center;">'
|
|
||||||
+ '<div style="background:' + accent + ';color:#fff;font-size:11px;font-weight:700;padding:8px 18px;border-radius:' + S.ctaR + ';box-shadow:' + S.shadow + ';">' + copy.cta + '</div>'
|
|
||||||
+ '<div style="background:transparent;color:' + sub + ';font-size:11px;padding:8px 14px;border-radius:' + S.ctaR + ';border:1px solid ' + S.border + ';">See how it works</div>'
|
|
||||||
+ '</div></div>'
|
|
||||||
+ pillsHtml
|
|
||||||
+ problemHtml
|
|
||||||
+ benefitsHtml
|
|
||||||
+ ctaSectionHtml
|
|
||||||
+ (DEVICE === 'mobile' ? mobileWrapClose : '</div>');
|
|
||||||
|
|
||||||
var el = document.getElementById('live-preview');
|
|
||||||
el.style.transition = 'opacity 0.25s';
|
|
||||||
el.style.opacity = '0';
|
|
||||||
setTimeout(function() { el.innerHTML = html; el.style.opacity = '1'; }, 150);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderCopy() {
|
|
||||||
var copy = getCopy();
|
|
||||||
var bullets = getBullets();
|
|
||||||
|
|
||||||
function fade(id, fn) {
|
|
||||||
var el = document.getElementById(id);
|
|
||||||
el.style.transition = 'opacity 0.2s';
|
|
||||||
el.style.opacity = '0';
|
|
||||||
setTimeout(function() { fn(el); el.style.opacity = '1'; }, 130);
|
|
||||||
}
|
|
||||||
|
|
||||||
fade('copy-headline', function(el) { el.textContent = copy.h; });
|
|
||||||
fade('copy-sub', function(el) { el.textContent = copy.s; });
|
|
||||||
fade('copy-cta', function(el) { el.textContent = copy.cta; });
|
|
||||||
fade('copy-bullets', function(el) {
|
|
||||||
el.innerHTML = bullets.map(function(b) {
|
|
||||||
return '<div style="display:flex;align-items:flex-start;gap:6px;font-size:11.5px;color:#6b7280;"><span style="color:#6366F1;flex-shrink:0;font-size:11px;">✓</span><span>' + b + '</span></div>';
|
|
||||||
}).join('');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update right panel topics
|
|
||||||
var rpTopics = document.getElementById('rp-topics-list');
|
|
||||||
if (rpTopics) {
|
|
||||||
rpTopics.innerHTML = Object.keys(TOPICS).filter(function(k) { return TOPICS[k]; }).map(function(k) {
|
|
||||||
return '<div class="deliverable-row"><div style="width:5px;height:5px;border-radius:50%;background:#6366F1;flex-shrink:0;"></div>' + TOPIC_RP_LABELS[k] + '</div>';
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Right-panel lookup tables ── */
|
|
||||||
var RP_TONE_ADJ = {Friendly:'Approachable', Balanced:'Measured', Professional:'Authoritative'};
|
|
||||||
var RP_STYLE_ADJ = {Casual:'conversational and informal', Balanced:'clear and well-crafted', Precise:'precise and data-led'};
|
|
||||||
var RP_PERS_ADJ = {Warm:'a warm, human feel', Steady:'a steady, reliable character', Direct:'a bold, direct edge'};
|
|
||||||
|
|
||||||
/* "Website experience" — bullet 1: visual style, bullet 2: writing style, bullet 3: tone */
|
|
||||||
var RP_EXP_VSTYLE = {
|
|
||||||
editorial: 'Strong visual hierarchy — bold headlines, designed to stop the scroll',
|
|
||||||
startup: 'Clean, conversion-first layout with benefit-led sections and clear CTAs',
|
|
||||||
minimal: 'Generous whitespace — nothing wasted, content takes centre stage',
|
|
||||||
warm: 'Approachable visuals and soft forms with trust signals woven in naturally'
|
|
||||||
};
|
|
||||||
var RP_EXP_WRITINGSTYLE = {
|
|
||||||
Casual: 'Copy that feels natural and conversational — no jargon, no fluff',
|
|
||||||
Balanced: 'Clear, well-crafted messaging that informs and builds confidence',
|
|
||||||
Precise: 'Data-led, specific language with measurable claims and no vagueness'
|
|
||||||
};
|
|
||||||
var RP_EXP_TONE = {
|
|
||||||
Friendly: 'Designed to welcome and reassure — visitors feel at ease immediately',
|
|
||||||
Balanced: 'Positioned to engage and convert — approachable and credible in equal measure',
|
|
||||||
Professional: 'Built to establish authority and attract qualified, high-intent leads'
|
|
||||||
};
|
|
||||||
|
|
||||||
/* "How users will perceive it" — bullet 1: visual style, bullet 2: tone, bullet 3: personality quote */
|
|
||||||
var RP_PERC_VSTYLE = {
|
|
||||||
editorial: 'Bold and self-assured',
|
|
||||||
startup: 'Modern and credible',
|
|
||||||
minimal: 'Calm and considered',
|
|
||||||
warm: 'Genuine and human'
|
|
||||||
};
|
|
||||||
var RP_PERC_TONE = {
|
|
||||||
Friendly: 'Inviting',
|
|
||||||
Balanced: 'Trustworthy',
|
|
||||||
Professional: 'Authoritative'
|
|
||||||
};
|
|
||||||
var RP_PERC_PERS = {
|
|
||||||
Warm: '\u201cThey actually get my problem\u201d',
|
|
||||||
Steady: '\u201cThese people know what they\u2019re doing\u201d',
|
|
||||||
Direct: '\u201cI want to try this right now\u201d'
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderRightPanel() {
|
|
||||||
var dot = '<div style="width:5px;height:5px;border-radius:50%;background:#6366F1;flex-shrink:0;"></div>';
|
|
||||||
|
|
||||||
// 1. Brand voice sentence
|
|
||||||
var vEl = document.getElementById('rp-voice-sentence');
|
|
||||||
if (vEl) {
|
|
||||||
vEl.textContent =
|
|
||||||
(RP_TONE_ADJ[VOICE.tone] || VOICE.tone) + ', ' +
|
|
||||||
(RP_STYLE_ADJ[VOICE.style]|| VOICE.style) + ' \u2014 with ' +
|
|
||||||
(RP_PERS_ADJ[VOICE.pers] || VOICE.pers) + '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Website experience — reflects website style + writing style + tone
|
|
||||||
var expEl = document.getElementById('rp-experience');
|
|
||||||
if (expEl) {
|
|
||||||
expEl.innerHTML = [
|
|
||||||
RP_EXP_VSTYLE[STYLE.id] || '',
|
|
||||||
RP_EXP_WRITINGSTYLE[VOICE.style] || '',
|
|
||||||
RP_EXP_TONE[VOICE.tone] || ''
|
|
||||||
].map(function(b) {
|
|
||||||
return '<div class="deliverable-row">' + dot + b + '</div>';
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. How users will perceive it — reflects website style + tone + personality
|
|
||||||
var percEl = document.getElementById('rp-perception');
|
|
||||||
if (percEl) {
|
|
||||||
percEl.innerHTML = [
|
|
||||||
RP_PERC_VSTYLE[STYLE.id] || '',
|
|
||||||
RP_PERC_TONE[VOICE.tone] || '',
|
|
||||||
RP_PERC_PERS[VOICE.pers] || ''
|
|
||||||
].map(function(b) {
|
|
||||||
return '<div class="deliverable-row">' + dot + b + '</div>';
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderAll() {
|
|
||||||
renderPreview();
|
|
||||||
renderCopy();
|
|
||||||
renderRightPanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── AI Decide ── */
|
|
||||||
function aiDecide() {
|
|
||||||
// Balanced tone, Balanced style, Warm personality
|
|
||||||
document.getElementById('pills-tone').querySelectorAll('.vpill').forEach(function(p, i) { p.classList.toggle('active', i === 1); });
|
|
||||||
VOICE.tone = 'Balanced'; document.getElementById('rp-tone').textContent = 'Balanced';
|
|
||||||
|
|
||||||
document.getElementById('pills-style').querySelectorAll('.vpill').forEach(function(p, i) { p.classList.toggle('active', i === 1); });
|
|
||||||
VOICE.style = 'Balanced'; document.getElementById('rp-style').textContent = 'Balanced';
|
|
||||||
|
|
||||||
document.getElementById('pills-pers').querySelectorAll('.vpill').forEach(function(p, i) { p.classList.toggle('active', i === 0); });
|
|
||||||
VOICE.pers = 'Warm'; document.getElementById('rp-pers').textContent = 'Warm';
|
|
||||||
|
|
||||||
// Topics: problem + audience + benefits
|
|
||||||
var aiTopics = {problem:true, audience:true, timing:false, benefits:true, comparison:false};
|
|
||||||
document.querySelectorAll('.tchip').forEach(function(c) {
|
|
||||||
var m = c.getAttribute('onclick').match(/'(\w+)'\)/);
|
|
||||||
if (m) { var k = m[1]; c.classList.toggle('active', !!aiTopics[k]); TOPICS[k] = !!aiTopics[k]; }
|
|
||||||
});
|
|
||||||
|
|
||||||
// Startup style (also calls renderAll internally)
|
|
||||||
setStyle('startup', '#f8fafc', '#0ea5e9', 'Startup energy', 'Clear, conversion-focused');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Theme ── */
|
|
||||||
function saveAndExit() { window.location.href = '03_dashboard.html'; }
|
|
||||||
function toggleTheme() {
|
|
||||||
const html = document.documentElement;
|
|
||||||
const isDark = html.dataset.theme === 'dark';
|
|
||||||
html.dataset.theme = isDark ? '' : 'dark';
|
|
||||||
document.getElementById('dark-toggle').textContent = isDark ? '🌙 Dark mode' : '☀️ Light mode';
|
|
||||||
localStorage.setItem('vibn-theme', isDark ? '' : 'dark');
|
|
||||||
}
|
|
||||||
(function() {
|
|
||||||
const saved = localStorage.getItem('vibn-theme');
|
|
||||||
if (saved === 'dark') {
|
|
||||||
document.documentElement.dataset.theme = 'dark';
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const btn = document.getElementById('dark-toggle');
|
|
||||||
if (btn) btn.textContent = '☀️ Light mode';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
try {
|
|
||||||
var name = localStorage.getItem('vibn_project_name') || 'My project';
|
|
||||||
var el = document.getElementById('sidebar-project-name');
|
|
||||||
el.textContent = name; el.style.display = 'block';
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
// Sync device mode from Architect frontend selection
|
|
||||||
try {
|
|
||||||
var frontendTip = localStorage.getItem('vibn_frontend');
|
|
||||||
if (frontendTip === 'Optimised for phones, still works on desktop') {
|
|
||||||
DEVICE = 'mobile';
|
|
||||||
document.querySelectorAll('[data-group="device"]').forEach(function(b) {
|
|
||||||
b.classList.toggle('selected', b.getAttribute('data-tip') === frontendTip);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch(e) {}
|
|
||||||
|
|
||||||
renderAll();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body></html>
|
|
||||||
@@ -1,412 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<script>if(localStorage.getItem('vibn-theme')==='dark')document.documentElement.dataset.theme='dark';</script>
|
|
||||||
<title>vibn — Build</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
:root{
|
|
||||||
--ink:#1A1A1A;--ink2:#2c2c2a;--ink3:#6366F1;--mid:#6B7280;--muted:#888780;
|
|
||||||
--stone:#b4b2a9;--parch:#d3d1c7;--cream:#eef2ff;--paper:#F5F3FF;--white:#FFFFFF;--border:#e0e7ff;
|
|
||||||
--indigo:#6366F1;--indigo-dark:#4338CA;--indigo-deep:#2E2A5E;
|
|
||||||
--indigo-soft:rgba(99,102,241,0.08);--indigo-ring:rgba(99,102,241,0.12);--subtitle:#7171b7;
|
|
||||||
}
|
|
||||||
body{font-family:'Plus Jakarta Sans',sans-serif;background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);display:flex;flex-direction:column;height:100vh;overflow:hidden;}
|
|
||||||
.f{font-family:'Plus Jakarta Sans',sans-serif;}
|
|
||||||
.sidebar-phase{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;}
|
|
||||||
.sidebar-phase.active{background:#fafaff;}
|
|
||||||
.phase-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;}
|
|
||||||
@keyframes spin{to{transform:rotate(360deg);}}
|
|
||||||
.spinning{display:inline-block;animation:spin 1s linear infinite;}
|
|
||||||
|
|
||||||
/* ── Progress screen ── */
|
|
||||||
.grad-title{background:linear-gradient(135deg,#1A1A2E 0%,#2E2A5E 30%,#4338CA 65%,#6366F1 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
[data-theme="dark"] .grad-title{background:linear-gradient(135deg,#d0d0f0 0%,#9090cc 30%,#6C7CFF 65%,#A8B4FF 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;}
|
|
||||||
@keyframes grad-flow{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
|
|
||||||
.grad-anim{background:linear-gradient(270deg,#1A1A2E,#4338CA,#6366F1,#A5B4FC,#6366F1,#4338CA,#1A1A2E);background-size:400% 400%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:grad-flow 4s ease infinite;}
|
|
||||||
[data-theme="dark"] .grad-anim{background:linear-gradient(270deg,#4B42D8,#6C7CFF,#C7D2FE,#A8B4FF,#6C7CFF,#4B42D8);background-size:400% 400%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:grad-flow 4s ease infinite;}
|
|
||||||
@keyframes confetti-fall{0%{transform:translateY(-20px) rotate(0deg);opacity:1}80%{opacity:1}100%{transform:translateY(105vh) rotate(800deg);opacity:0}}
|
|
||||||
.step-num{width:24px;height:24px;border-radius:50%;background:linear-gradient(135deg,#4338CA,#818CF8);display:flex;align-items:center;justify-content:center;font-size:11px;color:#fff;font-weight:700;flex-shrink:0;}
|
|
||||||
.btn-gitea{background:transparent;color:var(--mid);border:1px solid var(--border);border-radius:11px;padding:14px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:500;cursor:pointer;transition:background 0.18s,border-color 0.18s,color 0.18s;}
|
|
||||||
.btn-gitea:hover{background:var(--cream);border-color:rgba(99,102,241,0.22);color:var(--indigo);}
|
|
||||||
[data-theme="dark"] .btn-gitea{border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .btn-gitea:hover{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;color:var(--dm-accent)!important;}
|
|
||||||
|
|
||||||
/* ── Launch section ── */
|
|
||||||
.launch-divider{border:none;border-top:1px solid var(--border);margin:28px 0;}
|
|
||||||
.launch-card{background:var(--white);border:1px solid var(--border);border-radius:16px;padding:28px 28px 24px;box-shadow:0 2px 12px rgba(99,102,241,0.06),0 0 0 1px rgba(255,255,255,0.60);}
|
|
||||||
[data-theme="dark"] .launch-card{background:rgba(255,255,255,0.07)!important;border-color:rgba(255,255,255,0.10)!important;box-shadow:0 0 0 1px rgba(255,255,255,0.12),0 0 28px rgba(255,255,255,0.07),0 4px 24px rgba(0,0,0,0.35)!important;}
|
|
||||||
.build-step{display:flex;align-items:center;gap:11px;padding:9px 0;border-bottom:1px solid var(--border);}
|
|
||||||
.build-step:last-child{border-bottom:none;}
|
|
||||||
.build-step-icon{width:24px;height:24px;border-radius:6px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:11px;color:var(--ink3);flex-shrink:0;opacity:0.8;transform:scale(0.95);}
|
|
||||||
.build-step > div > span{font-size:14px!important;}
|
|
||||||
.build-step > span{font-size:12px!important;color:var(--muted)!important;}
|
|
||||||
.build-cta{width:100%;background:linear-gradient(135deg,#2E2A5E 0%,#4338CA 55%,#6366F1 100%);color:#fff;border:none;border-radius:12px;padding:16px 24px;font-family:'Plus Jakarta Sans',sans-serif;font-size:15px;font-weight:700;cursor:pointer;letter-spacing:-0.01em;transition:box-shadow 0.2s,transform 0.2s;box-shadow:0 8px 24px rgba(99,102,241,0.25),0 2px 6px rgba(99,102,241,0.12);}
|
|
||||||
.build-cta:hover{transform:translateY(-1px);box-shadow:0 12px 32px rgba(99,102,241,0.40),0 0 0 4px rgba(99,102,241,0.10);}
|
|
||||||
.build-cta:active{transform:translateY(0);box-shadow:0 2px 10px rgba(99,102,241,0.22);}
|
|
||||||
.build-cta:disabled{opacity:0.75;cursor:not-allowed;transform:none;}
|
|
||||||
.includes-chip{display:inline-flex;align-items:center;font-size:11px;font-weight:500;color:var(--mid);background:var(--cream);border-radius:20px;padding:3px 9px;}
|
|
||||||
[data-theme="dark"] .launch-divider{border-top-color:rgba(255,255,255,0.06)!important;}
|
|
||||||
[data-theme="dark"] .build-cta{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;box-shadow:0 4px 22px rgba(108,124,255,0.38),inset 0 1px 0 rgba(255,255,255,0.12)!important;}
|
|
||||||
[data-theme="dark"] .build-cta:hover{box-shadow:0 8px 36px rgba(108,124,255,0.55),0 0 0 4px rgba(108,124,255,0.16),inset 0 1px 0 rgba(255,255,255,0.16)!important;}
|
|
||||||
[data-theme="dark"] .includes-chip{background:rgba(108,124,255,0.12)!important;color:var(--dm-text-3)!important;}
|
|
||||||
|
|
||||||
/* ── Dark mode tokens — exact match with architect / describe ── */
|
|
||||||
[data-theme="dark"]{--ink:#EEEEFF;--ink2:#B8B8D0;--ink3:#8484A8;--mid:#9898B8;--muted:#c2c2ee;--border:rgba(255,255,255,0.08);
|
|
||||||
--cream:rgba(108,124,255,0.14);--paper:#0A1120;--white:rgba(255,255,255,0.05);--stone:rgba(255,255,255,0.08);
|
|
||||||
--parch:rgba(255,255,255,0.06);--indigo:#6C7CFF;--indigo-dark:#6C7CFF;--indigo-deep:#4B42D8;--subtitle:#a8a8d7;--text:#d9d9ee;
|
|
||||||
--indigo-soft:rgba(108,124,255,0.14);--indigo-ring:rgba(108,124,255,0.22);
|
|
||||||
--dm-surf-sidebar:rgba(12,18,34,0.72);--dm-surf-topbar:rgba(12,18,34,0.58);--dm-surf-panel:rgba(12,18,34,0.58);
|
|
||||||
--dm-surf-card:rgba(255,255,255,0.07);--dm-border:rgba(255,255,255,0.08);
|
|
||||||
--dm-border-strong:rgba(255,255,255,0.14);--dm-accent:#6C7CFF;
|
|
||||||
--dm-accent-fill:rgba(108,124,255,0.14);--dm-accent-fill-mid:rgba(108,124,255,0.20);--dm-accent-border:rgba(108,124,255,0.55);
|
|
||||||
--dm-text-1:#EEEEFF;--dm-text-2:#B4B4CC;--dm-text-3:#d2d2ef;}
|
|
||||||
[data-theme="dark"] body{background:linear-gradient(to bottom,rgba(12, 18, 34, 0.58) 0%,rgba(60,120,255,0.10) 38%,transparent 62%),linear-gradient(to bottom,rgba(108,124,255,0.10),transparent 180px),radial-gradient(900px 520px at 14% -8%,rgba(108,124,255,0.24),transparent 62%),radial-gradient(760px 420px at 88% 0%,rgba(72,145,255,0.16),transparent 60%),linear-gradient(180deg,#18213B 0%,#101726 48%,#0A1120 100%);}[data-theme="dark"] #mock {background: #fff !important;}
|
|
||||||
|
|
||||||
/* ── Sidebar ── */
|
|
||||||
[data-theme="dark"] .sidebar-col{background:var(--dm-surf-sidebar)!important;border-right:1px solid var(--dm-border)!important;box-shadow:2px 0 28px rgba(0,0,0,0.60)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="border-top:1px solid #e5e7eb"]{border-top-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#e5e7eb"]{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#1a1a1a"]{color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#6b7280"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#444441"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="color:#9ca3af"]{color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#6366F1"]{background:var(--dm-accent)!important;color:#0F1424!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase.active{background:var(--dm-accent-fill)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-phase:not(.active):hover{background:rgba(255,255,255,0.08)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] #sidebar-project-name{color:var(--dm-text-3)!important;}
|
|
||||||
|
|
||||||
/* ── Main content ── */
|
|
||||||
[data-theme="dark"] #screen-review{color:var(--dm-text-1);}
|
|
||||||
/* Cards */
|
|
||||||
[data-theme="dark"] [style*="background:var(--white)"]{background:var(--dm-surf-card)!important;}
|
|
||||||
[data-theme="dark"] [style*="border:1px solid var(--border)"]{border-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] [style*="border-bottom:1px solid var(--border)"]{border-bottom-color:var(--dm-border)!important;}
|
|
||||||
[data-theme="dark"] [style*="border-right:1px solid var(--border)"]{border-right-color:var(--dm-border)!important;}
|
|
||||||
/* Infra note & icon boxes using --cream */
|
|
||||||
[data-theme="dark"] [style*="background:var(--cream)"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-border)!important;}
|
|
||||||
/* Progress active row in renderSteps */
|
|
||||||
[data-theme="dark"] #steps-list [style*="background:var(--cream)"]{background:var(--dm-accent-fill)!important;}
|
|
||||||
/* Step done-dot: background:var(--ink) → accent; checkmark span inside: color:var(--white) → white */
|
|
||||||
[data-theme="dark"] #steps-list [style*="background:var(--ink)"]{background:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] #steps-list [style*="color:var(--white)"]{color:#fff!important;}
|
|
||||||
/* Done-section numbered circles and their text */
|
|
||||||
[data-theme="dark"] #done-section [style*="background:var(--ink)"]{background:var(--dm-accent)!important;color:#fff!important;}
|
|
||||||
/* Done-section "Open my app" button */
|
|
||||||
[data-theme="dark"] #done-section a button{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;color:#fff!important;box-shadow:0 4px 22px rgba(108,124,255,0.38)!important;}
|
|
||||||
/* Secondary "View in Gitea" button */
|
|
||||||
[data-theme="dark"] #done-section [style*="background:var(--white)"][style*="color:var(--mid)"]{background:rgba(255,255,255,0.07)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
|
|
||||||
/* ── CTA build button ── */
|
|
||||||
[data-theme="dark"] button[onclick="startBuild()"]{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;color:#fff!important;box-shadow:0 4px 22px rgba(108,124,255,0.38),inset 0 1px 0 rgba(255,255,255,0.14)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="startBuild()"]:hover{box-shadow:0 6px 32px rgba(108,124,255,0.50),inset 0 1px 0 rgba(255,255,255,0.18)!important;}
|
|
||||||
|
|
||||||
/* ── Save & dark-toggle buttons ── */
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover{background:var(--dm-accent-fill-mid)!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"]:hover span{color:#fff!important;}
|
|
||||||
[data-theme="dark"] button[onclick="saveAndExit()"] span{color:var(--dm-accent)!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle{background:rgba(255,255,255,0.05)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
|
|
||||||
[data-theme="dark"] #dark-toggle:hover{background:rgba(255,255,255,0.10)!important;color:var(--dm-text-1)!important;}
|
|
||||||
[data-theme="dark"] .vibn-avatar{background:var(--dm-accent)!important;}
|
|
||||||
|
|
||||||
/* ── Scrollbar ── */
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar{width:6px;height:6px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-track{background:#0A1120;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb{background:rgba(255,255,255,0.12);border-radius:3px;}
|
|
||||||
[data-theme="dark"] ::-webkit-scrollbar-thumb:hover{background:rgba(108,124,255,0.45);}
|
|
||||||
[data-theme="dark"] *{scrollbar-color:rgba(255,255,255,0.12) #0A1120;scrollbar-width:thin;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div style="display:flex;height:100%;overflow:hidden;">
|
|
||||||
|
|
||||||
<!-- SIDEBAR -->
|
|
||||||
<div class="sidebar-col" style="width:200px;background:#ffffff;border-right:1px solid #e5e7eb;display:flex;flex-direction:column;padding:18px 12px;flex-shrink:0;">
|
|
||||||
<div style="padding:0 6px;margin-bottom:26px;">
|
|
||||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
|
|
||||||
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;"><span class="f" style="font-size:13px;font-weight:700;color:#FFFFFF;">V</span></div>
|
|
||||||
<span class="f" style="font-size:16px;font-weight:700;color:#1a1a1a;letter-spacing:-0.02em;">vibn</span>
|
|
||||||
</div>
|
|
||||||
<div id="sidebar-project-name" style="font-size:11px;font-weight:500;color:#9ca3af;padding-left:34px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none;"></div>
|
|
||||||
</div>
|
|
||||||
<div style="font-size:9.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;padding:0 6px;margin-bottom:8px;">MVP Setup</div>
|
|
||||||
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='05_describe.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Describe</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='06_architect.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Architect</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='07_design.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Design</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase" onclick="window.location.href='08_website.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">✓</div>
|
|
||||||
<div style="font-size:12.5px;color:#6b7280;">Website</div>
|
|
||||||
</div>
|
|
||||||
<div class="sidebar-phase active">
|
|
||||||
<div class="phase-dot" style="background:#6366F1;color:#ffffff;">▲</div>
|
|
||||||
<div><div style="font-size:12.5px;font-weight:600;color:#1a1a1a;">Build MVP</div><div style="font-size:10px;color:#9ca3af;">Review & launch</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="border-top:1px solid #e5e7eb;margin-top:14px;padding-top:12px;">
|
|
||||||
<button onclick="saveAndExit()" style="display:flex;align-items:center;justify-content:center;gap:7px;width:100%;background:#eef2ff;border:1px solid #e0e7ff;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:background 0.15s;" onmouseover="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#e0e7ff'" onmouseout="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#eef2ff'">
|
|
||||||
<span style="font-size:12px;font-weight:600;color:#6366F1;">Save & go to dashboard</span>
|
|
||||||
</button>
|
|
||||||
<button id="dark-toggle" onclick="toggleTheme()" style="margin-top:8px;display:flex;align-items:center;justify-content:center;width:100%;background:transparent;border:1px solid var(--border);border-radius:8px;padding:8px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--mid);transition:background 0.15s,border-color 0.15s;" onmouseover="this.style.borderColor='#6366F1';this.style.color='#6366F1';" onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--mid)';">🌙 Dark mode</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- MAIN -->
|
|
||||||
<div style="flex:1;overflow-y:auto;">
|
|
||||||
|
|
||||||
<!-- REVIEW SCREEN -->
|
|
||||||
<div id="screen-review" style="padding:28px 32px;max-width:680px;margin:0 auto;">
|
|
||||||
<div class="f" style="font-size:22px;font-weight:700;color:var(--ink);margin-bottom:6px;">Ready to build</div>
|
|
||||||
<p style="font-size:13.5px;color:var(--muted);margin-bottom:22px;">Review everything below. Once you hit Build, AI codes your full product and deploys it.</p>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:12px;overflow:hidden;margin-bottom:14px;">
|
|
||||||
<div style="padding:12px 18px;border-bottom:1px solid var(--border);"><span style="font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;">What's being built</span></div>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;">
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">⛓</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Sign up & login</div><div style="font-size:13px;font-weight:600;color:var(--ink);">Email + social login</div></div></div>
|
|
||||||
<div style="padding:13px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">$</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Payments</div><div style="font-size:13px;font-weight:600;color:var(--ink);">Subscription billing</div></div></div>
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">✉</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Email</div><div style="font-size:13px;font-weight:600;color:var(--ink);">Transactional + marketing</div></div></div>
|
|
||||||
<div style="padding:13px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">◧</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Product style</div><div style="font-size:13px;font-weight:600;color:var(--ink);">Clean & focused</div></div></div>
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">◉</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Website style</div><div style="font-size:13px;font-weight:600;color:var(--ink);">Startup energy</div></div></div>
|
|
||||||
<div style="padding:13px 18px;display:flex;align-items:center;gap:10px;"><div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;"> ≡</div><div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Campaign topics</div><div style="font-size:13px;font-weight:600;color:var(--ink);">3 topics ready</div></div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:12px;overflow:hidden;margin-bottom:14px;">
|
|
||||||
<div style="padding:12px 18px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;"><span style="font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;">Pages</span><span style="font-size:12px;color:var(--muted);">14 pages total</span></div>
|
|
||||||
<div style="padding:16px 18px;display:grid;grid-template-columns:repeat(4,1fr);gap:0;">
|
|
||||||
<div style="padding:0 14px 0 0;border-right:1px solid var(--border);margin-right:14px;"><div style="font-size:9.5px;font-weight:700;color:var(--subtitle);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:8px;">Public</div><div style="font-size:12.5px;color:var(--ink2);line-height:2.0;">Landing page<br>Pricing<br>About<br>Blog</div></div>
|
|
||||||
<div style="padding:0 14px 0 0;border-right:1px solid var(--border);margin-right:14px;"><div style="font-size:9.5px;font-weight:700;color:var(--subtitle);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:8px;">Auth</div><div style="font-size:12.5px;color:var(--ink2);line-height:2.0;">Sign up<br>Log in<br>Forgot password</div></div>
|
|
||||||
<div style="padding:0 14px 0 0;border-right:1px solid var(--border);margin-right:14px;"><div style="font-size:9.5px;font-weight:700;color:var(--subtitle);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:8px;">App</div><div style="font-size:12.5px;color:var(--ink2);line-height:2.0;">Dashboard<br>Onboarding<br>Settings</div></div>
|
|
||||||
<div><div style="font-size:9.5px;font-weight:700;color:var(--subtitle);text-transform:uppercase;letter-spacing:0.07em;margin-bottom:8px;">Payments</div><div style="font-size:12.5px;color:var(--ink2);line-height:2.0;">Checkout<br>Success<br>Manage subscription</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:12px;overflow:hidden;margin-bottom:14px;">
|
|
||||||
<div style="padding:12px 18px;border-bottom:1px solid var(--border);"><span style="font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;">Your design</span></div>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;">
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">◈</div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Feel</div><div id="build-feel" style="font-size:13px;font-weight:600;color:var(--ink);">Friendly</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div id="build-color-swatch" style="width:26px;height:26px;border-radius:50%;background:#6366F1;flex-shrink:0;box-shadow:0 0 0 3px var(--white),0 0 0 4px #e0e7ff;"></div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Accent</div><div id="build-color" style="font-size:13px;font-weight:600;color:var(--ink);">Indigo</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:13px 18px;display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">◇</div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Layout</div><div id="build-structure" style="font-size:13px;font-weight:600;color:var(--ink);">Clean</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:12px;overflow:hidden;margin-bottom:14px;">
|
|
||||||
<div style="padding:12px 18px;border-bottom:1px solid var(--border);"><span style="font-size:10.5px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.06em;">Your website</span></div>
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;">
|
|
||||||
<div style="padding:13px 18px;border-right:1px solid var(--border);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">✦</div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Voice</div><div id="build-voice" style="font-size:13px;font-weight:600;color:var(--ink);">Friendly · Balanced · Warm</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:13px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">⬡</div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Website style</div><div id="build-ws-style" style="font-size:13px;font-weight:600;color:var(--ink);">Editorial</div></div>
|
|
||||||
</div>
|
|
||||||
<div style="padding:13px 18px;grid-column:1/-1;display:flex;align-items:center;gap:10px;">
|
|
||||||
<div style="width:26px;height:26px;border-radius:7px;background:var(--cream);display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--ink3);flex-shrink:0;">◉</div>
|
|
||||||
<div><div style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;">Topics</div><div id="build-topics" style="font-size:13px;font-weight:600;color:var(--ink);">The problem · Who it's for · Why now</div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- ── Launch section ── -->
|
|
||||||
<hr class="launch-divider">
|
|
||||||
|
|
||||||
<div class="launch-card">
|
|
||||||
<div style="margin-bottom:24px;">
|
|
||||||
<div style="font-size:24px;font-weight:700;color:var(--ink);letter-spacing:-0.01em;margin-bottom:8px;">You're ready to build your product</div>
|
|
||||||
<p style="font-size:14px;color:var(--muted);line-height:1.5;max-width:520px;">Your app will be generated, your backend configured, and everything deployed to your infrastructure — fully automated, no code needed.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top:26px;margin-bottom:6px;">
|
|
||||||
<div style="font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:0.08em;margin-bottom:14px;opacity:0.7;">What happens next</div>
|
|
||||||
<div class="build-step">
|
|
||||||
<div class="build-step-icon">✦</div>
|
|
||||||
<div style="flex:1;"><span style="font-size:13px;font-weight:500;color:var(--ink);">Generate UI & all pages</span></div>
|
|
||||||
<span style="font-size:11px;color:var(--stone);">~30s</span>
|
|
||||||
</div>
|
|
||||||
<div class="build-step">
|
|
||||||
<div class="build-step-icon">⛁</div>
|
|
||||||
<div style="flex:1;"><span style="font-size:13px;font-weight:500;color:var(--ink);">Set up database & backend</span></div>
|
|
||||||
<span style="font-size:11px;color:var(--stone);">~45s</span>
|
|
||||||
</div>
|
|
||||||
<div class="build-step">
|
|
||||||
<div class="build-step-icon">⛓</div>
|
|
||||||
<div style="flex:1;"><span style="font-size:13px;font-weight:500;color:var(--ink);">Connect auth, payments & email</span></div>
|
|
||||||
<span style="font-size:11px;color:var(--stone);">~30s</span>
|
|
||||||
</div>
|
|
||||||
<div class="build-step">
|
|
||||||
<div class="build-step-icon">▲</div>
|
|
||||||
<div style="flex:1;"><span style="font-size:13px;font-weight:500;color:var(--ink);">Deploy your app live</span></div>
|
|
||||||
<span style="font-size:11px;color:var(--stone);">~20s</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p style="font-size:11.5px;color:var(--muted);margin-bottom:28px;">Takes ~2–4 minutes · All steps run in parallel</p>
|
|
||||||
|
|
||||||
<button id="build-cta-btn" class="build-cta" onclick="startBuild()">Build my product</button>
|
|
||||||
<p style="font-size:12.5px;color:var(--muted);text-align:center;margin-top:10px;margin-bottom:4px;opacity:0.85;">No code needed · You can edit everything after</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div style="text-align:center;padding-bottom:8px;">
|
|
||||||
<button onclick="window.history.back()" style="background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;font-size:12.5px;color:var(--muted);cursor:pointer;padding:6px 0;transition:color 0.15s;" onmouseover="this.style.color='var(--ink)'" onmouseout="this.style.color='var(--muted)'">← Go back and tweak choices</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- PROGRESS SCREEN -->
|
|
||||||
<div id="screen-progress" style="display:none;padding:32px;max-width:580px;margin:0 auto;width:100%;">
|
|
||||||
<div id="prog-header" style="text-align:center;margin-bottom:26px;">
|
|
||||||
<div class="f grad-anim" style="font-size:24px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">Building your product…</div>
|
|
||||||
<div id="step-counter" style="font-size:13.5px;color:var(--muted);">Step 0 of 12</div>
|
|
||||||
</div>
|
|
||||||
<div id="steps-list" style="background:var(--white);border:1px solid var(--border);border-radius:13px;overflow:hidden;margin-bottom:18px;"></div>
|
|
||||||
<div id="done-section" style="display:none;">
|
|
||||||
<div style="background:var(--white);border:1px solid var(--border);border-radius:12px;padding:18px 20px;margin-bottom:14px;">
|
|
||||||
<div class="grad-title" style="font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;margin-bottom:12px;">Your next 3 actions</div>
|
|
||||||
<div style="display:flex;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);"><div class="step-num">1</div><div><div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Open your live app</div><div style="font-size:12px;color:var(--muted);line-height:1.5;">Share the URL with 5 real people today.</div></div></div>
|
|
||||||
<div style="display:flex;gap:12px;padding:10px 0;border-bottom:1px solid var(--border);"><div class="step-num">2</div><div><div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Sign up as a user</div><div style="font-size:12px;color:var(--muted);line-height:1.5;">Go through your own onboarding. Fix anything confusing.</div></div></div>
|
|
||||||
<div style="display:flex;gap:12px;padding:10px 0;"><div class="step-num">3</div><div><div style="font-size:13px;font-weight:600;color:var(--ink);margin-bottom:2px;">Post your first topic</div><div style="font-size:12px;color:var(--muted);line-height:1.5;">AI has drafted your first content batch. Publish one today.</div></div></div>
|
|
||||||
</div>
|
|
||||||
<div style="display:flex;gap:10px;">
|
|
||||||
<a href="10_vibe_editor.html" style="flex:2;display:block;text-decoration:none;"><button class="build-cta" style="border-radius:11px;font-size:14px;">Open my app ↗</button></a>
|
|
||||||
<button class="btn-gitea" style="flex:1;">View in Gitea ↗</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener('DOMContentLoaded', function(){
|
|
||||||
try {
|
|
||||||
var name = localStorage.getItem('vibn_project_name') || 'My project';
|
|
||||||
var el = document.getElementById('sidebar-project-name');
|
|
||||||
el.textContent = name;
|
|
||||||
el.style.display = 'block';
|
|
||||||
} catch(e){}
|
|
||||||
|
|
||||||
/* ── Populate design summary ── */
|
|
||||||
try {
|
|
||||||
var ds = JSON.parse(localStorage.getItem('vibn_design') || 'null');
|
|
||||||
if(ds){
|
|
||||||
var feelLabels = {premium:'Premium SaaS', friendly:'Friendly & Approachable', minimal:'Minimal & Clean'};
|
|
||||||
var structLabels = {clean:'Clean', data:'Data-rich', bold:'Bold'};
|
|
||||||
document.getElementById('build-feel').textContent = feelLabels[ds.feel] || ds.feel;
|
|
||||||
document.getElementById('build-structure').textContent = structLabels[ds.structure] || ds.structure;
|
|
||||||
document.getElementById('build-color').textContent = ds.colorName || ds.color;
|
|
||||||
var swatch = document.getElementById('build-color-swatch');
|
|
||||||
swatch.style.background = ds.colorHex || '#6366F1';
|
|
||||||
swatch.style.boxShadow = '0 0 0 3px var(--white),0 0 0 4px '+(ds.colorHex ? ds.colorHex+'44' : '#e0e7ff');
|
|
||||||
}
|
|
||||||
} catch(e){}
|
|
||||||
|
|
||||||
/* ── Populate website summary ── */
|
|
||||||
try {
|
|
||||||
var ws = JSON.parse(localStorage.getItem('vibn_website') || 'null');
|
|
||||||
if(ws){
|
|
||||||
if(ws.voice){
|
|
||||||
document.getElementById('build-voice').textContent = ws.voice.tone + ' · ' + ws.voice.style + ' · ' + ws.voice.pers;
|
|
||||||
}
|
|
||||||
if(ws.styleLabel){
|
|
||||||
document.getElementById('build-ws-style').textContent = ws.styleLabel;
|
|
||||||
}
|
|
||||||
if(ws.topics && ws.topics.length){
|
|
||||||
document.getElementById('build-topics').textContent = ws.topics.join(' · ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch(e){}
|
|
||||||
});
|
|
||||||
var STEPS=[
|
|
||||||
{l:'Creating Gitea repository',d:'Setting up version control for your project'},
|
|
||||||
{l:'Scaffolding the app',d:'Next.js · TypeScript · Tailwind CSS'},
|
|
||||||
{l:'Setting up your database',d:'PostgreSQL + schema based on your product plan'},
|
|
||||||
{l:'Building sign up & login',d:'Email + Google + GitHub OAuth'},
|
|
||||||
{l:'Wiring payments',d:'Stripe checkout, webhooks, billing portal'},
|
|
||||||
{l:'Generating app pages',d:'Dashboard, settings, onboarding, invite flow'},
|
|
||||||
{l:'Applying your design',d:'Clean & focused theme applied across all pages'},
|
|
||||||
{l:'Building marketing website',d:'Startup energy style · SEO-ready'},
|
|
||||||
{l:'Setting up email',d:'Welcome, password reset, and marketing templates'},
|
|
||||||
{l:'Pushing to Gitea',d:'Full codebase committed and pushed'},
|
|
||||||
{l:'Deploying via Coolify',d:'Building Docker image, deploying to your servers'},
|
|
||||||
{l:'Running health checks',d:'Verifying all pages, auth, and payments are live'},
|
|
||||||
];
|
|
||||||
var cur=0, iv=null;
|
|
||||||
function startBuild(){
|
|
||||||
var btn = document.getElementById('build-cta-btn');
|
|
||||||
if(btn){ btn.disabled=true; btn.innerHTML='<span class="spinning" style="display:inline-block;margin-right:7px;font-size:12px;">◎</span>Building your product…'; }
|
|
||||||
setTimeout(function(){
|
|
||||||
document.getElementById('screen-review').style.display='none';
|
|
||||||
document.getElementById('screen-progress').style.display='block';
|
|
||||||
renderSteps();
|
|
||||||
iv=setInterval(function(){
|
|
||||||
cur++;
|
|
||||||
if(cur>=STEPS.length){
|
|
||||||
clearInterval(iv);
|
|
||||||
document.getElementById('prog-header').innerHTML='<div style="font-size:36px;margin-bottom:12px;">🚀</div><div class="f grad-title" style="font-size:24px;font-weight:700;letter-spacing:-0.03em;margin-bottom:6px;">Your MVP is live</div><div style="font-size:13.5px;color:var(--muted);">Deployed to Coolify · Pushed to Gitea · Ready to share</div>';
|
|
||||||
document.getElementById('done-section').style.display='block';
|
|
||||||
triggerConfetti();
|
|
||||||
} else {
|
|
||||||
document.getElementById('step-counter').textContent='Step '+cur+' of 12';
|
|
||||||
}
|
|
||||||
renderSteps();
|
|
||||||
},700);
|
|
||||||
},400);
|
|
||||||
}
|
|
||||||
function renderSteps(){
|
|
||||||
var list=document.getElementById('steps-list'); list.innerHTML='';
|
|
||||||
STEPS.forEach(function(s,i){
|
|
||||||
var done=i<cur, active=i===cur;
|
|
||||||
var div=document.createElement('div');
|
|
||||||
div.style.cssText='display:flex;align-items:center;gap:12px;padding:10px 15px;border-bottom:'+(i<STEPS.length-1?'1px solid var(--border)':'none')+';background:'+(active?'var(--cream)':'transparent')+';transition:background 0.3s;';
|
|
||||||
var dotBg=done?'linear-gradient(135deg,#2E2A5E,#6366F1)':active?'linear-gradient(135deg,#4338CA,#6C7CFF)':'var(--parch)';
|
|
||||||
var dotContent=done?'<span style="color:#fff;font-size:9px;font-weight:900;">✓</span>'
|
|
||||||
:active?'<span class="spinning" style="color:#fff;font-size:8px;">◎</span>':'';
|
|
||||||
div.innerHTML='<div style="width:20px;height:20px;border-radius:50%;background:'+dotBg+';display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all 0.3s;">'+dotContent+'</div>'
|
|
||||||
+'<div style="flex:1;"><div style="font-size:12.5px;font-weight:'+(active?'600':'400')+';color:'+(done?'var(--muted)':active?'var(--ink)':'var(--stone)')+';">'+s.l+'</div>'
|
|
||||||
+((done||active)?'<div style="font-size:11px;color:var(--mid);margin-top:1px;">'+s.d+'</div>':'')+'</div>';
|
|
||||||
list.appendChild(div);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function triggerConfetti(){
|
|
||||||
var wrap=document.createElement('div');
|
|
||||||
wrap.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999;overflow:hidden;';
|
|
||||||
document.body.appendChild(wrap);
|
|
||||||
var colors=['#6366F1','#818CF8','#4338CA','#A5B4FC','#C7D2FE','#FCD34D','#F472B6','#34D399','#60A5FA','#FBBF24'];
|
|
||||||
var shapes=['50%','3px','0'];
|
|
||||||
for(var i=0;i<110;i++){
|
|
||||||
var el=document.createElement('div');
|
|
||||||
var color=colors[Math.floor(Math.random()*colors.length)];
|
|
||||||
var size=Math.random()*9+4;
|
|
||||||
var left=Math.random()*100;
|
|
||||||
var delay=Math.random()*1.2;
|
|
||||||
var dur=Math.random()*2.5+2;
|
|
||||||
var br=shapes[Math.floor(Math.random()*shapes.length)];
|
|
||||||
var xDrift=(Math.random()-0.5)*200;
|
|
||||||
el.style.cssText='position:absolute;top:-12px;left:'+left+'%;width:'+size+'px;height:'+(size*(Math.random()*0.6+0.4))+'px;background:'+color+';border-radius:'+br+';animation:confetti-fall '+dur+'s '+delay+'s ease-in forwards;transform:translateX('+xDrift+'px) rotate('+(Math.random()*360)+'deg);';
|
|
||||||
wrap.appendChild(el);
|
|
||||||
}
|
|
||||||
setTimeout(function(){if(wrap.parentNode)wrap.parentNode.removeChild(wrap);},5000);
|
|
||||||
}
|
|
||||||
function saveAndExit(){window.location.href='03_dashboard.html';}
|
|
||||||
function toggleTheme(){const html=document.documentElement;const isDark=html.dataset.theme==='dark';html.dataset.theme=isDark?'':'dark';document.getElementById('dark-toggle').textContent=isDark?'🌙 Dark mode':'☀️ Light mode';localStorage.setItem('vibn-theme',isDark?'':'dark');}
|
|
||||||
(function(){const saved=localStorage.getItem('vibn-theme');if(saved==='dark'){document.documentElement.dataset.theme='dark';document.addEventListener('DOMContentLoaded',function(){const btn=document.getElementById('dark-toggle');if(btn)btn.textContent='☀️ Light mode';});}})();
|
|
||||||
</script>
|
|
||||||
</body></html>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# vibn — UX Screen Pack
|
|
||||||
|
|
||||||
Design system: **Ink & parchment** — Lora serif + Inter sans, no colour accent.
|
|
||||||
All HTML files open directly in any browser — no build step required.
|
|
||||||
|
|
||||||
## Complete screen inventory
|
|
||||||
|
|
||||||
| File | Screen | Interactive? |
|
|
||||||
|------|--------|-------------|
|
|
||||||
| `01_homepage.html` | Marketing homepage | Scroll |
|
|
||||||
| `02_signup.html` | Sign up (3 steps) | ✓ Click through steps, mode selection |
|
|
||||||
| `03_dashboard.html` | Projects dashboard | ✓ Projects / Clients / Invoices / Costs nav |
|
|
||||||
| `04_welcome.html` | Welcome phase | Static |
|
|
||||||
| `05_discover.html` | Discover phase | ✓ Live chat, PRD fills in |
|
|
||||||
| `06_architect.html` | Architect phase | ✓ Change modal per block |
|
|
||||||
| `07_design.html` | Design phase | ✓ Live style preview switching |
|
|
||||||
| `08_market.html` | Market phase | ✓ Voice sliders, Topics, Website style |
|
|
||||||
| `09_build.html` | Build phase | ✓ Review + animated 12-step pipeline |
|
|
||||||
| `10_vibe_editor.html` | Vibe editor (post-launch) | ✓ Chat, preview updates, deploy status |
|
|
||||||
| `vibn-website.jsx` | Marketing site | React component |
|
|
||||||
| `vibn-dashboard.jsx` | Dashboard + billing | React component |
|
|
||||||
| `00_design-tokens.css` | Design tokens | Reference |
|
|
||||||
|
|
||||||
## Full user flow
|
|
||||||
01 Homepage → 02 Sign up → 03 Dashboard → 04 Welcome →
|
|
||||||
05 Discover → 06 Architect → 07 Design → 08 Market →
|
|
||||||
09 Build → 10 Vibe editor
|
|
||||||
|
|
||||||
## Design tokens
|
|
||||||
|
|
||||||
| Token | Value | Usage |
|
|
||||||
|-------|-------|-------|
|
|
||||||
| `--ink` | `#1a1510` | Primary text, buttons |
|
|
||||||
| `--ink3` | `#444441` | Secondary text |
|
|
||||||
| `--mid` | `#5f5e5a` | Body text |
|
|
||||||
| `--muted` | `#888780` | Labels, captions |
|
|
||||||
| `--stone` | `#b4b2a9` | Disabled, hints |
|
|
||||||
| `--cream` | `#f1efe8` | Surface tint, hover |
|
|
||||||
| `--paper` | `#f7f4ee` | Page background |
|
|
||||||
| `--white` | `#fdfcfa` | Card backgrounds |
|
|
||||||
| `--border` | `#e8e2d9` | All borders |
|
|
||||||
|
|
||||||
Heading font: **Lora** (serif)
|
|
||||||
Body font: **Inter** (sans-serif)
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 41 KiB |
@@ -1,119 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="icon" href="favicon_clean.ico">
|
|
||||||
<title>Sign in – Google Accounts</title>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;600&family=Roboto:wght@400;500&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
*{box-sizing:border-box;margin:0;padding:0;}
|
|
||||||
body{font-family:'Roboto',sans-serif;background:#FFFFFF;display:flex;flex-direction:column;align-items:center;min-height:100vh;padding:40px 24px;}
|
|
||||||
|
|
||||||
.card{width:100%;max-width:400px;border:1px solid #DADCE0;border-radius:8px;padding:40px 40px 28px;display:flex;flex-direction:column;align-items:center;}
|
|
||||||
|
|
||||||
.google-logo{margin-bottom:24px;}
|
|
||||||
|
|
||||||
h1{font-family:'Google Sans',sans-serif;font-size:24px;font-weight:400;color:#202124;margin-bottom:8px;text-align:center;}
|
|
||||||
.subtitle{font-size:16px;color:#202124;margin-bottom:24px;text-align:center;}
|
|
||||||
|
|
||||||
/* Account tile */
|
|
||||||
.account-tile{width:100%;border:1px solid #DADCE0;border-radius:8px;padding:12px 16px;display:flex;align-items:center;gap:16px;cursor:pointer;transition:background 0.15s;margin-bottom:12px;}
|
|
||||||
.account-tile:hover{background:#F8F9FA;}
|
|
||||||
.avatar{width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#4285F4,#34A853);display:flex;align-items:center;justify-content:center;font-family:'Google Sans',sans-serif;font-size:16px;font-weight:500;color:#FFFFFF;flex-shrink:0;}
|
|
||||||
.account-info{flex:1;text-align:left;}
|
|
||||||
.account-name{font-family:'Google Sans',sans-serif;font-size:14px;font-weight:500;color:#202124;margin-bottom:2px;}
|
|
||||||
.account-email{font-size:13px;color:#5F6368;}
|
|
||||||
.chevron{color:#5F6368;}
|
|
||||||
|
|
||||||
/* Add account */
|
|
||||||
.add-account{width:100%;border:1px solid #DADCE0;border-radius:8px;padding:12px 16px;display:flex;align-items:center;gap:16px;cursor:pointer;transition:background 0.15s;margin-bottom:24px;}
|
|
||||||
.add-account:hover{background:#F8F9FA;}
|
|
||||||
.add-icon{width:40px;height:40px;border-radius:50%;background:#F1F3F4;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:20px;color:#5F6368;}
|
|
||||||
.add-label{font-size:14px;color:#202124;}
|
|
||||||
|
|
||||||
.divider{width:100%;height:1px;background:#E8EAED;margin-bottom:20px;}
|
|
||||||
|
|
||||||
/* Continue button */
|
|
||||||
.btn-continue{background:#1A73E8;color:#FFFFFF;border:none;border-radius:4px;padding:10px 24px;font-family:'Google Sans',sans-serif;font-size:14px;font-weight:500;cursor:pointer;transition:background 0.15s,box-shadow 0.15s;box-shadow:0 1px 2px rgba(0,0,0,0.2);}
|
|
||||||
.btn-continue:hover{background:#1765CC;box-shadow:0 2px 6px rgba(0,0,0,0.2);}
|
|
||||||
|
|
||||||
.footer{margin-top:auto;padding-top:24px;display:flex;gap:24px;justify-content:center;}
|
|
||||||
.footer a{font-size:12px;color:#5F6368;text-decoration:none;}
|
|
||||||
.footer a:hover{text-decoration:underline;}
|
|
||||||
|
|
||||||
/* Loading state */
|
|
||||||
.loading{display:none;flex-direction:column;align-items:center;gap:16px;margin-top:20px;}
|
|
||||||
.spinner{width:32px;height:32px;border:3px solid #E8EAED;border-top-color:#1A73E8;border-radius:50%;animation:spin 0.8s linear infinite;}
|
|
||||||
@keyframes spin{to{transform:rotate(360deg);}}
|
|
||||||
.loading-text{font-size:14px;color:#5F6368;}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
|
|
||||||
<!-- Google logo -->
|
|
||||||
<div class="google-logo">
|
|
||||||
<svg width="75" height="24" viewBox="0 0 75 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M30.5 12.3c0-.7-.1-1.4-.2-2h-9.6v3.8h5.5c-.2 1.3-1 2.4-2.1 3.1v2.6h3.4c2-1.8 3-4.5 3-7.5z" fill="#4285F4"/>
|
|
||||||
<path d="M20.7 19.9c2.7 0 5-.9 6.7-2.4l-3.4-2.6c-.9.6-2 1-3.3 1-2.6 0-4.7-1.7-5.5-4.1H11.7v2.7c1.7 3.4 5.2 5.4 9 5.4z" fill="#34A853"/>
|
|
||||||
<path d="M15.2 11.8c-.2-.6-.3-1.2-.3-1.8s.1-1.2.3-1.8V7.5H11.7C11 8.8 10.7 10.3 10.7 12s.3 3.2 1 4.5l3.5-2.7z" fill="#FBBC05"/>
|
|
||||||
<path d="M20.7 5.9c1.4 0 2.7.5 3.7 1.5l2.8-2.8C25.7 3 23.4 2 20.7 2c-3.8 0-7.3 2-9 5.4l3.5 2.7c.8-2.4 2.9-4.2 5.5-4.2z" fill="#EA4335"/>
|
|
||||||
<text x="36" y="17" font-family="'Product Sans',Roboto,sans-serif" font-size="16" fill="#5F6368">Google</text>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1>Sign in</h1>
|
|
||||||
<p class="subtitle">to continue to vibn</p>
|
|
||||||
|
|
||||||
<!-- Account selector (shown by default) -->
|
|
||||||
<div id="account-select" style="width:100%;">
|
|
||||||
<div class="account-tile" onclick="selectAccount()">
|
|
||||||
<div class="avatar">J</div>
|
|
||||||
<div class="account-info">
|
|
||||||
<div class="account-name">Jane Smith</div>
|
|
||||||
<div class="account-email">jane@gmail.com</div>
|
|
||||||
</div>
|
|
||||||
<svg class="chevron" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="add-account" onclick="selectAccount()">
|
|
||||||
<div class="add-icon">+</div>
|
|
||||||
<div class="add-label">Use another account</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="divider"></div>
|
|
||||||
|
|
||||||
<p style="font-size:12px;color:#5F6368;text-align:center;line-height:1.6;">To continue, Google will share your name, email address, and profile picture with vibn.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Loading state (shown after selection) -->
|
|
||||||
<div class="loading" id="loading">
|
|
||||||
<div class="spinner"></div>
|
|
||||||
<div class="loading-text">Signing you in…</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="footer">
|
|
||||||
<a href="#">Help</a>
|
|
||||||
<a href="#">Privacy</a>
|
|
||||||
<a href="#">Terms</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function selectAccount(){
|
|
||||||
// Show loading
|
|
||||||
document.getElementById('account-select').style.display='none';
|
|
||||||
document.getElementById('loading').style.display='flex';
|
|
||||||
|
|
||||||
// After brief delay, notify parent and close
|
|
||||||
setTimeout(function(){
|
|
||||||
if(window.opener){
|
|
||||||
window.opener.postMessage({type:'google-auth-success',name:'Jane Smith',email:'jane@gmail.com'},'*');
|
|
||||||
}
|
|
||||||
window.close();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,484 +0,0 @@
|
|||||||
// vibn — Projects Dashboard
|
|
||||||
// Restyled from original (DM Sans + purple/colour accents) → Ink & parchment
|
|
||||||
// Design: Lora serif + Inter sans, #1a1510 ink, #f7f4ee paper, no colour accent
|
|
||||||
// Usage: default export, no required props
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
border2:"#d3d1c7",
|
|
||||||
};
|
|
||||||
|
|
||||||
const F = { serif: "'Lora', Georgia, serif", sans: "'Inter', sans-serif" };
|
|
||||||
|
|
||||||
// ─── Shared primitives ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function StatusPill({ label, variant = "default" }) {
|
|
||||||
const styles = {
|
|
||||||
live: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
building: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
default: { bg: T.paper, text: T.muted, border: T.border },
|
|
||||||
invoiced: { bg: T.ink, text: T.paper, border: T.ink },
|
|
||||||
unbilled: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
scheduled: { bg: T.parch, text: T.ink2, border: T.border2 },
|
|
||||||
};
|
|
||||||
const s = styles[variant] || styles.default;
|
|
||||||
return (
|
|
||||||
<span style={{
|
|
||||||
fontFamily: F.sans, fontSize: 10.5, fontWeight: 600,
|
|
||||||
color: s.text, background: s.bg,
|
|
||||||
border: `1px solid ${s.border}`,
|
|
||||||
borderRadius: 5, padding: "2px 8px", whiteSpace: "nowrap",
|
|
||||||
}}>{label}</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function InkBtn({ children, onClick, small, outline }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} style={{
|
|
||||||
fontFamily: F.sans, fontWeight: 600,
|
|
||||||
fontSize: small ? 12 : 13.5,
|
|
||||||
padding: small ? "6px 14px" : "10px 22px",
|
|
||||||
background: outline ? "transparent" : T.ink,
|
|
||||||
color: outline ? T.ink3 : T.paper,
|
|
||||||
border: outline ? `1px solid ${T.border2}` : "none",
|
|
||||||
borderRadius: 8, cursor: "pointer",
|
|
||||||
}}>{children}</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Nav ───────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ screen, setScreen }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.white, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 32px", height: 60, display: "flex",
|
|
||||||
alignItems: "center", justifyContent: "space-between",
|
|
||||||
}}>
|
|
||||||
<div onClick={() => setScreen("projects")} style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 700, color: T.paper }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 18, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 22 }}>
|
|
||||||
{screen === "billing" && (
|
|
||||||
<span onClick={() => setScreen("projects")} style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>← All projects</span>
|
|
||||||
)}
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>Settings</span>
|
|
||||||
<div style={{ width: 30, height: 30, borderRadius: "50%", background: T.ink3, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.sans, fontSize: 11, color: T.paper, fontWeight: 700 }}>JD</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Data ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PROJECTS = [
|
|
||||||
{
|
|
||||||
id: "launchpad", label: "Launchpad", initial: "L",
|
|
||||||
type: "own", status: "live", url: "launchpad.vibn.app",
|
|
||||||
stats: { visitors: "2.4k", signups: 183, mrr: "$840" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "flowmatic", label: "Flowmatic", initial: "F",
|
|
||||||
type: "client", status: "live", url: "flowmatic.app",
|
|
||||||
client: "Acme Corp",
|
|
||||||
stats: { visitors: "890", signups: 54, mrr: "$210" },
|
|
||||||
costs: { total: 48.20, llm: 29.20, compute: 11.60, other: 7.40, billed: false },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "taskly", label: "Taskly", initial: "T",
|
|
||||||
type: "client", status: "building", url: null,
|
|
||||||
client: "Beta Labs", buildProgress: 60,
|
|
||||||
costs: { total: 12.40, llm: 9.20, compute: 3.20, other: 0, billed: false },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ACTIVITY = [
|
|
||||||
{ text: "Launchpad — Blog post published:", detail: '"How to launch faster with AI"', time: "2h ago" },
|
|
||||||
{ text: "Flowmatic — New signup:", detail: "marcus@email.com", time: "4h ago" },
|
|
||||||
{ text: "Taskly — Checkout page built and deployed", detail: "", time: "6h ago" },
|
|
||||||
{ text: "Launchpad — Newsletter #12", detail: "scheduled", time: "Yesterday" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BILLING_ROWS = [
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme Corp", llm: 29.20, compute: 11.60, other: 7.40, total: 48.20, billed: false },
|
|
||||||
{ label: "Taskly", initial: "T", client: "Beta Labs", llm: 9.20, compute: 3.20, other: 0, total: 12.40, billed: false },
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme · Feb", llm: 22.10, compute: 8.40, other: 4.20, total: 34.70, billed: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const COST_LOG = [
|
|
||||||
{ time: "2h ago", desc: "LLM: Homepage copy generation", project: "Flowmatic", cost: 0.82 },
|
|
||||||
{ time: "3h ago", desc: "LLM: Checkout page code", project: "Taskly", cost: 1.24 },
|
|
||||||
{ time: "5h ago", desc: "LLM: Weekly newsletter draft", project: "Flowmatic", cost: 0.43 },
|
|
||||||
{ time: "6h ago", desc: "Compute: Build pipeline run", project: "Taskly", cost: 0.18 },
|
|
||||||
{ time: "8h ago", desc: "LLM: Discover phase Q&A", project: "Flowmatic", cost: 0.31 },
|
|
||||||
{ time: "Yesterday", desc: "Email delivery · 240 recipients", project: "Flowmatic", cost: 0.96 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── Project card ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectCard({ project }) {
|
|
||||||
const isClient = project.type === "client";
|
|
||||||
const isBuilding = project.status === "building";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
|
|
||||||
{/* Header preview */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<div style={{ height: 100, background: T.cream, borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11.5, color: T.muted, fontWeight: 500, marginBottom: 10 }}>
|
|
||||||
Build phase · {project.buildProgress}% complete
|
|
||||||
</div>
|
|
||||||
<div style={{ width: 160, height: 4, background: T.parch, borderRadius: 3, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${project.buildProgress}%`, height: "100%", background: T.ink3, borderRadius: 3 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isClient && (
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.mid, background: T.white, border: `1px solid ${T.border}`, borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
Client
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ height: 100, background: T.ink, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ background: "rgba(247,244,238,0.1)", borderRadius: 8, width: "55%", padding: "10px 14px" }}>
|
|
||||||
<div style={{ height: 8, background: "rgba(247,244,238,0.6)", borderRadius: 3, width: "65%", marginBottom: 6 }} />
|
|
||||||
<div style={{ height: 5, background: "rgba(247,244,238,0.25)", borderRadius: 3, width: "85%" }} />
|
|
||||||
</div>
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: "rgba(247,244,238,0.6)", background: "rgba(247,244,238,0.1)", borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
{isClient ? "Client" : "My product"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ padding: "18px 20px" }}>
|
|
||||||
{/* Identity row */}
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 12, color: T.paper, fontWeight: 700 }}>
|
|
||||||
{project.initial}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>{project.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>
|
|
||||||
{isClient ? `${project.client} · ` : ""}
|
|
||||||
{project.url || "Setting up pages…"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label={isBuilding ? "Building" : "Live"} variant={isBuilding ? "building" : "live"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Cost strip — client + building */}
|
|
||||||
{isClient && project.costs && isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs so far</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label="Unbilled" variant="unbilled" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Cost strip — client + live */}
|
|
||||||
{isClient && project.costs && !isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs this month</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.mid, lineHeight: 1.7 }}>
|
|
||||||
LLM ${project.costs.llm.toFixed(2)}<br />
|
|
||||||
Compute ${project.costs.compute.toFixed(2)}
|
|
||||||
</div>
|
|
||||||
{!project.costs.billed && (
|
|
||||||
<button style={{ background: T.ink, border: "none", color: T.paper, borderRadius: 7, padding: "7px 13px", fontFamily: F.sans, fontSize: 11.5, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Bill →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Stats */}
|
|
||||||
{!isBuilding && project.stats && (
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8, marginBottom: 14 }}>
|
|
||||||
{[["visitors", project.stats.visitors], ["signups", project.stats.signups], ["MRR", project.stats.mrr]].map(([k, v]) => (
|
|
||||||
<div key={k} style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 16, fontWeight: 700, color: T.ink }}>{v}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, color: T.muted }}>{k}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<button style={{ width: "100%", background: T.ink, border: "none", color: T.paper, borderRadius: 8, padding: 10, fontFamily: F.sans, fontSize: 13, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Continue building →
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div style={{ display: "flex", gap: 6 }}>
|
|
||||||
{[["⬡", "Build"], ["◈", "Grow"]].map(([icon, label]) => (
|
|
||||||
<div key={label} style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 5, padding: "7px 10px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, cursor: "pointer" }}>
|
|
||||||
<span style={{ fontSize: 11 }}>{icon}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.ink3 }}>{label}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ padding: "7px 12px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, fontFamily: F.sans, fontSize: 11.5, color: T.ink3, cursor: "pointer" }}>
|
|
||||||
↗
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Projects screen ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectsScreen({ setScreen }) {
|
|
||||||
const totalUnbilled = PROJECTS
|
|
||||||
.filter(p => p.type === "client" && p.costs?.billed === false)
|
|
||||||
.reduce((s, p) => s + p.costs.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "36px 32px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 32 }}>
|
|
||||||
<div>
|
|
||||||
<h1 style={{ fontFamily: F.serif, fontSize: 26, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", marginBottom: 4 }}>Your projects</h1>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted }}>3 active · 1 building</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
|
||||||
{totalUnbilled > 0 && (
|
|
||||||
<button onClick={() => setScreen("billing")} style={{ fontFamily: F.sans, fontSize: 13, color: T.ink3, background: T.cream, border: `1px solid ${T.border}`, borderRadius: 8, padding: "9px 16px", cursor: "pointer" }}>
|
|
||||||
${totalUnbilled.toFixed(2)} unbilled →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<InkBtn>+ New project</InkBtn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 28 }}>
|
|
||||||
{PROJECTS.map(p => <ProjectCard key={p.id} project={p} />)}
|
|
||||||
|
|
||||||
{/* New project CTA card */}
|
|
||||||
<div
|
|
||||||
style={{ background: "transparent", border: `1px dashed ${T.parch}`, borderRadius: 14, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, padding: 40, cursor: "pointer", minHeight: 220 }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = T.cream}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
|
|
||||||
>
|
|
||||||
<div style={{ width: 42, height: 42, borderRadius: 10, border: `1px solid ${T.parch}`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 22, color: T.stone }}>+</div>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>New project</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12.5, color: T.muted }}>For yourself or a client</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Activity feed */}
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, padding: "20px 24px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Recent activity</div>
|
|
||||||
{ACTIVITY.map((a, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 0", borderBottom: i < ACTIVITY.length - 1 ? `1px solid ${T.border}` : "none" }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink3, flexShrink: 0 }} />
|
|
||||||
<div style={{ flex: 1, fontFamily: F.sans, fontSize: 13.5, color: T.ink }}>
|
|
||||||
{a.text}{" "}{a.detail && <span style={{ color: T.muted }}>{a.detail}</span>}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.stone, whiteSpace: "nowrap" }}>{a.time}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Billing screen ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function BillingScreen() {
|
|
||||||
const [tab, setTab] = useState("billing");
|
|
||||||
const unbilled = BILLING_ROWS.filter(r => !r.billed).reduce((s, r) => s + r.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "28px 32px" }}>
|
|
||||||
|
|
||||||
{/* Sub-tabs */}
|
|
||||||
<div style={{ display: "flex", borderBottom: `1px solid ${T.border}`, marginBottom: 28 }}>
|
|
||||||
{[["billing", "Client billing"], ["costs", "Cost tracker"]].map(([id, label]) => (
|
|
||||||
<button key={id} onClick={() => setTab(id)} style={{
|
|
||||||
padding: "10px 18px", border: "none", background: "transparent",
|
|
||||||
borderBottom: tab === id ? `2px solid ${T.ink}` : "2px solid transparent",
|
|
||||||
fontFamily: F.sans, fontSize: 13.5, cursor: "pointer",
|
|
||||||
color: tab === id ? T.ink : T.muted, fontWeight: tab === id ? 600 : 400,
|
|
||||||
}}>{label}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{tab === "billing" && <>
|
|
||||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 22 }}>
|
|
||||||
<div>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Client billing</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>All costs tracked and ready to invoice</p>
|
|
||||||
</div>
|
|
||||||
<InkBtn>Generate invoice</InkBtn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 10, marginBottom: 24 }}>
|
|
||||||
{[
|
|
||||||
{ label: "Total unbilled", value: `$${unbilled.toFixed(2)}` },
|
|
||||||
{ label: "LLM costs", value: "$38.40" },
|
|
||||||
{ label: "Compute", value: "$14.80" },
|
|
||||||
{ label: "Other", value: "$7.40" },
|
|
||||||
].map(c => (
|
|
||||||
<div key={c.label} style={{ background: T.cream, borderRadius: 10, padding: "14px 16px" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted, marginBottom: 5 }}>{c.label}</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink }}>{c.value}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Breakdown by client</span>
|
|
||||||
<select style={{ border: `1px solid ${T.border}`, borderRadius: 6, padding: "4px 10px", fontFamily: F.sans, fontSize: 12, color: T.muted, background: T.paper, outline: "none" }}>
|
|
||||||
<option>March 2026</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Project / Client", "LLM", "Compute", "Other", "Total", "Status"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{BILLING_ROWS.map((r, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "13px 20px", borderBottom: i < BILLING_ROWS.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center", opacity: r.billed ? 0.5 : 1 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 24, height: 24, background: T.ink, borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 10, color: T.paper, fontWeight: 700 }}>{r.initial}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>{r.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>{r.client}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.llm.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.compute.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.other.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.total.toFixed(2)}</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
|
||||||
<StatusPill label={r.billed ? "Invoiced" : "Unbilled"} variant={r.billed ? "invoiced" : "unbilled"} />
|
|
||||||
{!r.billed && (
|
|
||||||
<button style={{ background: "transparent", border: `1px solid ${T.border2}`, borderRadius: 5, padding: "3px 9px", fontFamily: F.sans, fontSize: 11, color: T.muted, cursor: "pointer" }}>Invoice</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
{tab === "costs" && <>
|
|
||||||
<div style={{ marginBottom: 22 }}>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Cost tracker</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>Every dollar spent, broken down by type and project</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 20 }}>
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>LLM usage</div>
|
|
||||||
{[
|
|
||||||
{ label: "Code generation", amount: 21.40, pct: 56 },
|
|
||||||
{ label: "Content & marketing", amount: 10.20, pct: 27 },
|
|
||||||
{ label: "Chat assist", amount: 6.80, pct: 18 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ marginBottom: 14 }}>
|
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ height: 4, background: T.cream, borderRadius: 2, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${r.pct}%`, height: "100%", background: T.ink, borderRadius: 2 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total LLM</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$38.40</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Infrastructure</div>
|
|
||||||
{[
|
|
||||||
{ label: "Hosting & compute", amount: 11.60 },
|
|
||||||
{ label: "Database", amount: 3.20 },
|
|
||||||
{ label: "Email delivery", amount: 4.20 },
|
|
||||||
{ label: "Domain & SSL", amount: 3.20 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "8px 11px", background: T.cream, borderRadius: 7, marginBottom: 7 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total infra</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$22.20</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Recent charges</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Time", "Description", "Project", "Cost"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{COST_LOG.map((row, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "11px 20px", borderBottom: i < COST_LOG.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>{row.time}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>{row.desc}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.mid }}>{row.project}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${row.cost.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Root ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Dashboard() {
|
|
||||||
const [screen, setScreen] = useState("projects");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.paper, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
button { font-family: inherit; cursor: pointer; }
|
|
||||||
input, select { font-family: inherit; }
|
|
||||||
::-webkit-scrollbar { width: 4px; }
|
|
||||||
::-webkit-scrollbar-thumb { background: ${T.parch}; border-radius: 4px; }
|
|
||||||
`}</style>
|
|
||||||
<Nav screen={screen} setScreen={setScreen} />
|
|
||||||
{screen === "projects" && <ProjectsScreen setScreen={setScreen} />}
|
|
||||||
{screen === "billing" && <BillingScreen />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
// vibn — Marketing Website
|
|
||||||
// Design: Ink & parchment — Lora serif + Inter sans, no colour accent
|
|
||||||
// Usage: <Website onGetStarted={fn} onLogin={fn} />
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
};
|
|
||||||
|
|
||||||
const F = { serif: "'Lora', Georgia, serif", sans: "'Inter', sans-serif" };
|
|
||||||
|
|
||||||
// ─── Primitives ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Eyebrow({ children }) {
|
|
||||||
return (
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.13em", textTransform: "uppercase", color: T.muted, marginBottom: 16 }}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function PrimaryBtn({ children, onClick, large }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} style={{
|
|
||||||
background: T.ink, color: T.paper, border: "none",
|
|
||||||
fontFamily: F.sans, fontWeight: 600,
|
|
||||||
fontSize: large ? 15 : 14,
|
|
||||||
padding: large ? "14px 34px" : "10px 24px",
|
|
||||||
borderRadius: 10, cursor: "pointer",
|
|
||||||
}}>{children}</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Nav ───────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ onGetStarted, onLogin }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.paper, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 48px", height: 60,
|
|
||||||
display: "flex", alignItems: "center", justifyContent: "space-between",
|
|
||||||
position: "sticky", top: 0, zIndex: 50,
|
|
||||||
}}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 30, height: 30, background: T.ink, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.paper }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", gap: 32 }}>
|
|
||||||
{["Product", "Pricing", "Stories", "Blog"].map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: F.sans, fontSize: 14, color: T.muted, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
||||||
<span onClick={onLogin} style={{ fontFamily: F.sans, fontSize: 14, color: T.muted, cursor: "pointer" }}>Log in</span>
|
|
||||||
<PrimaryBtn onClick={onGetStarted}>Get started free</PrimaryBtn>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Hero ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Hero({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "88px 48px 72px" }}>
|
|
||||||
<Eyebrow>For non-technical founders</Eyebrow>
|
|
||||||
<h1 style={{
|
|
||||||
fontFamily: F.serif, fontSize: 64, fontWeight: 700, color: T.ink,
|
|
||||||
letterSpacing: "-0.03em", lineHeight: 1.07, marginBottom: 28, maxWidth: 680,
|
|
||||||
}}>
|
|
||||||
You have the idea.<br />
|
|
||||||
We handle<br />
|
|
||||||
<em style={{ fontStyle: "italic", color: T.ink3 }}>everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 17.5, color: T.mid, lineHeight: 1.75, maxWidth: 480, marginBottom: 40 }}>
|
|
||||||
No backend. No DevOps. No marketing agency. Describe your idea and vibn
|
|
||||||
builds, deploys, and promotes it — automatically.
|
|
||||||
</p>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 18, marginBottom: 14 }}>
|
|
||||||
<PrimaryBtn onClick={onCta} large>Start free — no code needed</PrimaryBtn>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13.5, color: T.stone }}>★★★★★ 280 founders launched</span>
|
|
||||||
</div>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 12, color: T.stone }}>No credit card required · Free forever plan</p>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Quote band ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const QUOTES = [
|
|
||||||
{ q: "I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing.", by: "Alex K.", role: "Founder, Taskly" },
|
|
||||||
{ q: "I have zero coding experience. Three weeks in I have 300 paying users. That's entirely because of vibn.", by: "Marcus L.", role: "Founder, Flowmatic" },
|
|
||||||
{ q: "The marketing autopilot alone saved me ten hours a week. My blog runs itself. I just focus on my product.", by: "Sara R.", role: "Founder, Nudge" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function QuoteBand() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.ink, padding: "52px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 40 }}>
|
|
||||||
{QUOTES.map((q, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", gap: 20 }}>
|
|
||||||
<div style={{ width: 3, background: T.mid, borderRadius: 2, flexShrink: 0 }} />
|
|
||||||
<div>
|
|
||||||
<p style={{ fontFamily: F.serif, fontSize: 15, color: T.parch, lineHeight: 1.72, fontStyle: "italic", marginBottom: 12 }}>"{q.q}"</p>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.muted, fontWeight: 600 }}>— {q.by}, {q.role}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── How it works ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PHASES = [
|
|
||||||
{ n: "01", id: "Discover", title: "Define your idea", body: "Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon." },
|
|
||||||
{ n: "02", id: "Design", title: "Choose your style", body: "Pick a visual style and see your exact site and emails live before a single line of code is written." },
|
|
||||||
{ n: "03", id: "Build", title: "Your app, live", body: "AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English." },
|
|
||||||
{ n: "04", id: "Grow", title: "Market & automate", body: "AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on your users." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function HowItWorks() {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "80px 48px" }}>
|
|
||||||
<Eyebrow>How it works</Eyebrow>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 40, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", lineHeight: 1.15, marginBottom: 52, maxWidth: 460 }}>
|
|
||||||
Four phases.<br />One complete product.
|
|
||||||
</h2>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
{PHASES.map((p, i) => (
|
|
||||||
<div key={i} style={{
|
|
||||||
padding: "38px 42px", background: T.white,
|
|
||||||
borderRight: (i % 2 === 0) ? `1px solid ${T.border}` : "none",
|
|
||||||
borderBottom: (i < 2) ? `1px solid ${T.border}` : "none",
|
|
||||||
}}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: T.muted, marginBottom: 14 }}>
|
|
||||||
{p.n} — {p.id}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 21, fontWeight: 700, color: T.ink, marginBottom: 10 }}>{p.title}</div>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13.5, color: T.mid, lineHeight: 1.7 }}>{p.body}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Stats bar ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const STATS = [
|
|
||||||
{ n: "280+", label: "founders launched" },
|
|
||||||
{ n: "72h", label: "average time to first version" },
|
|
||||||
{ n: "4.9★", label: "average rating" },
|
|
||||||
{ n: "3×", label: "faster than hiring a developer" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function StatsBar() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.white, borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", padding: "0 48px", display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr" }}>
|
|
||||||
{STATS.map((s, i) => (
|
|
||||||
<div key={i} style={{ padding: "38px 0", paddingLeft: i > 0 ? 36 : 0, borderRight: i < 3 ? `1px solid ${T.border}` : "none" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 38, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", marginBottom: 6 }}>{s.n}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>{s.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Empathy section ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PAINS = [
|
|
||||||
{ title: "No more \"I need to hire a developer first\"", body: "vibn is your developer. Start building the moment you have an idea." },
|
|
||||||
{ title: "No more staring at a blank marketing calendar", body: "AI generates and publishes your content every single week." },
|
|
||||||
{ title: "No more \"I'll launch when it's ready\"", body: "Most founders ship their first version in under 72 hours." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function EmpathySection() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.cream, borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}`, padding: "76px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 68, alignItems: "center" }}>
|
|
||||||
<div>
|
|
||||||
<Eyebrow>Sound familiar?</Eyebrow>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 36, fontWeight: 700, color: T.ink, lineHeight: 1.18, marginBottom: 24, letterSpacing: "-0.02em" }}>
|
|
||||||
The idea is the hard part. Everything else shouldn't be.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 15, color: T.mid, lineHeight: 1.8, marginBottom: 20 }}>
|
|
||||||
You know exactly what you want to build and who it's for. But the moment you think
|
|
||||||
about servers, databases, deployment pipelines, SEO strategies — the whole thing stalls.
|
|
||||||
</p>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 15, color: T.mid, lineHeight: 1.8 }}>
|
|
||||||
vibn exists to remove all of that. Not abstract it —{" "}
|
|
||||||
<em style={{ fontFamily: F.serif, fontStyle: "italic" }}>remove it entirely.</em>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
|
|
||||||
{PAINS.map((p, i) => (
|
|
||||||
<div key={i} style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px", display: "flex", gap: 14, alignItems: "flex-start" }}>
|
|
||||||
<div style={{ width: 20, height: 20, borderRadius: "50%", border: `1.5px solid ${T.stone}`, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, marginTop: 2 }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink }} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>{p.title}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.muted, lineHeight: 1.6 }}>{p.body}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Final CTA ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function FinalCta({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 660, margin: "0 auto", padding: "92px 48px", textAlign: "center" }}>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 46, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", lineHeight: 1.1, marginBottom: 20 }}>
|
|
||||||
Your idea deserves to exist.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 16, color: T.mid, lineHeight: 1.75, marginBottom: 36 }}>
|
|
||||||
Don't let the backend be the reason it doesn't. Start today — free, no code, no credit card.
|
|
||||||
</p>
|
|
||||||
<PrimaryBtn onClick={onCta} large>Build my product — free</PrimaryBtn>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12.5, color: T.stone, marginTop: 16 }}>
|
|
||||||
Joins 280+ non-technical founders already live
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Footer ────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Footer() {
|
|
||||||
return (
|
|
||||||
<footer style={{ borderTop: `1px solid ${T.border}`, padding: "32px 48px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 16, fontWeight: 700, color: T.ink }}>vibn</span>
|
|
||||||
<div style={{ display: "flex", gap: 28 }}>
|
|
||||||
{["Product", "Pricing", "Stories", "Blog", "Privacy", "Terms"].map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: F.sans, fontSize: 13, color: T.stone, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, color: T.stone }}>© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Root export ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Website({ onGetStarted = () => {}, onLogin = () => {} }) {
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.paper, color: T.ink, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body { font-family: 'Inter', sans-serif; }
|
|
||||||
button { font-family: inherit; }
|
|
||||||
`}</style>
|
|
||||||
<Nav onGetStarted={onGetStarted} onLogin={onLogin} />
|
|
||||||
<Hero onCta={onGetStarted} />
|
|
||||||
<QuoteBand />
|
|
||||||
<HowItWorks />
|
|
||||||
<StatsBar />
|
|
||||||
<EmpathySection />
|
|
||||||
<FinalCta onCta={onGetStarted} />
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,484 +0,0 @@
|
|||||||
// vibn — Projects Dashboard
|
|
||||||
// Restyled from original (DM Sans + purple/colour accents) → Ink & parchment
|
|
||||||
// Design: Lora serif + Inter sans, #1a1510 ink, #f7f4ee paper, no colour accent
|
|
||||||
// Usage: default export, no required props
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
border2:"#d3d1c7",
|
|
||||||
};
|
|
||||||
|
|
||||||
const F = { serif: "'Lora', Georgia, serif", sans: "'Inter', sans-serif" };
|
|
||||||
|
|
||||||
// ─── Shared primitives ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function StatusPill({ label, variant = "default" }) {
|
|
||||||
const styles = {
|
|
||||||
live: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
building: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
default: { bg: T.paper, text: T.muted, border: T.border },
|
|
||||||
invoiced: { bg: T.ink, text: T.paper, border: T.ink },
|
|
||||||
unbilled: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
scheduled: { bg: T.parch, text: T.ink2, border: T.border2 },
|
|
||||||
};
|
|
||||||
const s = styles[variant] || styles.default;
|
|
||||||
return (
|
|
||||||
<span style={{
|
|
||||||
fontFamily: F.sans, fontSize: 10.5, fontWeight: 600,
|
|
||||||
color: s.text, background: s.bg,
|
|
||||||
border: `1px solid ${s.border}`,
|
|
||||||
borderRadius: 5, padding: "2px 8px", whiteSpace: "nowrap",
|
|
||||||
}}>{label}</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function InkBtn({ children, onClick, small, outline }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} style={{
|
|
||||||
fontFamily: F.sans, fontWeight: 600,
|
|
||||||
fontSize: small ? 12 : 13.5,
|
|
||||||
padding: small ? "6px 14px" : "10px 22px",
|
|
||||||
background: outline ? "transparent" : T.ink,
|
|
||||||
color: outline ? T.ink3 : T.paper,
|
|
||||||
border: outline ? `1px solid ${T.border2}` : "none",
|
|
||||||
borderRadius: 8, cursor: "pointer",
|
|
||||||
}}>{children}</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Nav ───────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ screen, setScreen }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.white, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 32px", height: 60, display: "flex",
|
|
||||||
alignItems: "center", justifyContent: "space-between",
|
|
||||||
}}>
|
|
||||||
<div onClick={() => setScreen("projects")} style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 700, color: T.paper }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 18, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 22 }}>
|
|
||||||
{screen === "billing" && (
|
|
||||||
<span onClick={() => setScreen("projects")} style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>← All projects</span>
|
|
||||||
)}
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>Settings</span>
|
|
||||||
<div style={{ width: 30, height: 30, borderRadius: "50%", background: T.ink3, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.sans, fontSize: 11, color: T.paper, fontWeight: 700 }}>JD</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Data ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PROJECTS = [
|
|
||||||
{
|
|
||||||
id: "launchpad", label: "Launchpad", initial: "L",
|
|
||||||
type: "own", status: "live", url: "launchpad.vibn.app",
|
|
||||||
stats: { visitors: "2.4k", signups: 183, mrr: "$840" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "flowmatic", label: "Flowmatic", initial: "F",
|
|
||||||
type: "client", status: "live", url: "flowmatic.app",
|
|
||||||
client: "Acme Corp",
|
|
||||||
stats: { visitors: "890", signups: 54, mrr: "$210" },
|
|
||||||
costs: { total: 48.20, llm: 29.20, compute: 11.60, other: 7.40, billed: false },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "taskly", label: "Taskly", initial: "T",
|
|
||||||
type: "client", status: "building", url: null,
|
|
||||||
client: "Beta Labs", buildProgress: 60,
|
|
||||||
costs: { total: 12.40, llm: 9.20, compute: 3.20, other: 0, billed: false },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ACTIVITY = [
|
|
||||||
{ text: "Launchpad — Blog post published:", detail: '"How to launch faster with AI"', time: "2h ago" },
|
|
||||||
{ text: "Flowmatic — New signup:", detail: "marcus@email.com", time: "4h ago" },
|
|
||||||
{ text: "Taskly — Checkout page built and deployed", detail: "", time: "6h ago" },
|
|
||||||
{ text: "Launchpad — Newsletter #12", detail: "scheduled", time: "Yesterday" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BILLING_ROWS = [
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme Corp", llm: 29.20, compute: 11.60, other: 7.40, total: 48.20, billed: false },
|
|
||||||
{ label: "Taskly", initial: "T", client: "Beta Labs", llm: 9.20, compute: 3.20, other: 0, total: 12.40, billed: false },
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme · Feb", llm: 22.10, compute: 8.40, other: 4.20, total: 34.70, billed: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const COST_LOG = [
|
|
||||||
{ time: "2h ago", desc: "LLM: Homepage copy generation", project: "Flowmatic", cost: 0.82 },
|
|
||||||
{ time: "3h ago", desc: "LLM: Checkout page code", project: "Taskly", cost: 1.24 },
|
|
||||||
{ time: "5h ago", desc: "LLM: Weekly newsletter draft", project: "Flowmatic", cost: 0.43 },
|
|
||||||
{ time: "6h ago", desc: "Compute: Build pipeline run", project: "Taskly", cost: 0.18 },
|
|
||||||
{ time: "8h ago", desc: "LLM: Discover phase Q&A", project: "Flowmatic", cost: 0.31 },
|
|
||||||
{ time: "Yesterday", desc: "Email delivery · 240 recipients", project: "Flowmatic", cost: 0.96 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── Project card ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectCard({ project }) {
|
|
||||||
const isClient = project.type === "client";
|
|
||||||
const isBuilding = project.status === "building";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
|
|
||||||
{/* Header preview */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<div style={{ height: 100, background: T.cream, borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11.5, color: T.muted, fontWeight: 500, marginBottom: 10 }}>
|
|
||||||
Build phase · {project.buildProgress}% complete
|
|
||||||
</div>
|
|
||||||
<div style={{ width: 160, height: 4, background: T.parch, borderRadius: 3, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${project.buildProgress}%`, height: "100%", background: T.ink3, borderRadius: 3 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isClient && (
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.mid, background: T.white, border: `1px solid ${T.border}`, borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
Client
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ height: 100, background: T.ink, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ background: "rgba(247,244,238,0.1)", borderRadius: 8, width: "55%", padding: "10px 14px" }}>
|
|
||||||
<div style={{ height: 8, background: "rgba(247,244,238,0.6)", borderRadius: 3, width: "65%", marginBottom: 6 }} />
|
|
||||||
<div style={{ height: 5, background: "rgba(247,244,238,0.25)", borderRadius: 3, width: "85%" }} />
|
|
||||||
</div>
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: "rgba(247,244,238,0.6)", background: "rgba(247,244,238,0.1)", borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
{isClient ? "Client" : "My product"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ padding: "18px 20px" }}>
|
|
||||||
{/* Identity row */}
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 12, color: T.paper, fontWeight: 700 }}>
|
|
||||||
{project.initial}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>{project.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>
|
|
||||||
{isClient ? `${project.client} · ` : ""}
|
|
||||||
{project.url || "Setting up pages…"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label={isBuilding ? "Building" : "Live"} variant={isBuilding ? "building" : "live"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Cost strip — client + building */}
|
|
||||||
{isClient && project.costs && isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs so far</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label="Unbilled" variant="unbilled" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Cost strip — client + live */}
|
|
||||||
{isClient && project.costs && !isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs this month</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.mid, lineHeight: 1.7 }}>
|
|
||||||
LLM ${project.costs.llm.toFixed(2)}<br />
|
|
||||||
Compute ${project.costs.compute.toFixed(2)}
|
|
||||||
</div>
|
|
||||||
{!project.costs.billed && (
|
|
||||||
<button style={{ background: T.ink, border: "none", color: T.paper, borderRadius: 7, padding: "7px 13px", fontFamily: F.sans, fontSize: 11.5, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Bill →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Stats */}
|
|
||||||
{!isBuilding && project.stats && (
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8, marginBottom: 14 }}>
|
|
||||||
{[["visitors", project.stats.visitors], ["signups", project.stats.signups], ["MRR", project.stats.mrr]].map(([k, v]) => (
|
|
||||||
<div key={k} style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 16, fontWeight: 700, color: T.ink }}>{v}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, color: T.muted }}>{k}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<button style={{ width: "100%", background: T.ink, border: "none", color: T.paper, borderRadius: 8, padding: 10, fontFamily: F.sans, fontSize: 13, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Continue building →
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div style={{ display: "flex", gap: 6 }}>
|
|
||||||
{[["⬡", "Build"], ["◈", "Grow"]].map(([icon, label]) => (
|
|
||||||
<div key={label} style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 5, padding: "7px 10px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, cursor: "pointer" }}>
|
|
||||||
<span style={{ fontSize: 11 }}>{icon}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.ink3 }}>{label}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ padding: "7px 12px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, fontFamily: F.sans, fontSize: 11.5, color: T.ink3, cursor: "pointer" }}>
|
|
||||||
↗
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Projects screen ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectsScreen({ setScreen }) {
|
|
||||||
const totalUnbilled = PROJECTS
|
|
||||||
.filter(p => p.type === "client" && p.costs?.billed === false)
|
|
||||||
.reduce((s, p) => s + p.costs.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "36px 32px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 32 }}>
|
|
||||||
<div>
|
|
||||||
<h1 style={{ fontFamily: F.serif, fontSize: 26, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", marginBottom: 4 }}>Your projects</h1>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted }}>3 active · 1 building</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
|
||||||
{totalUnbilled > 0 && (
|
|
||||||
<button onClick={() => setScreen("billing")} style={{ fontFamily: F.sans, fontSize: 13, color: T.ink3, background: T.cream, border: `1px solid ${T.border}`, borderRadius: 8, padding: "9px 16px", cursor: "pointer" }}>
|
|
||||||
${totalUnbilled.toFixed(2)} unbilled →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<InkBtn>+ New project</InkBtn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 28 }}>
|
|
||||||
{PROJECTS.map(p => <ProjectCard key={p.id} project={p} />)}
|
|
||||||
|
|
||||||
{/* New project CTA card */}
|
|
||||||
<div
|
|
||||||
style={{ background: "transparent", border: `1px dashed ${T.parch}`, borderRadius: 14, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, padding: 40, cursor: "pointer", minHeight: 220 }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = T.cream}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
|
|
||||||
>
|
|
||||||
<div style={{ width: 42, height: 42, borderRadius: 10, border: `1px solid ${T.parch}`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 22, color: T.stone }}>+</div>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>New project</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12.5, color: T.muted }}>For yourself or a client</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Activity feed */}
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, padding: "20px 24px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Recent activity</div>
|
|
||||||
{ACTIVITY.map((a, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 0", borderBottom: i < ACTIVITY.length - 1 ? `1px solid ${T.border}` : "none" }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink3, flexShrink: 0 }} />
|
|
||||||
<div style={{ flex: 1, fontFamily: F.sans, fontSize: 13.5, color: T.ink }}>
|
|
||||||
{a.text}{" "}{a.detail && <span style={{ color: T.muted }}>{a.detail}</span>}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.stone, whiteSpace: "nowrap" }}>{a.time}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Billing screen ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function BillingScreen() {
|
|
||||||
const [tab, setTab] = useState("billing");
|
|
||||||
const unbilled = BILLING_ROWS.filter(r => !r.billed).reduce((s, r) => s + r.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "28px 32px" }}>
|
|
||||||
|
|
||||||
{/* Sub-tabs */}
|
|
||||||
<div style={{ display: "flex", borderBottom: `1px solid ${T.border}`, marginBottom: 28 }}>
|
|
||||||
{[["billing", "Client billing"], ["costs", "Cost tracker"]].map(([id, label]) => (
|
|
||||||
<button key={id} onClick={() => setTab(id)} style={{
|
|
||||||
padding: "10px 18px", border: "none", background: "transparent",
|
|
||||||
borderBottom: tab === id ? `2px solid ${T.ink}` : "2px solid transparent",
|
|
||||||
fontFamily: F.sans, fontSize: 13.5, cursor: "pointer",
|
|
||||||
color: tab === id ? T.ink : T.muted, fontWeight: tab === id ? 600 : 400,
|
|
||||||
}}>{label}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{tab === "billing" && <>
|
|
||||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 22 }}>
|
|
||||||
<div>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Client billing</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>All costs tracked and ready to invoice</p>
|
|
||||||
</div>
|
|
||||||
<InkBtn>Generate invoice</InkBtn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 10, marginBottom: 24 }}>
|
|
||||||
{[
|
|
||||||
{ label: "Total unbilled", value: `$${unbilled.toFixed(2)}` },
|
|
||||||
{ label: "LLM costs", value: "$38.40" },
|
|
||||||
{ label: "Compute", value: "$14.80" },
|
|
||||||
{ label: "Other", value: "$7.40" },
|
|
||||||
].map(c => (
|
|
||||||
<div key={c.label} style={{ background: T.cream, borderRadius: 10, padding: "14px 16px" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted, marginBottom: 5 }}>{c.label}</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink }}>{c.value}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Breakdown by client</span>
|
|
||||||
<select style={{ border: `1px solid ${T.border}`, borderRadius: 6, padding: "4px 10px", fontFamily: F.sans, fontSize: 12, color: T.muted, background: T.paper, outline: "none" }}>
|
|
||||||
<option>March 2026</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Project / Client", "LLM", "Compute", "Other", "Total", "Status"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{BILLING_ROWS.map((r, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "13px 20px", borderBottom: i < BILLING_ROWS.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center", opacity: r.billed ? 0.5 : 1 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 24, height: 24, background: T.ink, borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 10, color: T.paper, fontWeight: 700 }}>{r.initial}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>{r.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>{r.client}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.llm.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.compute.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.other.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.total.toFixed(2)}</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
|
||||||
<StatusPill label={r.billed ? "Invoiced" : "Unbilled"} variant={r.billed ? "invoiced" : "unbilled"} />
|
|
||||||
{!r.billed && (
|
|
||||||
<button style={{ background: "transparent", border: `1px solid ${T.border2}`, borderRadius: 5, padding: "3px 9px", fontFamily: F.sans, fontSize: 11, color: T.muted, cursor: "pointer" }}>Invoice</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
{tab === "costs" && <>
|
|
||||||
<div style={{ marginBottom: 22 }}>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Cost tracker</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>Every dollar spent, broken down by type and project</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 20 }}>
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>LLM usage</div>
|
|
||||||
{[
|
|
||||||
{ label: "Code generation", amount: 21.40, pct: 56 },
|
|
||||||
{ label: "Content & marketing", amount: 10.20, pct: 27 },
|
|
||||||
{ label: "Chat assist", amount: 6.80, pct: 18 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ marginBottom: 14 }}>
|
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ height: 4, background: T.cream, borderRadius: 2, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${r.pct}%`, height: "100%", background: T.ink, borderRadius: 2 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total LLM</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$38.40</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Infrastructure</div>
|
|
||||||
{[
|
|
||||||
{ label: "Hosting & compute", amount: 11.60 },
|
|
||||||
{ label: "Database", amount: 3.20 },
|
|
||||||
{ label: "Email delivery", amount: 4.20 },
|
|
||||||
{ label: "Domain & SSL", amount: 3.20 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "8px 11px", background: T.cream, borderRadius: 7, marginBottom: 7 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total infra</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$22.20</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Recent charges</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Time", "Description", "Project", "Cost"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{COST_LOG.map((row, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "11px 20px", borderBottom: i < COST_LOG.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>{row.time}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>{row.desc}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.mid }}>{row.project}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${row.cost.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Root ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Dashboard() {
|
|
||||||
const [screen, setScreen] = useState("projects");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.paper, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
button { font-family: inherit; cursor: pointer; }
|
|
||||||
input, select { font-family: inherit; }
|
|
||||||
::-webkit-scrollbar { width: 4px; }
|
|
||||||
::-webkit-scrollbar-thumb { background: ${T.parch}; border-radius: 4px; }
|
|
||||||
`}</style>
|
|
||||||
<Nav screen={screen} setScreen={setScreen} />
|
|
||||||
{screen === "projects" && <ProjectsScreen setScreen={setScreen} />}
|
|
||||||
{screen === "billing" && <BillingScreen />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
// vibn — Marketing Website
|
|
||||||
// Design: Ink & parchment — Lora serif + Inter sans, no colour accent
|
|
||||||
// Usage: <Website onGetStarted={fn} onLogin={fn} />
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
};
|
|
||||||
|
|
||||||
const F = { serif: "'Lora', Georgia, serif", sans: "'Inter', sans-serif" };
|
|
||||||
|
|
||||||
// ─── Primitives ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Eyebrow({ children }) {
|
|
||||||
return (
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.13em", textTransform: "uppercase", color: T.muted, marginBottom: 16 }}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function PrimaryBtn({ children, onClick, large }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} style={{
|
|
||||||
background: T.ink, color: T.paper, border: "none",
|
|
||||||
fontFamily: F.sans, fontWeight: 600,
|
|
||||||
fontSize: large ? 15 : 14,
|
|
||||||
padding: large ? "14px 34px" : "10px 24px",
|
|
||||||
borderRadius: 10, cursor: "pointer",
|
|
||||||
}}>{children}</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Nav ───────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ onGetStarted, onLogin }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.paper, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 48px", height: 60,
|
|
||||||
display: "flex", alignItems: "center", justifyContent: "space-between",
|
|
||||||
position: "sticky", top: 0, zIndex: 50,
|
|
||||||
}}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 30, height: 30, background: T.ink, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.paper }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", gap: 32 }}>
|
|
||||||
{["Product", "Pricing", "Stories", "Blog"].map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: F.sans, fontSize: 14, color: T.muted, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
|
|
||||||
<span onClick={onLogin} style={{ fontFamily: F.sans, fontSize: 14, color: T.muted, cursor: "pointer" }}>Log in</span>
|
|
||||||
<PrimaryBtn onClick={onGetStarted}>Get started free</PrimaryBtn>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Hero ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Hero({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "88px 48px 72px" }}>
|
|
||||||
<Eyebrow>For non-technical founders</Eyebrow>
|
|
||||||
<h1 style={{
|
|
||||||
fontFamily: F.serif, fontSize: 64, fontWeight: 700, color: T.ink,
|
|
||||||
letterSpacing: "-0.03em", lineHeight: 1.07, marginBottom: 28, maxWidth: 680,
|
|
||||||
}}>
|
|
||||||
You have the idea.<br />
|
|
||||||
We handle<br />
|
|
||||||
<em style={{ fontStyle: "italic", color: T.ink3 }}>everything else.</em>
|
|
||||||
</h1>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 17.5, color: T.mid, lineHeight: 1.75, maxWidth: 480, marginBottom: 40 }}>
|
|
||||||
No backend. No DevOps. No marketing agency. Describe your idea and vibn
|
|
||||||
builds, deploys, and promotes it — automatically.
|
|
||||||
</p>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 18, marginBottom: 14 }}>
|
|
||||||
<PrimaryBtn onClick={onCta} large>Start free — no code needed</PrimaryBtn>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13.5, color: T.stone }}>★★★★★ 280 founders launched</span>
|
|
||||||
</div>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 12, color: T.stone }}>No credit card required · Free forever plan</p>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Quote band ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const QUOTES = [
|
|
||||||
{ q: "I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing.", by: "Alex K.", role: "Founder, Taskly" },
|
|
||||||
{ q: "I have zero coding experience. Three weeks in I have 300 paying users. That's entirely because of vibn.", by: "Marcus L.", role: "Founder, Flowmatic" },
|
|
||||||
{ q: "The marketing autopilot alone saved me ten hours a week. My blog runs itself. I just focus on my product.", by: "Sara R.", role: "Founder, Nudge" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function QuoteBand() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.ink, padding: "52px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 40 }}>
|
|
||||||
{QUOTES.map((q, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", gap: 20 }}>
|
|
||||||
<div style={{ width: 3, background: T.mid, borderRadius: 2, flexShrink: 0 }} />
|
|
||||||
<div>
|
|
||||||
<p style={{ fontFamily: F.serif, fontSize: 15, color: T.parch, lineHeight: 1.72, fontStyle: "italic", marginBottom: 12 }}>"{q.q}"</p>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.muted, fontWeight: 600 }}>— {q.by}, {q.role}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── How it works ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PHASES = [
|
|
||||||
{ n: "01", id: "Discover", title: "Define your idea", body: "Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon." },
|
|
||||||
{ n: "02", id: "Design", title: "Choose your style", body: "Pick a visual style and see your exact site and emails live before a single line of code is written." },
|
|
||||||
{ n: "03", id: "Build", title: "Your app, live", body: "AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English." },
|
|
||||||
{ n: "04", id: "Grow", title: "Market & automate", body: "AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus on your users." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function HowItWorks() {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "80px 48px" }}>
|
|
||||||
<Eyebrow>How it works</Eyebrow>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 40, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", lineHeight: 1.15, marginBottom: 52, maxWidth: 460 }}>
|
|
||||||
Four phases.<br />One complete product.
|
|
||||||
</h2>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
{PHASES.map((p, i) => (
|
|
||||||
<div key={i} style={{
|
|
||||||
padding: "38px 42px", background: T.white,
|
|
||||||
borderRight: (i % 2 === 0) ? `1px solid ${T.border}` : "none",
|
|
||||||
borderBottom: (i < 2) ? `1px solid ${T.border}` : "none",
|
|
||||||
}}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: T.muted, marginBottom: 14 }}>
|
|
||||||
{p.n} — {p.id}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 21, fontWeight: 700, color: T.ink, marginBottom: 10 }}>{p.title}</div>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13.5, color: T.mid, lineHeight: 1.7 }}>{p.body}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Stats bar ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const STATS = [
|
|
||||||
{ n: "280+", label: "founders launched" },
|
|
||||||
{ n: "72h", label: "average time to first version" },
|
|
||||||
{ n: "4.9★", label: "average rating" },
|
|
||||||
{ n: "3×", label: "faster than hiring a developer" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function StatsBar() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.white, borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", padding: "0 48px", display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr" }}>
|
|
||||||
{STATS.map((s, i) => (
|
|
||||||
<div key={i} style={{ padding: "38px 0", paddingLeft: i > 0 ? 36 : 0, borderRight: i < 3 ? `1px solid ${T.border}` : "none" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 38, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", marginBottom: 6 }}>{s.n}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>{s.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Empathy section ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PAINS = [
|
|
||||||
{ title: "No more \"I need to hire a developer first\"", body: "vibn is your developer. Start building the moment you have an idea." },
|
|
||||||
{ title: "No more staring at a blank marketing calendar", body: "AI generates and publishes your content every single week." },
|
|
||||||
{ title: "No more \"I'll launch when it's ready\"", body: "Most founders ship their first version in under 72 hours." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function EmpathySection() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.cream, borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}`, padding: "76px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 68, alignItems: "center" }}>
|
|
||||||
<div>
|
|
||||||
<Eyebrow>Sound familiar?</Eyebrow>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 36, fontWeight: 700, color: T.ink, lineHeight: 1.18, marginBottom: 24, letterSpacing: "-0.02em" }}>
|
|
||||||
The idea is the hard part. Everything else shouldn't be.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 15, color: T.mid, lineHeight: 1.8, marginBottom: 20 }}>
|
|
||||||
You know exactly what you want to build and who it's for. But the moment you think
|
|
||||||
about servers, databases, deployment pipelines, SEO strategies — the whole thing stalls.
|
|
||||||
</p>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 15, color: T.mid, lineHeight: 1.8 }}>
|
|
||||||
vibn exists to remove all of that. Not abstract it —{" "}
|
|
||||||
<em style={{ fontFamily: F.serif, fontStyle: "italic" }}>remove it entirely.</em>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
|
|
||||||
{PAINS.map((p, i) => (
|
|
||||||
<div key={i} style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px", display: "flex", gap: 14, alignItems: "flex-start" }}>
|
|
||||||
<div style={{ width: 20, height: 20, borderRadius: "50%", border: `1.5px solid ${T.stone}`, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, marginTop: 2 }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink }} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>{p.title}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.muted, lineHeight: 1.6 }}>{p.body}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Final CTA ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function FinalCta({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 660, margin: "0 auto", padding: "92px 48px", textAlign: "center" }}>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 46, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", lineHeight: 1.1, marginBottom: 20 }}>
|
|
||||||
Your idea deserves to exist.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 16, color: T.mid, lineHeight: 1.75, marginBottom: 36 }}>
|
|
||||||
Don't let the backend be the reason it doesn't. Start today — free, no code, no credit card.
|
|
||||||
</p>
|
|
||||||
<PrimaryBtn onClick={onCta} large>Build my product — free</PrimaryBtn>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12.5, color: T.stone, marginTop: 16 }}>
|
|
||||||
Joins 280+ non-technical founders already live
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Footer ────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Footer() {
|
|
||||||
return (
|
|
||||||
<footer style={{ borderTop: `1px solid ${T.border}`, padding: "32px 48px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 16, fontWeight: 700, color: T.ink }}>vibn</span>
|
|
||||||
<div style={{ display: "flex", gap: 28 }}>
|
|
||||||
{["Product", "Pricing", "Stories", "Blog", "Privacy", "Terms"].map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: F.sans, fontSize: 13, color: T.stone, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, color: T.stone }}>© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Root export ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Website({ onGetStarted = () => {}, onLogin = () => {} }) {
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.paper, color: T.ink, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
body { font-family: 'Inter', sans-serif; }
|
|
||||||
button { font-family: inherit; }
|
|
||||||
`}</style>
|
|
||||||
<Nav onGetStarted={onGetStarted} onLogin={onLogin} />
|
|
||||||
<Hero onCta={onGetStarted} />
|
|
||||||
<QuoteBand />
|
|
||||||
<HowItWorks />
|
|
||||||
<StatsBar />
|
|
||||||
<EmpathySection />
|
|
||||||
<FinalCta onCta={onGetStarted} />
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>VIBN Assist Preview</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
1677
preview-assist-ui/package-lock.json
generated
1677
preview-assist-ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "preview-assist-ui",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@vitejs/plugin-react": "^4.2.0",
|
|
||||||
"vite": "^5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,933 +0,0 @@
|
|||||||
import { useState, useRef, useEffect } from "react";
|
|
||||||
import Website from "./Website.jsx";
|
|
||||||
import Dashboard from "./Dashboard.jsx";
|
|
||||||
|
|
||||||
// ─── DESIGN TOKENS ────────────────────────────────────────────────────────────
|
|
||||||
const C = {
|
|
||||||
ink: "#0f172a", ink2: "#1e293b", ink3: "#475569", muted: "#94a3b8",
|
|
||||||
border: "#f1f5f9", border2: "#e2e8f0", surface: "#f8fafc", white: "#fff",
|
|
||||||
green: "#22c55e", greenBg: "#f0fdf4",
|
|
||||||
purple: "#6366f1", purpleBg: "#eef2ff",
|
|
||||||
amber: "#f59e0b", amberBg: "#fffbeb", amberBorder: "#fde68a", amberText: "#92400e",
|
|
||||||
blue: "#0ea5e9", blueBg: "#f0f9ff",
|
|
||||||
rose: "#f43f5e", roseBg: "#fff1f2",
|
|
||||||
teal: "#14b8a6", tealBg: "#f0fdfa",
|
|
||||||
violet: "#8b5cf6", violetBg: "#f5f3ff",
|
|
||||||
};
|
|
||||||
|
|
||||||
const PHASES = [
|
|
||||||
{ id: "welcome", label: "Welcome", icon: "◎", desc: "How this works" },
|
|
||||||
{ id: "discover", label: "Discover", icon: "◇", desc: "Your idea" },
|
|
||||||
{ id: "architect", label: "Architect", icon: "⬡", desc: "What gets built" },
|
|
||||||
{ id: "design", label: "Design", icon: "◈", desc: "How it looks" },
|
|
||||||
{ id: "market", label: "Market", icon: "✦", desc: "How you'll grow" },
|
|
||||||
{ id: "build", label: "Build MVP", icon: "▲", desc: "Review & launch" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── DISCOVER DATA ────────────────────────────────────────────────────────────
|
|
||||||
const QUESTIONS = [
|
|
||||||
{ key: "idea", q: "Tell me about your idea. What does it do and who is it for?" },
|
|
||||||
{ key: "problem", q: "What's the painful thing your users are doing today instead?" },
|
|
||||||
{ key: "users", q: "Describe your ideal first customer in one sentence." },
|
|
||||||
{ key: "value", q: "What's the one thing they'll love most about your product?" },
|
|
||||||
{ key: "revenue", q: "How will you charge for it? Subscription, one-time, or free to start?" },
|
|
||||||
{ key: "features", q: "Name the 3 things users must be able to do in version one." },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── ARCHITECT DATA ───────────────────────────────────────────────────────────
|
|
||||||
const ARCH_BLOCKS = [
|
|
||||||
{ id: "frontend", icon: "◇", label: "Frontend", color: C.purple, bg: C.purpleBg, chosen: "Web app", what: "The screens your users see — dashboard, sign up, settings, and everything in between. Works on desktop and mobile.", alts: [{ id: "webapp", label: "Web app", desc: "Runs in any browser, desktop & mobile" }, { id: "mobile", label: "Mobile-first", desc: "Optimised for phones, still works on desktop" }] },
|
|
||||||
{ id: "backend", icon: "⬡", label: "Backend & Database", color: C.blue, bg: C.blueBg, chosen: "API + database", what: "The engine behind the scenes. Stores all your data securely and runs your product's logic.", alts: [{ id: "api_pg", label: "API + database", desc: "Standard setup, works for almost everything" }, { id: "realtime", label: "Real-time", desc: "If your product needs live updates between users" }] },
|
|
||||||
{ id: "auth", icon: "◎", label: "Sign up & Login", color: C.rose, bg: C.roseBg, chosen: "Email + social login", what: "How your users create accounts and log in. Getting this right reduces drop-off at the first step.", alts: [{ id: "email_social", label: "Email + social login", desc: "Email/password plus Google & GitHub — recommended" }, { id: "email_only", label: "Email & password only", desc: "Simpler, no third-party login" }, { id: "magic_link", label: "Magic link", desc: "No password — users get a login link by email" }, { id: "sso", label: "SSO for teams", desc: "If you're selling to companies, not individuals" }] },
|
|
||||||
{ id: "payments", icon: "◈", label: "Payments", color: C.amber, bg: C.amberBg, chosen: "Subscription billing", what: "How you collect money from customers. Set up now so you're ready to charge from day one.", alts: [{ id: "subscription", label: "Subscription billing", desc: "Monthly or annual plans — recommended for SaaS" }, { id: "one_time", label: "One-time purchase", desc: "Pay once, own forever" }, { id: "usage", label: "Usage-based", desc: "Charge based on what users consume" }, { id: "free", label: "Free for now", desc: "No payments yet — add billing later" }] },
|
|
||||||
{ id: "email", icon: "✦", label: "Email", color: C.teal, bg: C.tealBg, chosen: "Transactional + marketing", what: "Emails sent to your users — welcome messages, password resets, and newsletters when you're ready.", alts: [{ id: "both", label: "Transactional + marketing", desc: "Welcome emails, resets, plus campaign capability" }, { id: "trans", label: "Transactional only", desc: "Just the essential emails, nothing more" }, { id: "none", label: "None for now", desc: "Skip email entirely — add it later" }] },
|
|
||||||
{ id: "hosting", icon: "▲", label: "Hosting", color: C.violet, bg: C.violetBg, chosen: "Your own servers", what: "Where your product lives. Deployed to your own infrastructure — you own everything, no lock-in.", alts: [{ id: "own", label: "Your own servers", desc: "Coolify + Gitea — already configured" }], locked: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const PAGES_GENERATED = [
|
|
||||||
{ group: "Public", color: C.purple, pages: ["Landing page", "Pricing", "About", "Blog"] },
|
|
||||||
{ group: "Auth", color: C.rose, pages: ["Sign up", "Log in", "Forgot password"] },
|
|
||||||
{ group: "App", color: C.blue, pages: ["Dashboard", "Onboarding", "Settings", "Invite team"] },
|
|
||||||
{ group: "Payments", color: C.amber, pages: ["Checkout", "Success", "Manage subscription"] },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── DESIGN DATA ──────────────────────────────────────────────────────────────
|
|
||||||
// Production presets live in vibn-frontend/lib/design-kits/registry.ts (STARTER_KITS).
|
|
||||||
const DESIGN_FEELS = [
|
|
||||||
{ id: "clean", label: "Clean & focused", ref: "Like Notion or Linear", bg: "#fff", surface: "#f8fafc", text: "#0f172a", accent: "#6366f1", radius: 8 },
|
|
||||||
{ id: "bold", label: "Bold & confident", ref: "Like Stripe or Vercel", bg: "#0f172a", surface: "#1e293b", text: "#f8fafc", accent: "#f43f5e", radius: 8 },
|
|
||||||
{ id: "warm", label: "Warm & friendly", ref: "Like Mailchimp or Basecamp", bg: "#fffbeb", surface: "#fef3c7", text: "#78350f", accent: "#f59e0b", radius: 14 },
|
|
||||||
{ id: "fresh", label: "Fresh & modern", ref: "Like Loom or Superhuman", bg: "#f0fdf4", surface: "#dcfce7", text: "#14532d", accent: "#22c55e", radius: 10 },
|
|
||||||
{ id: "electric", label: "Electric & vivid", ref: "Like Figma or Framer", bg: "#faf5ff", surface: "#ede9fe", text: "#4c1d95", accent: "#8b5cf6", radius: 8 },
|
|
||||||
{ id: "luxury", label: "Premium & refined", ref: "Like Linear or Craft", bg: "#0c0a09", surface: "#1c1917", text: "#f5f5f4", accent: "#d4a853", radius: 6 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── MARKET DATA ─────────────────────────────────────────────────────────────
|
|
||||||
const VOICE_OPTIONS = [
|
|
||||||
{ key: "tone", label: "Tone", a: "Friendly & approachable", b: "Professional & authoritative" },
|
|
||||||
{ key: "style", label: "Style", a: "Conversational & casual", b: "Precise & concise" },
|
|
||||||
{ key: "personality", label: "Personality", a: "Warm & encouraging", b: "Direct & confident" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const SUGGESTED_TOPICS = () => [
|
|
||||||
{ id: "t1", title: "The problem we're solving", angle: "Show users you deeply understand their pain before pitching your solution.", channels: ["Blog", "Tweet thread", "LinkedIn", "Email"] },
|
|
||||||
{ id: "t2", title: "Who this is built for", angle: "Paint a picture of your ideal user — they should read it and think 'that's me'.", channels: ["Blog", "LinkedIn", "Website section"] },
|
|
||||||
{ id: "t3", title: "Why now is the right time", angle: "What's changed in the market that makes this product possible or necessary?", channels: ["Blog", "Tweet thread", "Email"] },
|
|
||||||
];
|
|
||||||
|
|
||||||
const WEBSITE_FEELS = [
|
|
||||||
{ id: "editorial", label: "Editorial", ref: "Bold headlines, strong opinions", bg: "#fff", accent: "#ef4444" },
|
|
||||||
{ id: "startup", label: "Startup energy", ref: "Clear, conversion-focused", bg: "#f8fafc", accent: "#0ea5e9" },
|
|
||||||
{ id: "minimal", label: "Ultra minimal", ref: "Let the product speak", bg: "#fff", accent: "#111" },
|
|
||||||
{ id: "warm_w", label: "Warm & human", ref: "Feels personal and trustworthy", bg: "#fff7ed", accent: "#ea580c" },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── SHARED UI ────────────────────────────────────────────────────────────────
|
|
||||||
function PhaseHeader({ title, desc, action }) {
|
|
||||||
return (
|
|
||||||
<div style={{ padding: "22px 32px 18px", background: C.white, borderBottom: `1px solid ${C.border}`, display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0 }}>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 18, fontWeight: 800, color: C.ink, letterSpacing: "-0.03em" }}>{title}</div>
|
|
||||||
{desc && <div style={{ fontSize: 13, color: C.muted, marginTop: 2 }}>{desc}</div>}
|
|
||||||
</div>
|
|
||||||
{action}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ContinueBtn({ label, onClick, disabled }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} disabled={disabled}
|
|
||||||
style={{ background: disabled ? C.border2 : C.ink, color: disabled ? C.muted : C.white, border: "none", borderRadius: 10, padding: "11px 22px", fontSize: 13.5, fontWeight: 700, cursor: disabled ? "default" : "pointer", letterSpacing: "-0.01em", transition: "all 0.2s" }}>
|
|
||||||
{label}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── LIVE APP MOCKUP ──────────────────────────────────────────────────────────
|
|
||||||
function AppMockup({ feel }) {
|
|
||||||
const t = DESIGN_FEELS.find((f) => f.id === feel) || DESIGN_FEELS[0];
|
|
||||||
const isDark = t.bg === "#0f172a" || t.bg === "#0c0a09";
|
|
||||||
const border = isDark ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.06)";
|
|
||||||
return (
|
|
||||||
<div style={{ width: "100%", height: "100%", background: t.bg, borderRadius: 10, overflow: "hidden", display: "flex", flexDirection: "column", transition: "all 0.4s", fontFamily: "DM Sans, sans-serif" }}>
|
|
||||||
<div style={{ padding: "10px 16px", display: "flex", alignItems: "center", justifyContent: "space-between", borderBottom: `1px solid ${border}`, flexShrink: 0 }}>
|
|
||||||
<div style={{ fontSize: 13, fontWeight: 800, color: t.text, letterSpacing: "-0.03em" }}>YourApp</div>
|
|
||||||
<div style={{ display: "flex", gap: 12 }}>{["Dashboard", "Settings", "Billing"].map((l) => <span key={l} style={{ fontSize: 10, color: t.text, opacity: 0.45 }}>{l}</span>)}</div>
|
|
||||||
<div style={{ background: t.accent, color: "#fff", fontSize: 10, fontWeight: 700, padding: "4px 10px", borderRadius: t.radius / 1.5 }}>Upgrade</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, padding: "14px", overflow: "hidden", display: "flex", flexDirection: "column", gap: 10 }}>
|
|
||||||
<div style={{ fontSize: 12, fontWeight: 700, color: t.text, opacity: 0.8 }}>Dashboard</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 7 }}>
|
|
||||||
{[["Users", "182"], ["Revenue", "$2.4k"], ["Active", "94%"]].map(([l, v], i) => (
|
|
||||||
<div key={l} style={{ background: t.surface, borderRadius: t.radius / 1.5, padding: "9px 11px", border: `1px solid ${border}` }}>
|
|
||||||
<div style={{ fontSize: 9, color: t.text, opacity: 0.45, marginBottom: 4 }}>{l}</div>
|
|
||||||
<div style={{ fontSize: 15, fontWeight: 800, color: i === 1 ? t.accent : t.text }}>{v}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div style={{ background: t.surface, borderRadius: t.radius / 1.5, padding: "10px 12px", border: `1px solid ${border}`, flex: 1 }}>
|
|
||||||
<div style={{ fontSize: 9, color: t.text, opacity: 0.4, marginBottom: 8 }}>Recent activity</div>
|
|
||||||
{["Alex signed up · 2m ago", "Priya upgraded to Pro · 1h ago", "Dan invited 3 teammates · 3h ago"].map((item, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 7, padding: "5px 0", borderBottom: i < 2 ? `1px solid ${border}` : "none" }}>
|
|
||||||
<div style={{ width: 5, height: 5, borderRadius: "50%", background: t.accent, flexShrink: 0 }} />
|
|
||||||
<span style={{ fontSize: 10, color: t.text, opacity: 0.65 }}>{item}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: WELCOME ───────────────────────────────────────────────────────────
|
|
||||||
function WelcomePhase({ onStart }) {
|
|
||||||
const steps = [
|
|
||||||
{ icon: "◇", label: "You describe your idea", desc: "A short conversation — no technical knowledge needed." },
|
|
||||||
{ icon: "⬡", label: "We plan what gets built", desc: "AI maps out your full product architecture in plain language." },
|
|
||||||
{ icon: "◈", label: "You choose how it looks", desc: "Pick a visual style for your product and marketing." },
|
|
||||||
{ icon: "✦", label: "You set your market angle", desc: "Define your voice and the topics that'll drive your content." },
|
|
||||||
{ icon: "▲", label: "We build it", desc: "AI codes everything and deploys to your live URL." },
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<div style={{ height: "100%", display: "flex", alignItems: "center", justifyContent: "center", padding: "40px" }}>
|
|
||||||
<div style={{ maxWidth: 540, width: "100%" }}>
|
|
||||||
<div style={{ marginBottom: 36 }}>
|
|
||||||
<div style={{ display: "inline-flex", alignItems: "center", gap: 8, background: C.purpleBg, border: "1px solid #c7d2fe", borderRadius: 20, padding: "5px 14px", marginBottom: 20 }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: C.green, animation: "vpulse 2s infinite" }} />
|
|
||||||
<span style={{ fontSize: 12, fontWeight: 600, color: C.purple }}>vibn · MVP Builder</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 32, fontWeight: 800, color: C.ink, letterSpacing: "-0.04em", lineHeight: 1.15, marginBottom: 14 }}>
|
|
||||||
From idea to live product.<br />No code needed.
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 15, color: C.ink3, lineHeight: 1.7, maxWidth: 440 }}>
|
|
||||||
Answer a few questions about your idea. AI plans, designs, and builds your full SaaS product — then deploys it to your own servers. Takes about 10 minutes to set up.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 10, marginBottom: 32 }}>
|
|
||||||
{steps.map((s, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "flex-start", gap: 14, padding: "14px 16px", background: C.white, borderRadius: 12, border: `1px solid ${C.border2}` }}>
|
|
||||||
<div style={{ width: 32, height: 32, borderRadius: 9, background: C.surface, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 14, color: C.ink3, flexShrink: 0, border: `1px solid ${C.border2}` }}>{s.icon}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 700, color: C.ink, marginBottom: 2 }}>{s.label}</div>
|
|
||||||
<div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>{s.desc}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 14 }}>
|
|
||||||
<button onClick={onStart} style={{ background: C.ink, color: C.white, border: "none", borderRadius: 12, padding: "14px 32px", fontSize: 15, fontWeight: 800, cursor: "pointer", letterSpacing: "-0.02em" }}>
|
|
||||||
Let's build it →
|
|
||||||
</button>
|
|
||||||
<span style={{ fontSize: 12.5, color: C.muted }}>~10 minutes · You own everything at the end</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: DISCOVER ─────────────────────────────────────────────────────────
|
|
||||||
function DiscoverPhase({ onComplete }) {
|
|
||||||
const [step, setStep] = useState(0);
|
|
||||||
const [answers, setAnswers] = useState({});
|
|
||||||
const [input, setInput] = useState("");
|
|
||||||
const [msgs, setMsgs] = useState([{ role: "assistant", text: "Let's start with the big picture.\n\n" + QUESTIONS[0].q }]);
|
|
||||||
const [done, setDone] = useState(false);
|
|
||||||
const bottomRef = useRef(null);
|
|
||||||
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); }, [msgs]);
|
|
||||||
|
|
||||||
const acks = { idea: "Good — I can work with that.", problem: "That's a real pain worth solving.", users: "Clear. That focus will shape everything.", value: "Strong differentiator.", revenue: "Makes sense for this type of product." };
|
|
||||||
|
|
||||||
const send = () => {
|
|
||||||
if (!input.trim()) return;
|
|
||||||
const val = input.trim(); setInput("");
|
|
||||||
const q = QUESTIONS[step];
|
|
||||||
const newA = { ...answers, [q.key]: val };
|
|
||||||
setAnswers(newA);
|
|
||||||
setMsgs((m) => [...m, { role: "user", text: val }]);
|
|
||||||
setTimeout(() => {
|
|
||||||
const next = step + 1;
|
|
||||||
if (next < QUESTIONS.length) {
|
|
||||||
setStep(next);
|
|
||||||
setMsgs((m) => [...m, { role: "assistant", text: `${acks[q.key] || "Got it."}\n\n${QUESTIONS[next].q}` }]);
|
|
||||||
} else {
|
|
||||||
setMsgs((m) => [...m, { role: "assistant", text: "That's all I need.\n\nI've drafted your product plan on the right. Have a read — if it looks right, let's move on to planning what gets built." }]);
|
|
||||||
setDone(true);
|
|
||||||
}
|
|
||||||
}, 400);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", height: "100%", overflow: "hidden" }}>
|
|
||||||
<div style={{ flex: 1, display: "flex", flexDirection: "column", borderRight: `1px solid ${C.border}` }}>
|
|
||||||
<PhaseHeader title="Discover" desc="Tell me about your idea — I'll turn it into a product plan" />
|
|
||||||
<div style={{ padding: "0 32px 12px", background: C.white, borderBottom: `1px solid ${C.border}` }}>
|
|
||||||
<div style={{ display: "flex", gap: 4 }}>
|
|
||||||
{QUESTIONS.map((_, i) => <div key={i} style={{ flex: 1, height: 3, borderRadius: 3, background: i < step ? C.ink : i === step ? C.purple : C.border2, transition: "background 0.3s" }} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, overflowY: "auto", padding: "24px 32px", display: "flex", flexDirection: "column", gap: 14 }}>
|
|
||||||
{msgs.map((msg, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", flexDirection: "column", alignItems: msg.role === "user" ? "flex-end" : "flex-start" }}>
|
|
||||||
<div style={{ maxWidth: "80%", background: msg.role === "user" ? C.ink : C.surface, color: msg.role === "user" ? C.white : C.ink2, borderRadius: msg.role === "user" ? "14px 14px 4px 14px" : "14px 14px 14px 4px", padding: "12px 16px", fontSize: 13.5, lineHeight: 1.65, border: msg.role === "assistant" ? `1px solid ${C.border2}` : "none", whiteSpace: "pre-line" }}>{msg.text}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div ref={bottomRef} />
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "14px 32px 24px", borderTop: `1px solid ${C.border}`, background: C.white }}>
|
|
||||||
{!done
|
|
||||||
? <div style={{ display: "flex", gap: 8, background: C.surface, border: `1px solid ${C.border2}`, borderRadius: 12, padding: "10px 14px" }}>
|
|
||||||
<input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} placeholder="Type your answer…" style={{ flex: 1, border: "none", background: "transparent", fontSize: 14, color: C.ink, outline: "none" }} autoFocus />
|
|
||||||
<button onClick={send} style={{ background: C.ink, color: C.white, border: "none", borderRadius: 8, padding: "8px 16px", fontSize: 13, fontWeight: 700, cursor: "pointer" }}>→</button>
|
|
||||||
</div>
|
|
||||||
: <button onClick={() => onComplete(answers)} style={{ width: "100%", background: C.ink, color: C.white, border: "none", borderRadius: 10, padding: "13px", fontSize: 14, fontWeight: 700, cursor: "pointer" }}>Plan looks good — next: Architect →</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ width: 280, overflowY: "auto", background: C.surface, padding: "22px 18px", display: "flex", flexDirection: "column", gap: 8 }}>
|
|
||||||
<div style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", color: C.muted, marginBottom: 4 }}>Your product plan</div>
|
|
||||||
{QUESTIONS.map((q, i) => (
|
|
||||||
<div key={i} style={{ padding: "11px 13px", background: C.white, borderRadius: 10, border: `1px solid ${i < step ? C.border2 : C.border}`, opacity: i > step ? 0.35 : 1, transition: "all 0.3s" }}>
|
|
||||||
<div style={{ fontSize: 10, fontWeight: 700, color: i <= step ? C.purple : C.muted, letterSpacing: "0.06em", textTransform: "uppercase", marginBottom: 3 }}>{q.key}</div>
|
|
||||||
<div style={{ fontSize: 12.5, color: i < step ? C.ink2 : C.muted, lineHeight: 1.5 }}>{answers[q.key] || "Waiting…"}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: ARCHITECT ─────────────────────────────────────────────────────────
|
|
||||||
function ArchCard({ block, label, onEdit }) {
|
|
||||||
return (
|
|
||||||
<div style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, overflow: "hidden", display: "flex", flexDirection: "column" }}
|
|
||||||
onMouseEnter={(e) => (e.currentTarget.style.boxShadow = "0 4px 20px rgba(0,0,0,0.07)")}
|
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.boxShadow = "none")}>
|
|
||||||
<div style={{ height: 3, background: block.color, opacity: 0.6 }} />
|
|
||||||
<div style={{ padding: "16px 18px", flex: 1 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 10 }}>
|
|
||||||
<div style={{ width: 34, height: 34, borderRadius: 9, background: block.bg, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 16, color: block.color, flexShrink: 0 }}>{block.icon}</div>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 700, color: C.ink }}>{block.label}</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 12.5, color: C.ink3, lineHeight: 1.65, marginBottom: 14 }}>{block.what}</div>
|
|
||||||
<div style={{ display: "inline-flex", alignItems: "center", gap: 6, background: block.bg, borderRadius: 20, padding: "5px 11px" }}>
|
|
||||||
<div style={{ width: 6, height: 6, borderRadius: "50%", background: block.color }} />
|
|
||||||
<span style={{ fontSize: 12, fontWeight: 600, color: block.color }}>{label}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "10px 18px", borderTop: `1px solid ${C.border}`, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontSize: 11.5, color: C.muted }}>{block.locked ? "Required for your setup" : "AI recommended this"}</span>
|
|
||||||
<button onClick={onEdit} style={{ fontSize: 12, fontWeight: 600, color: block.locked ? C.muted : C.ink3, background: "transparent", border: `1px solid ${C.border2}`, borderRadius: 7, padding: "5px 11px", cursor: "pointer" }}>{block.locked ? "Why? →" : "Change →"}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ArchitectPhase({ onComplete }) {
|
|
||||||
const [choices, setChoices] = useState(() => Object.fromEntries(ARCH_BLOCKS.map((b) => [b.id, b.alts[0].id])));
|
|
||||||
const [editing, setEditing] = useState(null);
|
|
||||||
const editBlock = ARCH_BLOCKS.find((b) => b.id === editing);
|
|
||||||
const save = (id, val) => { setChoices((c) => ({ ...c, [id]: val })); setEditing(null); };
|
|
||||||
const choiceLabel = (block) => block.alts.find((a) => a.id === choices[block.id])?.label || block.alts[0].label;
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", height: "100%", overflow: "hidden" }}>
|
|
||||||
<div style={{ flex: 1, display: "flex", flexDirection: "column" }}>
|
|
||||||
<PhaseHeader title="Architect" desc="Here's what we're going to build. AI has made smart choices — change anything that doesn't feel right."
|
|
||||||
action={<ContinueBtn label="Confirm — next: Design →" onClick={() => onComplete(choices)} />} />
|
|
||||||
<div style={{ flex: 1, overflowY: "auto", padding: "24px 32px" }}>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 14, marginBottom: 14 }}>
|
|
||||||
{ARCH_BLOCKS.slice(0, 3).map((b) => <ArchCard key={b.id} block={b} label={choiceLabel(b)} onEdit={() => setEditing(b.id)} />)}
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 14 }}>
|
|
||||||
{ARCH_BLOCKS.slice(3).map((b) => <ArchCard key={b.id} block={b} label={choiceLabel(b)} onEdit={() => setEditing(b.id)} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ width: 250, borderLeft: `1px solid ${C.border}`, background: C.white, overflowY: "auto", padding: "22px 18px", flexShrink: 0 }}>
|
|
||||||
<div style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", color: C.muted, marginBottom: 16 }}>Pages to build</div>
|
|
||||||
{PAGES_GENERATED.map((g) => (
|
|
||||||
<div key={g.group}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 7 }}>
|
|
||||||
<div style={{ width: 6, height: 6, borderRadius: "50%", background: g.color }} />
|
|
||||||
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: "0.08em", textTransform: "uppercase", color: C.muted }}>{g.group}</div>
|
|
||||||
</div>
|
|
||||||
{g.pages.map((p) => (
|
|
||||||
<div key={p} style={{ display: "flex", alignItems: "center", gap: 8, padding: "7px 10px", background: C.surface, borderRadius: 7, border: `1px solid ${C.border}`, marginBottom: 4, fontSize: 12.5, color: C.ink3 }}>
|
|
||||||
<div style={{ width: 5, height: 5, borderRadius: "50%", background: g.color, opacity: 0.5, flexShrink: 0 }} />{p}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 18, padding: "13px", background: C.violetBg, border: "1px solid #ddd6fe", borderRadius: 11 }}>
|
|
||||||
<div style={{ fontSize: 12, fontWeight: 700, color: C.violet, marginBottom: 4 }}>◉ Your infrastructure</div>
|
|
||||||
<div style={{ fontSize: 12, color: "#5b21b6", lineHeight: 1.6 }}>Code pushed to Gitea. Coolify auto-deploys. You own everything.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{editing && editBlock && (
|
|
||||||
<div style={{ position: "fixed", inset: 0, background: "rgba(15,23,42,0.5)", zIndex: 600, display: "flex", alignItems: "center", justifyContent: "center", backdropFilter: "blur(4px)", animation: "fadeIn 0.15s" }}>
|
|
||||||
<div style={{ background: C.white, borderRadius: 18, width: 420, boxShadow: "0 24px 60px rgba(0,0,0,0.2)", overflow: "hidden", animation: "slideup 0.2s" }}>
|
|
||||||
<div style={{ padding: "20px 24px", borderBottom: `1px solid ${C.border}`, display: "flex", alignItems: "center", gap: 12 }}>
|
|
||||||
<div style={{ width: 36, height: 36, borderRadius: 10, background: editBlock.bg, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 16, color: editBlock.color, flexShrink: 0 }}>{editBlock.icon}</div>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontSize: 15, fontWeight: 700, color: C.ink }}>{editBlock.label}</div>
|
|
||||||
<div style={{ fontSize: 12, color: C.muted, lineHeight: 1.5 }}>{editBlock.what}</div>
|
|
||||||
</div>
|
|
||||||
<button onClick={() => setEditing(null)} style={{ background: "transparent", border: "none", color: C.muted, cursor: "pointer", fontSize: 20 }}>×</button>
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "16px 24px", display: "flex", flexDirection: "column", gap: 8 }}>
|
|
||||||
{editBlock.alts.map((alt) => {
|
|
||||||
const isSel = choices[editBlock.id] === alt.id;
|
|
||||||
return (
|
|
||||||
<button key={alt.id} onClick={() => !editBlock.locked && setChoices((c) => ({ ...c, [editBlock.id]: alt.id }))}
|
|
||||||
style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", borderRadius: 11, border: isSel ? `2px solid ${editBlock.color}` : `1px solid ${C.border2}`, background: isSel ? editBlock.bg : C.white, cursor: editBlock.locked ? "default" : "pointer", textAlign: "left", transition: "all 0.15s" }}>
|
|
||||||
<div style={{ width: 18, height: 18, borderRadius: "50%", border: `2px solid ${isSel ? editBlock.color : C.border2}`, background: isSel ? editBlock.color : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
|
|
||||||
{isSel && <div style={{ width: 6, height: 6, borderRadius: "50%", background: C.white }} />}
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: isSel ? 700 : 500, color: C.ink }}>{alt.label}</div>
|
|
||||||
<div style={{ fontSize: 12, color: C.muted }}>{alt.desc}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "10px 24px 20px", display: "flex", gap: 8 }}>
|
|
||||||
<button onClick={() => setEditing(null)} style={{ flex: 1, background: C.surface, color: C.ink3, border: `1px solid ${C.border2}`, borderRadius: 10, padding: "11px", fontSize: 13, cursor: "pointer" }}>Cancel</button>
|
|
||||||
<button onClick={() => save(editing, choices[editing])} style={{ flex: 2, background: C.ink, color: C.white, border: "none", borderRadius: 10, padding: "11px", fontSize: 13, fontWeight: 700, cursor: "pointer" }}>Save</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: DESIGN ────────────────────────────────────────────────────────────
|
|
||||||
function DesignPhase({ onComplete }) {
|
|
||||||
const [feel, setFeel] = useState("clean");
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", height: "100%", overflow: "hidden" }}>
|
|
||||||
<div style={{ width: 280, borderRight: `1px solid ${C.border}`, display: "flex", flexDirection: "column", overflow: "hidden", background: C.white }}>
|
|
||||||
<PhaseHeader title="Design" desc="How should your product feel?" />
|
|
||||||
<div style={{ flex: 1, overflowY: "auto", padding: "20px 20px", display: "flex", flexDirection: "column", gap: 8 }}>
|
|
||||||
<div style={{ fontSize: 11.5, color: C.muted, lineHeight: 1.6, marginBottom: 8, padding: "0 2px" }}>
|
|
||||||
Pick the feeling you want users to get. You can refine every detail after launch — this sets the foundation.
|
|
||||||
</div>
|
|
||||||
{DESIGN_FEELS.map((f) => {
|
|
||||||
const isDark = f.bg === "#0f172a" || f.bg === "#0c0a09";
|
|
||||||
const isSel = feel === f.id;
|
|
||||||
return (
|
|
||||||
<button key={f.id} onClick={() => setFeel(f.id)}
|
|
||||||
style={{ borderRadius: 12, border: isSel ? `2px solid ${C.ink}` : `1px solid ${C.border2}`, overflow: "hidden", cursor: "pointer", textAlign: "left", transition: "all 0.15s", background: f.bg, padding: 0, boxShadow: isSel ? "0 2px 16px rgba(0,0,0,0.1)" : "none" }}>
|
|
||||||
<div style={{ padding: "12px 14px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }}>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 700, color: f.text }}>{f.label}</div>
|
|
||||||
{isSel && <span style={{ color: C.green, fontSize: 13, fontWeight: 900 }}>✓</span>}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 11.5, color: f.text, opacity: 0.55 }}>{f.ref}</div>
|
|
||||||
<div style={{ display: "flex", gap: 5, marginTop: 10 }}>
|
|
||||||
{[f.bg, f.accent, f.surface].map((col, i) => <div key={i} style={{ width: 14, height: 14, borderRadius: "50%", background: col, border: `1.5px solid ${isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)"}` }} />)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isSel && <div style={{ height: 3, background: f.accent }} />}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "14px 20px 20px", borderTop: `1px solid ${C.border}` }}>
|
|
||||||
<button onClick={() => onComplete({ feel })} style={{ width: "100%", background: C.ink, color: C.white, border: "none", borderRadius: 10, padding: "12px", fontSize: 13.5, fontWeight: 700, cursor: "pointer" }}>
|
|
||||||
Looks good — next: Market →
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, background: "#dde3ea", display: "flex", flexDirection: "column" }}>
|
|
||||||
<div style={{ padding: "11px 18px", background: C.white, borderBottom: `1px solid ${C.border}`, display: "flex", alignItems: "center", gap: 8 }}>
|
|
||||||
<div style={{ display: "flex", gap: 5 }}>{["#f43f5e", "#f59e0b", "#22c55e"].map((c) => <div key={c} style={{ width: 10, height: 10, borderRadius: "50%", background: c }} />)}</div>
|
|
||||||
<span style={{ fontFamily: "monospace", fontSize: 11.5, color: C.muted }}>app.yourproduct.com</span>
|
|
||||||
<span style={{ marginLeft: "auto", fontSize: 11.5, fontWeight: 600, color: C.ink3 }}>{DESIGN_FEELS.find((f) => f.id === feel)?.label}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, padding: 28, overflow: "hidden" }}>
|
|
||||||
<AppMockup feel={feel} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: MARKET ────────────────────────────────────────────────────────────
|
|
||||||
function WebsitePreview({ feel }) {
|
|
||||||
const t = WEBSITE_FEELS.find((f) => f.id === feel) || WEBSITE_FEELS[0];
|
|
||||||
const isDark = t.bg === "#0c0a09" || t.bg === "#0f172a";
|
|
||||||
const border = isDark ? "rgba(255,255,255,0.07)" : "rgba(0,0,0,0.06)";
|
|
||||||
const textCol = isDark ? "#fff" : (t.text || C.ink);
|
|
||||||
return (
|
|
||||||
<div style={{ width: "100%", height: "100%", background: t.bg, borderRadius: 10, overflow: "hidden", fontFamily: "DM Sans, sans-serif", display: "flex", flexDirection: "column", transition: "all 0.4s" }}>
|
|
||||||
<div style={{ padding: "11px 18px", display: "flex", alignItems: "center", justifyContent: "space-between", borderBottom: `1px solid ${border}` }}>
|
|
||||||
<div style={{ fontSize: 13, fontWeight: 800, color: textCol }}>YourApp</div>
|
|
||||||
<div style={{ display: "flex", gap: 14 }}>{["Product", "Blog", "Pricing"].map((l) => <span key={l} style={{ fontSize: 10, color: isDark ? "rgba(255,255,255,0.4)" : C.muted }}>{l}</span>)}</div>
|
|
||||||
<div style={{ background: t.accent, color: isDark && t.accent === "#d4a853" ? "#0c0a09" : "#fff", fontSize: 10, fontWeight: 700, padding: "5px 11px", borderRadius: 6 }}>Get started</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, padding: "22px 20px", display: "flex", flexDirection: "column", alignItems: "center", gap: 14 }}>
|
|
||||||
<div style={{ textAlign: "center", maxWidth: 320 }}>
|
|
||||||
<div style={{ fontSize: 22, fontWeight: 800, color: textCol, letterSpacing: "-0.04em", lineHeight: 1.2, marginBottom: 8 }}>Launch your SaaS in days, not months</div>
|
|
||||||
<div style={{ fontSize: 12, color: isDark ? "rgba(255,255,255,0.45)" : C.muted, lineHeight: 1.65, marginBottom: 16 }}>Describe your idea. AI builds, deploys, and markets your product automatically.</div>
|
|
||||||
<div style={{ display: "flex", gap: 8, justifyContent: "center" }}>
|
|
||||||
<div style={{ background: t.accent, color: "#fff", fontSize: 12, fontWeight: 700, padding: "9px 18px", borderRadius: 7 }}>Start free</div>
|
|
||||||
<div style={{ background: "transparent", color: isDark ? "rgba(255,255,255,0.6)" : C.ink3, fontSize: 12, padding: "9px 14px", borderRadius: 7, border: `1px solid ${border}` }}>See demo</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8, width: "100%" }}>
|
|
||||||
{[["⬡", "Build", "AI writes every line"], ["◇", "Grow", "Content on autopilot"], ["◈", "Launch", "Live in minutes"]].map(([ic, lb, desc]) => (
|
|
||||||
<div key={lb} style={{ background: isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.03)", borderRadius: 8, padding: "12px 10px", border: `1px solid ${border}`, textAlign: "center" }}>
|
|
||||||
<div style={{ fontSize: 16, marginBottom: 5 }}>{ic}</div>
|
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, color: textCol, marginBottom: 3 }}>{lb}</div>
|
|
||||||
<div style={{ fontSize: 10, color: isDark ? "rgba(255,255,255,0.35)" : C.muted }}>{desc}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MarketPhase({ prd, onComplete }) {
|
|
||||||
const [tab, setTab] = useState("voice");
|
|
||||||
const [voice, setVoice] = useState({ tone: 0.3, style: 0.4, personality: 0.3 });
|
|
||||||
const [topics, setTopics] = useState(SUGGESTED_TOPICS());
|
|
||||||
const [editingTopic, setEditingTopic] = useState(null);
|
|
||||||
const [websiteFeel, setWebsiteFeel] = useState("startup");
|
|
||||||
|
|
||||||
const tabs = ["voice", "topics", "style"];
|
|
||||||
const tabLabels = { voice: "Voice", topics: "Topics", style: "Website style" };
|
|
||||||
const voiceDesc = (key, val) => { const opt = VOICE_OPTIONS.find((v) => v.key === key); return val < 0.4 ? opt.a : val > 0.6 ? opt.b : "Balanced"; };
|
|
||||||
const addTopic = () => { const id = `t${Date.now()}`; setTopics((t) => [...t, { id, title: "", angle: "", channels: ["Blog", "Email"], editing: true }]); setEditingTopic(id); };
|
|
||||||
const updateTopic = (id, field, val) => setTopics((t) => t.map((tp) => (tp.id === id ? { ...tp, [field]: val } : tp)));
|
|
||||||
const removeTopic = (id) => setTopics((t) => t.filter((tp) => tp.id !== id));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ display: "flex", height: "100%", overflow: "hidden", flexDirection: "column" }}>
|
|
||||||
<PhaseHeader title="Market" desc="Set your brand voice, campaign topics, and website style — AI uses these to generate all your content."
|
|
||||||
action={<ContinueBtn label="Done — review & build →" onClick={() => onComplete({ voice, topics, websiteFeel })} />} />
|
|
||||||
<div style={{ display: "flex", borderBottom: `1px solid ${C.border}`, background: C.white, flexShrink: 0 }}>
|
|
||||||
{tabs.map((t) => (
|
|
||||||
<button key={t} onClick={() => setTab(t)} style={{ padding: "12px 24px", border: "none", background: "transparent", borderBottom: tab === t ? `2px solid ${C.ink}` : "2px solid transparent", cursor: "pointer", fontSize: 13.5, fontWeight: tab === t ? 700 : 400, color: tab === t ? C.ink : C.muted, transition: "all 0.15s", display: "flex", alignItems: "center", gap: 7 }}>
|
|
||||||
{tabLabels[t]}
|
|
||||||
{t === "topics" && <span style={{ fontSize: 11, background: C.surface, border: `1px solid ${C.border2}`, borderRadius: 10, padding: "1px 7px", color: C.ink3 }}>{topics.length}</span>}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, overflow: "hidden", display: "flex" }}>
|
|
||||||
{tab === "voice" && (
|
|
||||||
<div style={{ flex: 1, overflowY: "auto", padding: "32px", display: "flex", justifyContent: "center" }}>
|
|
||||||
<div style={{ width: "100%", maxWidth: 520 }}>
|
|
||||||
<div style={{ fontSize: 14, color: C.ink3, lineHeight: 1.7, marginBottom: 28 }}>This defines how your brand sounds — in emails, on your website, in social posts. AI uses this to write content that sounds like you.</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 24 }}>
|
|
||||||
{VOICE_OPTIONS.map((opt) => (
|
|
||||||
<div key={opt.key} style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, padding: "20px 24px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }}>
|
|
||||||
<div style={{ fontSize: 13, fontWeight: 700, color: C.ink }}>{opt.label}</div>
|
|
||||||
<div style={{ fontSize: 12.5, fontWeight: 600, color: C.purple, background: C.purpleBg, borderRadius: 20, padding: "3px 11px" }}>{voiceDesc(opt.key, voice[opt.key])}</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 10 }}>
|
|
||||||
<span style={{ fontSize: 12, color: C.ink3, width: 140, textAlign: "right", lineHeight: 1.4 }}>{opt.a}</span>
|
|
||||||
<input type="range" min="0" max="1" step="0.05" value={voice[opt.key]} onChange={(e) => setVoice((v) => ({ ...v, [opt.key]: parseFloat(e.target.value) }))} style={{ flex: 1, accentColor: C.ink, cursor: "pointer" }} />
|
|
||||||
<span style={{ fontSize: 12, color: C.ink3, width: 140, lineHeight: 1.4 }}>{opt.b}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div style={{ marginTop: 24, padding: "16px 20px", background: C.surface, borderRadius: 12, border: `1px solid ${C.border2}` }}>
|
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.07em", marginBottom: 6 }}>How your brand will sound</div>
|
|
||||||
<div style={{ fontSize: 13.5, color: C.ink2, lineHeight: 1.7 }}>
|
|
||||||
{voice.tone < 0.4 ? "Warm and approachable" : voice.tone > 0.6 ? "Professional and authoritative" : "Balanced"}
|
|
||||||
{" · "}{voice.style < 0.4 ? "conversational" : voice.style > 0.6 ? "precise" : "clear"}
|
|
||||||
{" · "}{voice.personality < 0.4 ? "encouraging" : voice.personality > 0.6 ? "direct" : "steady"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{tab === "topics" && (
|
|
||||||
<div style={{ flex: 1, overflowY: "auto", padding: "28px 32px" }}>
|
|
||||||
<div style={{ maxWidth: 640 }}>
|
|
||||||
<div style={{ fontSize: 14, color: C.ink3, lineHeight: 1.7, marginBottom: 20 }}>Topics are the campaign angles your content will be built around. Each topic generates a blog post, tweet thread, LinkedIn post, and email. AI suggested these from your PRD — edit or add your own.</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
|
||||||
{topics.map((topic) => (
|
|
||||||
<div key={topic.id} style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, overflow: "hidden" }}>
|
|
||||||
{editingTopic === topic.id ? (
|
|
||||||
<div style={{ padding: "18px 20px", display: "flex", flexDirection: "column", gap: 10 }}>
|
|
||||||
<input value={topic.title} onChange={(e) => updateTopic(topic.id, "title", e.target.value)} placeholder="Topic title" style={{ fontSize: 14, fontWeight: 600, color: C.ink, border: `1px solid ${C.border2}`, borderRadius: 8, padding: "9px 12px", outline: "none", background: C.surface }} autoFocus />
|
|
||||||
<textarea value={topic.angle} onChange={(e) => updateTopic(topic.id, "angle", e.target.value)} placeholder="What angle does this take?" style={{ fontSize: 13, color: C.ink2, border: `1px solid ${C.border2}`, borderRadius: 8, padding: "9px 12px", outline: "none", resize: "none", height: 72, background: C.surface, lineHeight: 1.6 }} />
|
|
||||||
<div style={{ display: "flex", gap: 8 }}>
|
|
||||||
<button onClick={() => setEditingTopic(null)} style={{ flex: 1, background: C.surface, color: C.ink3, border: `1px solid ${C.border2}`, borderRadius: 8, padding: "9px", fontSize: 13, cursor: "pointer" }}>Done</button>
|
|
||||||
<button onClick={() => { removeTopic(topic.id); setEditingTopic(null); }} style={{ background: "transparent", color: C.rose, border: "1px solid #fecdd3", borderRadius: 8, padding: "9px 14px", fontSize: 12, cursor: "pointer" }}>Remove</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ padding: "16px 20px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, marginBottom: 8 }}>
|
|
||||||
<div style={{ fontSize: 14, fontWeight: 700, color: C.ink, lineHeight: 1.4 }}>{topic.title || "Untitled topic"}</div>
|
|
||||||
<button onClick={() => setEditingTopic(topic.id)} style={{ fontSize: 12, color: C.ink3, background: "transparent", border: `1px solid ${C.border2}`, borderRadius: 7, padding: "4px 10px", cursor: "pointer", flexShrink: 0 }}>Edit</button>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 13, color: C.ink3, lineHeight: 1.6, marginBottom: 12 }}>{topic.angle}</div>
|
|
||||||
<div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
|
|
||||||
<span style={{ fontSize: 10.5, fontWeight: 600, color: C.muted }}>Generates:</span>
|
|
||||||
{topic.channels.map((ch) => <span key={ch} style={{ fontSize: 11, fontWeight: 600, color: C.ink3, background: C.surface, border: `1px solid ${C.border2}`, borderRadius: 5, padding: "2px 8px" }}>{ch}</span>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{topics.length < 5 && (
|
|
||||||
<button onClick={addTopic} style={{ padding: "14px", background: "transparent", border: `1.5px dashed ${C.border2}`, borderRadius: 14, fontSize: 13.5, color: C.muted, cursor: "pointer", fontWeight: 500 }}>+ Add a topic</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div style={{ marginTop: 16, padding: "13px 16px", background: C.amberBg, border: `1px solid ${C.amberBorder}`, borderRadius: 11 }}>
|
|
||||||
<div style={{ fontSize: 12.5, color: C.amberText, lineHeight: 1.6 }}>💡 Start with 3 topics — you can always add more after launch. AI will generate all the content once your product is live.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{tab === "style" && (
|
|
||||||
<div style={{ flex: 1, overflow: "hidden", display: "flex" }}>
|
|
||||||
<div style={{ width: 260, borderRight: `1px solid ${C.border}`, overflowY: "auto", background: C.white, padding: "20px 18px", display: "flex", flexDirection: "column", gap: 8 }}>
|
|
||||||
<div style={{ fontSize: 12, color: C.muted, lineHeight: 1.6, marginBottom: 6 }}>How should your marketing website feel to visitors?</div>
|
|
||||||
{WEBSITE_FEELS.map((f) => {
|
|
||||||
const isSel = websiteFeel === f.id;
|
|
||||||
const isDark = f.bg === "#0c0a09" || f.bg === "#0f172a";
|
|
||||||
return (
|
|
||||||
<button key={f.id} onClick={() => setWebsiteFeel(f.id)}
|
|
||||||
style={{ borderRadius: 12, border: isSel ? `2px solid ${C.ink}` : `1px solid ${C.border2}`, background: f.bg, cursor: "pointer", textAlign: "left", padding: "12px 14px", transition: "all 0.15s", boxShadow: isSel ? "0 2px 12px rgba(0,0,0,0.08)" : "none" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 700, color: isDark ? "#fff" : C.ink }}>{f.label}</div>
|
|
||||||
{isSel && <span style={{ color: C.green, fontSize: 12, fontWeight: 900 }}>✓</span>}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: 11.5, color: isDark ? "rgba(255,255,255,0.5)" : C.muted }}>{f.ref}</div>
|
|
||||||
<div style={{ display: "flex", gap: 5, marginTop: 9 }}>
|
|
||||||
{[f.bg, f.accent, "#f1f5f9"].map((col, i) => <div key={i} style={{ width: 12, height: 12, borderRadius: "50%", background: col, border: `1.5px solid ${isDark ? "rgba(255,255,255,0.15)" : "rgba(0,0,0,0.1)"}` }} />)}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, background: "#dde3ea", display: "flex", flexDirection: "column" }}>
|
|
||||||
<div style={{ padding: "11px 18px", background: C.white, borderBottom: `1px solid ${C.border}`, display: "flex", alignItems: "center", gap: 8 }}>
|
|
||||||
<div style={{ display: "flex", gap: 5 }}>{["#f43f5e", "#f59e0b", "#22c55e"].map((c) => <div key={c} style={{ width: 10, height: 10, borderRadius: "50%", background: c }} />)}</div>
|
|
||||||
<span style={{ fontFamily: "monospace", fontSize: 11.5, color: C.muted }}>yourproduct.com</span>
|
|
||||||
<span style={{ marginLeft: "auto", fontSize: 11.5, fontWeight: 600, color: C.ink3 }}>{WEBSITE_FEELS.find((f) => f.id === websiteFeel)?.label}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, padding: 24, overflow: "hidden" }}>
|
|
||||||
<WebsitePreview feel={websiteFeel} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── PHASE: BUILD ─────────────────────────────────────────────────────────────
|
|
||||||
function BuildPhase({ prd, arch, design, market }) {
|
|
||||||
const [building, setBuilding] = useState(false);
|
|
||||||
const [step, setStep] = useState(0);
|
|
||||||
const [done, setDone] = useState(false);
|
|
||||||
|
|
||||||
const BUILD_STEPS = [
|
|
||||||
{ label: "Creating Gitea repository", detail: "Setting up version control for your project" },
|
|
||||||
{ label: "Scaffolding the app", detail: "Next.js project structure, TypeScript, Tailwind" },
|
|
||||||
{ label: "Setting up your database", detail: "PostgreSQL + schema based on your product plan" },
|
|
||||||
{ label: "Building sign up & login", detail: arch?.auth === "email_social" ? "Email + Google + GitHub" : "Email & password" },
|
|
||||||
{ label: "Wiring payments", detail: arch?.payments === "free" ? "Skipped — no payments yet" : "Stripe checkout, webhooks, billing portal" },
|
|
||||||
{ label: "Generating all app pages", detail: "Dashboard, settings, onboarding, invite flow" },
|
|
||||||
{ label: "Applying your design", detail: `${DESIGN_FEELS.find((f) => f.id === design?.feel)?.label || "Clean & focused"} theme` },
|
|
||||||
{ label: "Building your marketing website", detail: `${WEBSITE_FEELS.find((f) => f.id === market?.websiteFeel)?.label || "Startup energy"} style` },
|
|
||||||
{ label: "Setting up email", detail: "Welcome, password reset, and marketing templates" },
|
|
||||||
{ label: "Pushing to Gitea", detail: "Full codebase committed and pushed" },
|
|
||||||
{ label: "Deploying via Coolify", detail: "Building Docker image, deploying to your servers" },
|
|
||||||
{ label: "Running health checks", detail: "Verifying all pages, auth, and payments are live" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const start = () => {
|
|
||||||
setBuilding(true);
|
|
||||||
let s = 0;
|
|
||||||
const iv = setInterval(() => {
|
|
||||||
s++;
|
|
||||||
setStep(s);
|
|
||||||
if (s >= BUILD_STEPS.length) { clearInterval(iv); setTimeout(() => setDone(true), 600); }
|
|
||||||
}, 800);
|
|
||||||
};
|
|
||||||
|
|
||||||
const feel = DESIGN_FEELS.find((f) => f.id === design?.feel) || DESIGN_FEELS[0];
|
|
||||||
|
|
||||||
if (building) {
|
|
||||||
return (
|
|
||||||
<div style={{ height: "100%", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 40, background: C.surface }}>
|
|
||||||
<div style={{ width: "100%", maxWidth: 520 }}>
|
|
||||||
<div style={{ textAlign: "center", marginBottom: 28 }}>
|
|
||||||
{done ? (
|
|
||||||
<>
|
|
||||||
<div style={{ fontSize: 36, marginBottom: 12 }}>🚀</div>
|
|
||||||
<div style={{ fontSize: 26, fontWeight: 800, color: C.ink, letterSpacing: "-0.04em", marginBottom: 8 }}>Your MVP is live</div>
|
|
||||||
<div style={{ fontSize: 14, color: C.muted }}>Deployed to Coolify · Pushed to Gitea · Ready to share</div>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div style={{ fontSize: 22, fontWeight: 800, color: C.ink, letterSpacing: "-0.04em", marginBottom: 6 }}>Building your product…</div>
|
|
||||||
<div style={{ fontSize: 13.5, color: C.muted }}>Step {Math.min(step, BUILD_STEPS.length)} of {BUILD_STEPS.length}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, overflow: "hidden", marginBottom: 20 }}>
|
|
||||||
{BUILD_STEPS.map((s, i) => {
|
|
||||||
const isActive = i === step - 1 && !done;
|
|
||||||
const isDoneStep = i < step;
|
|
||||||
return (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 16px", borderBottom: i < BUILD_STEPS.length - 1 ? `1px solid ${C.border}` : "none", background: isActive ? C.purpleBg : "transparent", transition: "background 0.3s" }}>
|
|
||||||
<div style={{ width: 22, height: 22, borderRadius: "50%", background: isDoneStep ? C.green : isActive ? C.purple : C.border2, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
|
|
||||||
{isDoneStep ? <span style={{ color: C.white, fontSize: 9, fontWeight: 900 }}>✓</span> : isActive ? <span style={{ color: C.white, fontSize: 9, display: "inline-block", animation: "vspin 1s linear infinite" }}>◎</span> : null}
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontSize: 13, fontWeight: isActive ? 600 : 400, color: isDoneStep ? C.ink3 : isActive ? C.purple : C.muted }}>{s.label}</div>
|
|
||||||
{(isActive || isDoneStep) && <div style={{ fontSize: 11.5, color: C.muted, marginTop: 1 }}>{s.detail}</div>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
{done && (
|
|
||||||
<>
|
|
||||||
<div style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, padding: "18px 20px", marginBottom: 16 }}>
|
|
||||||
<div style={{ fontSize: 12, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.07em", marginBottom: 12 }}>Your next 3 actions</div>
|
|
||||||
{[{ n: "1", label: "Open your live app", desc: "Share the URL with 5 real people today." }, { n: "2", label: "Sign up as a user", desc: "Go through your own onboarding." }, { n: "3", label: "Post your first topic", desc: "AI has drafted your first content batch." }].map((a) => (
|
|
||||||
<div key={a.n} style={{ display: "flex", gap: 12, padding: "10px 0", borderBottom: a.n !== "3" ? `1px solid ${C.border}` : "none" }}>
|
|
||||||
<div style={{ width: 24, height: 24, borderRadius: "50%", background: C.ink, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 700, color: C.white, flexShrink: 0 }}>{a.n}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 600, color: C.ink, marginBottom: 2 }}>{a.label}</div>
|
|
||||||
<div style={{ fontSize: 12.5, color: C.muted, lineHeight: 1.5 }}>{a.desc}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: 10 }}>
|
|
||||||
<button style={{ flex: 2, background: C.ink, color: C.white, border: "none", borderRadius: 11, padding: "14px", fontSize: 14, fontWeight: 700, cursor: "pointer" }}>Open my app ↗</button>
|
|
||||||
<button style={{ flex: 1, background: C.white, color: C.ink3, border: `1px solid ${C.border2}`, borderRadius: 11, padding: "14px", fontSize: 13, cursor: "pointer" }}>View in Gitea ↗</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const summaryItems = [
|
|
||||||
{ label: "Sign up & login", value: ARCH_BLOCKS.find((b) => b.id === "auth")?.alts.find((a) => a.id === arch?.auth)?.label || "Email + social login", color: C.rose, icon: "◎" },
|
|
||||||
{ label: "Payments", value: ARCH_BLOCKS.find((b) => b.id === "payments")?.alts.find((a) => a.id === arch?.payments)?.label || "Subscription billing", color: C.amber, icon: "◈" },
|
|
||||||
{ label: "Email", value: ARCH_BLOCKS.find((b) => b.id === "email")?.alts.find((a) => a.id === arch?.email)?.label || "Transactional + marketing", color: C.teal, icon: "✦" },
|
|
||||||
{ label: "Product style", value: feel.label, color: C.purple, icon: "◇" },
|
|
||||||
{ label: "Website style", value: WEBSITE_FEELS.find((f) => f.id === market?.websiteFeel)?.label || "Startup energy", color: C.blue, icon: "⬡" },
|
|
||||||
{ label: "Campaign topics", value: `${market?.topics?.length || 3} topics ready`, color: C.violet, icon: "✦" },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ height: "100%", overflowY: "auto", padding: "32px", display: "flex", justifyContent: "center", background: C.surface }}>
|
|
||||||
<div style={{ width: "100%", maxWidth: 620 }}>
|
|
||||||
<div style={{ marginBottom: 24 }}>
|
|
||||||
<div style={{ fontSize: 24, fontWeight: 800, color: C.ink, letterSpacing: "-0.04em", marginBottom: 6 }}>Ready to build</div>
|
|
||||||
<div style={{ fontSize: 14, color: C.muted, lineHeight: 1.6 }}>Review everything below. Once you hit Build, AI will code your full product and deploy it to your live URL.</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, overflow: "hidden", marginBottom: 16 }}>
|
|
||||||
<div style={{ padding: "14px 20px", borderBottom: `1px solid ${C.border}` }}>
|
|
||||||
<span style={{ fontSize: 11, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.07em" }}>What's being built</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0 }}>
|
|
||||||
{summaryItems.map((item, i) => (
|
|
||||||
<div key={i} style={{ padding: "14px 20px", borderRight: i % 2 === 0 ? `1px solid ${C.border}` : "none", borderBottom: i < summaryItems.length - 2 ? `1px solid ${C.border}` : "none", display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 28, height: 28, borderRadius: 8, background: `${item.color}15`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 12, color: item.color, flexShrink: 0 }}>{item.icon}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 11, color: C.muted, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 2 }}>{item.label}</div>
|
|
||||||
<div style={{ fontSize: 13.5, fontWeight: 600, color: C.ink2 }}>{item.value}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ background: C.white, borderRadius: 14, border: `1px solid ${C.border2}`, overflow: "hidden", marginBottom: 16 }}>
|
|
||||||
<div style={{ padding: "14px 20px", borderBottom: `1px solid ${C.border}`, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontSize: 11, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.07em" }}>Pages</span>
|
|
||||||
<span style={{ fontSize: 12, color: C.muted }}>{PAGES_GENERATED.reduce((a, g) => a + g.pages.length, 0)} pages total</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ padding: "16px 20px", display: "flex", gap: 20, flexWrap: "wrap" }}>
|
|
||||||
{PAGES_GENERATED.map((g) => (
|
|
||||||
<div key={g.group}>
|
|
||||||
<div style={{ fontSize: 10, fontWeight: 700, color: g.color, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, opacity: 0.8 }}>{g.group}</div>
|
|
||||||
{g.pages.map((p) => <div key={p} style={{ fontSize: 13, color: C.ink3, marginBottom: 4, display: "flex", gap: 6, alignItems: "center" }}><span style={{ fontSize: 8, color: g.color }}>●</span>{p}</div>)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ background: C.violetBg, border: "1px solid #ddd6fe", borderRadius: 12, padding: "14px 18px", marginBottom: 24, display: "flex", gap: 12 }}>
|
|
||||||
<span style={{ fontSize: 16, color: C.violet }}>◉</span>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontSize: 13, fontWeight: 700, color: C.violet, marginBottom: 3 }}>Deploying to your infrastructure</div>
|
|
||||||
<div style={{ fontSize: 12.5, color: "#5b21b6", lineHeight: 1.6 }}>Code is pushed to your Gitea repo. Coolify auto-deploys on push. Your domain, SSL, and database are all pre-configured.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button onClick={start} style={{ width: "100%", background: C.ink, color: C.white, border: "none", borderRadius: 13, padding: "18px", fontSize: 17, fontWeight: 800, cursor: "pointer", letterSpacing: "-0.02em", marginBottom: 10 }}>
|
|
||||||
▲ Build my MVP
|
|
||||||
</button>
|
|
||||||
<div style={{ fontSize: 12.5, color: C.muted, textAlign: "center" }}>~15 minutes · All pages built in parallel · You can refine everything after it's live</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── FLOATING CHAT ────────────────────────────────────────────────────────────
|
|
||||||
const STARTERS = {
|
|
||||||
welcome: ["What exactly gets built?", "How long does the whole thing take?", "What do I need to prepare?"],
|
|
||||||
discover: ["Help me think through my idea", "What makes a good SaaS product?", "Who should I target first?"],
|
|
||||||
architect: ["Why did you choose these components?", "What's the difference between these options?", "Can I change things after it's built?"],
|
|
||||||
design: ["Which style works best for B2B?", "Can I change it after launch?", "What do the reference products look like?"],
|
|
||||||
market: ["How should I describe my voice?", "What makes a good campaign topic?", "How much content will be generated?"],
|
|
||||||
build: ["What gets built first?", "What do I do when it's live?", "Can I make changes during the build?"],
|
|
||||||
};
|
|
||||||
|
|
||||||
function FloatingChat({ msgs, onSend, phase, onClose }) {
|
|
||||||
const [input, setInput] = useState("");
|
|
||||||
const bottomRef = useRef(null);
|
|
||||||
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); }, [msgs]);
|
|
||||||
|
|
||||||
const send = (val) => {
|
|
||||||
const text = val || input; if (!text.trim()) return; setInput("");
|
|
||||||
onSend({ role: "user", text });
|
|
||||||
setTimeout(() => onSend({ role: "assistant", text: "Good question. Based on everything you've told me so far, I'd say: focus on getting to a live URL first. Every decision you're making now can be refined after launch." }), 600);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ position: "fixed", bottom: 80, right: 24, width: 304, background: C.white, borderRadius: 18, boxShadow: "0 8px 40px rgba(0,0,0,0.18)", border: `1px solid ${C.border2}`, zIndex: 500, display: "flex", flexDirection: "column", overflow: "hidden", animation: "slideup 0.2s ease" }}>
|
|
||||||
<div style={{ padding: "12px 14px", background: C.ink, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: C.green, animation: "vpulse 2s infinite" }} />
|
|
||||||
<span style={{ fontSize: 13, fontWeight: 700, color: C.white }}>Assist</span>
|
|
||||||
<span style={{ fontSize: 11, color: "rgba(255,255,255,0.35)", textTransform: "capitalize" }}>· {phase}</span>
|
|
||||||
</div>
|
|
||||||
<button onClick={onClose} style={{ background: "transparent", border: "none", color: "rgba(255,255,255,0.45)", cursor: "pointer", fontSize: 20 }}>×</button>
|
|
||||||
</div>
|
|
||||||
<div style={{ maxHeight: 220, overflowY: "auto", padding: "12px 14px", display: "flex", flexDirection: "column", gap: 8 }}>
|
|
||||||
{msgs.map((msg, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", flexDirection: "column", alignItems: msg.role === "user" ? "flex-end" : "flex-start" }}>
|
|
||||||
<div style={{ maxWidth: "90%", background: msg.role === "user" ? C.ink : C.surface, color: msg.role === "user" ? C.white : C.ink2, borderRadius: msg.role === "user" ? "12px 12px 3px 12px" : "12px 12px 12px 3px", padding: "8px 11px", fontSize: 12.5, lineHeight: 1.55, border: msg.role === "assistant" ? `1px solid ${C.border}` : "none" }}>{msg.text}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div ref={bottomRef} />
|
|
||||||
</div>
|
|
||||||
{msgs.length < 2 && (
|
|
||||||
<div style={{ padding: "0 10px 8px", display: "flex", flexDirection: "column", gap: 4 }}>
|
|
||||||
{(STARTERS[phase] || []).map((s, i) => (
|
|
||||||
<button key={i} onClick={() => send(s)} style={{ fontSize: 12, color: C.ink3, background: C.surface, border: `1px solid ${C.border2}`, borderRadius: 7, padding: "6px 10px", cursor: "pointer", textAlign: "left" }}>{s}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div style={{ padding: "8px 10px 12px", borderTop: `1px solid ${C.border}`, display: "flex", gap: 6 }}>
|
|
||||||
<input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} placeholder="Ask anything…" style={{ flex: 1, border: `1px solid ${C.border2}`, borderRadius: 8, padding: "7px 10px", fontSize: 12.5, color: C.ink, outline: "none", background: C.surface }} />
|
|
||||||
<button onClick={() => send()} style={{ background: C.ink, color: C.white, border: "none", borderRadius: 8, padding: "7px 12px", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>→</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── ROOT ─────────────────────────────────────────────────────────────────────
|
|
||||||
const SIDEBAR_PHASES = PHASES.filter((p) => p.id !== "welcome");
|
|
||||||
const CHECKLIST = [
|
|
||||||
{ key: "prd", label: "Product plan", done: (p) => p !== "welcome" },
|
|
||||||
{ key: "arch", label: "Architecture", done: (p) => ["architect", "design", "market", "build"].includes(p) },
|
|
||||||
{ key: "design", label: "Product design", done: (p) => ["design", "market", "build"].includes(p) },
|
|
||||||
{ key: "market", label: "Marketing", done: (p) => ["market", "build"].includes(p) },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [phase, setPhase] = useState("website");
|
|
||||||
const [prd, setPrd] = useState({});
|
|
||||||
const [arch, setArch] = useState({});
|
|
||||||
const [design, setDesign] = useState({});
|
|
||||||
const [market, setMarket] = useState({ topics: SUGGESTED_TOPICS() });
|
|
||||||
const [chatOpen, setChatOpen] = useState(false);
|
|
||||||
const [chatMsgs, setChatMsgs] = useState([{ role: "assistant", text: "I'm here throughout the whole setup. Ask me anything." }]);
|
|
||||||
|
|
||||||
const goTo = (id) => setPhase(id);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ minHeight: "100vh", background: C.surface, display: "flex", flexDirection: "row" }}>
|
|
||||||
{["discover","architect","design","market","build"].includes(phase) && (
|
|
||||||
<aside style={{ width: 220, minWidth: 220, background: C.white, borderRight: `1px solid ${C.border2}`, flexShrink: 0, minHeight: "100vh", display: "flex", flexDirection: "column" }}>
|
|
||||||
{/* Logo */}
|
|
||||||
<div style={{ padding: "18px 18px 16px", display: "flex", alignItems: "center", gap: 9, borderBottom: `1px solid ${C.border}` }}>
|
|
||||||
<div style={{ width: 28, height: 28, borderRadius: 7, background: C.ink, display: "flex", alignItems: "center", justifyContent: "center", color: C.white, fontSize: 13, fontWeight: 700, letterSpacing: "-0.04em", flexShrink: 0 }}>v</div>
|
|
||||||
<span style={{ fontSize: 15, fontWeight: 700, color: C.ink, letterSpacing: "-0.04em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Progress */}
|
|
||||||
<div style={{ padding: "16px 16px 12px", borderBottom: `1px solid ${C.border}` }}>
|
|
||||||
<div style={{ fontSize: 10, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 10 }}>Progress</div>
|
|
||||||
{CHECKLIST.map((c) => (
|
|
||||||
<div key={c.key} style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 7, fontSize: 12.5, color: c.done(phase) ? C.ink3 : C.muted }}>
|
|
||||||
<span style={{ color: c.done(phase) ? C.green : C.border2, fontSize: 10, flexShrink: 0 }}>{c.done(phase) ? "✓" : "○"}</span>
|
|
||||||
{c.label}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Phase nav */}
|
|
||||||
<div style={{ padding: "12px 10px", flex: 1 }}>
|
|
||||||
<div style={{ fontSize: 10, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: "0.08em", padding: "0 8px", marginBottom: 6 }}>Phases</div>
|
|
||||||
{SIDEBAR_PHASES.map((p) => (
|
|
||||||
<button
|
|
||||||
key={p.id}
|
|
||||||
onClick={() => goTo(p.id)}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 9,
|
|
||||||
padding: "8px 10px",
|
|
||||||
marginBottom: 2,
|
|
||||||
borderRadius: 6,
|
|
||||||
border: "none",
|
|
||||||
background: phase === p.id ? C.purpleBg : "transparent",
|
|
||||||
color: phase === p.id ? C.purple : C.ink3,
|
|
||||||
cursor: "pointer",
|
|
||||||
textAlign: "left",
|
|
||||||
fontSize: 13,
|
|
||||||
fontWeight: phase === p.id ? 600 : 500,
|
|
||||||
transition: "background 0.12s",
|
|
||||||
}}
|
|
||||||
onMouseEnter={(e) => { if (phase !== p.id) e.currentTarget.style.background = C.surface; }}
|
|
||||||
onMouseLeave={(e) => { if (phase !== p.id) e.currentTarget.style.background = "transparent"; }}
|
|
||||||
>
|
|
||||||
<span style={{ opacity: 0.6, fontSize: 12, width: 16, textAlign: "center", flexShrink: 0 }}>{p.icon}</span>
|
|
||||||
{p.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* User */}
|
|
||||||
<div style={{ padding: "14px 18px", borderTop: `1px solid ${C.border}`, display: "flex", alignItems: "center", gap: 9 }}>
|
|
||||||
<div style={{ width: 28, height: 28, borderRadius: "50%", background: C.surface, border: `1px solid ${C.border2}`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 11, fontWeight: 700, color: C.ink3, flexShrink: 0 }}>M</div>
|
|
||||||
<div style={{ overflow: "hidden" }}>
|
|
||||||
<div style={{ fontSize: 12.5, fontWeight: 600, color: C.ink, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>Mark</div>
|
|
||||||
<div style={{ fontSize: 11, color: C.muted }}>Pro plan</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
)}
|
|
||||||
<main style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
|
|
||||||
{phase === "welcome" && <WelcomePhase onStart={() => setPhase("discover")} />}
|
|
||||||
{phase === "website" && <Website onGetStarted={() => setPhase("welcome")} onLogin={() => setPhase("dashboard")} />}
|
|
||||||
{phase === "dashboard" && (
|
|
||||||
<Dashboard
|
|
||||||
onNewProject={() => setPhase("welcome")}
|
|
||||||
onContinueBuilding={() => setPhase("build")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{phase === "discover" && <DiscoverPhase onComplete={(data) => { setPrd(data); setPhase("architect"); }} />}
|
|
||||||
{phase === "architect" && <ArchitectPhase onComplete={(data) => { setArch(data); setPhase("design"); }} />}
|
|
||||||
{phase === "design" && <DesignPhase onComplete={(data) => { setDesign(data); setPhase("market"); }} />}
|
|
||||||
{phase === "market" && <MarketPhase prd={prd} onComplete={(data) => { setMarket(data); setPhase("build"); }} />}
|
|
||||||
{phase === "build" && <BuildPhase prd={prd} arch={arch} design={design} market={market} />}
|
|
||||||
</main>
|
|
||||||
{["discover","architect","design","market","build"].includes(phase) && !chatOpen && (
|
|
||||||
<button
|
|
||||||
onClick={() => setChatOpen(true)}
|
|
||||||
style={{
|
|
||||||
position: "fixed",
|
|
||||||
bottom: 24,
|
|
||||||
right: 24,
|
|
||||||
width: 56,
|
|
||||||
height: 56,
|
|
||||||
borderRadius: "50%",
|
|
||||||
background: C.ink,
|
|
||||||
color: C.white,
|
|
||||||
border: "none",
|
|
||||||
boxShadow: "0 4px 20px rgba(0,0,0,0.2)",
|
|
||||||
cursor: "pointer",
|
|
||||||
fontSize: 20,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
zIndex: 400,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
💬
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{["discover","architect","design","market","build"].includes(phase) && chatOpen && (
|
|
||||||
<FloatingChat
|
|
||||||
msgs={chatMsgs}
|
|
||||||
onSend={(msg) => setChatMsgs((m) => [...m, msg])}
|
|
||||||
phase={phase}
|
|
||||||
onClose={() => setChatOpen(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,497 +0,0 @@
|
|||||||
// vibn — Projects Dashboard
|
|
||||||
// Restyled from original (DM Sans + purple/colour accents) → Ink & parchment
|
|
||||||
// Design: Lora serif + Inter sans, #1a1510 ink, #f7f4ee paper, no colour accent
|
|
||||||
// Usage: default export, no required props
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
border2:"#d3d1c7",
|
|
||||||
};
|
|
||||||
|
|
||||||
const F = { serif: "'Lora', Georgia, serif", sans: "'Inter', sans-serif" };
|
|
||||||
|
|
||||||
// ─── Shared primitives ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function StatusPill({ label, variant = "default" }) {
|
|
||||||
const styles = {
|
|
||||||
live: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
building: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
default: { bg: T.paper, text: T.muted, border: T.border },
|
|
||||||
invoiced: { bg: T.ink, text: T.paper, border: T.ink },
|
|
||||||
unbilled: { bg: T.cream, text: T.ink3, border: T.border },
|
|
||||||
scheduled: { bg: T.parch, text: T.ink2, border: T.border2 },
|
|
||||||
};
|
|
||||||
const s = styles[variant] || styles.default;
|
|
||||||
return (
|
|
||||||
<span style={{
|
|
||||||
fontFamily: F.sans, fontSize: 10.5, fontWeight: 600,
|
|
||||||
color: s.text, background: s.bg,
|
|
||||||
border: `1px solid ${s.border}`,
|
|
||||||
borderRadius: 5, padding: "2px 8px", whiteSpace: "nowrap",
|
|
||||||
}}>{label}</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function InkBtn({ children, onClick, small, outline }) {
|
|
||||||
return (
|
|
||||||
<button onClick={onClick} style={{
|
|
||||||
fontFamily: F.sans, fontWeight: 600,
|
|
||||||
fontSize: small ? 12 : 13.5,
|
|
||||||
padding: small ? "6px 14px" : "10px 22px",
|
|
||||||
background: outline ? "transparent" : T.ink,
|
|
||||||
color: outline ? T.ink3 : T.paper,
|
|
||||||
border: outline ? `1px solid ${T.border2}` : "none",
|
|
||||||
borderRadius: 8, cursor: "pointer",
|
|
||||||
}}>{children}</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Nav ───────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ screen, setScreen, onNewProject }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.white, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 32px", height: 60, display: "flex",
|
|
||||||
alignItems: "center", justifyContent: "space-between",
|
|
||||||
}}>
|
|
||||||
<div onClick={() => setScreen("projects")} style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer" }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 700, color: T.paper }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 18, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 22 }}>
|
|
||||||
{screen === "billing" && (
|
|
||||||
<span onClick={() => setScreen("projects")} style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>← All projects</span>
|
|
||||||
)}
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted, cursor: "pointer" }}>Settings</span>
|
|
||||||
<div style={{ width: 30, height: 30, borderRadius: "50%", background: T.ink3, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.sans, fontSize: 11, color: T.paper, fontWeight: 700 }}>MH</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Data ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PROJECTS = [
|
|
||||||
{
|
|
||||||
id: "launchpad", label: "Launchpad", initial: "L",
|
|
||||||
type: "own", status: "live", url: "launchpad.vibn.app",
|
|
||||||
stats: { visitors: "2.4k", signups: 183, mrr: "$840" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "flowmatic", label: "Flowmatic", initial: "F",
|
|
||||||
type: "client", status: "live", url: "flowmatic.app",
|
|
||||||
client: "Acme Corp",
|
|
||||||
stats: { visitors: "890", signups: 54, mrr: "$210" },
|
|
||||||
costs: { total: 48.20, llm: 29.20, compute: 11.60, other: 7.40, billed: false },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "taskly", label: "Taskly", initial: "T",
|
|
||||||
type: "client", status: "building", url: null,
|
|
||||||
client: "Beta Labs", buildProgress: 60,
|
|
||||||
costs: { total: 12.40, llm: 9.20, compute: 3.20, other: 0, billed: false },
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ACTIVITY = [
|
|
||||||
{ text: "Launchpad — Blog post published:", detail: '"How to launch faster with AI"', time: "2h ago" },
|
|
||||||
{ text: "Flowmatic — New signup:", detail: "marcus@email.com", time: "4h ago" },
|
|
||||||
{ text: "Taskly — Checkout page built and deployed", detail: "", time: "6h ago" },
|
|
||||||
{ text: "Launchpad — Newsletter #12", detail: "scheduled", time: "Yesterday" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const BILLING_ROWS = [
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme Corp", llm: 29.20, compute: 11.60, other: 7.40, total: 48.20, billed: false },
|
|
||||||
{ label: "Taskly", initial: "T", client: "Beta Labs", llm: 9.20, compute: 3.20, other: 0, total: 12.40, billed: false },
|
|
||||||
{ label: "Flowmatic", initial: "F", client: "Acme · Feb", llm: 22.10, compute: 8.40, other: 4.20, total: 34.70, billed: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
const COST_LOG = [
|
|
||||||
{ time: "2h ago", desc: "LLM: Homepage copy generation", project: "Flowmatic", cost: 0.82 },
|
|
||||||
{ time: "3h ago", desc: "LLM: Checkout page code", project: "Taskly", cost: 1.24 },
|
|
||||||
{ time: "5h ago", desc: "LLM: Weekly newsletter draft", project: "Flowmatic", cost: 0.43 },
|
|
||||||
{ time: "6h ago", desc: "Compute: Build pipeline run", project: "Taskly", cost: 0.18 },
|
|
||||||
{ time: "8h ago", desc: "LLM: Discover phase Q&A", project: "Flowmatic", cost: 0.31 },
|
|
||||||
{ time: "Yesterday", desc: "Email delivery · 240 recipients", project: "Flowmatic", cost: 0.96 },
|
|
||||||
];
|
|
||||||
|
|
||||||
// ─── Project card ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectCard({ project, onContinue }) {
|
|
||||||
const isClient = project.type === "client";
|
|
||||||
const isBuilding = project.status === "building";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
|
|
||||||
{/* Header preview */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<div style={{ height: 100, background: T.cream, borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11.5, color: T.muted, fontWeight: 500, marginBottom: 10 }}>
|
|
||||||
Build phase · {project.buildProgress}% complete
|
|
||||||
</div>
|
|
||||||
<div style={{ width: 160, height: 4, background: T.parch, borderRadius: 3, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${project.buildProgress}%`, height: "100%", background: T.ink3, borderRadius: 3 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{isClient && (
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.mid, background: T.white, border: `1px solid ${T.border}`, borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
Client
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div style={{ height: 100, background: T.ink, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" }}>
|
|
||||||
<div style={{ background: "rgba(247,244,238,0.1)", borderRadius: 8, width: "55%", padding: "10px 14px" }}>
|
|
||||||
<div style={{ height: 8, background: "rgba(247,244,238,0.6)", borderRadius: 3, width: "65%", marginBottom: 6 }} />
|
|
||||||
<div style={{ height: 5, background: "rgba(247,244,238,0.25)", borderRadius: 3, width: "85%" }} />
|
|
||||||
</div>
|
|
||||||
<div style={{ position: "absolute", top: 10, right: 12, fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: "rgba(247,244,238,0.6)", background: "rgba(247,244,238,0.1)", borderRadius: 5, padding: "2px 8px" }}>
|
|
||||||
{isClient ? "Client" : "My product"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ padding: "18px 20px" }}>
|
|
||||||
{/* Identity row */}
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 28, height: 28, background: T.ink, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 12, color: T.paper, fontWeight: 700 }}>
|
|
||||||
{project.initial}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>{project.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>
|
|
||||||
{isClient ? `${project.client} · ` : ""}
|
|
||||||
{project.url || "Setting up pages…"}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label={isBuilding ? "Building" : "Live"} variant={isBuilding ? "building" : "live"} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Cost strip — client + building */}
|
|
||||||
{isClient && project.costs && isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs so far</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<StatusPill label="Unbilled" variant="unbilled" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Cost strip — client + live */}
|
|
||||||
{isClient && project.costs && !isBuilding && (
|
|
||||||
<div style={{ background: T.cream, border: `1px solid ${T.border}`, borderRadius: 9, padding: "10px 14px", marginBottom: 12, display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em", marginBottom: 3 }}>Costs this month</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 17, fontWeight: 700, color: T.ink }}>${project.costs.total.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.mid, lineHeight: 1.7 }}>
|
|
||||||
LLM ${project.costs.llm.toFixed(2)}<br />
|
|
||||||
Compute ${project.costs.compute.toFixed(2)}
|
|
||||||
</div>
|
|
||||||
{!project.costs.billed && (
|
|
||||||
<button style={{ background: T.ink, border: "none", color: T.paper, borderRadius: 7, padding: "7px 13px", fontFamily: F.sans, fontSize: 11.5, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Bill →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Stats */}
|
|
||||||
{!isBuilding && project.stats && (
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8, marginBottom: 14 }}>
|
|
||||||
{[["visitors", project.stats.visitors], ["signups", project.stats.signups], ["MRR", project.stats.mrr]].map(([k, v]) => (
|
|
||||||
<div key={k} style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 16, fontWeight: 700, color: T.ink }}>{v}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 10, color: T.muted }}>{k}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
{isBuilding ? (
|
|
||||||
<button onClick={onContinue} style={{ width: "100%", background: T.ink, border: "none", color: T.paper, borderRadius: 8, padding: 10, fontFamily: F.sans, fontSize: 13, fontWeight: 600, cursor: "pointer" }}>
|
|
||||||
Continue building →
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<div style={{ display: "flex", gap: 6 }}>
|
|
||||||
{[["⬡", "Build"], ["◈", "Grow"]].map(([icon, label]) => (
|
|
||||||
<div key={label} style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 5, padding: "7px 10px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, cursor: "pointer" }}>
|
|
||||||
<span style={{ fontSize: 11 }}>{icon}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.ink3 }}>{label}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ padding: "7px 12px", background: T.cream, border: `1px solid ${T.border}`, borderRadius: 7, fontFamily: F.sans, fontSize: 11.5, color: T.ink3, cursor: "pointer" }}>
|
|
||||||
↗
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Projects screen ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ProjectsScreen({ setScreen, onNewProject, onContinueBuilding }) {
|
|
||||||
const totalUnbilled = PROJECTS
|
|
||||||
.filter(p => p.type === "client" && p.costs?.billed === false)
|
|
||||||
.reduce((s, p) => s + p.costs.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "36px 32px" }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 32 }}>
|
|
||||||
<div>
|
|
||||||
<h1 style={{ fontFamily: F.serif, fontSize: 26, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", marginBottom: 4 }}>Your projects</h1>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13.5, color: T.muted }}>3 active · 1 building</p>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: 10, alignItems: "center" }}>
|
|
||||||
{totalUnbilled > 0 && (
|
|
||||||
<button onClick={() => setScreen("billing")} style={{ fontFamily: F.sans, fontSize: 13, color: T.ink3, background: T.cream, border: `1px solid ${T.border}`, borderRadius: 8, padding: "9px 16px", cursor: "pointer" }}>
|
|
||||||
${totalUnbilled.toFixed(2)} unbilled →
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<InkBtn onClick={onNewProject}>+ New project</InkBtn>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 28 }}>
|
|
||||||
{PROJECTS.map(p => (
|
|
||||||
<ProjectCard
|
|
||||||
key={p.id}
|
|
||||||
project={p}
|
|
||||||
onContinue={p.status === "building" ? onContinueBuilding : undefined}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
{/* New project CTA card */}
|
|
||||||
<div
|
|
||||||
onClick={onNewProject}
|
|
||||||
style={{ background: "transparent", border: `1px dashed ${T.parch}`, borderRadius: 14, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, padding: 40, cursor: "pointer", minHeight: 220 }}
|
|
||||||
onMouseEnter={e => e.currentTarget.style.background = T.cream}
|
|
||||||
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
|
|
||||||
>
|
|
||||||
<div style={{ width: 42, height: 42, borderRadius: 10, border: `1px solid ${T.parch}`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 22, color: T.stone }}>+</div>
|
|
||||||
<div style={{ textAlign: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>New project</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12.5, color: T.muted }}>For yourself or a client</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Activity feed */}
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 14, padding: "20px 24px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Recent activity</div>
|
|
||||||
{ACTIVITY.map((a, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 0", borderBottom: i < ACTIVITY.length - 1 ? `1px solid ${T.border}` : "none" }}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink3, flexShrink: 0 }} />
|
|
||||||
<div style={{ flex: 1, fontFamily: F.sans, fontSize: 13.5, color: T.ink }}>
|
|
||||||
{a.text}{" "}{a.detail && <span style={{ color: T.muted }}>{a.detail}</span>}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 11.5, color: T.stone, whiteSpace: "nowrap" }}>{a.time}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Billing screen ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function BillingScreen() {
|
|
||||||
const [tab, setTab] = useState("billing");
|
|
||||||
const unbilled = BILLING_ROWS.filter(r => !r.billed).reduce((s, r) => s + r.total, 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ maxWidth: 1000, margin: "0 auto", padding: "28px 32px" }}>
|
|
||||||
|
|
||||||
{/* Sub-tabs */}
|
|
||||||
<div style={{ display: "flex", borderBottom: `1px solid ${T.border}`, marginBottom: 28 }}>
|
|
||||||
{[["billing", "Client billing"], ["costs", "Cost tracker"]].map(([id, label]) => (
|
|
||||||
<button key={id} onClick={() => setTab(id)} style={{
|
|
||||||
padding: "10px 18px", border: "none", background: "transparent",
|
|
||||||
borderBottom: tab === id ? `2px solid ${T.ink}` : "2px solid transparent",
|
|
||||||
fontFamily: F.sans, fontSize: 13.5, cursor: "pointer",
|
|
||||||
color: tab === id ? T.ink : T.muted, fontWeight: tab === id ? 600 : 400,
|
|
||||||
}}>{label}</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{tab === "billing" && <>
|
|
||||||
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 22 }}>
|
|
||||||
<div>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Client billing</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>All costs tracked and ready to invoice</p>
|
|
||||||
</div>
|
|
||||||
<InkBtn>Generate invoice</InkBtn>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 10, marginBottom: 24 }}>
|
|
||||||
{[
|
|
||||||
{ label: "Total unbilled", value: `$${unbilled.toFixed(2)}` },
|
|
||||||
{ label: "LLM costs", value: "$38.40" },
|
|
||||||
{ label: "Compute", value: "$14.80" },
|
|
||||||
{ label: "Other", value: "$7.40" },
|
|
||||||
].map(c => (
|
|
||||||
<div key={c.label} style={{ background: T.cream, borderRadius: 10, padding: "14px 16px" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted, marginBottom: 5 }}>{c.label}</div>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink }}>{c.value}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}`, display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Breakdown by client</span>
|
|
||||||
<select style={{ border: `1px solid ${T.border}`, borderRadius: 6, padding: "4px 10px", fontFamily: F.sans, fontSize: 12, color: T.muted, background: T.paper, outline: "none" }}>
|
|
||||||
<option>March 2026</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Project / Client", "LLM", "Compute", "Other", "Total", "Status"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{BILLING_ROWS.map((r, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "2fr 1fr 1fr 1fr 1fr 120px", padding: "13px 20px", borderBottom: i < BILLING_ROWS.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center", opacity: r.billed ? 0.5 : 1 }}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{ width: 24, height: 24, background: T.ink, borderRadius: 6, display: "flex", alignItems: "center", justifyContent: "center", fontFamily: F.serif, fontSize: 10, color: T.paper, fontWeight: 700 }}>{r.initial}</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>{r.label}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 11, color: T.muted }}>{r.client}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.llm.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.compute.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>${r.other.toFixed(2)}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.total.toFixed(2)}</div>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
|
||||||
<StatusPill label={r.billed ? "Invoiced" : "Unbilled"} variant={r.billed ? "invoiced" : "unbilled"} />
|
|
||||||
{!r.billed && (
|
|
||||||
<button style={{ background: "transparent", border: `1px solid ${T.border2}`, borderRadius: 5, padding: "3px 9px", fontFamily: F.sans, fontSize: 11, color: T.muted, cursor: "pointer" }}>Invoice</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
|
|
||||||
{tab === "costs" && <>
|
|
||||||
<div style={{ marginBottom: 22 }}>
|
|
||||||
<h2 style={{ fontFamily: F.serif, fontSize: 22, fontWeight: 700, color: T.ink, marginBottom: 4 }}>Cost tracker</h2>
|
|
||||||
<p style={{ fontFamily: F.sans, fontSize: 13, color: T.muted }}>Every dollar spent, broken down by type and project</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14, marginBottom: 20 }}>
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>LLM usage</div>
|
|
||||||
{[
|
|
||||||
{ label: "Code generation", amount: 21.40, pct: 56 },
|
|
||||||
{ label: "Content & marketing", amount: 10.20, pct: 27 },
|
|
||||||
{ label: "Chat assist", amount: 6.80, pct: 18 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ marginBottom: 14 }}>
|
|
||||||
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 6 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12.5, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ height: 4, background: T.cream, borderRadius: 2, overflow: "hidden" }}>
|
|
||||||
<div style={{ width: `${r.pct}%`, height: "100%", background: T.ink, borderRadius: 2 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total LLM</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$38.40</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, padding: "18px 20px" }}>
|
|
||||||
<div style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 16 }}>Infrastructure</div>
|
|
||||||
{[
|
|
||||||
{ label: "Hosting & compute", amount: 11.60 },
|
|
||||||
{ label: "Database", amount: 3.20 },
|
|
||||||
{ label: "Email delivery", amount: 4.20 },
|
|
||||||
{ label: "Domain & SSL", amount: 3.20 },
|
|
||||||
].map(r => (
|
|
||||||
<div key={r.label} style={{ display: "flex", justifyContent: "space-between", padding: "8px 11px", background: T.cream, borderRadius: 7, marginBottom: 7 }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, color: T.mid }}>{r.label}</span>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${r.amount.toFixed(2)}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div style={{ marginTop: 14, paddingTop: 12, borderTop: `1px solid ${T.border}`, display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>Total infra</span>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 15, fontWeight: 700, color: T.ink }}>$22.20</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ background: T.white, border: `1px solid ${T.border}`, borderRadius: 12, overflow: "hidden" }}>
|
|
||||||
<div style={{ padding: "13px 20px", borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
<span style={{ fontFamily: F.serif, fontSize: 14, fontWeight: 600, color: T.ink }}>Recent charges</span>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "9px 20px", background: T.cream, borderBottom: `1px solid ${T.border}` }}>
|
|
||||||
{["Time", "Description", "Project", "Cost"].map(h => (
|
|
||||||
<div key={h} style={{ fontFamily: F.sans, fontSize: 10.5, fontWeight: 600, color: T.muted, textTransform: "uppercase", letterSpacing: "0.05em" }}>{h}</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{COST_LOG.map((row, i) => (
|
|
||||||
<div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 2fr 1fr 80px", padding: "11px 20px", borderBottom: i < COST_LOG.length - 1 ? `1px solid ${T.border}` : "none", alignItems: "center" }}>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.muted }}>{row.time}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, color: T.ink }}>{row.desc}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 12, color: T.mid }}>{row.project}</div>
|
|
||||||
<div style={{ fontFamily: F.sans, fontSize: 13, fontWeight: 600, color: T.ink }}>${row.cost.toFixed(2)}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Root export ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Dashboard({ onNewProject, onContinueBuilding }) {
|
|
||||||
const [screen, setScreen] = useState("projects");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ background: T.paper, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
button { font-family: inherit; cursor: pointer; }
|
|
||||||
input, select { font-family: inherit; }
|
|
||||||
::-webkit-scrollbar { width: 4px; }
|
|
||||||
::-webkit-scrollbar-thumb { background: ${T.parch}; border-radius: 4px; }
|
|
||||||
`}</style>
|
|
||||||
<Nav screen={screen} setScreen={setScreen} />
|
|
||||||
{screen === "projects" && (
|
|
||||||
<ProjectsScreen
|
|
||||||
setScreen={setScreen}
|
|
||||||
onNewProject={onNewProject}
|
|
||||||
onContinueBuilding={onContinueBuilding}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{screen === "billing" && <BillingScreen />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
// ── DESIGN TOKENS ─────────────────────────────────────────────────────────────
|
|
||||||
const T = {
|
|
||||||
ink: "#1a1510",
|
|
||||||
ink2: "#2c2c2a",
|
|
||||||
ink3: "#444441",
|
|
||||||
mid: "#5f5e5a",
|
|
||||||
muted: "#888780",
|
|
||||||
stone: "#b4b2a9",
|
|
||||||
parch: "#d3d1c7",
|
|
||||||
cream: "#f1efe8",
|
|
||||||
paper: "#f7f4ee",
|
|
||||||
white: "#fdfcfa",
|
|
||||||
border: "#e8e2d9",
|
|
||||||
border2: "#d3d1c7",
|
|
||||||
};
|
|
||||||
|
|
||||||
const serif = "'Lora', Georgia, serif";
|
|
||||||
const sans = "'Inter', sans-serif";
|
|
||||||
|
|
||||||
// ── SHARED COMPONENTS ─────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Nav({ onGetStarted, onLogin }) {
|
|
||||||
return (
|
|
||||||
<nav style={{
|
|
||||||
background: T.paper, borderBottom: `1px solid ${T.border}`,
|
|
||||||
padding: "0 48px", display: "flex", alignItems: "center",
|
|
||||||
justifyContent: "space-between", height: 60,
|
|
||||||
position: "sticky", top: 0, zIndex: 50,
|
|
||||||
}}>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<div style={{
|
|
||||||
width: 30, height: 30, background: T.ink, borderRadius: 7,
|
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
|
||||||
}}>
|
|
||||||
<span style={{ fontFamily: serif, fontSize: 15, fontWeight: 700, color: T.paper, lineHeight: 1 }}>V</span>
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: serif, fontSize: 19, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em" }}>vibn</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 30 }}>
|
|
||||||
{["Product", "Pricing", "Stories", "Blog"].map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: sans, fontSize: 14, color: T.muted, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
|
||||||
<button onClick={onLogin} style={{ fontFamily: sans, fontSize: 14, color: T.muted, cursor: "pointer", background: "none", border: "none", padding: 0 }}>Log in</button>
|
|
||||||
<button onClick={onGetStarted} style={{
|
|
||||||
background: T.ink, color: T.paper, fontFamily: sans,
|
|
||||||
fontSize: 13.5, fontWeight: 600, padding: "9px 22px",
|
|
||||||
border: "none", borderRadius: 8, cursor: "pointer",
|
|
||||||
}}>Get started free</button>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── HERO ──────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Hero({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "84px 48px 68px" }}>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.13em", textTransform: "uppercase", color: T.muted, marginBottom: 22 }}>
|
|
||||||
For non-technical founders
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 style={{
|
|
||||||
fontFamily: serif, fontSize: 62, fontWeight: 700, color: T.ink,
|
|
||||||
letterSpacing: "-0.03em", lineHeight: 1.07, marginBottom: 26, maxWidth: 700,
|
|
||||||
}}>
|
|
||||||
You have the idea.<br />
|
|
||||||
We handle<br />
|
|
||||||
<em style={{ fontStyle: "italic", color: T.ink3 }}>everything else.</em>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 17.5, color: T.mid, lineHeight: 1.75, maxWidth: 500, marginBottom: 38 }}>
|
|
||||||
No backend. No DevOps. No marketing agency. Describe your idea and vibn builds,
|
|
||||||
deploys, and promotes it — automatically.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap", marginBottom: 16 }}>
|
|
||||||
<button onClick={onCta} style={{
|
|
||||||
background: T.ink, color: T.paper, fontFamily: sans,
|
|
||||||
fontSize: 15, fontWeight: 600, padding: "14px 32px",
|
|
||||||
border: "none", borderRadius: 10, cursor: "pointer",
|
|
||||||
}}>
|
|
||||||
Start free — no code needed
|
|
||||||
</button>
|
|
||||||
<span style={{ fontFamily: sans, fontSize: 13.5, color: T.stone }}>
|
|
||||||
★★★★★ 280 founders launched
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 12, color: T.stone }}>
|
|
||||||
No credit card required · Free forever plan
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── PULL QUOTE BAND ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const QUOTES = [
|
|
||||||
{ text: "I had the idea for 2 years. The backend terrified me. vibn shipped it in 4 days and handles all my marketing.", name: "Alex K.", company: "founder of Taskly" },
|
|
||||||
{ text: "I have zero coding experience. Three weeks in, I have 300 paying users. That's entirely because of vibn.", name: "Marcus L.", company: "founder of Flowmatic" },
|
|
||||||
{ text: "The marketing autopilot alone saved me ten hours a week. My blog runs itself. I just focus on product.", name: "Sara R.", company: "founder of Nudge" },
|
|
||||||
];
|
|
||||||
|
|
||||||
function QuoteBand() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.ink, padding: "52px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 40 }}>
|
|
||||||
{QUOTES.map((q, i) => (
|
|
||||||
<div key={i} style={{ display: "flex", gap: 18 }}>
|
|
||||||
<div style={{ width: 3, background: T.mid, borderRadius: 2, flexShrink: 0 }} />
|
|
||||||
<div>
|
|
||||||
<p style={{ fontFamily: serif, fontSize: 15, color: T.parch, lineHeight: 1.7, fontStyle: "italic", marginBottom: 10 }}>
|
|
||||||
"{q.text}"
|
|
||||||
</p>
|
|
||||||
<span style={{ fontFamily: sans, fontSize: 11.5, color: T.muted, fontWeight: 600 }}>
|
|
||||||
— {q.name}, {q.company}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── HOW IT WORKS ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const HOW_PHASES = [
|
|
||||||
{ n: "01", phase: "Discover", title: "Define your idea", body: "Six guided questions turn a rough idea into a full product plan — pages, architecture, revenue model. No jargon, no assumptions." },
|
|
||||||
{ n: "02", phase: "Design", title: "Choose your style", body: "Pick a visual style and see your exact site and emails live before a single line of code is written." },
|
|
||||||
{ n: "03", phase: "Build", title: "Your app, live", body: "AI writes every line. Auth, database, payments, all pages — deployed and live. Describe changes in plain English." },
|
|
||||||
{ n: "04", phase: "Grow", title: "Market & automate", body: "AI generates your blog, emails, and social schedule — publishing on autopilot so you can focus entirely on your users." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function HowItWorks() {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 960, margin: "0 auto", padding: "80px 48px" }}>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.13em", textTransform: "uppercase", color: T.muted, marginBottom: 16 }}>
|
|
||||||
How it works
|
|
||||||
</div>
|
|
||||||
<h2 style={{ fontFamily: serif, fontSize: 38, fontWeight: 700, color: T.ink, letterSpacing: "-0.02em", marginBottom: 52, maxWidth: 480, lineHeight: 1.15 }}>
|
|
||||||
Four phases. One complete product.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0, border: `1px solid ${T.border}`, borderRadius: 14, overflow: "hidden" }}>
|
|
||||||
{HOW_PHASES.map((p, i) => (
|
|
||||||
<div key={i} style={{
|
|
||||||
padding: "36px 40px",
|
|
||||||
background: T.white,
|
|
||||||
borderRight: i % 2 === 0 ? `1px solid ${T.border}` : "none",
|
|
||||||
borderBottom: i < 2 ? `1px solid ${T.border}` : "none",
|
|
||||||
}}>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: T.muted, marginBottom: 16 }}>
|
|
||||||
{p.n} — {p.phase}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontFamily: serif, fontSize: 21, fontWeight: 700, color: T.ink, marginBottom: 10 }}>{p.title}</div>
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 13.5, color: T.mid, lineHeight: 1.7 }}>{p.body}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── EMPATHY SECTION ───────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
const PAINS = [
|
|
||||||
{ title: "No more \"I need to hire a developer first\"", body: "vibn is your developer. Start building the moment you have an idea." },
|
|
||||||
{ title: "No more staring at a blank marketing calendar", body: "AI generates and publishes your content every single week." },
|
|
||||||
{ title: "No more \"I'll launch when it's ready\"", body: "Most founders ship their first version in under 72 hours." },
|
|
||||||
];
|
|
||||||
|
|
||||||
function EmpathySection() {
|
|
||||||
return (
|
|
||||||
<section style={{ background: T.cream, borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}`, padding: "72px 48px" }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 64, alignItems: "center" }}>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 11, fontWeight: 600, letterSpacing: "0.13em", textTransform: "uppercase", color: T.muted, marginBottom: 18 }}>
|
|
||||||
Sound familiar?
|
|
||||||
</div>
|
|
||||||
<h2 style={{ fontFamily: serif, fontSize: 34, fontWeight: 700, color: T.ink, lineHeight: 1.2, marginBottom: 24, letterSpacing: "-0.02em" }}>
|
|
||||||
The idea is the hard part. Everything else shouldn't be.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 15, color: T.mid, lineHeight: 1.8, marginBottom: 20 }}>
|
|
||||||
You know exactly what you want to build and who it's for. But the moment you think about
|
|
||||||
servers, databases, deployment pipelines, SEO — the whole thing stalls.
|
|
||||||
</p>
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 15, color: T.mid, lineHeight: 1.8 }}>
|
|
||||||
vibn exists to remove all of that. Not abstract it —{" "}
|
|
||||||
<em style={{ fontFamily: serif, fontStyle: "italic" }}>remove it entirely.</em>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
|
|
||||||
{PAINS.map((p, i) => (
|
|
||||||
<div key={i} style={{
|
|
||||||
background: T.white, border: `1px solid ${T.border}`,
|
|
||||||
borderRadius: 10, padding: "18px 20px",
|
|
||||||
display: "flex", alignItems: "flex-start", gap: 14,
|
|
||||||
}}>
|
|
||||||
<div style={{
|
|
||||||
width: 20, height: 20, borderRadius: "50%",
|
|
||||||
border: `1.5px solid ${T.stone}`,
|
|
||||||
display: "flex", alignItems: "center", justifyContent: "center",
|
|
||||||
flexShrink: 0, marginTop: 2,
|
|
||||||
}}>
|
|
||||||
<div style={{ width: 7, height: 7, borderRadius: "50%", background: T.ink }} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div style={{ fontFamily: serif, fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4 }}>{p.title}</div>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 13, color: T.muted, lineHeight: 1.6 }}>{p.body}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── STATS BAR ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function StatsBar() {
|
|
||||||
const stats = [
|
|
||||||
{ n: "280+", label: "founders launched" },
|
|
||||||
{ n: "72h", label: "average time to first version" },
|
|
||||||
{ n: "4.9", label: "average rating" },
|
|
||||||
{ n: "3×", label: "faster than hiring a developer" },
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<section style={{ borderTop: `1px solid ${T.border}`, borderBottom: `1px solid ${T.border}`, background: T.white }}>
|
|
||||||
<div style={{ maxWidth: 960, margin: "0 auto", padding: "0 48px", display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr" }}>
|
|
||||||
{stats.map((s, i) => (
|
|
||||||
<div key={i} style={{
|
|
||||||
padding: "36px 0",
|
|
||||||
borderRight: i < 3 ? `1px solid ${T.border}` : "none",
|
|
||||||
paddingLeft: i > 0 ? 36 : 0,
|
|
||||||
}}>
|
|
||||||
<div style={{ fontFamily: serif, fontSize: 36, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", marginBottom: 6 }}>{s.n}</div>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 13, color: T.muted, lineHeight: 1.5 }}>{s.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── FINAL CTA ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function FinalCta({ onCta }) {
|
|
||||||
return (
|
|
||||||
<section style={{ maxWidth: 680, margin: "0 auto", padding: "88px 48px", textAlign: "center" }}>
|
|
||||||
<h2 style={{ fontFamily: serif, fontSize: 44, fontWeight: 700, color: T.ink, letterSpacing: "-0.03em", lineHeight: 1.1, marginBottom: 20 }}>
|
|
||||||
Your idea deserves to exist.
|
|
||||||
</h2>
|
|
||||||
<p style={{ fontFamily: sans, fontSize: 16, color: T.mid, lineHeight: 1.75, marginBottom: 36 }}>
|
|
||||||
Don't let the backend be the reason it doesn't. Start today — free, no code, no credit card.
|
|
||||||
</p>
|
|
||||||
<button onClick={onCta} style={{
|
|
||||||
display: "inline-block", background: T.ink, color: T.paper,
|
|
||||||
fontFamily: sans, fontSize: 15, fontWeight: 600,
|
|
||||||
padding: "15px 40px", border: "none", borderRadius: 10,
|
|
||||||
cursor: "pointer", marginBottom: 16,
|
|
||||||
}}>
|
|
||||||
Build my product — free
|
|
||||||
</button>
|
|
||||||
<div style={{ fontFamily: sans, fontSize: 12.5, color: T.stone }}>
|
|
||||||
Joins 280+ non-technical founders already live
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── FOOTER ────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function Footer() {
|
|
||||||
const links = ["Product", "Pricing", "Stories", "Blog", "Privacy", "Terms"];
|
|
||||||
return (
|
|
||||||
<footer style={{ borderTop: `1px solid ${T.border}`, padding: "32px 48px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
|
||||||
<span style={{ fontFamily: serif, fontSize: 16, fontWeight: 700, color: T.ink }}>vibn</span>
|
|
||||||
<div style={{ display: "flex", gap: 28 }}>
|
|
||||||
{links.map(l => (
|
|
||||||
<span key={l} style={{ fontFamily: sans, fontSize: 13, color: T.stone, cursor: "pointer" }}>{l}</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<span style={{ fontFamily: sans, fontSize: 12.5, color: T.stone }}>© 2026 vibn</span>
|
|
||||||
</footer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── ROOT EXPORT ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
export default function Website({ onGetStarted, onLogin }) {
|
|
||||||
return (
|
|
||||||
<div style={{ fontFamily: sans, background: T.paper, color: T.ink, minHeight: "100vh" }}>
|
|
||||||
<style>{`
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;0,700;1,400;1,600&family=Inter:wght@400;500;600&display=swap');
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
||||||
button { font-family: inherit; }
|
|
||||||
`}</style>
|
|
||||||
|
|
||||||
<Nav onGetStarted={onGetStarted} onLogin={onLogin} />
|
|
||||||
<Hero onCta={onGetStarted} />
|
|
||||||
<QuoteBand />
|
|
||||||
<HowItWorks />
|
|
||||||
<StatsBar />
|
|
||||||
<EmpathySection />
|
|
||||||
<FinalCta onCta={onGetStarted} />
|
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
* { box-sizing: border-box; }
|
|
||||||
body { margin: 0; font-family: system-ui, -apple-system, sans-serif; }
|
|
||||||
@keyframes vpulse {
|
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0.4; }
|
|
||||||
}
|
|
||||||
@keyframes vspin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
@keyframes slideup {
|
|
||||||
from { opacity: 0; transform: translateY(12px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import './index.css'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import App from './App.jsx'
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<App />
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
})
|
|
||||||
5
root.txt
5
root.txt
@@ -1,5 +0,0 @@
|
|||||||
1|clqAOiQPygpuwyA5HVDyWoKfNRtDH8l4Huge2PCmef521cc1
|
|
||||||
|
|
||||||
gitea
|
|
||||||
8f6b2cbc2c9722aedbd6e88b83078371fc1bf67f
|
|
||||||
|
|
||||||
Submodule vibn-frontend updated: fa9cfda712...2e03a96b20
Reference in New Issue
Block a user