Files
vibn-agent-runner/justine/vibn front end/05_describe.html
mawkone 99deb546c8 Rip out Theia, bump submodules, retire platform/ scaffold, snapshot docs + design assets
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
2026-04-22 18:06:37 -07:00

1222 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"><link rel="icon" href="favicon_clean.ico">
<script>if(localStorage.getItem('vibn-theme')==='dark')document.documentElement.dataset.theme='dark';</script>
<title>vibn — Describe</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:#f7f4ee;--white:#ffffff;--border:#e5e7eb;--accent-primary:#6366f1;--accent-secondary:#8b5cf6;--accent-soft:#818cf8;--accent-dark:#2e2a5e;--accent-gradient:#4338ca;}
body{font-family:'Plus Jakarta Sans',sans-serif;background:linear-gradient(to bottom, #fafafa, #f5f3ff);display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden;}
.f{font-family:'Plus Jakarta Sans',sans-serif;}
input::placeholder{color:var(--muted);}
/* ── Dark mode tokens — exact match with architect.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);--accent-primary:#6C7CFF;--accent-secondary:#8B7CFF;--accent-soft:#8C90FF;--accent-dark:#4B42D8;--accent-gradient:#6C7CFF;
--section:rgba(108,124,255,0.55);--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%);}
/* 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-phase.active{background:var(--dm-accent-fill)!important;}
[data-theme="dark"] .sidebar-phase.active .phase-dot{color:#0F1424!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;}
/* Chat column */
[data-theme="dark"] .chat-col{background:var(--dm-surf-panel)!important;border-right:1px solid var(--dm-border)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
/* PRD panel */
[data-theme="dark"] .prd-panel{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;}
/* PRD cards */
[data-theme="dark"] .prd-card{background:rgba(255,255,255,0.06)!important;border-color:var(--dm-border)!important;}
[data-theme="dark"] .prd-card-answered{box-shadow:0 0 14px rgba(108,124,255,0.07),0 0 2px rgba(108,124,255,0.10)!important;}
[data-theme="dark"] .prd-card-active{background:var(--dm-accent-fill)!important;border-left-color:var(--dm-accent)!important;box-shadow:0 0 0 1px rgba(108,124,255,0.18),0 2px 12px rgba(108,124,255,0.12)!important;}
[data-theme="dark"] .prd-card-answered:hover{background:rgba(108,124,255,0.07)!important;border-color:var(--dm-accent-border)!important;}
[data-theme="dark"] .prd-card-pending{opacity:0.5;}
[data-theme="dark"] .prd-field-label-answered{color:var(--dm-text-3)!important;}
[data-theme="dark"] .prd-field-value-answered{color:var(--dm-text-2)!important;}
[data-theme="dark"] .prd-next-wrap{border-top-color:var(--dm-border)!important;}
/* PRD header border */
[data-theme="dark"] [style*="border-bottom:1px solid #c7d2fe"]{border-bottom-color:var(--dm-border)!important;}
[data-theme="dark"] [style*="color:#4338ca"]{color:var(--dm-accent)!important;}
[data-theme="dark"] [style*="color:#6366f1"],[data-theme="dark"] [style*="color:#6366F1"]{color:var(--dm-accent)!important;}
/* Chat bubbles */
[data-theme="dark"] .bubble-ai{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;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"] .ai-bubble{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;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"] .typing-bubble{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;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;}
/* Chat input area */
[data-theme="dark"] .chat-input-wrap{background:rgba(255,255,255,0.06)!important;border-color:var(--dm-border)!important;}
[data-theme="dark"] .chat-input-wrap:focus-within{border-color:var(--dm-accent)!important;box-shadow:0 0 0 1px rgba(108,124,255,0.35),0 2px 16px rgba(108,124,255,0.28),0 0 32px rgba(108,124,255,0.14)!important;}
[data-theme="dark"] #chat-input{color:var(--dm-text-1)!important;}
/* Topbar + input area divs inside chat-col — transparent so they match the chat column surface */
[data-theme="dark"] .chat-col [style*="background:var(--white)"]{background:transparent!important;}
[data-theme="dark"] [style*="background:#fafafa"]{background:rgba(255,255,255,0.03)!important;}
/* User message text — fix color:var(--white) in class becoming near-invisible */
[data-theme="dark"] .bubble-user{color:#FFFFFF!important;box-shadow:0 0 0 1px rgba(108,124,255,0.20),0 2px 18px rgba(108,124,255,0.18),0 0 32px rgba(108,124,255,0.08)!important;}
[data-theme="dark"] .user-bubble{color:#FFFFFF!important;box-shadow:0 0 0 1px rgba(108,124,255,0.20),0 2px 18px rgba(108,124,255,0.18),0 0 32px rgba(108,124,255,0.08)!important;}
/* Buttons */
[data-theme="dark"] #send-btn{background:linear-gradient(135deg,var(--indigo-deep),var(--dm-accent))!important;color:#FFFFFF!important;box-shadow:0 4px 22px rgba(108,124,255,0.38)!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"] 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"] .btn-option{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;}
[data-theme="dark"] .btn-option:hover{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!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"] #next-architect-btn{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;color:#FFFFFF!important;box-shadow:0 4px 22px rgba(108,124,255,0.38)!important;}
/* Background helpers */
[data-theme="dark"] [style*="background:#fafaff"]{background:rgba(108,124,255,0.04)!important;}
[data-theme="dark"] [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;}
[data-theme="dark"] [style*="background:#f0f4ff"]{background:var(--dm-surf-card)!important;}
[data-theme="dark"] [style*="border-color:rgba(99,102,241,0.12)"]{border-color:var(--dm-border)!important;}
/* Modals */
[data-theme="dark"] .edit-modal{background:rgba(0,8,20,0.55)!important;backdrop-filter:blur(4px)!important;}
[data-theme="dark"] .edit-modal-box{background:var(--dm-surf-panel)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;box-shadow:0 0 0 1px rgba(108,124,255,0.22),0 8px 32px rgba(0,0,0,0.55),0 0 40px rgba(108,124,255,0.12)!important;}
[data-theme="dark"] .edit-modal-box h3{color:#8C9CFF!important;}
[data-theme="dark"] .btn-option-title{color:#8C9CFF!important;}
[data-theme="dark"] .edit-modal-box p{color:var(--dm-text-3)!important;}
[data-theme="dark"] #inline-input{background:rgba(255,255,255,0.06)!important;border-color:var(--dm-border)!important;color:var(--dm-text-1)!important;}
[data-theme="dark"] #save-exit-box{background:#111828!important;box-shadow:0 0 0 1px rgba(108,124,255,0.16),0 24px 64px rgba(0,0,0,0.68),0 0 48px rgba(108,124,255,0.09)!important;}
[data-theme="dark"] #plan-loading-popup{background:#111828!important;border-color:var(--dm-border)!important;}
/* vibn avatar */
[data-theme="dark"] .vibn-avatar{background:var(--dm-accent)!important;}
/* Mobile tabs */
[data-theme="dark"] .mobile-tabs{background:rgba(8,12,24,0.90)!important;border-color:var(--dm-border)!important;}
[data-theme="dark"] .mobile-tab-btn{color:var(--dm-text-3)!important;}
[data-theme="dark"] .mobile-tab-btn.active{color:var(--dm-accent)!important;border-color:var(--dm-accent)!important;}
/* Scrollbars */
[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;}
/* Project setup modal — always light mode (shown before dark toggle is accessible) */
[data-theme="dark"] #project-type-modal{background:rgba(15,14,26,0.6);}
[data-theme="dark"] #project-type-box{background:#FFFFFF!important;border-color:#E5E7EB!important;}
[data-theme="dark"] #project-type-box h3{color:#1A1A1A!important;}
[data-theme="dark"] #project-type-box>p{color:#9CA3AF!important;}
[data-theme="dark"] #project-type-box label{color:#6B7280!important;}
[data-theme="dark"] #project-name-input{background:#FAFAFA!important;border-color:#E5E7EB!important;color:#1A1A1A!important;}
[data-theme="dark"] .project-type-opt{background:transparent!important;border-color:#E5E7EB!important;}
[data-theme="dark"] .project-type-opt:hover,[data-theme="dark"] .project-type-opt.selected{background:#FAFAFF!important;border-color:#6366F1!important;}
[data-theme="dark"] .project-type-opt div[style*="color:#1A1A1A"]{color:#1A1A1A!important;}
[data-theme="dark"] .project-type-opt div[style*="color:#9CA3AF"]{color:#9CA3AF!important;}
[data-theme="dark"] .project-type-icon{background:#F5F3FF!important;color:#6366F1!important;}
/* ── Fade-up animation for new bubbles (improvement 5) ── */
@keyframes fadeUp{from{opacity:0;transform:translateY(8px);}to{opacity:1;transform:translateY(0);}}
/* ── Bubble classes (improvement 1) ── */
.bubble{max-width:84%;font-size:13px;line-height:1.65;}
.bubble-ai{background:#f0f4ff;border:1px solid #e0e7ff;border-radius:4px 12px 12px 12px;padding:11px 14px;color:var(--ink);white-space:pre-line;animation:fadeUp 0.3s ease;box-shadow:0 2px 12px rgba(99,102,241,0.08);}
.bubble-user{background:var(--accent-primary);border-radius:12px 12px 4px 12px;padding:11px 14px;color:var(--white);animation:fadeUp 0.3s ease;box-shadow:0 2px 14px rgba(99,102,241,0.18);}
/* Legacy static bubble classes (keep for the pre-seeded chat HTML) */
.ai-bubble{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;white-space:pre-line;box-shadow:0 2px 12px rgba(99,102,241,0.08);}
.user-bubble{max-width:84%;background:var(--accent-primary);border-radius:12px 12px 4px 12px;padding:11px 14px;font-size:13px;color:var(--white);line-height:1.65;box-shadow:0 2px 14px rgba(99,102,241,0.18);}
@keyframes prdPulse{0%{background-color:#f0f4ff;}50%{background-color:#e0e7ff;}100%{background-color:#f0f4ff;}}
@keyframes bubbleHighlight{0%{background:#e0e7ff;border-color:#a5b4fc;}70%{background:#e0e7ff;border-color:#a5b4fc;}100%{background:#f0f4ff;border-color:#e0e7ff;}}
.bubble-highlight{animation:bubbleHighlight 1.4s ease forwards !important;}
/* ── PRD card base (improvement 1) ── */
.prd-card{padding:11px 13px;background:var(--white);border-radius:9px;border:1px solid var(--border);margin-bottom:8px;transition:opacity 0.3s;}
.prd-card.updating{animation:prdPulse 0.6s ease-out;}
/* Answered state — full opacity, pointer cursor for jump-to (improvement 8) */
.prd-card-answered{opacity:1;cursor:pointer;transition:background 0.15s,border-color 0.15s;box-shadow:0 1px 8px rgba(99,102,241,0.06);}
.prd-card-answered:hover{background:#f0f4ff;border-color:#c7d2fe;}
/* Pending state — muted for cards not yet reached */
.prd-card-pending{opacity:0.45;border-style:dashed;}
/* Active card — currently being filled */
.prd-card-active{opacity:1 !important;border-style:solid !important;border-left:3px solid var(--accent-primary) !important;background:#fafaff !important;box-shadow:0 0 0 3px rgba(99,102,241,0.08),0 2px 14px rgba(99,102,241,0.07);}
/* Pending field hint */
.prd-hint{font-size:11px;color:var(--muted);font-style:italic;margin-top:4px;}
/* ── PRD field label (improvement 1 & 3) ── */
.prd-field-label{font-size:9.5px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;margin-bottom:4px;}
.prd-field-label-answered{color:var(--ink3);}
.prd-field-label-pending{color:var(--muted);}
/* ── PRD field value (improvement 3) ── */
.prd-field-value{font-size:12px;line-height:1.55;}
.prd-field-value-answered{color:#1A1A1A;}
.prd-field-value-pending{color:#9CA3AF;}
.prd-edit-btn{background:none;border:none;cursor:pointer;font-size:14px;padding:4px 8px;color:var(--ink3);opacity:1;transition:color 0.2s;flex-shrink:0;}
.prd-edit-btn:hover:not(:disabled){color:var(--accent-primary);}
.prd-edit-btn:disabled{opacity:0.3;cursor:not-allowed;}
/* ── PRD panel layout — Next button outside scroll ── */
.prd-panel{width:384px;background:#f5f3ff;display:flex;flex-direction:column;padding:20px 16px 0 16px;flex-shrink:0;border-left:1px solid var(--border);height:100vh;}
.prd-scroll{flex:1;overflow-y:auto;padding-bottom:24px;margin-right:-16px;padding-right:16px;}
.prd-next-wrap{flex-shrink:0;padding:9px 0 13px;border-top:1px solid var(--border);display:flex;flex-direction:column;align-items:center;}
/* ── Chat input wrap (improvement 1) ── */
.chat-input-wrap{display:flex;gap:8px;align-items:center;background:#fafafa;border:1px solid var(--border);border-radius:11px;padding:9px 13px;transition:border-color 0.15s,box-shadow 0.15s;}
.chat-input-wrap:focus-within{border-color:var(--accent-primary);box-shadow:0 0 0 3px rgba(99,102,241,0.10),0 2px 14px rgba(99,102,241,0.07);}
@keyframes shake{0%,100%{transform:translateX(0);}20%{transform:translateX(-5px);}40%{transform:translateX(5px);}60%{transform:translateX(-4px);}80%{transform:translateX(4px);}}
.chat-input-wrap.shake{animation:shake 0.35s ease;border-color:#f87171;box-shadow:none;}
/* ── Button classes (improvement 1) ── */
.btn-primary{background:linear-gradient(135deg,#2e2a5e,#4338ca);color:var(--white);border:none;border-radius:8px;padding:10px 20px;font-size:13px;font-weight:600;cursor:pointer;}
.btn-secondary{background:transparent;border:1px solid var(--border);color:var(--ink);border-radius:8px;padding:10px 20px;font-size:13px;cursor:pointer;}
#send-btn{box-shadow:0 2px 14px rgba(99,102,241,0.22);}
#next-architect-btn{box-shadow:0 4px 18px rgba(99,102,241,0.22),0 1px 4px rgba(99,102,241,0.12);}
.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;}
/* Edit modal */
.edit-modal{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.35);z-index:1000;align-items:center;justify-content:center;}
.edit-modal.visible{display:flex;}
.edit-modal-box{background:var(--white);border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,0.10),0 1px 4px rgba(0,0,0,0.06);padding:28px;width:90%;max-width:400px;}
.edit-modal-box h3{font-size:16px;font-weight:700;color:var(--accent-soft);margin-bottom:16px;}
.edit-modal-box p{font-size:13px;color:var(--muted);line-height:1.6;margin-bottom:20px;}
.edit-options{display:flex;flex-direction:column;gap:10px;margin-bottom:20px;}
.btn-option{border:1px solid var(--border);background:transparent;border-radius:9px;padding:12px;text-align:left;cursor:pointer;transition:all 0.15s;}
.btn-option:hover{border-color:var(--accent-primary);background:#fafaff;}
.btn-option-title{font-size:13px;font-weight:600;color:var(--accent-soft);margin-bottom:4px;}
.btn-option-desc{font-size:12px;color:var(--muted);}
.edit-modal-actions{display:flex;gap:10px;justify-content:flex-end;}
/* ── Mobile dashboard button in tab bar ── */
.mobile-dash-btn{background:none;border:none;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--muted);cursor:pointer;padding:11px 12px;white-space:nowrap;flex-shrink:0;transition:color 0.15s;}
.mobile-dash-btn:hover{color:var(--ink);}
/* ── Mobile journey strip (hidden on desktop) ── */
.mobile-journey{display:none;align-items:center;gap:0;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;margin-bottom:10px;}
.mobile-journey::-webkit-scrollbar{display:none;}
.journey-step{display:flex;align-items:center;gap:0;flex-shrink:0;}
.journey-step-label{font-size:10px;font-weight:600;color:var(--muted);white-space:nowrap;padding:3px 8px;border-radius:20px;transition:color 0.2s,background 0.2s;}
.journey-step-label.current{color:var(--accent-primary);background:#ede9fe;}
.journey-step-arrow{font-size:9px;color:var(--border);margin:0 2px;}
@media(max-width:900px){.mobile-journey{display:flex;}}
/* ── Mobile tab bar (hidden on desktop) ── */
.mobile-tabs{display:none;flex-shrink:0;background:#EEF0FF;border-bottom:1px solid #D4D8FA;}
[data-theme="dark"] .mobile-tabs{background:#212840!important;}
.mobile-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-bottom:2px solid transparent;transition:color 0.15s,border-color 0.15s;}
.mobile-tab-btn.active{color:var(--accent-primary);border-bottom-color:var(--accent-primary);font-weight:600;}
.tab-badge{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent-primary);margin-left:5px;vertical-align:middle;position:relative;top:-1px;}
.tab-badge.hidden{display:none;}
.tab-live{font-size:9px;font-weight:600;color:var(--accent-primary);background:#ede9fe;border-radius:4px;padding:1px 5px;margin-left:5px;letter-spacing:0.04em;vertical-align:middle;transition:background 0.2s,color 0.2s;}
@keyframes livePulse{0%,100%{background:#ede9fe;color:var(--accent-primary);}50%{background:var(--accent-primary);color:#fff;}}
.tab-live.pulsing{animation:livePulse 0.6s ease;}
/* ── Typing indicator ── */
.typing-bubble{background:#f0f4ff;border:1px solid #e0e7ff;border-radius:4px 12px 12px 12px;padding:11px 14px;display:inline-flex;gap:5px;align-items:center;box-shadow:0 2px 12px rgba(99,102,241,0.08);}
.typing-bubble span{width:5px;height:5px;border-radius:50%;background:#a5b4fc;animation:loadDot 1.2s infinite ease-in-out;}
.typing-bubble span:nth-child(2){animation-delay:0.2s;}
.typing-bubble span:nth-child(3){animation-delay:0.4s;}
/* ── Save & exit popup ── */
#save-exit-popup{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.35);z-index:700;align-items:center;justify-content:center;padding:24px;}
#save-exit-popup.visible{display:flex;}
#save-exit-box{background:var(--white);border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,0.15);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-family:'Plus Jakarta Sans',sans-serif;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;}
#save-exit-box .save-cancel:hover{color:var(--ink);}
/* ── Project type modal ── */
#project-type-modal{position:fixed;inset:0;background:rgba(15,14,26,0.45);backdrop-filter:blur(2px);z-index:600;display:flex;align-items:center;justify-content:center;padding:24px;}
#project-type-modal.hidden{display:none;}
#project-type-box{background:#FFFFFF;border-radius:16px;box-shadow:0 24px 64px rgba(30,27,75,0.18);padding:28px;width:100%;max-width:420px;}
#project-type-box h3{font-family:'Plus Jakarta Sans',sans-serif;font-size:19px;font-weight:700;color:#1A1A1A;margin-bottom:4px;letter-spacing:-0.02em;}
#project-type-box>p{font-size:13px;color:#9CA3AF;margin-bottom:20px;line-height:1.5;}
#project-type-box label{display:block;font-size:11px;font-weight:600;color:#6B7280;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:6px;}
#project-name-input{width:100%;border:1px solid #E5E7EB;border-radius:8px;padding:10px 13px;font-family:'Plus Jakarta Sans',sans-serif;font-size:14px;color:#1A1A1A;background:#FAFAFA;outline:none;transition:border-color 0.15s,box-shadow 0.15s;}
#project-name-input:focus{border-color:#6366F1;box-shadow:0 0 0 3px rgba(99,102,241,0.12);}
#project-setup-btn{width:100%;margin-top:16px;padding:11px;background:linear-gradient(135deg,#2E2A5E,#4338CA);color:#FFFFFF;border:none;border-radius:8px;font-family:'Plus Jakarta Sans',sans-serif;font-size:14px;font-weight:600;cursor:pointer;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:opacity 0.15s,box-shadow 0.2s;}
.project-type-opt{border:1px solid var(--border);background:transparent;border-radius:10px;padding:14px 16px;cursor:pointer;margin-bottom:10px;display:flex;align-items:center;gap:12px;transition:all 0.15s;width:100%;text-align:left;}
.project-type-opt:hover{border-color:var(--accent-primary);background:#fafaff;}
.project-type-opt.selected{border-color:var(--accent-primary);background:#fafaff;box-shadow:0 0 0 3px rgba(99,102,241,0.1);}
.project-type-icon{width:36px;height:36px;border-radius:9px;background:var(--soft, #f5f3ff);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;color:var(--accent-primary);}
/* ── Plan loading popup ── */
@keyframes loadDot{0%,80%,100%{opacity:0.25;transform:scale(0.75);}40%{opacity:1;transform:scale(1);}}
@keyframes popIn{from{opacity:0;transform:translate(-50%,-50%) scale(0.88);}to{opacity:1;transform:translate(-50%,-50%) scale(1);}}
#plan-loading-popup{display:none;position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:500;background:var(--white);border:1px solid var(--border);border-radius:14px;box-shadow:0 8px 32px rgba(0,0,0,0.12);padding:20px 24px;text-align:center;min-width:180px;animation:popIn 0.2s ease;}
#plan-loading-popup.visible{display:block;}
#plan-loading-popup .popup-label{font-size:13px;font-weight:600;color:var(--ink);margin-bottom:12px;}
.plan-loading-dots{display:inline-flex;gap:6px;justify-content:center;}
.plan-loading-dots span{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent-primary);animation:loadDot 1.2s infinite ease-in-out;}
.plan-loading-dots span:nth-child(2){animation-delay:0.2s;}
.plan-loading-dots span:nth-child(3){animation-delay:0.4s;}
/* ── Tablet: 601900px — tab switching (replaces stacking) ── */
@media (max-width:900px){
body{height:100dvh;overflow:hidden;}
.main-layout{flex-direction:column !important;height:100dvh !important;overflow:hidden !important;}
.sidebar-col{display:none !important;}
.mobile-tabs{display:flex !important;}
.chat-col{display:none !important;flex:1;min-height:0 !important;border-right:none !important;}
.chat-col.tab-active{display:flex !important;}
.prd-panel{display:none !important;flex:1;width:100% !important;height:auto !important;border-left:none !important;}
.prd-panel.tab-active{display:flex !important;}
}
/* ── Mobile: ≤ 600px — tab switching, tabs at bottom ── */
@media (max-width:600px){
body{height:100dvh !important;overflow:hidden !important;}
.main-layout{flex-direction:column !important;height:100dvh !important;overflow:hidden !important;}
.mobile-tabs{display:flex !important;order:2;border-bottom:none;border-top:1px solid var(--border);padding-bottom:env(safe-area-inset-bottom);}
.chat-col{order:1;height:0;min-height:0 !important;flex:1;display:none !important;border-right:none !important;}
.chat-col.tab-active{display:flex !important;}
.prd-panel{order:1;height:0;flex:1;display:none !important;width:100% !important;border-left:none !important;border-top:none !important;}
.prd-panel.tab-active{display:flex !important;}
#chat{padding:14px 16px !important;}
#chat-input{font-size:16px !important;}
#input-area{padding:8px 14px 12px !important;}
#send-btn{padding:10px 18px !important;font-size:14px !important;}
.chat-col>div:first-child{padding:12px 14px 10px !important;}
.prd-scroll{flex:1;max-height:none !important;}
.prd-next-wrap{padding:9px 0 13px !important;}
.ai-bubble,.user-bubble{max-width:90% !important;}
.bubble{max-width:90% !important;}
}
/* ── Laptop narrow (≤ 600px, mouse device) — tabs at top ── */
/* hover:hover + pointer:fine identifies mouse/trackpad devices (laptop/desktop),
excluding phones/tablets (hover:none, pointer:coarse) */
@media (max-width:600px) and (hover:hover) and (pointer:fine){
.mobile-tabs{order:0 !important;border-top:none !important;border-bottom:1px solid var(--border) !important;padding-bottom:0 !important;}
.chat-col{order:1 !important;}
.prd-panel{order:1 !important;}
}
</style>
</head>
<body>
<div class="main-layout" style="display:flex;height:100%;overflow:hidden;">
<!-- Mobile tab bar (only visible on ≤ 900px) -->
<div class="mobile-tabs" id="mobile-tabs">
<button onclick="saveAndExit()" class="mobile-dash-btn">Dashboard</button>
<div style="width:1px;background:var(--border);margin:8px 0;flex-shrink:0;"></div>
<button class="mobile-tab-btn active" id="tab-chat" onclick="switchTab('chat')">Chat</button>
<button class="mobile-tab-btn" id="tab-plan" onclick="switchTab('plan')">Your Plan<span class="tab-live">auto-fill</span><span class="tab-badge hidden" id="plan-tab-badge"></span></button>
</div>
<!-- Save & exit 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>
<!-- Project type modal -->
<div id="project-type-modal" class="hidden">
<div id="project-type-box">
<h3>Set up your project</h3>
<p>Just a few things before we dive in.</p>
<div style="margin-bottom:18px;">
<label>Project name</label>
<input id="project-name-input" type="text" placeholder="e.g. My First Project" maxlength="50">
</div>
<label style="margin-bottom:8px;">Who is this for?</label>
<button class="project-type-opt" onclick="selectProjectType('self', this)">
<div class="project-type-icon"></div>
<div>
<div style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:2px;">It's my own idea</div>
<div style="font-size:12px;color:#9CA3AF;">A personal product or SaaS</div>
</div>
</button>
<button class="project-type-opt" onclick="selectProjectType('client', this)">
<div class="project-type-icon"></div>
<div>
<div style="font-size:14px;font-weight:600;color:#1A1A1A;margin-bottom:2px;">It's for a client</div>
<div style="font-size:12px;color:#9CA3AF;">Building on behalf of someone else</div>
</div>
</button>
<button id="project-setup-btn" onclick="confirmProjectSetup()" disabled style="opacity:0.35;">
Start building →
</button>
</div>
</div>
<!-- Plan loading popup -->
<div id="plan-loading-popup">
<div class="popup-label">Building your plan…</div>
<div class="plan-loading-dots"><span></span><span></span><span></span></div>
</div>
<!-- SIDEBAR -->
<div class="sidebar-col" style="width:200px;background:var(--white);border-right:1px solid var(--border);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:var(--muted);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:var(--muted);padding:0 6px;margin-bottom:8px;">MVP Setup</div>
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
<div class="sidebar-phase active">
<div class="phase-dot" style="background:var(--accent-primary);color:var(--white);"></div>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);">Describe</div><div style="font-size:10px;color:var(--muted);">Your idea</div></div>
</div>
<div class="sidebar-phase"><div class="phase-dot" style="background:var(--border);color:var(--muted);"></div><div style="font-size:12.5px;color:var(--muted);">Architect</div></div>
<div class="sidebar-phase"><div class="phase-dot" style="background:var(--border);color:var(--muted);"></div><div style="font-size:12.5px;color:var(--muted);">Design</div></div>
<div class="sidebar-phase"><div class="phase-dot" style="background:var(--border);color:var(--muted);"></div><div style="font-size:12.5px;color:var(--muted);">Website</div></div>
<div class="sidebar-phase"><div class="phase-dot" style="background:var(--border);color:var(--muted);"></div><div style="font-size:12.5px;color:var(--muted);">Build MVP</div></div>
</div>
<div style="border-top:1px solid var(--border);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>
<!-- CHAT -->
<div class="chat-col" style="flex:1;display:flex;flex-direction:column;border-right:1px solid var(--border);min-width:0;">
<div style="padding:18px 28px 14px;background:var(--white);border-bottom:1px solid var(--border);flex-shrink:0;">
<div style="margin-bottom:3px;">
<div class="f" style="font-size:17px;font-weight:700;color:var(--ink);">Describe</div>
</div>
<!-- Journey strip — mobile/tablet only -->
<div class="mobile-journey">
<div class="journey-step"><span class="journey-step-label current">◇ Describe</span><span class="journey-step-arrow"></span></div>
<div class="journey-step"><span class="journey-step-label">⬡ Architect</span><span class="journey-step-arrow"></span></div>
<div class="journey-step"><span class="journey-step-label">◈ Design</span><span class="journey-step-arrow"></span></div>
<div class="journey-step"><span class="journey-step-label">✦ Website</span><span class="journey-step-arrow"></span></div>
<div class="journey-step"><span class="journey-step-label">▲ Build MVP</span></div>
</div>
<div id="chat-subtitle" style="font-size:12px;color:var(--muted);margin-bottom:12px;">Tell me about your idea — I'll turn it into a product plan</div>
<div id="progress-bar" style="display:flex;gap:4px;">
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb0"></div>
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb1"></div>
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb2"></div>
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb3"></div>
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb4"></div>
<div style="flex:1;height:3px;border-radius:3px;background:var(--border);transition:background 0.3s;" id="pb5"></div>
</div>
</div>
<div id="chat" style="flex:1;overflow-y:auto;padding:20px 28px;display:flex;flex-direction:column;gap:12px;">
<!-- id="q-idea" marks where the "idea" question bubble lives (improvement 8) -->
<!-- id="q-idea" marks where the "idea" question bubble lives (improvement 8) -->
<div id="q-idea" 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="ai-bubble">Let's build your product plan. I'll ask you 6 quick questions — your answers fill your plan in real time.
<strong style="font-weight:700;color:#6366f1;">IDEA:</strong> Describe your product idea in one sentence. What does it do and who is it for?</div></div>
<div id="scroll-anchor"></div>
</div>
<div style="padding:14px 28px 20px;border-top:1px solid var(--border);background:var(--white);flex-shrink:0;" id="input-area">
<!-- .chat-input-wrap replaces the inline style (improvement 1) -->
<div class="chat-input-wrap">
<input id="chat-input" placeholder="Type your answer…" onkeydown="if(event.key==='Enter')sendMsg()" style="flex:1;border:none;background:transparent;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;color:var(--ink);outline:none;"/>
<button id="send-btn" onclick="sendMsg()" style="background:linear-gradient(135deg, #2e2a5e 0%, #4338ca 100%);color:var(--white);border:none;border-radius:7px;padding:7px 14px;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:600;cursor:pointer;display:flex;align-items:center;justify-content:center;"></button>
</div>
</div>
</div>
<!-- PRD PANEL — uses .prd-panel class; Next button is outside the scroll div (improvement 4) -->
<div class="prd-panel">
<div style="flex-shrink:0;margin-bottom: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 product plan</div>
<div id="prd-subtitle" style="font-size:12px;color:var(--ink3);line-height:1.5;">Your plan builds as you answer.</div>
</div>
<div id="prd-content" class="prd-scroll">
<!-- idea — pending -->
<div class="prd-card prd-card-pending" id="prd-card-idea">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-idea">Idea</div>
<div id="prd-idea" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-idea">What does your product do?</span>
</div>
<button class="prd-edit-btn" id="btn-idea" onclick="event.stopPropagation();editPRDField('idea')" disabled style="font-size:12px;"></button>
</div>
</div>
<!-- problem — pending -->
<div class="prd-card prd-card-pending" id="prd-card-problem">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-problem">Problem</div>
<div id="prd-problem" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-problem">What pain does it solve?</span>
</div>
<button class="prd-edit-btn" id="btn-problem" onclick="event.stopPropagation();editPRDField('problem')" disabled style="font-size:12px;"></button>
</div>
</div>
<!-- users — pending -->
<div class="prd-card prd-card-pending" id="prd-card-users">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-users">Users</div>
<div id="prd-users" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-users">Who is your ideal first customer?</span>
</div>
<button class="prd-edit-btn" id="btn-users" onclick="event.stopPropagation();editPRDField('users')" disabled style="font-size:12px;"></button>
</div>
</div>
<!-- value — pending -->
<div class="prd-card prd-card-pending" id="prd-card-value">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-value">Value</div>
<div id="prd-value" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-value">What will they love most?</span>
</div>
<button class="prd-edit-btn" id="btn-value" onclick="event.stopPropagation();editPRDField('value')" disabled style="font-size:12px;"></button>
</div>
</div>
<!-- revenue — pending -->
<div class="prd-card prd-card-pending" id="prd-card-revenue">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-revenue">Revenue</div>
<div id="prd-revenue" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-revenue">How will you charge for it?</span>
</div>
<button class="prd-edit-btn" id="btn-revenue" onclick="event.stopPropagation();editPRDField('revenue')" disabled style="font-size:12px;"></button>
</div>
</div>
<!-- features — pending -->
<div class="prd-card prd-card-pending" id="prd-card-features">
<div style="display:flex;justify-content:space-between;align-items:flex-start;">
<div style="flex:1;">
<div class="prd-field-label prd-field-label-pending" id="lbl-features">Features</div>
<div id="prd-features" class="prd-field-value prd-field-value-pending">Waiting…</div>
<span class="prd-hint" id="hint-features">3 must-have features for v1</span>
</div>
<button class="prd-edit-btn" id="btn-features" onclick="event.stopPropagation();editPRDField('features')" disabled style="font-size:12px;"></button>
</div>
</div>
</div>
<!-- Next button lives OUTSIDE the scrollable area (improvement 4) -->
<!-- Footer — consistent position across all setup pages -->
<div class="prd-next-wrap">
<p style="font-size:11.5px;color:var(--muted);text-align:center;margin:0 0 10px;line-height:1.5;">vibn will now pick the best tech stack for your idea.</p>
<a href="06_architect.html" style="text-decoration:none;display:block;width:80%;">
<button id="next-architect-btn" disabled style="width:100%;background:linear-gradient(135deg,#2e2a5e,#4338ca);color:var(--white);border:none;border-radius:8px;padding:12px 14px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:600;cursor:not-allowed;box-shadow:0 10px 25px rgba(30,27,75,0.15);transition:all 0.2s ease;opacity:0.5;" onmouseover="if(!this.disabled){this.style.cursor='pointer';this.style.boxShadow='0 10px 25px rgba(30,27,75,0.15), 0 0 0 6px rgba(99,102,241,0.15)';this.style.transform='translateY(-1px)';}" onmouseout="if(!this.disabled){this.style.boxShadow='0 10px 25px rgba(30,27,75,0.15)';this.style.transform='translateY(0)';this.style.cursor='pointer';}" onclick="return !this.disabled;">
Next: Architect
</button>
</a>
<div id="next-btn-hint" style="text-align:center;font-size:11px;color:var(--muted);margin-top:7px;">Answer all 6 questions to continue</div>
</div>
</div>
</div>
<script>
var step = 0;
var answeredCount = 0;
var currentEditingField = null;
var reAnswerMode = false;
var reAnswerField = null;
// Map field keys to their chat question bubble IDs (improvement 8)
var fieldToQId = {
idea: 'q-idea',
problem: 'q-problem',
users: 'q-users',
value: 'q-value',
revenue: 'q-revenue',
features: 'q-features'
};
// Jump to the corresponding question bubble in the chat panel (improvement 8)
function jumpToField(fieldKey){
var id = fieldToQId[fieldKey];
if(!id) return;
var el = document.getElementById(id);
if(!el) return;
function highlightBubble(){
var bubble = el.querySelector('.ai-bubble');
if(!bubble) return;
bubble.classList.remove('bubble-highlight');
void bubble.offsetWidth; // force reflow so re-triggering works
bubble.classList.add('bubble-highlight');
setTimeout(function(){ bubble.classList.remove('bubble-highlight'); }, 1400);
}
if(window.innerWidth < 900){
switchTab('chat');
setTimeout(function(){
el.scrollIntoView({behavior:'smooth'});
setTimeout(highlightBubble, 300);
}, 50);
} else {
el.scrollIntoView({behavior:'smooth'});
setTimeout(highlightBubble, 300);
}
}
// Promote a PRD card from pending to answered state (improvements 3 & 8)
function markCardAnswered(fieldKey){
var card = document.getElementById('prd-card-'+fieldKey);
if(!card) return;
card.classList.remove('prd-card-pending');
card.classList.add('prd-card-answered');
// Label
var lbl = document.getElementById('lbl-'+fieldKey);
if(lbl){
lbl.classList.remove('prd-field-label-pending');
lbl.classList.add('prd-field-label-answered');
}
// Value
var val = document.getElementById('prd-'+fieldKey);
if(val){
val.classList.remove('prd-field-value-pending');
val.classList.add('prd-field-value-answered');
}
// Bind jump-to click once, and count as newly answered (improvement 8)
if(!card.dataset.jumpBound){
card.dataset.jumpBound = '1';
card.onclick = function(){ jumpToField(fieldKey); };
card.title = 'Click to jump to this question in chat';
answeredCount++;
// First answer: update PRD subtitle
if(answeredCount === 1){
var sub = document.getElementById('prd-subtitle');
if(sub) sub.textContent = 'Answered fields can be edited or clicked to jump to the question.';
} else if(answeredCount === 6){
var sub = document.getElementById('prd-subtitle');
if(sub) sub.textContent = 'Your plan is ready — review and continue to the next step.';
}
}
// Hide pending hint
var hint = document.getElementById('hint-'+fieldKey);
if(hint) hint.style.display = 'none';
// Update progress counter
updateAnsweredCounter();
// Show badge + pulse live pill on mobile if user is on the chat tab
if(window.innerWidth < 900){
var chatColEl = document.querySelector('.chat-col');
if(chatColEl && chatColEl.classList.contains('tab-active')){
var badge = document.getElementById('plan-tab-badge');
if(badge) badge.classList.remove('hidden');
var livePill = document.querySelector('.tab-live');
if(livePill){
livePill.classList.remove('pulsing');
void livePill.offsetWidth;
livePill.classList.add('pulsing');
setTimeout(function(){ livePill.classList.remove('pulsing'); }, 600);
}
}
}
}
function setChatEnabled(enabled){
var inp = document.getElementById('chat-input');
var sendBtn = document.getElementById('send-btn');
if(inp){
inp.disabled = !enabled;
inp.style.opacity = enabled ? '1' : '0.6';
inp.style.cursor = enabled ? 'text' : 'not-allowed';
if(enabled){
inp.placeholder = 'Type your answer…';
} else {
inp.value = '';
inp.placeholder = 'All done! Edit any field above or tap "Your Plan" to continue.';
}
}
if(sendBtn){
sendBtn.disabled = !enabled;
sendBtn.style.opacity = enabled ? '1' : '0.6';
sendBtn.style.cursor = enabled ? 'pointer' : 'not-allowed';
}
}
var QS = [
{q:"Describe your product idea in one sentence. What does it do and who is it for?",label:"IDEA",key:"idea",ack:"Good — I can work with that.",qid:"q-idea"},
{q:"What's the painful thing your users are doing today instead?",label:"PROBLEM",key:"problem",ack:"That's a real pain worth solving.",qid:"q-problem"},
{q:"Describe your ideal first customer in one sentence.",label:"USERS",key:"users",ack:"Clear. That focus will shape everything.",qid:"q-users"},
{q:"What's the one thing they'll love most about your product?",label:"VALUE",key:"value",ack:"Strong differentiator.",qid:"q-value"},
{q:"How will you charge for it? Subscription, one-time, or free to start?",label:"REVENUE",key:"revenue",ack:"Makes sense for this type of product.",qid:"q-revenue"},
{q:"Name the 3 things users must be able to do in version one.",label:"FEATURES",key:"features",ack:"That's everything I need.",qid:"q-features"}
];
// Check if all questions have been answered
function checkAllQuestionsAnswered(){
if(step <= 5) return false;
var allFilled = document.getElementById('prd-idea').textContent !== 'Waiting…' &&
document.getElementById('prd-problem').textContent !== 'Waiting…' &&
document.getElementById('prd-users').textContent !== 'Waiting…' &&
document.getElementById('prd-value').textContent !== 'Waiting…' &&
document.getElementById('prd-revenue').textContent !== 'Waiting…' &&
document.getElementById('prd-features').textContent !== 'Waiting…';
return allFilled;
}
// Update the chat subtitle
function updateAnsweredCounter(){
var sub = document.getElementById('chat-subtitle');
if(sub){
if(answeredCount === 0) sub.textContent = 'Tell me about your idea — I\'ll turn it into a product plan';
else if(answeredCount < 6) sub.textContent = answeredCount + ' of 6 questions answered — keep going!';
else sub.textContent = 'All done! Review your plan or move to the next step.';
}
}
// Trigger pulse animation on PRD card update
function triggerPulsAnimation(fieldKey){
var card = document.getElementById('prd-card-'+fieldKey);
if(card){
card.classList.add('updating');
setTimeout(function(){
card.classList.remove('updating');
}, 600);
}
}
// Highlight the currently active PRD card
function setActiveCard(fieldKey){
document.querySelectorAll('.prd-card').forEach(function(c){ c.classList.remove('prd-card-active'); });
if(fieldKey){
var card = document.getElementById('prd-card-'+fieldKey);
if(card && !card.classList.contains('prd-card-answered')) card.classList.add('prd-card-active');
}
}
// Enable edit button for a field
function enableEditButton(fieldKey){
var btn = document.getElementById('btn-'+fieldKey);
if(btn) btn.disabled = false;
}
// Edit PRD field - shows modal with options
function editPRDField(fieldKey){
currentEditingField = fieldKey;
var modal = document.getElementById('edit-modal');
if(modal) modal.classList.add('visible');
}
// Close modal
function closeEditModal(){
var modal = document.getElementById('edit-modal');
if(modal) modal.classList.remove('visible');
currentEditingField = null;
}
// Re-answer mode: inject a new AI bubble and scroll to the original question (improvement 4)
function editByReAnswer(){
if(!currentEditingField) return;
var questionField = currentEditingField; // SAVE IT FIRST before closeEditModal() clears it!
reAnswerMode = true;
reAnswerField = currentEditingField;
closeEditModal();
// On mobile, switch to the chat tab so the user sees the conversation
if(window.innerWidth < 900) switchTab('chat');
var chat = document.getElementById('chat');
var anchor = document.getElementById('scroll-anchor');
if(!chat || !anchor) {
console.error('Chat or anchor element not found');
return;
}
setChatEnabled(true);
var q = QS.find(function(item){ return item.key === questionField; });
var customQuestions = {
idea: "Tell me about your idea. What does it do and who is it for?",
problem: "What's the painful thing your users are doing today instead?"
};
var questionText = q ? q.q : customQuestions[questionField];
if(!questionText) return;
var wrapper = document.createElement('div');
wrapper.style.cssText = 'display:flex;align-items:flex-start;gap:10px;';
var avatar = document.createElement('div');
avatar.className = 'vibn-avatar';
avatar.style.cssText = '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;';
avatar.innerHTML = '<span style="font-size:12px;font-weight:700;color:#FFFFFF;">V</span>';
var bubble = document.createElement('div');
// Use animated bubble class (improvement 5)
bubble.className = 'ai-bubble bubble bubble-ai';
bubble.textContent = 'Let me ask that again:\n\n' + questionText;
wrapper.appendChild(avatar);
wrapper.appendChild(bubble);
chat.insertBefore(wrapper, anchor);
// Smooth-scroll the chat to the original question bubble (improvement 4)
setTimeout(function(){
var origQId = fieldToQId[questionField];
if(origQId){
var origEl = document.getElementById(origQId);
if(origEl){ origEl.scrollIntoView({behavior:'smooth'}); }
} else {
wrapper.scrollIntoView({behavior:'smooth'});
}
var inp = document.getElementById('chat-input');
if(inp) inp.focus();
}, 100);
}
// Inline edit mode: show text input modal
function editByInline(){
if(!currentEditingField) return;
var currentValue = document.getElementById('prd-'+currentEditingField).textContent;
var modal = document.getElementById('edit-modal');
modal.classList.remove('visible');
var inlineModal = document.getElementById('inline-edit-modal');
if(inlineModal){
document.getElementById('inline-input').value = currentValue;
inlineModal.classList.add('visible');
}
}
// Save inline edit — captures previous value for undo (improvement 9)
function saveInlineEdit(){
if(!currentEditingField) return;
var newValue = document.getElementById('inline-input').value.trim();
if(!newValue) return;
var fieldEl = document.getElementById('prd-'+currentEditingField);
fieldEl.textContent = newValue;
fieldEl.classList.remove('prd-field-value-pending');
fieldEl.classList.add('prd-field-value-answered');
markCardAnswered(currentEditingField);
triggerPulsAnimation(currentEditingField);
enableEditButton(currentEditingField);
var inlineModal = document.getElementById('inline-edit-modal');
if(inlineModal) inlineModal.classList.remove('visible');
currentEditingField = null;
}
// Cancel inline edit
function cancelInlineEdit(){
var inlineModal = document.getElementById('inline-edit-modal');
if(inlineModal) inlineModal.classList.remove('visible');
}
// Save & exit
function saveAndExit(){
try { localStorage.setItem('vibn_answered_count', answeredCount); } catch(e){}
document.getElementById('save-exit-popup').classList.add('visible');
}
function cancelSaveExit(){
document.getElementById('save-exit-popup').classList.remove('visible');
}
// Project type modal
var projectType = null;
var projectName = 'My First Project';
function selectProjectType(type, el){
projectType = type;
document.querySelectorAll('.project-type-opt').forEach(function(o){ o.classList.remove('selected'); });
el.classList.add('selected');
updateSetupBtn();
}
function updateSetupBtn(){
var btn = document.getElementById('project-setup-btn');
var nameVal = document.getElementById('project-name-input').value.trim();
var ready = projectType !== null && nameVal.length > 0;
btn.disabled = !ready;
btn.style.opacity = ready ? '1' : '0.35';
}
function confirmProjectSetup(){
var nameVal = document.getElementById('project-name-input').value.trim();
if(!nameVal || !projectType) return;
projectName = nameVal;
try { localStorage.setItem('vibn_project_name', projectName); } catch(e){}
try { localStorage.setItem('vibn_project_type', projectType); } catch(e){}
document.getElementById('project-type-modal').classList.add('hidden');
showSidebarProjectName(projectName);
// If the user seeded an idea during signup, auto-send it as their first message
try {
var seedIdea = sessionStorage.getItem('vibn_seed_idea');
if(seedIdea){
sessionStorage.removeItem('vibn_seed_idea');
setTimeout(function(){
var inp = document.getElementById('chat-input');
inp.value = seedIdea;
sendMsg();
}, 400);
}
} catch(e){}
}
function showSidebarProjectName(name){
var el = document.getElementById('sidebar-project-name');
if(!el || !name) return;
el.textContent = name;
el.style.display = 'block';
}
// Show modal only when coming from signup; skip it when navigating from the dashboard
document.addEventListener('DOMContentLoaded', function(){
document.getElementById('project-name-input').addEventListener('input', updateSetupBtn);
var fromSignup = false;
var dashName = null;
try {
fromSignup = sessionStorage.getItem('vibn_new_project') === '1';
dashName = sessionStorage.getItem('vibn_dashboard_project');
} catch(e){}
if(dashName) {
// Coming from dashboard — skip modal, use name & type already chosen there
try {
projectName = dashName;
projectType = sessionStorage.getItem('vibn_dashboard_type') || 'self';
sessionStorage.removeItem('vibn_dashboard_project');
sessionStorage.removeItem('vibn_dashboard_type');
localStorage.setItem('vibn_project_name', projectName);
localStorage.setItem('vibn_project_type', projectType);
} catch(e){}
document.getElementById('project-type-modal').classList.add('hidden');
showSidebarProjectName(projectName);
} else {
// Coming from signup or direct navigation — show the setup modal
if(fromSignup) {
try { sessionStorage.removeItem('vibn_new_project'); } catch(e){}
}
try { localStorage.removeItem('vibn_project_name'); localStorage.removeItem('vibn_project_type'); localStorage.removeItem('vibn_answered_count'); } catch(e){}
document.getElementById('project-type-modal').classList.remove('hidden');
}
});
// Typing indicator helpers
function showTyping(){
var chat = document.getElementById('chat');
var anchor = document.getElementById('scroll-anchor');
var wrapper = document.createElement('div');
wrapper.id = 'typing-indicator';
wrapper.style.cssText = 'display:flex;align-items:flex-start;gap:10px;';
var avatar = document.createElement('div');
avatar.className = 'vibn-avatar';
avatar.style.cssText = '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;';
avatar.innerHTML = '<span style="font-size:12px;font-weight:700;color:#FFFFFF;">V</span>';
var bubble = document.createElement('div');
bubble.className = 'typing-bubble';
bubble.innerHTML = '<span></span><span></span><span></span>';
wrapper.appendChild(avatar);
wrapper.appendChild(bubble);
chat.insertBefore(wrapper, anchor);
anchor.scrollIntoView({behavior:'smooth'});
}
function hideTyping(){
var el = document.getElementById('typing-indicator');
if(el) el.parentNode.removeChild(el);
}
// Format bubble text: escape HTML, preserve newlines via pre-line, style [LABEL] markers
function formatBubbleText(text){
var escaped = text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
return escaped.replace(/\[([A-Z]+)\]/g,'<strong style="font-weight:700;color:#6366f1;">$1:</strong>');
}
// Helper: create animated AI bubble wrapper (improvement 5)
function createAiBubble(text, optionalId){
var wrapper = document.createElement('div');
wrapper.style.cssText = 'display:flex;align-items:flex-start;gap:10px;';
if(optionalId) wrapper.id = optionalId;
var avatar = document.createElement('div');
avatar.className = 'vibn-avatar';
avatar.style.cssText = '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;';
avatar.innerHTML = '<span style="font-size:12px;font-weight:700;color:#FFFFFF;">V</span>';
var b = document.createElement('div');
b.className = 'ai-bubble bubble bubble-ai';
b.innerHTML = formatBubbleText(text);
wrapper.appendChild(avatar);
wrapper.appendChild(b);
return wrapper;
}
// Helper: create animated user bubble wrapper (improvement 5)
function createUserBubble(text){
var wrapper = document.createElement('div');
wrapper.style.cssText = 'display:flex;flex-direction:column;align-items:flex-end;';
var b = document.createElement('div');
b.className = 'user-bubble bubble bubble-user';
b.textContent = text;
wrapper.appendChild(b);
return wrapper;
}
function sendMsg(){
var inp = document.getElementById('chat-input');
var val = inp.value.trim();
if(!val){
var wrap = inp.closest('.chat-input-wrap');
if(wrap){ wrap.classList.add('shake'); setTimeout(function(){ wrap.classList.remove('shake'); }, 400); }
return;
}
inp.value = '';
var chat = document.getElementById('chat');
var anchor = document.getElementById('scroll-anchor');
// User bubble with fade-up animation (improvement 5)
var u = createUserBubble(val);
chat.insertBefore(u, anchor);
// If in re-answer mode, only update that specific field
if(reAnswerMode && reAnswerField){
document.getElementById('prd-'+reAnswerField).textContent = val;
document.getElementById('prd-'+reAnswerField).classList.remove('prd-field-value-pending');
document.getElementById('prd-'+reAnswerField).classList.add('prd-field-value-answered');
markCardAnswered(reAnswerField);
triggerPulsAnimation(reAnswerField);
enableEditButton(reAnswerField);
var savedRAField = reAnswerField;
reAnswerMode = false;
reAnswerField = null;
setTimeout(function(){
var allAnswered = checkAllQuestionsAnswered();
if(allAnswered){
var aiFinal = createAiBubble("Updated! Feel free to keep editing, or hit Next when you're ready.");
chat.insertBefore(aiFinal, anchor);
setChatEnabled(false);
if(window.innerWidth < 900){ showPlanLoading(); setTimeout(function(){ switchTab('plan'); }, 3000); }
anchor.scrollIntoView({behavior:'smooth'});
return;
}
// Acknowledgment bubble (improvement 5)
var aiAck = createAiBubble("Got it, I've updated that field.");
chat.insertBefore(aiAck, anchor);
// Next question bubble — assign its id so jump-to works (improvement 8)
var nextQ = QS[step];
var nextText = nextQ ? '[' + nextQ.label + '] ' + nextQ.q : 'Your product plan is ready on the right.';
var aiNext = createAiBubble(nextText, nextQ ? nextQ.qid : null);
chat.insertBefore(aiNext, anchor);
anchor.scrollIntoView({behavior:'smooth'});
}, 100);
return;
}
// Update PRD
var q = QS[step];
if(q && document.getElementById('prd-'+q.key) && !reAnswerMode){
document.getElementById('prd-'+q.key).textContent = val;
document.getElementById('prd-'+q.key).classList.remove('prd-field-value-pending');
document.getElementById('prd-'+q.key).classList.add('prd-field-value-answered');
markCardAnswered(q.key);
triggerPulsAnimation(q.key);
enableEditButton(q.key);
setActiveCard(null);
}
// Update progress bar + label
if(step < 6){
var pb = document.getElementById('pb'+step); if(pb) pb.style.background = 'var(--accent-primary)';
}
showTyping();
setTimeout(function(){
var nextQ = QS[step+1];
step++;
if(checkAllQuestionsAnswered()){
setTimeout(function(){
hideTyping();
var btn = document.getElementById('next-architect-btn');
if(btn){ btn.disabled = false; btn.style.opacity = '1'; btn.style.cursor = 'pointer'; }
var hint = document.getElementById('next-btn-hint'); if(hint) hint.style.display = 'none';
document.getElementById('prog-1').style.background = 'var(--accent-primary)';
document.getElementById('prog-1').innerHTML = '<span style="color:var(--white);font-size:7px;font-weight:900;">✓</span>';
var doneBubble = createAiBubble("That's everything I need. You can edit your plan or go to the next step!");
chat.insertBefore(doneBubble, anchor);
setChatEnabled(false);
anchor.scrollIntoView({behavior:'smooth'});
if(window.innerWidth < 900){ showPlanLoading(); setTimeout(function(){ switchTab('plan'); }, 3000); }
}, 600);
} else if(nextQ) {
// Ack bubble — hide typing first
setTimeout(function(){
hideTyping();
if(q){
var ackBubble = createAiBubble(q.ack);
chat.insertBefore(ackBubble, anchor);
anchor.scrollIntoView({behavior:'smooth'});
}
// Show typing again before next question
showTyping();
setTimeout(function(){
hideTyping();
var questionBubble = createAiBubble('[' + nextQ.label + '] ' + nextQ.q, nextQ.qid);
chat.insertBefore(questionBubble, anchor);
setActiveCard(nextQ.key);
anchor.scrollIntoView({behavior:'smooth'});
}, 600);
}, 600);
}
anchor.scrollIntoView({behavior:'smooth'});
}, 500);
anchor.scrollIntoView({behavior:'smooth'});
}
// ── Plan loading popup helpers ────────────────────────────────────────────────
function showPlanLoading(){
if(window.innerWidth >= 900) return;
var popup = document.getElementById('plan-loading-popup');
if(popup) popup.classList.add('visible');
}
function hidePlanLoading(){
var popup = document.getElementById('plan-loading-popup');
if(popup) popup.classList.remove('visible');
}
// ── Mobile tab switching ──────────────────────────────────────────────────────
function switchTab(tab){
hidePlanLoading();
var chatCol = document.querySelector('.chat-col');
var prdPanel = document.querySelector('.prd-panel');
var tabChat = document.getElementById('tab-chat');
var tabPlan = document.getElementById('tab-plan');
if(tab === 'chat'){
chatCol.classList.add('tab-active');
prdPanel.classList.remove('tab-active');
tabChat.classList.add('active');
tabPlan.classList.remove('active');
} else {
prdPanel.classList.add('tab-active');
chatCol.classList.remove('tab-active');
tabPlan.classList.add('active');
tabChat.classList.remove('active');
var badge = document.getElementById('plan-tab-badge');
if(badge) badge.classList.add('hidden');
}
}
// Initialize chat tab as active on mobile/narrow
function initTabsIfNeeded(){
if(window.innerWidth < 900){
var chatCol = document.querySelector('.chat-col');
var prdPanel = document.querySelector('.prd-panel');
if(!chatCol.classList.contains('tab-active') && !prdPanel.classList.contains('tab-active')){
chatCol.classList.add('tab-active');
document.getElementById('tab-chat').classList.add('active');
document.getElementById('tab-plan').classList.remove('active');
}
}
}
initTabsIfNeeded();
window.addEventListener('resize', initTabsIfNeeded);
setActiveCard(QS[0].key);
// ─────────────────────────────────────────────────────────────────────────────
// Initialize chat input enabled by default
setChatEnabled(true);
// ── Dark mode ──
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', () => {
const btn = document.getElementById('dark-toggle');
if (btn) btn.textContent = '☀️ Light mode';
});
}
})();
</script>
<!-- Edit modal - shows two options -->
<div class="edit-modal" id="edit-modal">
<div class="edit-modal-box">
<h3>Edit this field</h3>
<p>How would you like to update this?</p>
<div class="edit-options">
<button class="btn-option" onclick="editByReAnswer()">
<div class="btn-option-title">Re-answer the question</div>
<div class="btn-option-desc">I'll ask the question again in chat, and you can refine your answer.</div>
</button>
<button class="btn-option" onclick="editByInline()">
<div class="btn-option-title">Edit directly</div>
<div class="btn-option-desc">Quickly update the text without re-answering the whole question.</div>
</button>
</div>
<div class="edit-modal-actions">
<button class="btn-secondary" onclick="closeEditModal()">Cancel</button>
</div>
</div>
</div>
<!-- Inline edit modal -->
<div class="edit-modal" id="inline-edit-modal">
<div class="edit-modal-box">
<h3>Edit field</h3>
<p style="margin-bottom:14px;">Update the text below:</p>
<input id="inline-input" type="text" style="width:100%;border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;color:var(--ink);margin-bottom:18px;outline:none;" />
<div class="edit-modal-actions">
<button class="btn-secondary" onclick="cancelInlineEdit()">Cancel</button>
<button class="btn-primary" onclick="saveInlineEdit()">Save</button>
</div>
</div>
</div>
</body></html>