Theia rip-out (parent):
- Remove theia submodule entry (the local fork, Gitea repo, Coolify app,
Cloud Run services, and Artifact Registry image are all gone)
- Drop README.md + INFRASTRUCTURE.md (obsolete "Project OS" snapshots
that also leaked API tokens) and setup.sh (Theia clone bootstrap)
- Delete UI-DESIGN-GUIDE.md, BACKEND_AGENTS_PLAN.md, VIBN_BUILD_PLAN.md,
VISUAL_EDITOR_PLAN.md, core-packages.md, ai-packages.md, tools-list.md
(all 100% Theia-specific or superseded)
- Surgical scrubs of remaining Theia mentions in
AGENT_EXECUTION_ARCHITECTURE.md and TURBOREPO_MIGRATION_PLAN.md
Submodule bumps:
- vibn-agent-runner: Theia rip-out + MCP refactor (api/wrapper/server
pattern across shell/file/git/memory/prd/search/agent/gitea/coolify)
- vibn-frontend: Theia rip-out + P5.1 attach E2E + Justine UI WIP
Retire platform/ scaffold:
- Remove platform/backend/ (control-plane, executors, mcp-adapter),
platform/client-ide/ (gcp-productos extension), platform/contracts/,
platform/infra/terraform/, platform/scripts/templates/turborepo/
(replaced by vibn-agent-runner + vibn-frontend + Coolify direct)
- Drop architecture.md, technical_spec.md, vision-ext.md,
"1.Generate Control Plane API scaffold.md" (same era)
Docs / planning snapshots (new):
- AI_CAPABILITIES.md, AI_CAPABILITIES_ROADMAP.md
- AGENT_TELEMETRY_STREAMING_PROJECT.md
- VIBN_PRD.md, product-idea-a.md
Design assets (new):
- branding/{coolify,gitea,ux-testing}/ static brand collateral
- justine/ HTML mockups for the new onboarding/build flows
- preview-assist-ui/ Vite scratch app
- master-ai.code-workspace
Infra helpers (new):
- setup-coolify-montreal.sh provisioner
- gitea-docker-compose.yml
- vibn-coolify-schema.sql for the Coolify Postgres extensions
- prd-agent-prompt.pdf, prompt, root.txt, remixed-9edec9e9.tsx scratch
- flatten.sh helper
.gitignore: ignore **/node_modules, **/.next, **/.turbo, **/coverage
Made-with: Cursor
590 lines
42 KiB
HTML
590 lines
42 KiB
HTML
<!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>
|