Files
vibn-agent-runner/justine/vibn front end/07_design.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

1362 lines
86 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">
<script>if(localStorage.getItem('vibn-theme')==='dark')document.documentElement.dataset.theme='dark';</script>
<title>vibn — Design</title>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0;}
:root{--ink:#1a1510;--ink2:#2c2c2a;--ink3:#444441;--mid:#5f5e5a;--muted:#888780;--stone:#b4b2a9;--parch:#d3d1c7;--cream:#f1efe8;--paper:#f7f4ee;--white:#fdfcfa;--border:#e8e2d9;--section: #b8a9e93f;}
html:not([data-theme="dark"]) .preview-box {background: linear-gradient(to bottom, #FAFAFA, #F5F3FF);}
[data-theme="dark"]{--ink:#EEEEFF;--ink2:#B8B8D0;--ink3:#8484A8;--mid:#9898B8;--muted:#c2c2ee;--border:rgba(255,255,255,0.08);--cream:rgba(108,124,255,0.14);--paper:#0A1120;--white:rgba(255,255,255,0.05);--stone:rgba(255,255,255,0.08);--parch:rgba(255,255,255,0.06);--section:rgba(108,124,255,0.55);--accent-primary:#6C7CFF;--dm-surf-sidebar:rgba(12,18,34,0.72);--dm-surf-topbar:rgba(12,18,34,0.58);--dm-surf-panel:rgba(12,18,34,0.58);--dm-surf-right:rgba(12,18,34,0.58);--dm-surf-card:rgba(255,255,255,0.05);--dm-surf-refine:rgba(8,12,22,0.60);--dm-border:rgba(255,255,255,0.08);--dm-border-strong:rgba(255,255,255,0.14);--dm-border-hero:rgba(255,255,255,0.18);--dm-accent:#6C7CFF;--dm-accent-fill:rgba(108,124,255,0.14);--dm-accent-fill-mid:rgba(108,124,255,0.20);--dm-accent-border:rgba(108,124,255,0.55);--dm-text-1:#EEEEFF;--dm-text-2:#B4B4CC;--dm-text-3:#d2d2ef;--dm-shadow-panel:0 4px 36px rgba(0,0,0,0.55);--dm-shadow-hero:0 20px 70px rgba(0,0,0,0.65),0 6px 24px rgba(0,0,0,0.40),inset 0 1px 0 rgba(255,255,255,0.10);}
[data-theme="dark"] body{background:linear-gradient(to bottom,rgba(108,80,255,0.06) 0%,rgba(60,120,255,0.10) 38%,transparent 62%),linear-gradient(to bottom,rgba(108,124,255,0.10),transparent 180px),radial-gradient(900px 520px at 14% -8%,rgba(108,124,255,0.24),transparent 62%),radial-gradient(760px 420px at 88% 0%,rgba(72,145,255,0.16),transparent 60%),linear-gradient(180deg,#18213B 0%,#101726 48%,#0A1120 100%);}[data-theme="dark"] #mock {background: #fff !important;}
[data-theme="dark"] .sidebar-col{background:var(--dm-surf-sidebar)!important;border-right:1px solid var(--dm-border)!important;box-shadow:2px 0 28px rgba(0,0,0,0.60)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
[data-theme="dark"] .sidebar-col [style*="color:#1a1a1a"]{color:var(--dm-text-1)!important;}
[data-theme="dark"] .sidebar-col [style*="color:#6b7280"]{color:var(--dm-text-3)!important;}
.ph-name{color:#9ca3af;}
[data-theme="dark"] .ph-name{color:var(--dm-text-3);}
[data-theme="dark"] .sidebar-col [style*="background:#6366F1"]{background:var(--dm-accent)!important;color:#0F1424!important;}
[data-theme="dark"] .sidebar-col [style*="background:#e5e7eb"]{background:rgba(255,255,255,0.08)!important;}
[data-theme="dark"] .sidebar-phase.active{background:var(--dm-accent-fill)!important;}
[data-theme="dark"] .sidebar-phase:not(.active):hover{background:rgba(255,255,255,0.08)!important;}
[data-theme="dark"] .sidebar-col [style*="border-top:1px solid #e5e7eb"]{border-top-color:var(--dm-border)!important;}
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
[data-theme="dark"] .sidebar-col [style*="background:#eef2ff"] span{color:var(--dm-accent)!important;}
[data-theme="dark"] button[onclick="saveAndExit()"]{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;}
[data-theme="dark"] button[onclick="saveAndExit()"]:hover{background:var(--dm-accent-fill-mid)!important;}
[data-theme="dark"] button[onclick="saveAndExit()"]:hover span{color:#fff!important;}
[data-theme="dark"] button[onclick="saveAndExit()"] span{color:var(--dm-accent)!important;}
#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);}
[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"] #sidebar-project-name{color:var(--dm-text-3)!important;}
[data-theme="dark"] #dark-toggle{background:rgba(255,255,255,0.05)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
[data-theme="dark"] #dark-toggle:hover{background:rgba(255,255,255,0.10)!important;color:var(--dm-text-1)!important;}
[data-theme="dark"] .vibn-avatar{background:var(--dm-accent)!important;}
[data-theme="dark"] .arch-topbar{
background: var(--dm-surf-topbar) !important;
border-bottom: 1px solid rgba(255,255,255,0.08) !important;
box-shadow: none !important;
-webkit-backdrop-filter: none !important;
backdrop-filter: none !important;
position: relative;
}
[data-theme="dark"] .arch-topbar .f{
color: var(--dm-text-1) !important;
}
[data-theme="dark"] .arch-topbar [style*="color:#9ca3af"]{
color: var(--dm-text-3) !important;
}
[data-theme="dark"] .design-left{background:var(--dm-surf-panel)!important;border-right:1px solid var(--dm-border)!important;box-shadow:4px 0 36px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
[data-theme="dark"] .design-left .sec-head{color:var(--dm-accent)!important;opacity:0.85;}
[data-theme="dark"] .design-left .sec-div{background:rgba(108,124,255,0.20)!important;}
[data-theme="dark"] .fpill{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;}
[data-theme="dark"] .fpill .fpill-name{color:var(--dm-text-2)!important;}
[data-theme="dark"] .fpill .fpill-desc{color:var(--dm-text-3)!important;}
[data-theme="dark"] .fpill.active{background:var(--dm-accent-fill-mid)!important;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"] .fpill.active .fpill-name{color:var(--dm-text-1)!important;}
[data-theme="dark"] .fpill.active .fpill-desc{color:var(--dm-text-2)!important;}
[data-theme="dark"] .fpill:hover:not(.active){background:rgba(255,255,255,0.07)!important;border-color:var(--dm-border-strong)!important;}
[data-theme="dark"] .ccircle{border-color:transparent!important;}
[data-theme="dark"] .ccircle.active{border-color:var(--dm-accent)!important;box-shadow:0 0 0 3px rgba(108,124,255,0.35),0 0 22px rgba(108,124,255,0.28)!important;}
[data-theme="dark"] .schoice{background:rgba(255,255,255,0.04)!important;border-color:rgba(255,255,255,0.09)!important;box-shadow:0 2px 14px rgba(0,0,0,0.32)!important;}
[data-theme="dark"] .schoice:hover:not(.active){background:rgba(255,255,255,0.07)!important;border-color:rgba(255,255,255,0.15)!important;box-shadow:0 4px 18px rgba(0,0,0,0.38)!important;}
[data-theme="dark"] .schoice.active{border-color:var(--dm-accent)!important;background:var(--dm-accent-fill)!important;box-shadow:0 0 0 2px rgba(108,124,255,0.42),0 4px 24px rgba(0,0,0,0.45),0 0 38px rgba(108,124,255,0.22)!important;}
[data-theme="dark"] .schoice-name{color:var(--dm-text-1)!important;}
[data-theme="dark"] .schoice-sub{color:var(--dm-text-3)!important;}
[data-theme="dark"] .schoice-thumb{background:#0C1120!important;border-color:rgba(255,255,255,0.08)!important;}
[data-theme="dark"] .surprise-btn{background:linear-gradient(135deg,#5450CC,var(--dm-accent))!important;border-color:rgba(108,124,255,0.35)!important;color:#fff!important;box-shadow:0 4px 18px rgba(108,124,255,0.22)!important;}
[data-theme="dark"] .surprise-btn:hover{background:linear-gradient(135deg,#4840BE,#6070F8)!important;box-shadow:0 6px 26px rgba(108,124,255,0.30)!important;}
[data-theme="dark"] .design-content-row{background:transparent!important;}
[data-theme="dark"] .preview-box{background:rgba(6,10,20,0.32)!important;}
[data-theme="dark"] .preview-chrome-box{border:1.5px solid var(--dm-border-hero)!important;box-shadow:var(--dm-shadow-hero),0 0 0 1px rgba(108,124,255,0.08)!important;}
[data-theme="dark"] .opt-btns{border-color:var(--dm-border)!important;}
[data-theme="dark"] .opt-btn{color:var(--dm-text-2)!important;border-right-color:var(--dm-border)!important;}
[data-theme="dark"] .opt-btn:hover{background:rgba(108,124,255,0.09)!important;color:var(--dm-accent)!important;}
[data-theme="dark"] .opt-btn.selected{background:var(--dm-accent-fill)!important;color:var(--dm-accent)!important;font-weight:600!important;box-shadow:inset 0 0 0 1px rgba(108,124,255,0.32),0 2px 16px rgba(108,124,255,0.22)!important;}
[data-theme="dark"] #color-label{color:var(--dm-text-3)!important;}
[data-theme="dark"] .next-hint{color:var(--dm-text-3)!important;}
[data-theme="dark"] .refine-toggle{border-top-color:var(--dm-border)!important;color:var(--dm-accent)!important;}
[data-theme="dark"] .refine-toggle:hover{color:var(--dm-text-1)!important;}
[data-theme="dark"] .refine-inner{background:var(--dm-surf-refine)!important;border-bottom:1px solid var(--dm-border)!important;}
[data-theme="dark"] .ri-label{color:var(--dm-text-3)!important;}
[data-theme="dark"] .rpill{background:var(--dm-surf-card)!important;border-color:var(--dm-border)!important;color:var(--dm-text-3)!important;}
[data-theme="dark"] .rpill:hover{border-color:rgba(108,124,255,0.45)!important;color:var(--dm-text-2)!important;}
[data-theme="dark"] .rpill.active{background:var(--dm-accent-fill-mid)!important;border-color:var(--dm-accent)!important;color:var(--dm-accent)!important;box-shadow:0 0 0 1px rgba(108,124,255,0.32),0 2px 14px rgba(108,124,255,0.24)!important;}
[data-theme="dark"] .xswatch{border-color:transparent!important;}
[data-theme="dark"] .xswatch.active{border-color:var(--dm-accent)!important;box-shadow:0 0 0 2.5px rgba(108,124,255,0.24)!important;}
[data-theme="dark"] .try-btn{background:var(--dm-accent-fill)!important;border-color:var(--dm-accent-border)!important;color:var(--dm-accent)!important;}
[data-theme="dark"] .try-btn:hover{background:var(--dm-accent-fill-mid)!important;}
[data-theme="dark"] .next-btn{background:linear-gradient(135deg,#4B42D8 0%,#6C7CFF 100%)!important;box-shadow:0 4px 22px rgba(108,124,255,0.38),inset 0 1px 0 rgba(255,255,255,0.14)!important;transition:box-shadow 0.18s,opacity 0.15s!important;}
[data-theme="dark"] .next-btn:hover{opacity:1!important;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"] ::-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;}
body{font-family:'Plus Jakarta Sans',sans-serif;background:var(--paper);display:flex;flex-direction:column;height:100vh;overflow:hidden;}
.f{font-family:'Plus Jakarta Sans',sans-serif;}
.sidebar-phase{display:flex;align-items:center;gap:9px;padding:9px 10px;border-radius:8px;}
.sidebar-phase.active{background:#fafaff;}
.phase-dot{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:10px;}
/* FEEL PILLS */
.fpill{display:block;width:100%;text-align:left;padding:10px 13px;border:1.5px solid #e0e7ff;border-radius:9px;background:#f8f9ff;font-family:'Plus Jakarta Sans',sans-serif;cursor:pointer;transition:all 0.15s;margin-bottom:6px;}
.fpill:last-child{margin-bottom:0;}
.fpill:hover:not(.active){background:#f0f4ff;border-color:#c7d2fe;}
.fpill.active{background:#eef2ff;border-color:#6366F1;box-shadow:0 2px 10px rgba(99,102,241,0.12);}
.fpill-name{font-size:12.5px;font-weight:600;color:#1a1510;line-height:1.2;display:block;}
.fpill.active .fpill-name{color:#4338ca;}
.fpill-desc{font-size:10.5px;color:#9ca3af;line-height:1.3;display:block;margin-top:2px;}
/* COLOR CIRCLES */
.ccircle{width:28px;height:28px;border-radius:50%;border:2.5px solid transparent;cursor:pointer;transition:all 0.15s;flex-shrink:0;padding:0;background:transparent;display:flex;align-items:center;justify-content:center;}
.ccircle.active{border-color:#1a1510;box-shadow:0 0 0 3px rgba(26,21,16,0.1);}
.ccircle-inner{width:20px;height:20px;border-radius:50%;}
/* STRUCTURE CARDS */
.schoice{border:1.5px solid #e0e7ff;border-radius:10px;cursor:pointer;background:#f8f9ff;padding:9px 8px 8px;transition:all 0.15s;text-align:left;}
.schoice:hover:not(.active){border-color:#c7d2fe;background:#f0f4ff;}
.schoice.active{border-color:#6366F1;background:#eef2ff;box-shadow:0 0 0 3px rgba(99,102,241,0.1),0 2px 10px rgba(99,102,241,0.12);}
.schoice-thumb{height:50px;border-radius:5px;overflow:hidden;margin-bottom:6px;background:#fff;border:1px solid rgba(0,0,0,0.06);}
.schoice-name{font-size:11px;font-weight:700;color:#1a1510;line-height:1.2;}
.schoice-sub{font-size:9.5px;color:#9ca3af;margin-top:1px;line-height:1.3;}
@keyframes pulse-dot{0%,100%{opacity:1}50%{opacity:0.35}}
.opt-btns{display:flex;gap:0;flex-shrink:0;border:1.5px solid var(--border);border-radius:8px;overflow:visible;}
.opt-btn{flex:1;text-align:center;font-size:12px;font-weight:500;color:var(--mid);background:transparent;border:none;border-right:1.5px solid var(--border);border-radius:0;padding:6px 13px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:all 0.15s;white-space:nowrap;position:relative;}
.opt-btn:first-child{border-radius:7px 0 0 7px;}
.opt-btn:last-child{border-right:none;border-radius:0 7px 7px 0;}
.opt-btn:hover{background:#f5f5ff;color:#6366F1;}
.opt-btn.selected{background:rgba(99,102,241,0.08);color:#4338CA;font-weight:600;}
/* SECTION HEADERS */
.sec-head{font-size:9.5px;font-weight:700;letter-spacing:0.09em;text-transform:uppercase;color:#6366F1;margin-bottom:10px;}
.sec-div{height:1px;background: var(--section);}
/* BOTTOM BUTTONS */
.surprise-btn{width:100%;padding:10px;border:1.5px solid #e0e7ff;border-radius:9px;background:#6366F1;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:600;color:#ffffff;cursor:pointer;transition:all 0.15s;text-align:center;display:block;box-shadow:0 2px 14px rgba(99,102,241,0.22);}
.surprise-btn:hover{background:#5045c8;border-color:#c7d2fe;}
.next-btn{width:100%;padding:11px;border:none;border-radius:9px;background:linear-gradient(135deg,#4338CA,#6366F1);font-family:'Plus Jakarta Sans',sans-serif;font-size:13px;font-weight:700;color:#fff;cursor:pointer;transition:opacity 0.15s,box-shadow 0.2s;text-align:center;display:block;margin-top:8px;box-shadow:0 4px 18px rgba(99,102,241,0.22),0 1px 4px rgba(99,102,241,0.12);}
.next-btn:hover{opacity:0.88;box-shadow:0 6px 24px rgba(99,102,241,0.30),0 1px 4px rgba(99,102,241,0.14);}
.next-hint{font-size:10.5px;color:#9ca3af;text-align:center;margin:10px 0 4px;}
/* DESIGN RIGHT PANEL */
.design-right{width:384px;border-left:1px solid var(--border);background:#f5f3ff;display:flex;flex-direction:column;flex-shrink:0;}
.dp-card{background:var(--white);border:1px solid var(--border);border-radius:10px;box-shadow:0 2px 12px rgba(99,102,241,0.08);}
.dp-sec{font-size:9.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--muted);margin-bottom:8px;}
.deliverable-row{display:flex;align-items:center;gap:8px;padding:7px 13px;font-size:12px;color:var(--mid);border-bottom:1px solid var(--border);}
.deliverable-row:last-child{border-bottom:none;}
[data-theme="dark"] .design-right{background:var(--dm-surf-right)!important;border-left:1px solid var(--dm-border)!important;box-shadow:-4px 0 36px rgba(0,0,0,0.55)!important;-webkit-backdrop-filter:blur(20px)!important;backdrop-filter:blur(20px)!important;}
[data-theme="dark"] .dp-header-border{border-bottom-color:var(--dm-border)!important;}
[data-theme="dark"] .dp-panel-title{color:var(--dm-accent)!important;}
[data-theme="dark"] .dp-inner-border{border-bottom-color:var(--dm-border)!important;}
[data-theme="dark"] .dp-footer-border{border-top-color:var(--dm-border)!important;}
[data-theme="dark"] .design-right .dp-card{background:rgba(255,255,255,0.05)!important;border-color:rgba(255,255,255,0.10)!important;box-shadow:0 2px 16px rgba(0,0,0,0.36),inset 0 1px 0 rgba(255,255,255,0.04)!important;}
[data-theme="dark"] .design-right [style*="color:#4338ca"]{color:var(--dm-accent)!important;}
/* REFINE PANEL */
.refine-toggle{width:100%;background:none;border:none;border-top:1px solid #e0e7ff;cursor:pointer;display:flex;align-items:center;justify-content:space-between;padding:10px 16px;font-family:'Plus Jakarta Sans',sans-serif;font-size:11.5px;font-weight:600;color:#6366F1;transition:color 0.15s;}
.refine-toggle:hover{color:#4338ca;}
.refine-toggle .rt-chev{font-size:8px;transition:transform 0.22s;display:inline-block;opacity:0.55;margin-left:4px;}
.refine-toggle.open .rt-chev{transform:rotate(180deg);}
.refine-panel{overflow:hidden;max-height:0;transition:max-height 0.28s ease;}
.refine-panel.open{max-height:500px;}
.refine-inner{padding:12px 16px 14px;background:#fafbff;border-bottom:1px solid #e0e7ff;}
.ri-label{font-size:9px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#9ca3af;margin-bottom:6px;}
.rpill{padding:5px 9px;border:1.5px solid #e0e7ff;border-radius:7px;background:#ffffff;font-family:'Plus Jakarta Sans',sans-serif;font-size:11px;font-weight:500;color:#5f5e5a;cursor:pointer;transition:all 0.15s;white-space:nowrap;}
.rpill:hover{border-color:#c7d2fe;color:#4338ca;}
.rpill.active{background:#eef2ff;border-color:#6366F1;color:#4338ca;font-weight:600;box-shadow:0 1px 6px rgba(99,102,241,0.10);}
.xswatch{width:22px;height:22px;border-radius:50%;border:2.5px solid transparent;cursor:pointer;padding:0;background:transparent;display:flex;align-items:center;justify-content:center;transition:all 0.15s;flex-shrink:0;}
.xswatch.active{border-color:#1a1510;box-shadow:0 0 0 2.5px rgba(26,21,16,0.1);}
.xswatch-inner{width:15px;height:15px;border-radius:50%;}
.try-btn{width:100%;padding:8px;border:1.5px solid #e0e7ff;border-radius:8px;background:#ffffff;font-family:'Plus Jakarta Sans',sans-serif;font-size:11.5px;font-weight:600;color:#6366F1;cursor:pointer;transition:all 0.15s;text-align:center;}
.try-btn:hover{background:#eef2ff;border-color:#c7d2fe;}
</style>
</head>
<body>
<div style="display:flex;height:100%;overflow:hidden;">
<!-- SIDEBAR -->
<div class="sidebar-col" style="width:200px;background:#ffffff;border-right:1px solid #e5e7eb;display:flex;flex-direction:column;padding:18px 12px;flex-shrink:0;">
<div style="padding:0 6px;margin-bottom:26px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
<div class="vibn-avatar" style="width:26px;height:26px;background:linear-gradient(135deg,#2E2A5E,#4338CA);border-radius:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0;"><span class="f" style="font-size:13px;font-weight:700;color:#FFFFFF;">V</span></div>
<span class="f" style="font-size:16px;font-weight:700;color:#1a1a1a;letter-spacing:-0.02em;">vibn</span>
</div>
<div id="sidebar-project-name" style="font-size:11px;font-weight:500;color:#9ca3af;padding-left:34px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:none;"></div>
</div>
<div class="ph-name" style="font-size:9.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;padding:0 6px;margin-bottom:8px;">MVP Setup</div>
<div style="display:flex;flex-direction:column;gap:2px;flex:1;">
<div class="sidebar-phase" onclick="window.location.href='05_describe.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
<div class="phase-dot" style="background:#6366F1;color:#ffffff;"></div>
<div class="ph-name" style="font-size:12.5px;">Describe</div>
</div>
<div class="sidebar-phase" onclick="window.location.href='06_architect.html'" style="cursor:pointer;" onmouseover="this.style.background='#f5f3ff'" onmouseout="this.style.background='transparent'">
<div class="phase-dot" style="background:#6366F1;color:#ffffff;"></div>
<div class="ph-name" style="font-size:12.5px;">Architect</div>
</div>
<div class="sidebar-phase active">
<div class="phase-dot" style="background:#6366F1;color:#ffffff;"></div>
<div><div style="font-size:12.5px;font-weight:600;color:var(--ink);">Design</div><div class="ph-name" style="font-size:10px;">How it looks</div></div>
</div>
<div class="sidebar-phase">
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;"></div>
<div class="ph-name" style="font-size:12.5px;">Website</div>
</div>
<div class="sidebar-phase">
<div class="phase-dot" style="background:#e5e7eb;color:#9ca3af;"></div>
<div class="ph-name" style="font-size:12.5px;">Build MVP</div>
</div>
</div>
<div style="border-top:1px solid #e5e7eb;margin-top:14px;padding-top:12px;">
<button onclick="saveAndExit()" style="display:flex;align-items:center;justify-content:center;gap:7px;width:100%;background:#eef2ff;border:1px solid #e0e7ff;border-radius:8px;padding:9px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;transition:background 0.15s;" onmouseover="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#e0e7ff'" onmouseout="this.style.background=document.documentElement.dataset.theme==='dark'?'':'#eef2ff'">
<span style="font-size:12px;font-weight:600;color:#6366F1;">Save & go to dashboard</span>
</button>
<button id="dark-toggle" onclick="toggleTheme()" style="margin-top:8px;display:flex;align-items:center;justify-content:center;width:100%;background:transparent;border:1px solid var(--border);border-radius:8px;padding:8px 10px;cursor:pointer;font-family:'Plus Jakarta Sans',sans-serif;font-size:12px;font-weight:500;color:var(--mid);transition:background 0.15s,border-color 0.15s;" onmouseover="this.style.borderColor='#6366F1';this.style.color='#6366F1';" onmouseout="this.style.borderColor='var(--border)';this.style.color='var(--mid)';">🌙 Dark mode</button>
</div>
</div>
<!-- MAIN -->
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0;">
<!-- 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's look & feel</div>
<div style="font-size:12.5px;color:#9ca3af;">Pick a feel, a color, and a structure — we'll generate a live theme. You can refine everything after launch.</div>
</div>
</div>
<!-- CONTENT ROW -->
<div class="design-content-row" style="flex:1;overflow:hidden;display:flex;background:#f8f9ff;">
<!-- LEFT CONTROLS PANEL -->
<div class="design-left" style="width:272px;border-right:1px solid #e0e7ff;overflow:hidden;display:flex;flex-direction:column;flex-shrink:0;background:linear-gradient(to bottom,#FAFAFA,#F5F3FF);">
<!-- SCROLLABLE CONTENT -->
<div style="flex:1;overflow-y:auto;min-height:0;">
<!-- FEEL section -->
<div style="padding:16px 16px 14px;">
<div class="sec-head">Feel</div>
<button class="fpill active" id="fpill-friendly" onclick="setFeel('friendly')">
<span class="fpill-name">Friendly &amp; approachable</span>
<span class="fpill-desc">Light, warm, human</span>
</button>
<button class="fpill" id="fpill-minimal" onclick="setFeel('minimal')">
<span class="fpill-name">Modern &amp; minimal</span>
<span class="fpill-desc">Clean, sharp, focused</span>
</button>
<button class="fpill" id="fpill-premium" onclick="setFeel('premium')">
<span class="fpill-name">Premium SaaS</span>
<span class="fpill-desc">Dark, polished, high-trust</span>
</button>
</div>
<div class="sec-div" style="margin:0 14px;"></div>
<!-- COLOR section -->
<div style="padding:16px 16px 14px;">
<div class="sec-head">Accent color</div>
<div style="display:flex;align-items:center;gap:10px;">
<button class="ccircle active" id="cc-indigo" onclick="setColor('indigo')" title="Indigo">
<div class="ccircle-inner" style="background:#6366F1;"></div>
</button>
<button class="ccircle" id="cc-blue" onclick="setColor('blue')" title="Blue">
<div class="ccircle-inner" style="background:#3B82F6;"></div>
</button>
<button class="ccircle" id="cc-teal" onclick="setColor('teal')" title="Teal">
<div class="ccircle-inner" style="background:#0D9488;"></div>
</button>
<span id="color-label" style="font-size:11px;color:#9ca3af;margin-left:4px;">Indigo</span>
</div>
</div>
<div class="sec-div" style="margin:0 14px;"></div>
<!-- STRUCTURE section -->
<div style="padding:16px 16px 14px;">
<div class="sec-head">Layout structure</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:7px;">
<button class="schoice active" id="sc-clean" onclick="setStructure('clean')">
<div class="schoice-thumb" id="thumb-clean"></div>
<div class="schoice-name">Clean</div>
<div class="schoice-sub">like Notion</div>
</button>
<button class="schoice" id="sc-data" onclick="setStructure('data')">
<div class="schoice-thumb" id="thumb-data"></div>
<div class="schoice-name">Data-rich</div>
<div class="schoice-sub">like Stripe</div>
</button>
<button class="schoice" id="sc-bold" onclick="setStructure('bold')">
<div class="schoice-thumb" id="thumb-bold"></div>
<div class="schoice-name">Bold</div>
<div class="schoice-sub">like Linear</div>
</button>
</div>
</div>
<!-- REFINE TOGGLE -->
<button class="refine-toggle" id="refine-toggle" onclick="toggleRefine()">
<span>Refine your style</span>
<span class="rt-chev"></span>
</button>
<!-- REFINE PANEL (collapsed by default) -->
<div class="refine-panel" id="refine-panel">
<div class="refine-inner">
<!-- Extra color swatches -->
<div style="margin-bottom:11px;">
<div class="ri-label">More colors</div>
<div style="display:flex;align-items:center;gap:6px;">
<button class="xswatch" id="xs-violet" onclick="setExtraColor('violet','#7C3AED')" title="Purple"><div class="xswatch-inner" style="background:#7C3AED;"></div></button>
<button class="xswatch" id="xs-rose" onclick="setExtraColor('rose', '#E11D48')" title="Rose"> <div class="xswatch-inner" style="background:#E11D48;"></div></button>
<button class="xswatch" id="xs-amber" onclick="setExtraColor('amber', '#D97706')" title="Amber"> <div class="xswatch-inner" style="background:#D97706;"></div></button>
</div>
</div>
<!-- Visual intensity -->
<div style="margin-bottom:10px;">
<div class="ri-label">Visual intensity</div>
<div style="display:flex;gap:5px;">
<button class="rpill" id="ri-subtle" onclick="setIntensity('subtle')">Subtle</button>
<button class="rpill active" id="ri-balanced" onclick="setIntensity('balanced')">Balanced</button>
<button class="rpill" id="ri-bold" onclick="setIntensity('bold')">Bold</button>
</div>
</div>
<!-- Content density -->
<div id="density-row" style="margin-bottom:12px;">
<div class="ri-label">Content density</div>
<div style="display:flex;gap:5px;">
<button class="rpill" id="rd-spacious" onclick="setDensity('spacious')">Spacious</button>
<button class="rpill active" id="rd-balanced" onclick="setDensity('balanced')">Balanced</button>
<button class="rpill" id="rd-compact" onclick="setDensity('compact')">Compact</button>
</div>
</div>
</div>
</div>
</div><!-- end scrollable content -->
<!-- BOTTOM ACTIONS -->
<div style="flex-shrink:0;padding:14px 16px 18px;">
<div class="sec-div" style="margin:0 0 14px;"></div>
<button class="surprise-btn" onclick="surpriseMe()">✨ Surprise me</button>
</div>
</div>
<!-- RIGHT PREVIEW -->
<div class="preview-box" style="flex:1;display:flex;flex-direction:column;overflow:hidden;padding:4px 6px 6px;min-width:0;">
<!-- Label + device toggle -->
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;padding:0 2px;">
<div style="font-size:9.5px;font-weight:700;letter-spacing:0.1em;text-transform:uppercase;color:#9ca3af;display:flex;align-items:center;gap:7px;">
<span style="width:6px;height:6px;border-radius:50%;background:#22c55e;display:inline-block;animation:pulse-dot 2s infinite;"></span>
Live preview
</div>
<div class="opt-btns">
<button class="opt-btn selected" data-group="device" data-tip="Runs in any browser, desktop & mobile" onclick="setDevice(this)">Web app</button>
<button class="opt-btn" data-group="device" data-tip="Optimised for phones, still works on desktop" onclick="setDevice(this)">Mobile-first</button>
</div>
</div>
<!-- Browser chrome -->
<div class="preview-chrome-box" style="flex:1;border-radius:12px;overflow:hidden;border:1.5px solid #e0e7ff;box-shadow:0 4px 24px rgba(99,102,241,0.08);display:flex;flex-direction:column;min-height:0;">
<div class="preview-chrome" style="background:#f5f3ff;padding:7px 12px;display:flex;align-items:center;gap:8px;border-bottom:1px solid #e0e7ff;flex-shrink:0;">
<div style="display:flex;gap:4px;"><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div><div style="width:7px;height:7px;border-radius:50%;background:#c7d2fe;"></div></div>
<div class="url-bar" style="flex:1;background:#fff;border:1px solid #e0e7ff;border-radius:4px;padding:2px 9px;font-family:monospace;font-size:10px;color:#6366F1;">app.yourproduct.com</div>
</div>
<div class="preview-bg" style="flex:1;overflow:hidden;display:flex;align-items:stretch;">
<div data-theme="" id="mock" style="flex:1;overflow:hidden;transition:all 0.3s;font-family:'Plus Jakarta Sans',sans-serif;"></div>
</div>
</div>
</div>
</div>
</div>
<!-- DESIGN RIGHT PANEL -->
<div class="design-right">
<!-- Header -->
<div style="flex-shrink:0;padding:18px 0 0;">
<div class="dp-header-border" style="margin:0 16px;padding-bottom:14px;border-bottom:1px solid #c7d2fe;">
<div class="dp-panel-title" style="font-size:15px;font-weight:800;letter-spacing:0.04em;text-transform:uppercase;color:#4338ca;margin-bottom:5px;">Your app's design</div>
<div style="font-size:12px;color:#A0A0B8;line-height:1.5;">How your product will look and feel to users on day one.</div>
</div>
</div>
<!-- Scrollable content -->
<div style="flex:1;overflow-y:auto;padding:16px;">
<!-- First impression -->
<div class="dp-sec">First impression</div>
<div class="dp-card" style="padding:11px 13px;margin-bottom:16px;font-size:12.5px;color:var(--ink);line-height:1.55;" id="dp-impression"></div>
<!-- Visual identity -->
<div class="dp-sec">Visual identity</div>
<div class="dp-card" style="overflow:hidden;margin-bottom:16px;" id="dp-identity"></div>
<!-- How users will experience it -->
<div class="dp-sec">How users will experience it</div>
<div class="dp-card" style="overflow:hidden;margin-bottom:16px;" id="dp-perceptions"></div>
<!-- Design detail -->
<div class="dp-sec">Design detail</div>
<div class="dp-card" style="overflow:hidden;margin-bottom:16px;" id="dp-detail"></div>
</div>
<!-- Footer -->
<div class="dp-footer-border" style="flex-shrink:0;padding:9px 0 13px;border-top:1px solid var(--border);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;">Happy with this? Set up your website next.</p>
<a href="08_website.html" style="text-decoration:none;display:block;width:80%;">
<button class="next-btn" style="width:100%;border-radius:8px;padding:12px 14px;font-size:13px;font-weight:600;margin-top:0;">Next: Website</button>
</a>
</div>
</div>
</div>
<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>
<script>
var state = {feel:'friendly', color:'indigo', structure:'clean'};
var refine = {intensity:'balanced', density:'balanced', extraColor:null};
var DEVICE = 'desktop';
var COLORS = {
indigo:{h:'#6366F1',hl:'#818CF8',name:'Indigo', lightBg:'#f5f3ff',darkBg:'#0d1021',darkSurf:'#141929',darkBorder:'rgba(99,102,241,0.18)'},
teal: {h:'#0D9488',hl:'#2DD4BF',name:'Teal', lightBg:'rgba(193, 229, 217, 0.03)',darkBg:'#021a17',darkSurf:'#0a2a24',darkBorder:'rgba(13,148,136,0.18)'},
blue: {h:'#3B82F6',hl:'#60A5FA',name:'Blue', lightBg:'#eff6ff',darkBg:'#050d1f',darkSurf:'#0d1a35',darkBorder:'rgba(59,130,246,0.18)'},
rose: {h:'#E11D48',hl:'#F43F5E',name:'Rose', lightBg:'#fff1f2',darkBg:'#1a0007',darkSurf:'#2d0010',darkBorder:'rgba(225,29,72,0.18)'},
violet:{h:'#7C3AED',hl:'#8B5CF6',name:'Violet', lightBg:'#f5f3ff',darkBg:'#12071e',darkSurf:'#1e0f30',darkBorder:'rgba(124,58,237,0.18)'},
amber: {h:'#D97706',hl:'#F59E0B',name:'Amber', lightBg:'rgba(245, 245, 220, 0.1)',darkBg:'#1a1000',darkSurf:'#2d1d00',darkBorder:'rgba(217,119,6,0.18)'}
};
function getTheme(){
var c = refine.extraColor ? COLORS[refine.extraColor] : COLORS[state.color];
var t;
if(state.feel === 'premium'){
t = {bg:'#1a1f35', surf:'#252b45', text:'#f0eeff', sub:'rgba(240,238,255,0.52)',
accent:c.hl, border:'rgba(255,255,255,0.13)',
sidebarBg:'#171c30', navText:'rgba(240,238,255,0.48)', navActive:'rgba(240,238,255,0.95)', r:7};
} else if(state.feel === 'friendly'){
t = {bg:c.lightBg, surf:'#ffffff', text:'#1a1410', sub:'#78716c',
accent:c.h, border:'rgba(0,0,0,0.07)',
sidebarBg:'rgba(0,0,0,0.04)', navText:'#78716c', navActive:'#1a1410', r:14};
} else {
t = {bg:'#ffffff', surf:'#f8fafc', text:'#09090b', sub:'#94a3b8',
accent:c.h, border:'rgba(0,0,0,0.07)',
sidebarBg:'#f8fafc', navText:'#94a3b8', navActive:'#09090b', r:4};
}
/* ── intensity ── */
var ix = refine.intensity;
var ir = {subtle:3, balanced:0, bold:-2}[ix];
t.r = Math.max(t.r + ir, 2);
// Shadows: subtle=none, balanced=light, bold=stronger
t.shadow = {subtle:'none', balanced:'0 1px 3px rgba(0,0,0,0.06)', bold:'0 3px 12px rgba(0,0,0,0.12)'}[ix];
// Borders: subtle=softer, balanced=normal, bold=slightly thicker
t.bw = {subtle:'1px', balanced:'1px', bold:'1.5px'}[ix];
t.borderAlpha = {subtle:0.06, balanced:1, bold:1}[ix]; // used below to soften border color in subtle
if(ix === 'subtle') t.border = t.border.replace('0.07','0.05');
// CTA button: subtle=outlined (less visual weight), bold=filled+shadow
t.ctaOutlined = ix === 'subtle';
t.ctaShadow = ix === 'bold' ? '0 2px 8px '+hexRgba(t.accent,0.32) : 'none';
// Nav active item
t.navActiveBg = {subtle:'rgba(128,128,128,0.06)', balanced:'rgba(128,128,128,0.10)', bold:hexRgba(t.accent,0.14)}[ix];
t.navActiveDot = t.accent; // keep accent visible in all states
t.navActiveDotOp = {subtle:'0.55', balanced:'0.85', bold:'1'}[ix];
// Trend badges
t.badgeAlpha = {subtle:0.07, balanced:0.10, bold:0.18}[ix];
// Chart bars
t.barActive = {subtle:0.45, balanced:0.60, bold:0.92}[ix];
t.barDim = {subtle:0.12, balanced:0.18, bold:0.30}[ix];
// Accent stripe on stat cards (bold only)
t.accentStripe = ix === 'bold';
// Text opacity: subtle keeps full readability
t.subOp = {subtle:'1', balanced:'1', bold:'1'}[ix];
t.textOp = {subtle:'1', balanced:'1', bold:'1'}[ix];
// Progress bars
t.progressOp = {subtle:0.50, balanced:0.70, bold:1.00}[ix];
// Surface contrast: subtle uses slightly more muted surface
if(ix === 'subtle' && state.feel !== 'premium'){
t.surf = t.surf === '#ffffff' ? '#fafafa' : t.surf;
}
// Bold: boost surface contrast slightly
if(ix === 'bold' && state.feel !== 'premium'){
t.sidebarBg = state.feel === 'friendly' ? 'rgba(0,0,0,0.06)' : '#f1f5f9';
}
// Premium: override shadows and accent glow regardless of intensity
if(state.feel === 'premium'){
t.shadow = '0 2px 14px rgba(0,0,0,0.45),0 0 0 1px rgba(255,255,255,0.06)';
t.ctaShadow = '0 2px 14px '+hexRgba(t.accent,0.48);
t.navActiveBg = hexRgba(t.accent,0.18);
}
/* ── density ── */
var dp = {
spacious: {cp:22, rg:10, sp:12, rows:{proj:2, act:2, tbl:3, sig:2}},
balanced: {cp:14, rg:6, sp:7, rows:{proj:3, act:3, tbl:4, sig:3}},
compact: {cp:7, rg:3, sp:4, rows:{proj:5, act:5, tbl:6, sig:4}}
}[refine.density];
t.cp = dp.cp; t.rg = dp.rg; t.sp = dp.sp; t.rows = dp.rows;
return t;
}
function hexRgba(hex, a){
var r=parseInt(hex.slice(1,3),16), g=parseInt(hex.slice(3,5),16), b=parseInt(hex.slice(5,7),16);
return 'rgba('+r+','+g+','+b+','+a+')';
}
/* ── Mock helpers ── */
function sideNav(label, active, t){
return '<div style="padding:4px 8px;margin:1px 5px;display:flex;align-items:center;gap:5px;border-radius:5px;background:'+(active?t.navActiveBg:'transparent')+';">'
+'<div style="width:6px;height:6px;border-radius:1px;background:'+(active?t.navActiveDot:t.navText)+';opacity:'+(active?t.navActiveDotOp:'0.28')+';flex-shrink:0;"></div>'
+'<span style="font-size:8px;font-weight:'+(active?'600':'400')+';color:'+(active?t.navActive:t.navText)+';">'+label+'</span>'
+'</div>';
}
function statCard(val, label, trend, t){
var up = trend.charAt(0)==='+';
var tc = up ? (state.feel==='premium'?'#86efac':'#16a34a') : (state.feel==='premium'?'#fca5a5':'#dc2626');
var tbg = up ? (state.feel==='premium'?'rgba(134,239,172,'+t.badgeAlpha+')':'rgba(22,163,74,'+t.badgeAlpha+')')
: (state.feel==='premium'?'rgba(252,165,165,'+t.badgeAlpha+')':'rgba(220,38,38,'+t.badgeAlpha+')');
var stripe = t.accentStripe ? '<div style="height:3px;background:'+t.accent+';margin:-1px -1px 0;"></div>' : '';
return '<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;box-shadow:'+t.shadow+';min-width:0;overflow:hidden;">'
+stripe
+'<div style="padding:'+t.sp+'px '+(t.sp+1)+'px;">'
+'<div style="font-size:6.5px;color:'+t.sub+';letter-spacing:0.04em;text-transform:uppercase;margin-bottom:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:'+t.subOp+';">'+label+'</div>'
+'<div style="font-size:12px;font-weight:800;color:'+t.text+';line-height:1.2;margin-bottom:2px;opacity:'+t.textOp+';">'+val+'</div>'
+'<div style="display:inline-block;background:'+tbg+';border-radius:3px;padding:1px 4px;">'
+'<span style="font-size:6.5px;font-weight:700;color:'+tc+';">'+trend+'</span>'
+'</div>'
+'</div>'
+'</div>';
}
function projectCard(name, status, pct, t){
var sc = {'In progress':t.accent,'Review':'#f59e0b','Planning':t.sub,'Done':'#22c55e'}[status]||t.accent;
return '<div style="background:'+t.surf+';border:1px solid '+t.border+';border-radius:'+t.r+'px;padding:6px 8px;box-shadow:'+t.shadow+';overflow:hidden;">'
+'<div style="font-size:8px;font-weight:600;color:'+t.text+';margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="display:inline-flex;align-items:center;gap:2px;margin-bottom:4px;">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+sc+';"></div>'
+'<span style="font-size:6.5px;color:'+t.sub+';">'+status+'</span>'
+'</div>'
+'<div style="height:2px;background:rgba(128,128,128,0.12);border-radius:2px;overflow:hidden;">'
+'<div style="height:100%;background:'+sc+';width:'+pct+'%;opacity:0.7;border-radius:2px;"></div>'
+'</div>'
+'</div>';
}
function actRow(text, time, t){
var dotOp = {subtle:0.4, balanced:0.5, bold:0.75}[refine.intensity];
return '<div style="display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+t.accent+';opacity:'+dotOp+';flex-shrink:0;"></div>'
+'<div style="flex:1;font-size:7.5px;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+text+'</div>'
+'<div style="font-size:7px;color:'+t.sub+';flex-shrink:0;">'+time+'</div>'
+'</div>';
}
function tableRow(name, date, amount, status, t){
var sc = {'Active':'#22c55e','Pending':'#f59e0b','Cancelled':t.sub}[status]||t.accent;
return '<div style="display:grid;grid-template-columns:1.6fr 0.7fr 0.7fr 0.8fr;gap:4px;padding:5px 9px;border-bottom:1px solid '+t.border+';align-items:center;">'
+'<div style="font-size:7.5px;font-weight:500;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="font-size:7px;color:'+t.sub+';">'+date+'</div>'
+'<div style="font-size:7.5px;font-weight:600;color:'+t.text+';">'+amount+'</div>'
+'<div style="display:inline-flex;align-items:center;gap:2px;">'
+'<div style="width:4px;height:4px;border-radius:50%;background:'+sc+';flex-shrink:0;"></div>'
+'<span style="font-size:7px;color:'+t.sub+';">'+status+'</span>'
+'</div>'
+'</div>';
}
function signupRow(name, email, time, t){
var avatarOp = {subtle:0.10, balanced:0.18, bold:0.28}[refine.intensity];
return '<div style="display:flex;align-items:center;gap:7px;padding:4px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:18px;height:18px;border-radius:50%;background:'+t.accent+';opacity:'+avatarOp+';flex-shrink:0;display:flex;align-items:center;justify-content:center;">'
+'<span style="font-size:7.5px;font-weight:700;color:'+t.accent+';opacity:5;">'+name.charAt(0)+'</span>'
+'</div>'
+'<div style="flex:1;min-width:0;">'
+'<div style="font-size:7.5px;font-weight:600;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="font-size:7px;color:'+t.sub+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+email+'</div>'
+'</div>'
+'<div style="font-size:7px;color:'+t.sub+';flex-shrink:0;">'+time+'</div>'
+'</div>';
}
function boldCard(name, status, pct, t){
var sc = {'In progress':t.accent,'Review':'#f59e0b','Planning':t.sub,'Active':t.accent,'Done':'#22c55e'}[status]||t.accent;
return '<div style="background:'+t.surf+';border:1px solid '+t.border+';border-radius:'+t.r+'px;overflow:hidden;box-shadow:'+t.shadow+';">'
+'<div style="height:3px;background:'+sc+';opacity:0.65;"></div>'
+'<div style="padding:6px 8px;">'
+'<div style="font-size:8.5px;font-weight:700;color:'+t.text+';margin-bottom:3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="display:inline-flex;align-items:center;gap:2px;background:rgba(128,128,128,0.07);border-radius:3px;padding:1px 5px;margin-bottom:5px;">'
+'<div style="width:4px;height:4px;border-radius:50%;background:'+sc+';"></div>'
+'<span style="font-size:6.5px;color:'+t.sub+';">'+status+'</span>'
+'</div>'
+'<div style="height:2px;background:rgba(128,128,128,0.1);border-radius:2px;overflow:hidden;">'
+'<div style="height:100%;background:'+sc+';width:'+pct+'%;opacity:0.7;border-radius:2px;"></div>'
+'</div>'
+'</div>'
+'</div>';
}
function issueRow(text, priority, t){
var pc = {'High':'#ef4444','Medium':'#f59e0b','Low':t.sub}[priority]||t.sub;
return '<div style="display:flex;align-items:center;gap:6px;padding:4px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+pc+';flex-shrink:0;"></div>'
+'<div style="flex:1;font-size:7.5px;color:'+t.text+';opacity:0.75;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+text+'</div>'
+'<div style="font-size:6.5px;color:'+pc+';flex-shrink:0;font-weight:600;opacity:0.85;">'+priority+'</div>'
+'</div>';
}
function mockLogo(t){
return '<div style="padding:9px 10px;border-bottom:1px solid '+t.border+';display:flex;align-items:center;gap:5px;flex-shrink:0;">'
+'<div style="width:18px;height:18px;background:'+t.accent+';border-radius:4px;flex-shrink:0;"></div>'
+'<span style="font-size:9.5px;font-weight:700;color:'+t.navActive+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">Acme</span>'
+'</div>';
}
function miniBarChart(label, data, t){
return '<div style="flex-shrink:0;">'
+'<div style="font-size:7px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.sub+';opacity:'+t.subOp+';margin-bottom:5px;">'+label+'</div>'
+'<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;padding:7px 10px 5px;box-shadow:'+t.shadow+';">'
+'<div style="display:flex;align-items:flex-end;gap:3px;height:28px;">'
+data.map(function(bar,i){return '<div style="flex:1;background:'+t.accent+';border-radius:2px 2px 0 0;height:'+bar.v+'%;opacity:'+(i===data.length-2?t.barActive:t.barDim)+';"></div>';}).join('')
+'</div>'
+'<div style="display:flex;gap:3px;margin-top:4px;">'
+data.map(function(bar){return '<div style="flex:1;text-align:center;font-size:5.5px;color:'+t.sub+';opacity:'+t.subOp+';">'+bar.d+'</div>';}).join('')
+'</div>'
+'</div>'
+'</div>';
}
function projectRow(name, status, pct, t){
var sc = {'In progress':t.accent,'Review':'#f59e0b','Planning':t.sub,'Done':'#22c55e'}[status]||t.accent;
return '<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;padding:7px 10px;box-shadow:'+t.shadow+';margin-bottom:'+t.rg+'px;flex-shrink:0;">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px;">'
+'<div style="font-size:8.5px;font-weight:600;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0;margin-right:8px;opacity:'+t.textOp+';">'+name+'</div>'
+'<div style="display:flex;align-items:center;gap:3px;flex-shrink:0;">'
+'<div style="width:5px;height:5px;border-radius:50%;background:'+sc+';opacity:'+t.progressOp+';"></div>'
+'<span style="font-size:6.5px;color:'+t.sub+';opacity:'+t.subOp+';">'+status+'</span>'
+'</div>'
+'</div>'
+'<div style="height:'+( t.bw==='1.5px'?'4':'3')+'px;background:rgba(128,128,128,0.1);border-radius:2px;overflow:hidden;">'
+'<div style="height:100%;background:'+sc+';width:'+pct+'%;opacity:'+t.progressOp+';border-radius:2px;"></div>'
+'</div>'
+'</div>';
}
function mockTopbar(title, actionLabel, t){
var btnBg = t.ctaOutlined ? 'transparent' : t.accent;
var btnBdr = t.ctaOutlined ? '1px solid '+t.accent : 'none';
var btnClr = t.ctaOutlined ? t.accent : '#fff';
var btnShdw = t.ctaShadow !== 'none' ? ';box-shadow:'+t.ctaShadow : '';
return '<div style="padding:8px 12px;border-bottom:1px solid '+t.border+';display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">'
+ '<div style="font-size:11px;font-weight:700;color:'+t.text+';opacity:'+t.textOp+';">'+title+'</div>'
+ '<div style="display:flex;align-items:center;justify-content:center;cursor:pointer;min-height:28px;padding:0 12px;background:'+btnBg+';border:'+btnBdr+';border-radius:'+t.r+'px'+btnShdw+';">'
+ '<span style="font-size:8px;font-weight:600;color:'+btnClr+';">'+actionLabel+'</span>'
+ '</div>'
+ '</div>';
}
function renderClean(t){
var allProj = [
projectRow('Website redesign','In progress',60,t),
projectRow('Mobile app','Review',85,t),
projectRow('API v2','Planning',20,t),
projectRow('Analytics dashboard','In progress',45,t),
projectRow('Onboarding flow','Done',100,t)
];
var allAct = [
actRow('Figma file updated · Design team','2m',t),
actRow('PR #84 merged to main · @alex','14m',t),
actRow('New signup: holly@acme.com','1h',t),
actRow('Deploy to staging · @kai','2h',t),
actRow('Issue #42 closed · @priya','4h',t)
];
return '<div style="height:100%;background:'+t.bg+';display:flex;justify-content:center;align-items:stretch;">'
+'<div style="flex:1;max-width:860px;display:flex;overflow:hidden;box-shadow:0 0 0 1px rgba(0,0,0,0.08),0 8px 32px rgba(0,0,0,0.1);">'
+'<div style="width:90px;background:'+t.sidebarBg+';border-right:1px solid '+t.border+';display:flex;flex-direction:column;flex-shrink:0;overflow:hidden;">'
+mockLogo(t)
+'<div style="padding-top:4px;">'
+sideNav('Dashboard',true,t)
+sideNav('Projects',false,t)
+sideNav('Analytics',false,t)
+sideNav('Settings',false,t)
+'</div>'
+'<div style="height:1px;background:'+t.border+';margin:7px 8px 5px;"></div>'
+'<div style="padding:0 8px 3px;font-size:6.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.navText+';opacity:0.5;">Pinned</div>'
+sideNav('Q4 Report',false,t)
+sideNav('Roadmap',false,t)
+'</div>'
+'<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">'
+mockTopbar('Dashboard','+ New page',t)
+'<div style="padding:'+t.sp+'px 10px;display:grid;grid-template-columns:repeat(3,1fr);gap:'+t.rg+'px;flex-shrink:0;">'
+statCard('2,847','Active users','+12%',t)
+statCard('$18.3k','Revenue','+8%',t)
+statCard('34%','Conversion','+4pt',t)
+'</div>'
+'<div style="padding:0 10px 10px;flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0;">'
+'<div style="font-size:10px;font-weight:700;color:'+t.text+';margin-bottom:7px;flex-shrink:0;opacity:'+t.textOp+';">Projects</div>'
+allProj.slice(0,t.rows.proj).join('')
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+'<div style="font-size:7px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.sub+';margin-bottom:5px;flex-shrink:0;opacity:'+t.subOp+';">Recent activity</div>'
+allAct.slice(0,t.rows.act).join('')
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+miniBarChart('Performance',[{v:55,d:'M'},{v:72,d:'T'},{v:48,d:'W'},{v:88,d:'T'},{v:65,d:'F'},{v:91,d:'S'},{v:79,d:'S'}],t)
+'</div>'
+'</div>'
+'</div>'
+'</div>';
}
function renderData(t){
var allTbl = [
tableRow('Acme Corp','Jan 12','$2,400','Active',t),
tableRow('TechFlow Ltd','Jan 11','$980','Active',t),
tableRow('Moonbase Inc','Jan 10','$3,100','Pending',t),
tableRow('Stellar LLC','Jan 9','$640','Cancelled',t),
tableRow('Harbor Co','Jan 8','$1,200','Active',t),
tableRow('Nimbus AI','Jan 7','$890','Pending',t)
];
var allSig = [
signupRow('James Liu','james@techflow.io','5m',t),
signupRow('Ana Perez','ana@moonbase.co','1h',t),
signupRow('Tom K.','tom@stellar.xyz','3h',t),
signupRow('Chen Wei','chen@harbor.co','8h',t)
];
var btnBg = t.ctaOutlined ? 'transparent' : t.accent;
var btnBdr = t.ctaOutlined ? '1px solid '+t.accent : 'none';
var btnClr = t.ctaOutlined ? t.accent : '#fff';
return '<div style="height:100%;background:'+t.bg+';display:flex;justify-content:center;align-items:stretch;">'
+'<div style="flex:1;max-width:860px;display:flex;overflow:hidden;box-shadow:0 0 0 1px rgba(0,0,0,0.08),0 8px 32px rgba(0,0,0,0.1);">'
+'<div style="width:92px;background:'+t.sidebarBg+';border-right:1px solid '+t.border+';display:flex;flex-direction:column;flex-shrink:0;overflow:hidden;">'
+mockLogo(t)
+'<div style="padding:5px 8px 2px;font-size:6.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.navText+';opacity:0.5;">Overview</div>'
+sideNav('Dashboard',true,t)
+sideNav('Revenue',false,t)
+sideNav('Customers',false,t)
+'<div style="padding:6px 8px 2px;font-size:6.5px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.navText+';opacity:0.5;">Manage</div>'
+sideNav('Invoices',false,t)
+sideNav('Settings',false,t)
+'</div>'
+'<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">'
+'<div style="padding:8px 12px;border-bottom:1px solid '+t.border+';display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">'
+'<div style="font-size:11px;font-weight:700;color:'+t.text+';opacity:'+t.textOp+';">Overview</div>'
+'<div style="display:flex;align-items:center;gap:6px;">'
+'<div style="display:flex;align-items:center;justify-content:center;min-height:22px;padding:0 10px;border:1px solid '+t.border+';border-radius:'+t.r+'px;font-size:7px;color:'+t.sub+';opacity:'+t.subOp+';">Last 30 days</div>'
+'<div style="display:flex;align-items:center;justify-content:center;min-height:22px;padding:0 10px;background:'+btnBg+';border:'+btnBdr+';border-radius:'+t.r+'px;font-size:7px;font-weight:600;color:'+btnClr+';">Export</div>'
+'</div>'
+'</div>'
+'<div style="padding:'+t.sp+'px 10px;display:grid;grid-template-columns:repeat(3,1fr);gap:'+t.rg+'px;flex-shrink:0;">'
+statCard('$18.3k','MRR','+8%',t)
+statCard('1,234','Customers','+14%',t)
+statCard('98.2%','Uptime','+0.1%',t)
+'</div>'
+'<div style="padding:0 10px 10px;flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0;">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:7px;flex-shrink:0;">'
+'<div style="font-size:10px;font-weight:700;color:'+t.text+';opacity:'+t.textOp+';">Transactions</div>'
+'<div style="font-size:7.5px;color:'+t.accent+';">View all →</div>'
+'</div>'
+'<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;overflow:hidden;box-shadow:'+t.shadow+';flex-shrink:0;">'
+'<div style="display:grid;grid-template-columns:1.6fr 0.7fr 0.7fr 0.8fr;gap:4px;padding:5px 9px;border-bottom:1px solid '+t.border+';background:rgba(128,128,128,0.03);">'
+'<div style="font-size:6.5px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;color:'+t.sub+';opacity:'+t.subOp+';">Customer</div>'
+'<div style="font-size:6.5px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;color:'+t.sub+';opacity:'+t.subOp+';">Date</div>'
+'<div style="font-size:6.5px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;color:'+t.sub+';opacity:'+t.subOp+';">Amount</div>'
+'<div style="font-size:6.5px;font-weight:700;text-transform:uppercase;letter-spacing:0.06em;color:'+t.sub+';opacity:'+t.subOp+';">Status</div>'
+'</div>'
+allTbl.slice(0,t.rows.tbl).join('')
+'</div>'
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+'<div style="font-size:7px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.sub+';margin-bottom:5px;flex-shrink:0;opacity:'+t.subOp+';">New signups</div>'
+allSig.slice(0,t.rows.sig).join('')
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+miniBarChart('Performance',[{v:60,d:'M'},{v:65,d:'T'},{v:58,d:'W'},{v:80,d:'T'},{v:70,d:'F'},{v:95,d:'S'},{v:82,d:'S'}],t)
+'</div>'
+'</div>'
+'</div>'
+'</div>';
}
function renderBold(t){
var iconItem = function(active){
return '<div style="width:28px;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;background:'+(active?'rgba(128,128,128,0.12)':'transparent')+';margin-bottom:1px;">'
+'<div style="width:12px;height:12px;border-radius:2px;background:'+(active?t.navActive:t.navText)+';opacity:'+(active?0.65:0.25)+';"></div>'
+'</div>';
};
return '<div style="height:100%;background:'+t.bg+';display:flex;justify-content:center;align-items:stretch;">'
+'<div style="flex:1;max-width:860px;display:flex;overflow:hidden;box-shadow:0 0 0 1px rgba(0,0,0,0.08),0 8px 32px rgba(0,0,0,0.1);">'
/* icon sidebar */
+'<div style="width:44px;background:'+t.sidebarBg+';border-right:1px solid '+t.border+';display:flex;flex-direction:column;align-items:center;padding:10px 0;gap:2px;flex-shrink:0;">'
+'<div style="width:22px;height:22px;background:'+t.accent+';border-radius:'+(Math.min(t.r,5))+'px;margin-bottom:8px;flex-shrink:0;"></div>'
+iconItem(true)+iconItem(false)+iconItem(false)+iconItem(false)
+'</div>'
/* main */
+'<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;">'
/* header */
+'<div style="padding:9px 12px 8px;border-bottom:1px solid '+t.border+';display:flex;align-items:center;justify-content:space-between;flex-shrink:0;">'
+'<div>'
+'<div style="font-size:7.5px;color:'+t.sub+';text-transform:uppercase;letter-spacing:0.07em;margin-bottom:1px;">My workspace</div>'
+'<div style="font-size:13px;font-weight:800;color:'+t.text+';line-height:1.15;">Good morning 👋</div>'
+'</div>'
+'<div style="display:flex;align-items:center;justify-content:center;min-height:24px;padding:0 12px;background:'+t.accent+';border-radius:'+t.r+'px;">'
+'<span style="font-size:8px;font-weight:700;color:#fff;">+ Project</span>'
+'</div>'
+'</div>'
/* stats */
+'<div style="padding:'+t.sp+'px 10px;display:grid;grid-template-columns:repeat(3,1fr);gap:'+t.rg+'px;flex-shrink:0;">'
+statCard('24','Open issues','+3',t)
+statCard('8','In review','-1',t)
+statCard('91%','Velocity','+5%',t)
+'</div>'
/* vertical content */
+'<div style="padding:0 10px 10px;flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0;">'
+'<div style="font-size:10px;font-weight:700;color:'+t.text+';margin-bottom:7px;flex-shrink:0;opacity:'+t.textOp+';">Projects</div>'
+[projectRow('Alpha launch','In progress',62,t),projectRow('Design system','Review',85,t),projectRow('API v2','Planning',18,t),projectRow('Mobile app','Review',55,t),projectRow('Documentation','Planning',30,t)].slice(0,t.rows.proj).join('')
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+'<div style="font-size:7px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:'+t.sub+';margin-bottom:5px;flex-shrink:0;opacity:'+t.subOp+';">Recent issues</div>'
+[issueRow('Button states not matching design spec','High',t),issueRow('API timeout on large dataset queries','Medium',t),issueRow('Update onboarding copy for v2','Low',t),issueRow('Missing loading states in dashboard','Medium',t),issueRow('Improve error messages for auth flow','Low',t)].slice(0,t.rows.act).join('')
+'<div style="height:1px;background:'+t.border+';margin:10px 0 8px;flex-shrink:0;opacity:0.4;"></div>'
+miniBarChart('Performance',[{v:40,d:'M'},{v:68,d:'T'},{v:55,d:'W'},{v:82,d:'T'},{v:74,d:'F'},{v:90,d:'S'},{v:61,d:'S'}],t)
+'</div>'
+'</div>'
+'</div>'
+'</div>';
}
var DESIGN_COPY = {
feel: {
premium: {
impression: 'Dark and polished. Users trust it before clicking anything.',
perceptions: ['High-trust','Enterprise-ready','Sophisticated']
},
friendly: {
impression: 'Light and welcoming. Lowers the barrier to getting started.',
perceptions: ['Approachable','Human','Easy to start']
},
minimal: {
impression: 'Clean and sharp — every element earns its place.',
perceptions: ['Focused','Professional','No-nonsense']
}
},
color: {
indigo: 'Indigo — trusted and focused.',
teal: 'Teal — calm and precise.',
blue: 'Blue — clear, reliable, trusted.',
rose: 'Rose — bold and warm.',
violet: 'Violet — creative and distinct.',
amber: 'Amber — grounded, approachable.'
},
structure: {
clean: {
label: 'Clean layout',
note: 'Pages and sections. Easy to browse, easy to grow.'
},
data: {
label: 'Data-rich layout',
note: 'Numbers and tables front and centre.'
},
bold: {
label: 'Bold layout',
note: 'Tasks, status, and momentum.'
}
},
intensity: {
subtle: 'Gentle shadows, muted accents.',
balanced: 'Neither flat nor heavy.',
bold: 'Strong contrast and depth.'
},
density: {
spacious: 'Generous whitespace, easy to scan.',
balanced: 'Standard spacing.',
compact: 'Tight — more on screen at once.'
}
};
var DP_FEEL_IDENTITY = {
friendly: 'Light, warm surfaces — welcoming from the first glance',
minimal: 'Clean whites and precise spacing — clarity above all',
premium: 'Deep, polished backgrounds — instantly commands respect'
};
var DP_INTENSITY_ROW = {
subtle: 'Muted accents and soft borders — calm and understated',
balanced: 'Balanced contrast — clear without being loud',
bold: 'Strong shadows and vivid accents — impossible to miss'
};
var DP_DENSITY_ROW = {
spacious: 'Generous whitespace — relaxed and easy to scan',
balanced: 'Standard spacing — comfortable at any pace',
compact: 'Dense information layout — more data, less scrolling'
};
var DP_STRUCTURE_ROWS = {
clean: ['Pages and sections — easy to browse, easy to grow', 'Clear hierarchy with a prominent sidebar'],
data: ['Data-first layout — metrics and tables at the forefront', 'Stripe-style — dense but scannable'],
bold: ['Workspace layout — tasks, status, momentum', 'Linear-style — high focus, minimal chrome']
};
var DP_PERC_FEEL = {
friendly: 'Approachable — lowers the barrier to getting started',
minimal: 'Focused — every pixel earns its place',
premium: 'High-trust — users believe in it before clicking anything'
};
var DP_PERC_COLOR = {
indigo: 'Focused and trustworthy',
blue: 'Clear and reliable',
teal: 'Calm and precise',
rose: 'Bold and energetic',
violet: 'Creative and distinct',
amber: 'Grounded and approachable'
};
function updatePanel(){
var colorKey = refine.extraColor || state.color;
var c = COLORS[colorKey];
var fc = DESIGN_COPY.feel[state.feel];
var str = DESIGN_COPY.structure[state.structure];
document.getElementById('dp-impression').textContent = fc.impression;
// Visual identity — color dot row + feel row + structure rows
var dot = function(color){
return '<div style="width:5px;height:5px;border-radius:50%;background:#6366F1;flex-shrink:0;"></div>';};
var identityRows = [
{color: c.h, text: DESIGN_COPY.color[colorKey]},
{color: '#6366F1', text: DP_FEEL_IDENTITY[state.feel]},
].concat(DP_STRUCTURE_ROWS[state.structure].map(function(t){ return {color:'#94a3b8', text:t}; }));
document.getElementById('dp-identity').innerHTML = identityRows.map(function(row){
return '<div class="deliverable-row">'+dot(row.color)+row.text+'</div>';
}).join('');
// How users will experience it — feel + color perception + intensity
var percRows = [
{color: '#6366F1', text: DP_PERC_FEEL[state.feel]},
{color: c.h, text: DP_PERC_COLOR[colorKey]},
].concat(fc.perceptions.map(function(tag){ return {color:'#94a3b8', text:tag}; }));
document.getElementById('dp-perceptions').innerHTML = percRows.map(function(row){
return '<div class="deliverable-row">'+dot(row.color)+row.text+'</div>';
}).join('');
// Design detail — intensity + density
document.getElementById('dp-detail').innerHTML = [
{color:'#6366F1', text: DP_INTENSITY_ROW[refine.intensity]},
{color:'#94a3b8', text: DP_DENSITY_ROW[refine.density]}
].map(function(row){
return '<div class="deliverable-row">'+dot(row.color)+row.text+'</div>';
}).join('');
}
function renderMobile(t){
var p = 14;
var acc = t.accent;
var btnBg = t.ctaOutlined?'transparent':acc, btnBdr = t.ctaOutlined?'1px solid '+acc:'none', btnClr = t.ctaOutlined?acc:'#fff';
function mStat(val,label,trend){
var up=trend.charAt(0)==='+';
var tc=up?(state.feel==='premium'?'#86efac':'#16a34a'):(state.feel==='premium'?'#fca5a5':'#dc2626');
var tbg=up?(state.feel==='premium'?'rgba(134,239,172,0.18)':'rgba(22,163,74,0.12)'):(state.feel==='premium'?'rgba(252,165,165,0.18)':'rgba(220,38,38,0.12)');
return '<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;padding:9px 9px 8px;box-shadow:'+t.shadow+';">'
+'<div style="font-size:8px;color:'+t.sub+';text-transform:uppercase;letter-spacing:0.04em;margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">'+label+'</div>'
+'<div style="font-size:16px;font-weight:800;color:'+t.text+';line-height:1;margin-bottom:4px;">'+val+'</div>'
+'<div style="display:inline-block;background:'+tbg+';border-radius:3px;padding:2px 5px;"><span style="font-size:8px;font-weight:700;color:'+tc+';">'+trend+'</span></div>'
+'</div>';
}
function mProj(name,status,pct){
var sc={'In progress':acc,'Review':'#f59e0b','Planning':t.sub,'Done':'#22c55e'}[status]||acc;
return '<div style="padding:10px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">'
+'<div style="font-size:12px;font-weight:600;color:'+t.text+';flex:1;min-width:0;margin-right:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="display:flex;align-items:center;gap:4px;flex-shrink:0;"><div style="width:6px;height:6px;border-radius:50%;background:'+sc+';"></div><span style="font-size:10px;color:'+t.sub+';">'+status+'</span></div>'
+'</div>'
+'<div style="height:3px;background:rgba(128,128,128,0.12);border-radius:2px;overflow:hidden;"><div style="height:100%;background:'+sc+';width:'+pct+'%;border-radius:2px;"></div></div>'
+'</div>';
}
function mAct(text,time){
return '<div style="display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:6px;height:6px;border-radius:50%;background:'+acc+';opacity:0.45;flex-shrink:0;"></div>'
+'<div style="flex:1;font-size:11px;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+text+'</div>'
+'<div style="font-size:10px;color:'+t.sub+';flex-shrink:0;">'+time+'</div>'
+'</div>';
}
function mTx(name,date,amount,status){
var sc={'Active':'#22c55e','Pending':'#f59e0b','Cancelled':t.sub}[status]||acc;
return '<div style="padding:9px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:2px;">'
+'<div style="font-size:12px;font-weight:500;color:'+t.text+';">'+name+'</div>'
+'<div style="font-size:12px;font-weight:600;color:'+t.text+';">'+amount+'</div>'
+'</div>'
+'<div style="display:flex;align-items:center;justify-content:space-between;">'
+'<div style="font-size:10px;color:'+t.sub+';">'+date+'</div>'
+'<div style="display:flex;align-items:center;gap:4px;"><div style="width:5px;height:5px;border-radius:50%;background:'+sc+';"></div><span style="font-size:10px;color:'+t.sub+';">'+status+'</span></div>'
+'</div>'
+'</div>';
}
function mSignup(name,email,time){
return '<div style="display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:28px;height:28px;border-radius:50%;background:'+acc+';opacity:0.14;flex-shrink:0;"></div>'
+'<div style="flex:1;min-width:0;">'
+'<div style="font-size:12px;font-weight:600;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+name+'</div>'
+'<div style="font-size:10px;color:'+t.sub+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+email+'</div>'
+'</div>'
+'<div style="font-size:10px;color:'+t.sub+';flex-shrink:0;">'+time+'</div>'
+'</div>';
}
function mIssue(text,priority){
var pc={'High':'#ef4444','Medium':'#f59e0b','Low':t.sub}[priority]||t.sub;
return '<div style="display:flex;align-items:center;gap:8px;padding:8px 0;border-bottom:1px solid '+t.border+';">'
+'<div style="width:7px;height:7px;border-radius:50%;background:'+pc+';flex-shrink:0;"></div>'
+'<div style="flex:1;font-size:11px;color:'+t.text+';white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">'+text+'</div>'
+'<span style="font-size:10px;font-weight:600;color:'+pc+';flex-shrink:0;">'+priority+'</span>'
+'</div>';
}
function mChart(label,data){
return '<div style="font-size:12px;font-weight:700;color:'+t.text+';margin-bottom:8px;">'+label+'</div>'
+'<div style="background:'+t.surf+';border:'+t.bw+' solid '+t.border+';border-radius:'+t.r+'px;padding:12px 14px 10px;box-shadow:'+t.shadow+';">'
+'<div style="display:flex;align-items:flex-end;gap:4px;height:44px;">'
+data.map(function(b,i){return '<div style="flex:1;background:'+acc+';border-radius:3px 3px 0 0;height:'+b.v+'%;opacity:'+(i===data.length-2?'1':'0.35')+';"></div>';}).join('')
+'</div>'
+'<div style="display:flex;gap:4px;margin-top:5px;">'
+data.map(function(b){return '<div style="flex:1;text-align:center;font-size:8px;color:'+t.sub+';">'+b.d+'</div>';}).join('')
+'</div>'
+'</div>';
}
function sec(title,content){
return '<div style="padding:16px '+p+'px 0;">'
+'<div style="font-size:10px;font-weight:700;color:'+t.sub+';text-transform:uppercase;letter-spacing:0.06em;margin-bottom:10px;">'+title+'</div>'
+content
+'</div>';
}
function divider(){ return '<div style="height:1px;background:'+t.border+';margin:16px '+p+'px 0;opacity:0.4;"></div>'; }
var nav = '<div style="padding:12px '+p+'px;border-bottom:1px solid '+t.border+';display:flex;align-items:center;justify-content:space-between;background:'+t.sidebarBg+';">'
+'<div style="display:flex;align-items:center;gap:7px;">'
+'<div style="width:22px;height:22px;background:'+acc+';border-radius:'+Math.min(t.r,6)+'px;flex-shrink:0;"></div>'
+'<span style="font-size:13px;font-weight:700;color:'+t.navActive+';">Acme</span>'
+'</div>'
+'<div style="display:inline-flex;align-items:center;height:30px;padding:0 14px;background:'+btnBg+';border:'+btnBdr+';border-radius:'+t.r+'px;">'
+'<span style="font-size:11px;font-weight:700;color:'+btnClr+';">'+(state.structure==='clean'?'+ New':state.structure==='data'?'Export':'+ Project')+'</span>'
+'</div>'
+'</div>';
var header, stats, body;
if(state.structure==='clean'){
header='<div style="padding:16px '+p+'px 10px;">'
+'<div style="font-size:18px;font-weight:800;color:'+t.text+';margin-bottom:2px;">Dashboard</div>'
+'<div style="font-size:11px;color:'+t.sub+';">Mon, 12 Jan 2026</div>'
+'</div>';
stats='<div style="padding:4px '+p+'px 12px;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;">'
+mStat('2,847','Active users','+12%')+mStat('$18.3k','Revenue','+8%')+mStat('34%','Conversion','+4pt')
+'</div>';
body=sec('Projects',
mProj('Website redesign','In progress',60)+mProj('Mobile app','Review',85)
+mProj('API v2','Planning',20)+mProj('Analytics dashboard','In progress',45)+mProj('Onboarding flow','Done',100)
)+divider()+sec('Recent activity',
mAct('Figma file updated · Design team','2m')+mAct('PR #84 merged to main · @alex','14m')
+mAct('New signup: holly@acme.com','1h')+mAct('Deploy to staging · @kai','2h')
)+divider()+'<div style="padding:16px '+p+'px 24px;">'+mChart('Performance',[{v:55,d:'M'},{v:72,d:'T'},{v:48,d:'W'},{v:88,d:'T'},{v:65,d:'F'},{v:91,d:'S'},{v:79,d:'S'}])+'</div>';
} else if(state.structure==='data'){
header='<div style="padding:16px '+p+'px 10px;">'
+'<div style="font-size:18px;font-weight:800;color:'+t.text+';margin-bottom:2px;">Overview</div>'
+'<div style="font-size:11px;color:'+t.sub+';">Last 30 days</div>'
+'</div>';
stats='<div style="padding:4px '+p+'px 12px;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;">'
+mStat('$18.3k','MRR','+8%')+mStat('1,234','Customers','+14%')+mStat('98.2%','Uptime','+0.1%')
+'</div>';
body=sec('Transactions',
mTx('Acme Corp','Jan 12','$2,400','Active')+mTx('TechFlow Ltd','Jan 11','$980','Active')
+mTx('Moonbase Inc','Jan 10','$3,100','Pending')+mTx('Stellar LLC','Jan 9','$640','Cancelled')+mTx('Harbor Co','Jan 8','$1,200','Active')
)+divider()+sec('New signups',
mSignup('James Liu','james@techflow.io','5m')+mSignup('Ana Perez','ana@moonbase.co','1h')
+mSignup('Tom K.','tom@stellar.xyz','3h')+mSignup('Chen Wei','chen@harbor.co','8h')
)+divider()+'<div style="padding:16px '+p+'px 24px;">'+mChart('Performance',[{v:60,d:'M'},{v:65,d:'T'},{v:58,d:'W'},{v:80,d:'T'},{v:70,d:'F'},{v:95,d:'S'},{v:82,d:'S'}])+'</div>';
} else {
header='<div style="padding:16px '+p+'px 10px;">'
+'<div style="font-size:11px;color:'+t.sub+';text-transform:uppercase;letter-spacing:0.07em;margin-bottom:3px;">My workspace</div>'
+'<div style="font-size:18px;font-weight:800;color:'+t.text+';">Good morning 👋</div>'
+'</div>';
stats='<div style="padding:4px '+p+'px 12px;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;">'
+mStat('24','Open issues','+3')+mStat('8','In review','-1')+mStat('91%','Velocity','+5%')
+'</div>';
body=sec('Projects',
mProj('Alpha launch','In progress',62)+mProj('Design system','Review',85)
+mProj('API v2','Planning',18)+mProj('Mobile app','Review',55)+mProj('Documentation','Planning',30)
)+divider()+sec('Recent issues',
mIssue('Button states not matching design spec','High')+mIssue('API timeout on large dataset queries','Medium')
+mIssue('Update onboarding copy for v2','Low')+mIssue('Missing loading states in dashboard','Medium')
+mIssue('Improve error messages for auth flow','Low')
)+divider()+'<div style="padding:16px '+p+'px 24px;">'+mChart('Performance',[{v:40,d:'M'},{v:68,d:'T'},{v:55,d:'W'},{v:82,d:'T'},{v:74,d:'F'},{v:90,d:'S'},{v:61,d:'S'}])+'</div>';
}
return '<div style="background:'+t.bg+';font-family:\'Plus Jakarta Sans\',sans-serif;">'+nav+header+stats+body+'</div>';
}
function render(){
var t = getTheme();
var html = '';
if(DEVICE === 'mobile'){
html = renderMobile(t);
} else {
if(state.structure === 'clean') html = renderClean(t);
else if(state.structure === 'data') html = renderData(t);
else html = renderBold(t);
}
var mock = document.getElementById('mock');
var previewBg = mock.parentElement;
if(DEVICE === 'mobile'){
var isDark = document.documentElement.dataset.theme === 'dark';
var isBgDark = state.feel === 'premium';
var outerBg = isDark ? '#0A1120' : '#f0f0f8';
var phoneGlow = isDark
? (state.feel === 'premium'
? '0 8px 40px rgba(0,0,0,0.72),0 0 0 1px rgba(108,124,255,0.55),0 0 0 5px rgba(108,124,255,0.16),0 0 56px rgba(108,124,255,0.38),0 0 100px rgba(108,124,255,0.14)'
: '0 8px 40px rgba(0,0,0,0.65),0 0 0 1px rgba(108,124,255,0.18),0 0 48px rgba(108,124,255,0.18),0 0 80px rgba(108,124,255,0.08)')
: (state.feel === 'premium'
? '0 8px 32px rgba(0,0,0,0.12),0 0 0 1px rgba(99,102,241,0.16),0 0 32px rgba(99,102,241,0.14)'
: '0 8px 32px rgba(0,0,0,0.15)');
var phoneBg = (isDark && state.feel === 'friendly') ? '#ffffff' : t.bg;
html = '<div style="background:'+outerBg+';padding:20px 0;min-height:100%;">'
+'<div style="background:'+phoneBg+';max-width:375px;margin:0 auto;border-radius:16px;overflow:hidden;box-shadow:'+phoneGlow+';">'+html+'</div></div>';
mock.style.cssText = 'flex:1;overflow-y:auto;transition:none;font-family:\'Plus Jakarta Sans\',sans-serif;background:'+outerBg+';';
previewBg.style.background = outerBg;
} else {
var isDarkMode = document.documentElement.dataset.theme === 'dark';
var desktopBg = (isDarkMode && state.feel === 'friendly') ? '#ffffff' : t.bg;
mock.style.cssText = 'flex:1;overflow:hidden;transition:none;font-family:\'Plus Jakarta Sans\',sans-serif;background:'+desktopBg+';';
previewBg.style.background = desktopBg;
}
mock.innerHTML = html;
updatePanel();
}
function saveDesignState(){
try {
var colorKey = refine.extraColor || state.color;
localStorage.setItem('vibn_design', JSON.stringify({
feel: state.feel,
color: state.color,
extraColor: refine.extraColor,
structure: state.structure,
colorHex: COLORS[colorKey].h,
colorName: COLORS[colorKey].name
}));
} catch(e){}
}
function setFeel(f){
state.feel = f;
['premium','friendly','minimal'].forEach(function(x){
document.getElementById('fpill-'+x).classList.toggle('active', x===f);
});
render();
saveDesignState();
}
function setColor(c){
state.color = c;
refine.extraColor = null;
['rose','violet','amber'].forEach(function(x){
document.getElementById('xs-'+x).classList.remove('active');
});
['indigo','teal','blue'].forEach(function(x){
document.getElementById('cc-'+x).classList.toggle('active', x===c);
});
document.getElementById('color-label').textContent = COLORS[c].name;
render();
saveDesignState();
}
function toggleRefine(){
var toggle = document.getElementById('refine-toggle');
var panel = document.getElementById('refine-panel');
var open = panel.classList.contains('open');
toggle.classList.toggle('open', !open);
panel.classList.toggle('open', !open);
}
function setExtraColor(id, hex){
refine.extraColor = id;
['indigo','teal','blue'].forEach(function(x){
document.getElementById('cc-'+x).classList.remove('active');
});
['rose','violet','amber'].forEach(function(x){
document.getElementById('xs-'+x).classList.toggle('active', x===id);
});
document.getElementById('color-label').textContent = COLORS[id].name;
render();
saveDesignState();
}
function setIntensity(v){
refine.intensity = v;
['subtle','balanced','bold'].forEach(function(x){
document.getElementById('ri-'+x).classList.toggle('active', x===v);
});
render();
}
function setDensity(v){
refine.density = v;
['spacious','balanced','compact'].forEach(function(x){
document.getElementById('rd-'+x).classList.toggle('active', x===v);
});
render();
}
function tryAnotherVersion(){
var intensities = ['subtle','balanced','bold'];
var densities = ['spacious','balanced','compact'];
var ni, nd;
do {
ni = intensities[Math.floor(Math.random()*3)];
nd = densities[Math.floor(Math.random()*3)];
} while(ni===refine.intensity && nd===refine.density);
setIntensity(ni);
setDensity(nd);
}
function setStructure(s){
state.structure = s;
['clean','data','bold'].forEach(function(x){
document.getElementById('sc-'+x).classList.toggle('active', x===s);
});
render();
saveDesignState();
}
function surpriseMe(){
var feels = ['premium','friendly','minimal'];
var colors = ['indigo','teal','blue'];
var structs = ['clean','data','bold'];
var nf, nc, ns;
do {
nf = feels[Math.floor(Math.random()*3)];
nc = colors[Math.floor(Math.random()*3)];
ns = structs[Math.floor(Math.random()*3)];
} while(nf===state.feel && nc===state.color && ns===state.structure);
setFeel(nf); setColor(nc); setStructure(ns);
}
function buildThumbs(){
var dk = document.documentElement.dataset.theme === 'dark';
var cleanSide = dk ? '#1A2035' : '#f1f0ed';
var cleanSideBorder = dk ? '#2A3050' : '#e5e3de';
var cleanMain = dk ? '#1F2436' : '#fff';
var cleanText = dk ? '#fff' : '#1a1510';
var dataSide = dk ? '#1A1F35' : '#f8f9ff';
var dataSideBorder = dk ? '#2A3560' : '#e0e7ff';
var dataMain = dk ? '#1F2436' : '#fff';
var dataCard = dk ? 'rgba(99,102,241,0.18)' : '#e0e7ff';
var dataText = dk ? '#fff' : '#1a1510';
document.getElementById('thumb-clean').innerHTML =
'<div style="display:flex;height:100%;">'
+'<div style="width:22px;background:'+cleanSide+';border-right:1px solid '+cleanSideBorder+';"></div>'
+'<div style="flex:1;padding:5px 6px;display:flex;flex-direction:column;gap:2px;background:'+cleanMain+';">'
+'<div style="height:3px;background:'+cleanText+';opacity:0.2;border-radius:1px;width:55%;"></div>'
+'<div style="height:2px;background:#6366F1;opacity:0.5;border-radius:1px;width:30%;"></div>'
+'<div style="height:2px;background:'+cleanText+';opacity:0.1;border-radius:1px;width:70%;margin-top:2px;"></div>'
+'<div style="height:2px;background:'+cleanText+';opacity:0.1;border-radius:1px;width:45%;"></div>'
+'<div style="height:2px;background:'+cleanText+';opacity:0.08;border-radius:1px;width:60%;"></div>'
+'</div>'
+'</div>';
document.getElementById('thumb-data').innerHTML =
'<div style="display:flex;height:100%;">'
+'<div style="width:24px;background:'+dataSide+';border-right:1px solid '+dataSideBorder+';"></div>'
+'<div style="flex:1;padding:4px 5px;background:'+dataMain+';display:flex;flex-direction:column;gap:3px;">'
+'<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:2px;">'
+'<div style="height:11px;background:'+dataCard+';border-radius:2px;"></div>'
+'<div style="height:11px;background:'+dataCard+';border-radius:2px;"></div>'
+'<div style="height:11px;background:'+dataCard+';border-radius:2px;"></div>'
+'</div>'
+'<div style="height:2px;background:'+dataText+';opacity:0.1;border-radius:1px;"></div>'
+'<div style="height:2px;background:'+dataText+';opacity:0.07;border-radius:1px;"></div>'
+'<div style="height:2px;background:'+dataText+';opacity:0.07;border-radius:1px;"></div>'
+'</div>'
+'</div>';
document.getElementById('thumb-bold').innerHTML =
'<div style="display:flex;height:100%;">'
+'<div style="width:13px;background:#141929;display:flex;flex-direction:column;align-items:center;padding-top:5px;gap:3px;">'
+'<div style="width:7px;height:7px;border-radius:2px;background:#6366F1;"></div>'
+'<div style="width:6px;height:6px;border-radius:1px;background:rgba(255,255,255,0.3);"></div>'
+'<div style="width:6px;height:6px;border-radius:1px;background:rgba(255,255,255,0.2);"></div>'
+'</div>'
+'<div style="flex:1;background:#1a2235;padding:5px;">'
+'<div style="height:3.5px;background:rgba(255,255,255,0.3);border-radius:1px;width:60%;margin-bottom:4px;"></div>'
+'<div style="display:grid;grid-template-columns:1fr 1fr;gap:2px;">'
+'<div style="height:14px;background:rgba(255,255,255,0.09);border-radius:2px;"></div>'
+'<div style="height:14px;background:rgba(255,255,255,0.09);border-radius:2px;"></div>'
+'<div style="height:14px;background:rgba(255,255,255,0.07);border-radius:2px;"></div>'
+'<div style="height:14px;background:rgba(255,255,255,0.07);border-radius:2px;"></div>'
+'</div>'
+'</div>'
+'</div>';
}
function setDevice(btn){
document.querySelectorAll('[data-group="device"]').forEach(function(b){ b.classList.remove('selected'); });
btn.classList.add('selected');
DEVICE = btn.getAttribute('data-tip') === 'Optimised for phones, still works on desktop' ? 'mobile' : 'desktop';
document.getElementById('density-row').style.display = DEVICE === 'mobile' ? 'none' : '';
render();
}
function saveAndExit(){
document.getElementById('save-exit-popup').classList.add('visible');
}
function cancelSaveExit(){
document.getElementById('save-exit-popup').classList.remove('visible');
}
function toggleTheme(){
var html = document.documentElement;
var isDark = html.dataset.theme === 'dark';
html.dataset.theme = isDark ? '' : 'dark';
localStorage.setItem('vibn-theme', isDark ? '' : 'dark');
document.getElementById('dark-toggle').textContent = isDark ? '🌙 Dark mode' : '☀️ Light mode';
buildThumbs();
render();
}
document.addEventListener('DOMContentLoaded', function(){
buildThumbs();
var pn = localStorage.getItem('vibn_project_name');
if(pn){ var el=document.getElementById('sidebar-project-name'); el.textContent=pn; el.style.display='block'; }
if(document.documentElement.dataset.theme === 'dark'){
document.getElementById('dark-toggle').textContent = '☀️ Light mode';
}
// Sync device mode from Architect frontend selection
try {
var frontendTip = localStorage.getItem('vibn_frontend');
if (frontendTip === 'Optimised for phones, still works on desktop') {
DEVICE = 'mobile';
document.querySelectorAll('[data-group="device"]').forEach(function(b) {
b.classList.toggle('selected', b.getAttribute('data-tip') === frontendTip);
});
document.getElementById('density-row').style.display = 'none';
}
} catch(e) {}
render();
});
</script>
</body>
</html>