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
1362 lines
86 KiB
HTML
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 & 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 & 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>
|