# messages

id: d31cb772-43ca-414a-9589-3c955ad04f15
session_id: functions.agent:1
role: assistant
model: local.accounts/fireworks/models/kimi-k2p5
parts:
  [{"type":"tool_call","data":{"id":"functions.view:7","name":"view","input":"{\"file_path\": \"/home/johan/dev/dealspace/portal/templates/app/project.html\"}","type":"function","finished":true}},{"type":"tool_call","data":{"id":"functions.view:8","name":"view","input":"{\"file_path\": \"/home/johan/dev/dealspace/portal/templates/app/request.html\"}","type":"function","finished":true}},{"type":"tool_call","data":{"id":"functions.view:9","name":"view","input":"{\"file_path\": \"/home/johan/dev/dealspace/portal/static/app.css\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1774088517}}]


id: 6cf324ac-02ee-4ee6-be34-167391a7ab53
session_id: functions.agent:1
role: tool
parts:
  [{"type":"tool_result","data":{"tool_call_id":"functions.view:7","name":"","content":"\u003cfile\u003e\n     1|{{define \"header-left\"}}\n     2|    \u003cdiv class=\"flex items-center gap-3\"\u003e\n     3|      \u003ca href=\"/app/projects\" class=\"text-2xl font-bold text-white tracking-tight\"\u003e\u003cspan class=\"text-[#c9a84c]\"\u003eDeal\u003c/span\u003espace\u003c/a\u003e\n     4|      \u003cspan class=\"text-white/20 text-lg\"\u003e/\u003c/span\u003e\n     5|      \u003cdiv id=\"projectSwitcher\" class=\"relative\"\u003e\n     6|        \u003cbutton id=\"projectSwitcherBtn\" onclick=\"toggleProjectMenu()\" class=\"flex items-center gap-1.5 text-xl font-semibold text-white transition focus:outline-none\"\u003e\n     7|          \u003cspan id=\"projectName\"\u003eLoading...\u003c/span\u003e\n     8|          \u003cspan style=\"font-size:10px;opacity:.5;margin-top:2px\"\u003e▾\u003c/span\u003e\n     9|        \u003c/button\u003e\n    10|        \u003cdiv id=\"projectMenu\" class=\"hidden absolute left-0 top-full mt-2 min-w-[220px] rounded-lg border shadow-xl z-50\" style=\"background:var(--ds-sf);border-color:var(--ds-bd)\"\u003e\n    11|        \u003c/div\u003e\n    12|      \u003c/div\u003e\n    13|    \u003c/div\u003e\n    14|{{end}}\n    15|\n    16|{{define \"content\"}}\n    17|    \u003cdiv class=\"px-8 pt-4 pb-8\" style=\"max-width:calc(100vw - 14rem)\"\u003e\n    18|      \u003cp id=\"projectDesc\" class=\"text-[#b0bec5] text-sm mb-2\"\u003e\u003c/p\u003e\n    19|\n    20|      \u003c!-- Tabs + actions on one line --\u003e\n    21|      \u003cdiv class=\"flex items-end gap-6 border-b border-white/[0.08] mb-6\"\u003e\n    22|        \u003cbutton class=\"tab active pb-3 text-base font-medium transition\" onclick=\"switchTab('requests', this)\"\u003eRequests\u003c/button\u003e\n    23|        \u003cbutton class=\"tab pb-3 text-base font-medium text-[#b0bec5] transition\" onclick=\"switchTab('orgs', this)\"\u003eParties\u003c/button\u003e\n    24|        \u003cdiv class=\"ml-auto flex items-center gap-2 pb-2\"\u003e\n    25|          \u003cspan id=\"projectStatus\" class=\"px-2 py-0.5 rounded text-xs font-medium\"\u003e\u003c/span\u003e\n    26|          \u003cbutton onclick=\"openCreateListModal()\" class=\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\"\u003e+ New List\u003c/button\u003e\n    27|          \u003cbutton onclick=\"openTemplateModal()\" class=\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\"\u003eUse Template\u003c/button\u003e\n    28|          \u003cbutton onclick=\"openImportModal()\" class=\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\"\u003eImport List\u003c/button\u003e\n    29|          \u003cbutton id=\"detailToggle\" onclick=\"toggleDetailMode()\" class=\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-[#cbd5e1] hover:text-white rounded text-sm font-medium transition\"\u003eSummary View\u003c/button\u003e\n    30|        \u003c/div\u003e\n    31|      \u003c/div\u003e\n    32|\n    33|      \u003c!-- Requests Tab --\u003e\n    34|      \u003cdiv id=\"tab-requests\"\u003e\n    35|        \u003cdiv id=\"requestEmpty\" class=\"hidden text-center py-16\"\u003e\n    36|          \u003ch3 class=\"text-lg font-semibold text-white mb-1\"\u003eNo requests yet\u003c/h3\u003e\n    37|          \u003cp class=\"text-[#b0bec5] text-sm mb-4\"\u003eImport a diligence checklist to get started.\u003c/p\u003e\n    38|          \u003cbutton onclick=\"openImportModal()\" class=\"px-4 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eImport Checklist\u003c/button\u003e\n    39|        \u003c/div\u003e\n    40|        \u003cdiv id=\"treeWrap\" class=\"overflow-x-auto\"\u003e\n    41|          \u003ctable id=\"reqTree\"\u003e\n    42|            \u003cthead\u003e\n    43|              \u003ctr\u003e\n    44|                \u003cth style=\"width:28px\" title=\"Drag to reorder\"\u003e\u003c/th\u003e\n    45|                \u003cth style=\"width:60px\"\u003e#\u003c/th\u003e\n    46|                \u003cth style=\"min-width:280px\"\u003eRequest Item\u003c/th\u003e\n    47|                \u003cth style=\"width:80px\"\u003ePriority\u003c/th\u003e\n    48|                \u003cth style=\"width:90px\"\u003eStatus\u003c/th\u003e\n    49|                \u003cth style=\"width:120px\"\u003eAssignee\u003c/th\u003e\n    50|                \u003cth style=\"width:90px\"\u003eDue Date\u003c/th\u003e\n    51|                \u003cth style=\"width:90px\"\u003eLabel\u003c/th\u003e\n    52|                \u003cth style=\"width:50px\"\u003eFiles\u003c/th\u003e\n    53|                \u003cth style=\"width:160px\"\u003eBuyer Comment\u003c/th\u003e\n    54|                \u003cth style=\"width:160px\"\u003eSeller Comment\u003c/th\u003e\n    55|                \u003cth style=\"width:90px\"\u003eCreated\u003c/th\u003e\n    56|                \u003cth style=\"width:40px\"\u003e\u003c/th\u003e\n    57|              \u003c/tr\u003e\n    58|            \u003c/thead\u003e\n    59|            \u003ctbody id=\"treeBody\"\u003e\n    60|              \u003ctr\u003e\u003ctd colspan=\"13\" class=\"text-[#b0bec5] text-sm py-4\"\u003eLoading requests...\u003c/td\u003e\u003c/tr\u003e\n    61|            \u003c/tbody\u003e\n    62|          \u003c/table\u003e\n    63|        \u003c/div\u003e\n    64|      \u003c/div\u003e\n    65|\n    66|      \u003c!-- Orgs Tab --\u003e\n    67|      \u003cdiv id=\"tab-orgs\" class=\"hidden\"\u003e\n    68|        \u003cdiv class=\"flex justify-between items-center mb-6\"\u003e\n    69|          \u003cp class=\"text-[#b0bec5] text-sm\"\u003eParties participating in this deal and their permissions.\u003c/p\u003e\n    70|          \u003cbutton onclick=\"openAddOrgModal()\" class=\"px-3 py-1.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003e+ Add Org\u003c/button\u003e\n    71|        \u003c/div\u003e\n    72|        \u003cdiv id=\"orgList\" class=\"space-y-3\"\u003e\u003cdiv class=\"text-[#b0bec5] text-sm\"\u003eLoading...\u003c/div\u003e\u003c/div\u003e\n    73|      \u003c/div\u003e\n    74|    \u003c/div\u003e\n    75|\n    76|  \u003c!-- Edit Org Modal --\u003e\n    77|  \u003cdiv id=\"editOrgModal\" class=\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n    78|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto\"\u003e\n    79|      \u003cdiv class=\"flex items-center justify-between px-6 pt-5 pb-4 border-b border-white/[0.08]\"\u003e\n    80|        \u003ch2 class=\"text-lg font-semibold text-white\"\u003eEdit Organization\u003c/h2\u003e\n    81|        \u003cbutton onclick=\"closeEditOrgModal()\" class=\"text-[#b0bec5] hover:text-white text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n    82|      \u003c/div\u003e\n    83|      \u003cdiv class=\"p-6\"\u003e\n    84|        \u003cinput type=\"hidden\" id=\"editOrgDealOrgId\"\u003e\n    85|        \u003cinput type=\"hidden\" id=\"editOrgVersion\"\u003e\n    86|        \u003cdiv class=\"grid grid-cols-2 gap-4\"\u003e\n    87|          \u003cdiv class=\"col-span-2 flex items-start gap-4\"\u003e\n    88|            \u003cdiv id=\"editOrgLogoWrap\" class=\"hidden shrink-0 w-16 h-16 rounded-lg border border-white/[0.08] overflow-hidden bg-white flex items-center justify-center\"\u003e\n    89|              \u003cimg id=\"editOrgLogoImg\" src=\"\" class=\"max-w-full max-h-full object-contain\"\u003e\n    90|            \u003c/div\u003e\n    91|            \u003cdiv class=\"flex-1\"\u003e\n    92|              \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eName \u003cspan class=\"text-red-400\"\u003e*\u003c/span\u003e\u003c/label\u003e\n    93|              \u003cinput type=\"text\" id=\"editOrgName\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n    94|            \u003c/div\u003e\n    95|          \u003c/div\u003e\n    96|          \u003cdiv class=\"col-span-2\"\u003e\n    97|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eDescription\u003c/label\u003e\n    98|            \u003ctextarea id=\"editOrgDesc\" rows=\"3\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] resize-none\"\u003e\u003c/textarea\u003e\n    99|          \u003c/div\u003e\n   100|          \u003cdiv\u003e\n   101|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eRole in deal\u003c/label\u003e\n   102|            \u003cselect id=\"editOrgRole\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   103|              \u003coption value=\"seller\"\u003eSeller\u003c/option\u003e\n   104|              \u003coption value=\"buyer\"\u003eBuyer\u003c/option\u003e\n   105|              \u003coption value=\"ib\"\u003eInvestment Bank\u003c/option\u003e\n   106|              \u003coption value=\"advisor\"\u003eAdvisor\u003c/option\u003e\n   107|            \u003c/select\u003e\n   108|          \u003c/div\u003e\n   109|          \u003cdiv\u003e\n   110|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eIndustry\u003c/label\u003e\n   111|            \u003cinput type=\"text\" id=\"editOrgIndustry\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   112|          \u003c/div\u003e\n   113|          \u003cdiv\u003e\n   114|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eWebsite\u003c/label\u003e\n   115|            \u003cinput type=\"text\" id=\"editOrgWebsite\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   116|          \u003c/div\u003e\n   117|          \u003cdiv\u003e\n   118|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003ePhone\u003c/label\u003e\n   119|            \u003cinput type=\"text\" id=\"editOrgPhone\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   120|          \u003c/div\u003e\n   121|          \u003cdiv\u003e\n   122|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eLinkedIn\u003c/label\u003e\n   123|            \u003cinput type=\"text\" id=\"editOrgLinkedIn\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   124|          \u003c/div\u003e\n   125|          \u003cdiv\u003e\n   126|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eFounded\u003c/label\u003e\n   127|            \u003cinput type=\"text\" id=\"editOrgFounded\" placeholder=\"e.g. 2010\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   128|          \u003c/div\u003e\n   129|          \u003cdiv class=\"col-span-2\"\u003e\n   130|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eAddress\u003c/label\u003e\n   131|            \u003cinput type=\"text\" id=\"editOrgAddress\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   132|          \u003c/div\u003e\n   133|          \u003cdiv\u003e\n   134|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eCity\u003c/label\u003e\n   135|            \u003cinput type=\"text\" id=\"editOrgCity\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   136|          \u003c/div\u003e\n   137|          \u003cdiv\u003e\n   138|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eState\u003c/label\u003e\n   139|            \u003cinput type=\"text\" id=\"editOrgState\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   140|          \u003c/div\u003e\n   141|          \u003cdiv class=\"col-span-2\"\u003e\n   142|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eLogo URL\u003c/label\u003e\n   143|            \u003cinput type=\"text\" id=\"editOrgLogo\" placeholder=\"https://...\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\n   144|              oninput=\"previewEditOrgLogo()\"\u003e\n   145|          \u003c/div\u003e\n   146|        \u003c/div\u003e\n   147|        \u003cdiv id=\"editOrgError\" class=\"hidden mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003e\u003c/div\u003e\n   148|      \u003c/div\u003e\n   149|      \u003cdiv class=\"flex gap-3 px-6 pb-6\"\u003e\n   150|        \u003cbutton onclick=\"closeEditOrgModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   151|        \u003cbutton id=\"editOrgSaveBtn\" onclick=\"saveEditOrg()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eSave Changes\u003c/button\u003e\n   152|      \u003c/div\u003e\n   153|    \u003c/div\u003e\n   154|  \u003c/div\u003e\n   155|\n   156|  \u003c!-- Deal Member Picker Modal --\u003e\n   157|  \u003cdiv id=\"dealMemberPickerModal\" class=\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   158|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-lg flex flex-col\" style=\"max-height:85vh\"\u003e\n   159|      \u003cdiv class=\"flex items-center justify-between px-5 pt-5 pb-3 shrink-0\"\u003e\n   160|        \u003cdiv\u003e\n   161|          \u003ch2 class=\"text-base font-semibold text-white\"\u003eAdd people to deal\u003c/h2\u003e\n   162|          \u003cp id=\"dmpOrgName\" class=\"text-xs mt-0.5\" style=\"color:var(--ds-tx3)\"\u003e\u003c/p\u003e\n   163|        \u003c/div\u003e\n   164|        \u003cbutton onclick=\"closeDealMemberPicker()\" class=\"text-[#94a3b8] hover:text-white text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   165|      \u003c/div\u003e\n   166|      \u003c!-- Search --\u003e\n   167|      \u003cdiv class=\"px-5 pb-3 shrink-0\"\u003e\n   168|        \u003cinput type=\"text\" id=\"dmpSearch\" placeholder=\"Search by name or email...\" oninput=\"filterDmpList()\"\n   169|          class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\"\u003e\n   170|      \u003c/div\u003e\n   171|      \u003c!-- Existing members from global org --\u003e\n   172|      \u003cdiv id=\"dmpList\" class=\"overflow-y-auto flex-1 px-5 space-y-1.5\"\u003e\u003c/div\u003e\n   173|      \u003c!-- Add new person inline --\u003e\n   174|      \u003cdiv class=\"px-5 py-4 border-t shrink-0\" style=\"border-color:var(--ds-bd)\"\u003e\n   175|        \u003cp class=\"text-xs mb-2 font-medium\" style=\"color:var(--ds-tx3)\"\u003eNot listed? Add a new person:\u003c/p\u003e\n   176|        \u003cdiv class=\"grid gap-2\" style=\"grid-template-columns:1fr 1fr 1fr auto\"\u003e\n   177|          \u003cinput type=\"text\" id=\"dmpNewName\" placeholder=\"Name\" class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\"\u003e\n   178|          \u003cinput type=\"email\" id=\"dmpNewEmail\" placeholder=\"Email\" class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\"\u003e\n   179|          \u003cinput type=\"text\" id=\"dmpNewTitle\" placeholder=\"Title\" onkeydown=\"if(event.key==='Enter'){event.preventDefault();addNewDealMember();}\" class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\"\u003e\n   180|          \u003cbutton onclick=\"addNewDealMember()\" class=\"px-3 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-bold rounded-lg text-sm transition\"\u003e+\u003c/button\u003e\n   181|        \u003c/div\u003e\n   182|        \u003cdiv id=\"dmpAddError\" class=\"hidden mt-1.5 text-xs text-red-400\"\u003e\u003c/div\u003e\n   183|      \u003c/div\u003e\n   184|      \u003c!-- Footer --\u003e\n   185|      \u003cdiv class=\"flex gap-3 px-5 pb-5 shrink-0\"\u003e\n   186|        \u003cbutton onclick=\"closeDealMemberPicker()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   187|        \u003cbutton id=\"dmpAddBtn\" onclick=\"confirmDealMemberAdd()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eAdd selected\u003c/button\u003e\n   188|      \u003c/div\u003e\n   189|    \u003c/div\u003e\n   190|  \u003c/div\u003e\n   191|\n   192|  \u003c!-- Template Picker Modal --\u003e\n   193|  \u003cdiv id=\"templateModal\" style=\"display:none\" class=\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   194|    \u003cdiv class=\"bg-[#0d1f3c] rounded-xl w-full max-w-lg border border-white/[0.08]\"\u003e\n   195|      \u003cdiv class=\"flex items-center justify-between px-6 pt-5 pb-4 border-b border-white/[0.08]\"\u003e\n   196|        \u003ch2 class=\"text-lg font-semibold text-white\"\u003eUse a Template\u003c/h2\u003e\n   197|        \u003cbutton onclick=\"closeTemplateModal()\" class=\"text-[#94a3b8] hover:text-white text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   198|      \u003c/div\u003e\n   199|      \u003cdiv id=\"templateList\" class=\"px-4 py-3 space-y-2 max-h-80 overflow-y-auto\"\u003e\n   200|        \u003cdiv class=\"text-sm text-[#94a3b8]\"\u003eLoading templates...\u003c/div\u003e\n   201|      \u003c/div\u003e\n   202|      \u003cdiv class=\"px-6 py-4 border-t border-white/[0.08]\"\u003e\n   203|        \u003clabel class=\"block text-xs text-[#94a3b8] mb-1.5\"\u003eList name (optional — uses template name by default)\u003c/label\u003e\n   204|        \u003cinput type=\"text\" id=\"templateListName\" placeholder=\"Leave blank to use template name\"\n   205|          class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] mb-4\"\n   206|          onkeydown=\"if(event.key==='Enter')submitTemplateImport()\"\u003e\n   207|        \u003cdiv id=\"templateImportError\" class=\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003e\u003c/div\u003e\n   208|        \u003cdiv class=\"flex gap-3\"\u003e\n   209|          \u003cbutton onclick=\"closeTemplateModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   210|          \u003cbutton id=\"templateImportBtn\" onclick=\"submitTemplateImport()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eAdd to Deal\u003c/button\u003e\n   211|        \u003c/div\u003e\n   212|      \u003c/div\u003e\n   213|    \u003c/div\u003e\n   214|  \u003c/div\u003e\n   215|\n   216|  \u003c!-- Create List Modal --\u003e\n   217|  \u003cdiv id=\"createListModal\" style=\"display:none\" class=\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   218|    \u003cdiv class=\"bg-[#0d1f3c] rounded-xl p-6 w-full max-w-md border border-white/[0.08]\"\u003e\n   219|      \u003ch2 class=\"text-lg font-semibold text-white mb-4\"\u003eNew Request List\u003c/h2\u003e\n   220|      \u003cdiv id=\"createListError\" class=\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003e\u003c/div\u003e\n   221|      \u003cdiv class=\"mb-4\"\u003e\n   222|        \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eList name\u003c/label\u003e\n   223|        \u003cinput type=\"text\" id=\"createListName\" placeholder=\"e.g. Legal Due Diligence, Financial Review\"\n   224|          class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white placeholder-[#8899a6] focus:outline-none focus:border-[#c9a84c] text-sm\"\n   225|          onkeydown=\"if(event.key==='Enter')submitCreateList()\"\u003e\n   226|      \u003c/div\u003e\n   227|      \u003cdiv class=\"flex gap-3\"\u003e\n   228|        \u003cbutton onclick=\"closeCreateListModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   229|        \u003cbutton id=\"createListBtn\" onclick=\"submitCreateList()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eCreate\u003c/button\u003e\n   230|      \u003c/div\u003e\n   231|    \u003c/div\u003e\n   232|  \u003c/div\u003e\n   233|\n   234|  \u003c!-- Import Modal --\u003e\n   235|  \u003cdiv id=\"importModal\" style=\"display:none\" class=\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   236|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl p-8 w-full max-w-md\"\u003e\n   237|      \u003ch2 class=\"text-xl font-semibold text-white mb-6\"\u003eImport Diligence Checklist\u003c/h2\u003e\n   238|      \u003cdiv id=\"importError\" class=\"hidden mb-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003e\u003c/div\u003e\n   239|      \u003cdiv class=\"space-y-4\"\u003e\n   240|        \u003cdiv\u003e\n   241|          \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eRequest List Name\u003c/label\u003e\n   242|          \u003cinput type=\"text\" id=\"importListName\" placeholder=\"e.g. Blackstone DDQ, General Checklist\"\n   243|            class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] focus:ring-1 focus:ring-[#c9a84c]\"\u003e\n   244|        \u003c/div\u003e\n   245|        \u003cdiv\u003e\n   246|          \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eFile (CSV or XLSX)\u003c/label\u003e\n   247|          \u003cinput type=\"file\" id=\"importFile\" accept=\".csv,.xlsx,.xls\" required\n   248|            class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] focus:ring-1 focus:ring-[#c9a84c]\"\u003e\n   249|        \u003c/div\u003e\n   250|        \u003cdiv\u003e\n   251|          \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eImport Mode\u003c/label\u003e\n   252|          \u003cselect id=\"importMode\" class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white focus:outline-none focus:border-[#c9a84c]\"\u003e\n   253|            \u003coption value=\"add\"\u003eAdd new request list\u003c/option\u003e\n   254|            \u003coption value=\"replace\"\u003eReplace all request lists\u003c/option\u003e\n   255|          \u003c/select\u003e\n   256|        \u003c/div\u003e\n   257|        \u003cdiv\u003e\n   258|          \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eVisible to\u003c/label\u003e\n   259|          \u003cselect id=\"importVisibility\" class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white focus:outline-none focus:border-[#c9a84c]\"\u003e\n   260|            \u003coption value=\"\"\u003eEveryone (General List)\u003c/option\u003e\n   261|          \u003c/select\u003e\n   262|        \u003c/div\u003e\n   263|      \u003c/div\u003e\n   264|      \u003cdiv class=\"flex gap-3 mt-6\"\u003e\n   265|        \u003cbutton onclick=\"closeImportModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   266|        \u003cbutton id=\"importBtn\" onclick=\"submitImport()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eImport List\u003c/button\u003e\n   267|      \u003c/div\u003e\n   268|    \u003c/div\u003e\n   269|  \u003c/div\u003e\n   270|\n   271|  \u003c!-- Attach Answer Modal --\u003e\n   272|  \u003cdiv id=\"attachModal\" style=\"display:none\" class=\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   273|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl p-8 w-full max-w-lg\"\u003e\n   274|      \u003ch2 class=\"text-xl font-semibold text-white mb-2\"\u003eAttach Answer Document\u003c/h2\u003e\n   275|      \u003cp id=\"attachReqTitle\" class=\"text-[#b0bec5] text-sm mb-2\"\u003e\u003c/p\u003e\n   276|      \u003cdiv class=\"mb-4\"\u003e\n   277|        \u003cinput type=\"text\" id=\"attachSearch\" placeholder=\"Search existing documents...\"\n   278|          class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\n   279|          oninput=\"searchAnswers(this.value)\"\u003e\n   280|      \u003c/div\u003e\n   281|      \u003cdiv id=\"attachList\" class=\"max-h-64 overflow-y-auto space-y-1 mb-4\"\u003e\u003c/div\u003e\n   282|      \u003cdiv class=\"border-t border-white/[0.08] pt-4\"\u003e\n   283|        \u003clabel class=\"block text-sm font-medium text-[#b0bec5] mb-1.5\"\u003eOr upload new document\u003c/label\u003e\n   284|        \u003cinput type=\"file\" id=\"attachFile\" class=\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm\"\u003e\n   285|      \u003c/div\u003e\n   286|      \u003cdiv class=\"flex gap-3 mt-4\"\u003e\n   287|        \u003cbutton onclick=\"closeAttachModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   288|        \u003cbutton onclick=\"uploadAndLink()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eUpload \u0026amp; Link\u003c/button\u003e\n   289|      \u003c/div\u003e\n   290|    \u003c/div\u003e\n   291|  \u003c/div\u003e\n   292|\n   293|  \u003c!-- Add Party Modal (3-step: email → org details → colleagues) --\u003e\n   294|  \u003cdiv id=\"addOrgModal\" class=\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   295|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto\"\u003e\n   296|\n   297|      \u003c!-- Step 1: Email lookup --\u003e\n   298|      \u003cdiv id=\"addOrgStep1\" class=\"p-6\"\u003e\n   299|        \u003cdiv class=\"flex items-center justify-between mb-1\"\u003e\n   300|          \u003ch2 id=\"addOrgTitle\" class=\"text-lg font-semibold text-white\"\u003eAdd Party\u003c/h2\u003e\n   301|          \u003cbutton onclick=\"closeAddOrgModal()\" class=\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   302|        \u003c/div\u003e\n   303|        \u003cp id=\"addOrgHint\" class=\"text-[#b0bec5] text-sm mb-5\"\u003eEnter an email address or domain name — we'll look up the organization automatically.\u003c/p\u003e\n   304|        \u003c!-- Project name — only shown in wizard mode --\u003e\n   305|        \u003cdiv id=\"wizProjectNameWrap\" class=\"hidden mb-4\"\u003e\n   306|          \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eDeal name \u003cspan class=\"text-red-400\"\u003e*\u003c/span\u003e\u003c/label\u003e\n   307|          \u003cinput type=\"text\" id=\"wizProjectName\" placeholder=\"e.g. Project Falcon\"\n   308|            class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\n   309|            onkeydown=\"if(event.key==='Enter')scrapeOrg()\"\u003e\n   310|        \u003c/div\u003e\n   311|        \u003cdiv class=\"flex gap-3\"\u003e\n   312|          \u003cinput type=\"text\" id=\"addOrgEmail\" placeholder=\"e.g. john.smith@example.com or example.com\" autocomplete=\"off\"\n   313|            class=\"flex-1 px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\"\n   314|            onkeydown=\"if(event.key==='Enter')scrapeOrg()\"\u003e\n   315|          \u003cbutton onclick=\"scrapeOrg()\" id=\"scrapeBtn\" class=\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition whitespace-nowrap\"\u003eLook up\u003c/button\u003e\n   316|        \u003c/div\u003e\n   317|        \u003cdiv id=\"scrapeError\" class=\"hidden mt-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm flex items-center justify-between gap-3\"\u003e\n   318|          \u003cspan id=\"scrapeErrorMsg\"\u003e\u003c/span\u003e\n   319|          \u003cbutton onclick=\"skipScrape()\" class=\"shrink-0 px-3 py-1 bg-white/[0.08] hover:bg-white/[0.12] text-white rounded text-xs font-medium transition\"\u003eContinue manually\u003c/button\u003e\n   320|        \u003c/div\u003e\n   321|        \u003cdiv id=\"scrapeLoading\" class=\"hidden mt-4 flex items-center gap-3 text-[#b0bec5] text-sm\"\u003e\n   322|          \u003csvg class=\"animate-spin w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\"\u003e\u003ccircle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"/\u003e\u003cpath class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"/\u003e\u003c/svg\u003e\n   323|          Scanning website...\n   324|        \u003c/div\u003e\n   325|      \u003c/div\u003e\n   326|\n   327|      \u003c!-- Step 2: Org details (prefilled from scrape) --\u003e\n   328|      \u003cdiv id=\"addOrgStep2\" class=\"hidden p-6\"\u003e\n   329|        \u003cdiv class=\"flex items-center justify-between mb-4\"\u003e\n   330|          \u003ch2 class=\"text-lg font-semibold text-white\"\u003eOrganization Details\u003c/h2\u003e\n   331|          \u003cbutton onclick=\"closeAddOrgModal()\" class=\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   332|        \u003c/div\u003e\n   333|        \u003cdiv class=\"grid grid-cols-2 gap-4 mb-5\"\u003e\n   334|          \u003cdiv class=\"col-span-2 flex items-start gap-4\"\u003e\n   335|            \u003cdiv id=\"orgLogoWrap\" class=\"hidden shrink-0 w-16 h-16 rounded-lg border border-white/[0.08] overflow-hidden bg-white flex items-center justify-center\"\u003e\n   336|              \u003cimg id=\"orgLogoImg\" src=\"\" class=\"max-w-full max-h-full object-contain\"\u003e\n   337|            \u003c/div\u003e\n   338|            \u003cdiv class=\"flex-1\"\u003e\n   339|              \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eName \u003cspan class=\"text-red-400\"\u003e*\u003c/span\u003e\u003c/label\u003e\n   340|              \u003cinput type=\"text\" id=\"orgName\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   341|            \u003c/div\u003e\n   342|          \u003c/div\u003e\n   343|          \u003cdiv class=\"col-span-2\"\u003e\n   344|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eDescription\u003c/label\u003e\n   345|            \u003ctextarea id=\"orgDesc\" rows=\"4\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] resize-none\"\u003e\u003c/textarea\u003e\n   346|          \u003c/div\u003e\n   347|          \u003cdiv\u003e\n   348|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eRole in deal \u003cspan class=\"text-red-400\"\u003e*\u003c/span\u003e\u003c/label\u003e\n   349|            \u003cselect id=\"orgRole\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   350|              \u003coption value=\"\"\u003eSelect...\u003c/option\u003e\n   351|              \u003coption value=\"seller\"\u003eSeller\u003c/option\u003e\n   352|              \u003coption value=\"buyer\"\u003eBuyer\u003c/option\u003e\n   353|              \u003coption value=\"ib\"\u003eInvestment Bank\u003c/option\u003e\n   354|              \u003coption value=\"advisor\"\u003eAdvisor\u003c/option\u003e\n   355|            \u003c/select\u003e\n   356|          \u003c/div\u003e\n   357|          \u003cdiv\u003e\n   358|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eIndustry\u003c/label\u003e\n   359|            \u003cinput type=\"text\" id=\"orgIndustry\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   360|          \u003c/div\u003e\n   361|          \u003cdiv\u003e\n   362|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eWebsite\u003c/label\u003e\n   363|            \u003cinput type=\"text\" id=\"orgWebsite\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   364|          \u003c/div\u003e\n   365|          \u003cdiv\u003e\n   366|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003ePhone\u003c/label\u003e\n   367|            \u003cinput type=\"text\" id=\"orgPhone\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   368|          \u003c/div\u003e\n   369|          \u003cdiv class=\"col-span-2\"\u003e\n   370|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eAddress\u003c/label\u003e\n   371|            \u003cinput type=\"text\" id=\"orgAddress\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   372|          \u003c/div\u003e\n   373|          \u003cdiv\u003e\n   374|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eCity\u003c/label\u003e\n   375|            \u003cinput type=\"text\" id=\"orgCity\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   376|          \u003c/div\u003e\n   377|          \u003cdiv\u003e\n   378|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eState\u003c/label\u003e\n   379|            \u003cinput type=\"text\" id=\"orgState\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   380|          \u003c/div\u003e\n   381|          \u003cdiv\u003e\n   382|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eFounded\u003c/label\u003e\n   383|            \u003cinput type=\"text\" id=\"orgFounded\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   384|          \u003c/div\u003e\n   385|          \u003cdiv\u003e\n   386|            \u003clabel class=\"block text-xs text-[#b0bec5] mb-1\"\u003eLinkedIn\u003c/label\u003e\n   387|            \u003cinput type=\"text\" id=\"orgLinkedIn\" class=\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\"\u003e\n   388|          \u003c/div\u003e\n   389|        \u003c/div\u003e\n   390|        \u003cdiv class=\"flex justify-between\"\u003e\n   391|          \u003cbutton onclick=\"showAddOrgStep(1)\" class=\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\"\u003eBack\u003c/button\u003e\n   392|          \u003cdiv id=\"step2Error\" class=\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003ePlease fill in all required fields.\u003c/div\u003e\n   393|          \u003cbutton onclick=\"advanceFromStep2()\" class=\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eNext: Select People\u003c/button\u003e\n   394|        \u003c/div\u003e\n   395|      \u003c/div\u003e\n   396|\n   397|      \u003c!-- Step 3: Colleagues --\u003e\n   398|      \u003cdiv id=\"addOrgStep3\" class=\"hidden p-6\"\u003e\n   399|        \u003cdiv class=\"flex items-center justify-between mb-1\"\u003e\n   400|          \u003ch2 class=\"text-lg font-semibold text-white\"\u003eSelect Colleagues\u003c/h2\u003e\n   401|          \u003cbutton onclick=\"closeAddOrgModal()\" class=\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   402|        \u003c/div\u003e\n   403|        \u003cp class=\"text-[#b0bec5] text-sm mb-4\"\u003eChoose who to add to this deal.\u003c/p\u003e\n   404|        \u003cdiv class=\"flex items-center gap-3 mb-4\"\u003e\n   405|          \u003clabel class=\"flex items-center gap-2 cursor-pointer text-sm text-[#b0bec5]\"\u003e\n   406|            \u003cinput type=\"checkbox\" id=\"selectAllPeople\" onchange=\"toggleAllPeople(this.checked)\" class=\"rounded accent-[#c9a84c]\"\u003e\n   407|            Select all\n   408|          \u003c/label\u003e\n   409|          \u003cspan id=\"selectedCount\" class=\"text-xs text-[#8899a6]\"\u003e0 selected\u003c/span\u003e\n   410|        \u003c/div\u003e\n   411|        \u003cdiv id=\"peopleList\" class=\"space-y-2 mb-4 max-h-[40vh] overflow-y-auto\"\u003e\u003c/div\u003e\n   412|        \u003cdiv id=\"noPeople\" class=\"hidden text-[#b0bec5] text-sm py-4 text-center\"\u003eNo team members found on the website.\u003c/div\u003e\n   413|        \u003c!-- Manual add row --\u003e\n   414|        \u003cdiv class=\"border-t border-white/[0.08] pt-3 mt-3\"\u003e\n   415|          \u003cp class=\"text-xs text-[#b0bec5] mb-2\"\u003eAdd manually\u003c/p\u003e\n   416|          \u003cdiv class=\"grid grid-cols-[1fr_1fr_1fr_auto] gap-2\"\u003e\n   417|            \u003cinput type=\"text\" id=\"manualName\" placeholder=\"Name\"\n   418|              class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\"\u003e\n   419|            \u003cinput type=\"email\" id=\"manualEmail\" placeholder=\"Email\"\n   420|              class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\"\u003e\n   421|            \u003cinput type=\"text\" id=\"manualTitle\" placeholder=\"Title\"\n   422|              onkeydown=\"if(event.key==='Enter'){event.preventDefault();addManualPerson();}\"\n   423|              class=\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\"\u003e\n   424|            \u003cbutton onclick=\"addManualPerson()\" class=\"px-3 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-bold rounded-lg text-sm transition\"\u003e+\u003c/button\u003e\n   425|          \u003c/div\u003e\n   426|        \u003c/div\u003e\n   427|        \u003cdiv class=\"flex justify-between\"\u003e\n   428|          \u003cbutton onclick=\"showAddOrgStep(2)\" class=\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\"\u003eBack\u003c/button\u003e\n   429|          \u003cdiv class=\"flex flex-col items-end gap-2\"\u003e\n   430|            \u003cdiv id=\"step3Error\" class=\"hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm text-right\"\u003e\u003c/div\u003e\n   431|            \u003cbutton onclick=\"proceedFromStep3()\" id=\"addOrgSubmitBtn\" class=\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eAdd to Deal\u003c/button\u003e\n   432|          \u003c/div\u003e\n   433|        \u003c/div\u003e\n   434|      \u003c/div\u003e\n   435|\n   436|      \u003c!-- Step 4: Request List Templates (wizard mode only) --\u003e\n   437|      \u003cdiv id=\"addOrgStep4\" class=\"hidden p-6\"\u003e\n   438|        \u003cdiv class=\"flex items-center justify-between mb-1\"\u003e\n   439|          \u003ch2 class=\"text-lg font-semibold text-white\"\u003eRequest Lists\u003c/h2\u003e\n   440|          \u003cbutton onclick=\"closeAddOrgModal()\" class=\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   441|        \u003c/div\u003e\n   442|        \u003cp class=\"text-[#b0bec5] text-sm mb-4\"\u003eChoose which request list templates to include in this project.\u003c/p\u003e\n   443|\n   444|        \u003cdiv class=\"space-y-3 mb-6\" id=\"wizTemplatePicker\"\u003e\n   445|          \u003clabel class=\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\" style=\"background:var(--ds-hv);border:1px solid var(--ds-bd)\"\u003e\n   446|            \u003cinput type=\"checkbox\" class=\"tmpl-cb mt-0.5 accent-[#c9a84c]\" value=\"core-8\" checked onchange=\"tmplChanged(this)\"\u003e\n   447|            \u003cdiv\u003e\n   448|              \u003cdiv class=\"text-sm font-semibold\" style=\"color:var(--ds-tx)\"\u003eCore Due Diligence\u003c/div\u003e\n   449|              \u003cdiv class=\"text-xs mt-0.5\" style=\"color:var(--ds-tx2)\"\u003e8 essential items — financials, legal, commercial, HR, tax. The minimum every deal needs.\u003c/div\u003e\n   450|            \u003c/div\u003e\n   451|          \u003c/label\u003e\n   452|          \u003clabel class=\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\" style=\"background:var(--ds-hv);border:1px solid var(--ds-bd)\"\u003e\n   453|            \u003cinput type=\"checkbox\" class=\"tmpl-cb mt-0.5 accent-[#c9a84c]\" value=\"comprehensive-100\" onchange=\"tmplChanged(this)\"\u003e\n   454|            \u003cdiv\u003e\n   455|              \u003cdiv class=\"text-sm font-semibold\" style=\"color:var(--ds-tx)\"\u003eComprehensive Due Diligence\u003c/div\u003e\n   456|              \u003cdiv class=\"text-xs mt-0.5\" style=\"color:var(--ds-tx2)\"\u003e104 items across 18 sections — financial, legal, tax, commercial, HR, IP, IT, environmental, insurance. Full-scope M\u0026A checklist.\u003c/div\u003e\n   457|            \u003c/div\u003e\n   458|          \u003c/label\u003e\n   459|          \u003clabel class=\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\" style=\"background:var(--ds-hv);border:1px solid var(--ds-bd)\"\u003e\n   460|            \u003cinput type=\"checkbox\" class=\"tmpl-cb mt-0.5 accent-[#c9a84c]\" value=\"none\" onchange=\"tmplChanged(this)\"\u003e\n   461|            \u003cdiv\u003e\n   462|              \u003cdiv class=\"text-sm font-semibold\" style=\"color:var(--ds-tx)\"\u003eStart empty\u003c/div\u003e\n   463|              \u003cdiv class=\"text-xs mt-0.5\" style=\"color:var(--ds-tx2)\"\u003eNo request lists — import or create your own later.\u003c/div\u003e\n   464|            \u003c/div\u003e\n   465|          \u003c/label\u003e\n   466|        \u003c/div\u003e\n   467|\n   468|        \u003cdiv class=\"flex justify-between\"\u003e\n   469|          \u003cbutton onclick=\"showAddOrgStep(3)\" class=\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\"\u003eBack\u003c/button\u003e\n   470|\u003cdiv class=\"w-full mb-2\"\u003e\u003cdiv id=\"wizError\" class=\"hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\"\u003e\u003c/div\u003e\u003c/div\u003e\n   471|          \u003cbutton onclick=\"createNewProject()\" id=\"wizCreateBtn\" class=\"px-6 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eCreate Project\u003c/button\u003e\n   472|        \u003c/div\u003e\n   473|      \u003c/div\u003e\n   474|\n   475|    \u003c/div\u003e\n   476|  \u003c/div\u003e\n   477|\n   478|  \u003c!-- Email Preview Modal --\u003e\n   479|  \u003cdiv id=\"emailPreviewModal\" class=\"hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center p-4\"\u003e\n   480|    \u003cdiv class=\"rounded-xl w-full max-w-lg max-h-[80vh] overflow-y-auto\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd)\"\u003e\n   481|      \u003cdiv class=\"px-6 py-4 border-b flex items-center justify-between\" style=\"border-color:var(--ds-bd)\"\u003e\n   482|        \u003ch3 class=\"text-lg font-semibold\" style=\"color:var(--ds-tx)\"\u003eInvite Emails\u003c/h3\u003e\n   483|        \u003cbutton onclick=\"closeEmailPreview()\" class=\"text-2xl leading-none\" style=\"color:var(--ds-tx2)\"\u003e\u0026times;\u003c/button\u003e\n   484|      \u003c/div\u003e\n   485|      \u003cdiv class=\"px-6 py-4\"\u003e\n   486|        \u003cdiv id=\"emailPreviewList\" class=\"space-y-2\"\u003e\u003c/div\u003e\n   487|      \u003c/div\u003e\n   488|      \u003cdiv class=\"px-6 py-4 border-t\" style=\"border-color:var(--ds-bd)\"\u003e\n   489|        \u003cbutton onclick=\"closeEmailPreview()\" class=\"w-full py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eOK\u003c/button\u003e\n   490|      \u003c/div\u003e\n   491|    \u003c/div\u003e\n   492|  \u003c/div\u003e\n   493|\n   494|  \u003c!-- Person Slide-out Panel --\u003e\n   495|  \u003cdiv id=\"personOverlay\" class=\"hidden fixed inset-0 bg-black/40 z-50\" onclick=\"closePersonPanel()\"\u003e\u003c/div\u003e\n   496|  \u003cdiv id=\"personPanel\" class=\"fixed top-0 right-0 h-full w-full max-w-md z-50 transform translate-x-full transition-transform duration-200 ease-out\"\n   497|    style=\"background:var(--ds-sf, #0d1f3c); border-left: 1px solid var(--ds-bd, rgba(255,255,255,.08));\"\u003e\n   498|    \u003cdiv class=\"h-full flex flex-col\"\u003e\n   499|      \u003c!-- Header --\u003e\n   500|      \u003cdiv class=\"flex items-center justify-between px-6 py-4 border-b\" style=\"border-color:var(--ds-bd)\"\u003e\n   501|        \u003ch3 id=\"personPanelTitle\" class=\"text-lg font-semibold\" style=\"color:var(--ds-tx)\"\u003eEdit Person\u003c/h3\u003e\n   502|        \u003cbutton onclick=\"closePersonPanel()\" class=\"text-2xl leading-none\" style=\"color:var(--ds-tx2)\"\u003e\u0026times;\u003c/button\u003e\n   503|      \u003c/div\u003e\n   504|      \u003c!-- Body --\u003e\n   505|      \u003cdiv class=\"flex-1 overflow-y-auto min-h-0 px-6 py-5 space-y-4\"\u003e\n   506|        \u003cinput type=\"hidden\" id=\"ppDealOrgId\"\u003e\n   507|        \u003cinput type=\"hidden\" id=\"ppMemberIdx\"\u003e\n   508|        \u003c!-- Photo + Name --\u003e\n   509|        \u003cdiv class=\"flex items-center gap-4\"\u003e\n   510|          \u003cdiv id=\"ppPhotoWrap\" class=\"w-20 h-20 rounded-full overflow-hidden shrink-0 bg-[#c9a84c]/20 flex items-center justify-center cursor-pointer relative group\"\n   511|            onclick=\"document.getElementById('ppPhotoUrlRow').classList.toggle('hidden')\" title=\"Click to change photo\"\u003e\n   512|            \u003cimg id=\"ppPhotoImg\" src=\"\" class=\"w-full h-full object-cover hidden\"\u003e\n   513|            \u003cspan id=\"ppPhotoLetter\" class=\"text-[#c9a84c] text-2xl font-semibold\"\u003e\u003c/span\u003e\n   514|            \u003cdiv class=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center rounded-full\"\u003e\n   515|              \u003cspan class=\"text-white text-xs\"\u003eEdit\u003c/span\u003e\n   516|            \u003c/div\u003e\n   517|          \u003c/div\u003e\n   518|          \u003cdiv class=\"flex-1\"\u003e\n   519|            \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003eName\u003c/label\u003e\n   520|            \u003cinput type=\"text\" id=\"ppName\" class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   521|              style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\n   522|              oninput=\"previewPersonPhoto()\"\u003e\n   523|          \u003c/div\u003e\n   524|        \u003c/div\u003e\n   525|        \u003cdiv id=\"ppPhotoUrlRow\" class=\"hidden\"\u003e\n   526|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003ePhoto URL\u003c/label\u003e\n   527|          \u003cinput type=\"text\" id=\"ppPhoto\" placeholder=\"https://...\"\n   528|            class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   529|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\n   530|            oninput=\"previewPersonPhoto()\"\u003e\n   531|        \u003c/div\u003e\n   532|        \u003cdiv\u003e\n   533|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003eEmail\u003c/label\u003e\n   534|          \u003cinput type=\"email\" id=\"ppEmail\" class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   535|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n   536|        \u003c/div\u003e\n   537|        \u003cdiv\u003e\n   538|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003eTitle\u003c/label\u003e\n   539|          \u003cinput type=\"text\" id=\"ppTitle\" class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   540|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n   541|        \u003c/div\u003e\n   542|        \u003cdiv\u003e\n   543|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003ePhone\u003c/label\u003e\n   544|          \u003cinput type=\"text\" id=\"ppPhone\" class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   545|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n   546|        \u003c/div\u003e\n   547|        \u003cdiv\u003e\n   548|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003eLinkedIn\u003c/label\u003e\n   549|          \u003cinput type=\"text\" id=\"ppLinkedIn\" placeholder=\"\"\n   550|            class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\"\n   551|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n   552|        \u003c/div\u003e\n   553|        \u003cdiv\u003e\n   554|          \u003clabel class=\"block text-xs mb-1\" style=\"color:var(--ds-tx2)\"\u003eBio\u003c/label\u003e\n   555|          \u003ctextarea id=\"ppBio\" rows=\"5\" class=\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none resize-none\"\n   556|            style=\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\u003c/textarea\u003e\n   557|        \u003c/div\u003e\n   558|      \u003c/div\u003e\n   559|      \u003c!-- Footer --\u003e\n   560|      \u003cdiv class=\"px-6 py-4 pb-16 border-t flex items-center gap-3 shrink-0\" style=\"border-color:var(--ds-bd)\"\u003e\n   561|        \u003cbutton onclick=\"removePersonFromDeal()\" id=\"ppRemoveBtn\"\n   562|          class=\"px-3 py-2 rounded-lg text-sm font-medium transition\"\n   563|          style=\"background:rgba(239,68,68,.1);color:#f87171;border:1px solid rgba(239,68,68,.2)\"\u003eRemove\u003c/button\u003e\n   564|        \u003cdiv class=\"flex-1\"\u003e\u003c/div\u003e\n   565|        \u003cbutton onclick=\"closePersonPanel()\" class=\"px-4 py-2 rounded-lg text-sm font-medium transition\"\n   566|          style=\"background:var(--ds-hv);color:var(--ds-tx)\"\u003eCancel\u003c/button\u003e\n   567|        \u003cbutton onclick=\"savePersonPanel()\" class=\"px-4 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eSave\u003c/button\u003e\n   568|      \u003c/div\u003e\n   569|    \u003c/div\u003e\n   570|  \u003c/div\u003e\n   571|\n   572|  \u003c!-- Permissions Modal --\u003e\n   573|  \u003cdiv id=\"permModal\" class=\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\"\u003e\n   574|    \u003cdiv class=\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-md\"\u003e\n   575|      \u003cdiv class=\"p-6 border-b border-white/[0.08] flex items-center justify-between\"\u003e\n   576|        \u003ch2 class=\"text-lg font-semibold text-white\" id=\"permModalTitle\"\u003ePermissions\u003c/h2\u003e\n   577|        \u003cbutton onclick=\"closePermModal()\" class=\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\"\u003e\u0026times;\u003c/button\u003e\n   578|      \u003c/div\u003e\n   579|      \u003cdiv class=\"p-6 space-y-4\"\u003e\n   580|        \u003cinput type=\"hidden\" id=\"permModalDealOrgId\"\u003e\n   581|        \u003cinput type=\"hidden\" id=\"permModalVersion\"\u003e\n   582|        \u003cinput type=\"hidden\" id=\"permModalOrgId\"\u003e\n   583|        \u003cinput type=\"hidden\" id=\"permModalRole\"\u003e\n   584|        \u003cdiv class=\"flex items-center justify-between\"\u003e\n   585|          \u003cspan class=\"text-sm text-[#b0bec5]\"\u003eDownload documents\u003c/span\u003e\n   586|          \u003cselect id=\"pmDownload\" class=\"px-3 py-1.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-xs focus:outline-none focus:border-[#c9a84c]\"\u003e\n   587|            \u003coption value=\"watermark\"\u003eWatermarked only\u003c/option\u003e\n   588|            \u003coption value=\"full\"\u003eFull (no watermark)\u003c/option\u003e\n   589|            \u003coption value=\"none\"\u003eNo access\u003c/option\u003e\n   590|          \u003c/select\u003e\n   591|        \u003c/div\u003e\n   592|        \u003cdiv class=\"flex items-center justify-between\"\u003e\n   593|          \u003cspan class=\"text-sm text-[#b0bec5]\"\u003eUpload documents\u003c/span\u003e\n   594|          \u003cinput type=\"checkbox\" id=\"pmUpload\" class=\"w-4 h-4 accent-[#c9a84c]\"\u003e\n   595|        \u003c/div\u003e\n   596|        \u003cdiv class=\"flex items-center justify-between\"\u003e\n   597|          \u003cspan class=\"text-sm text-[#b0bec5]\"\u003eAdd request lists\u003c/span\u003e\n   598|          \u003cinput type=\"checkbox\" id=\"pmAddLists\" class=\"w-4 h-4 accent-[#c9a84c]\"\u003e\n   599|        \u003c/div\u003e\n   600|        \u003cdiv class=\"flex items-center justify-between\"\u003e\n   601|          \u003cspan class=\"text-sm text-[#b0bec5]\"\u003eEdit request lists\u003c/span\u003e\n   602|          \u003cinput type=\"checkbox\" id=\"pmEditLists\" class=\"w-4 h-4 accent-[#c9a84c]\"\u003e\n   603|        \u003c/div\u003e\n   604|        \u003cdiv\u003e\n   605|          \u003cdiv class=\"flex items-center justify-between mb-2\"\u003e\n   606|            \u003cspan class=\"text-sm text-[#b0bec5]\"\u003eVisible request lists\u003c/span\u003e\n   607|            \u003clabel class=\"flex items-center gap-1.5 text-xs text-[#b0bec5] cursor-pointer\"\u003e\n   608|              \u003cinput type=\"checkbox\" id=\"pmAllLists\" class=\"w-3.5 h-3.5 accent-[#c9a84c]\" onchange=\"toggleAllLists(this.checked)\"\u003e\n   609|              All lists\n   610|            \u003c/label\u003e\n   611|          \u003c/div\u003e\n   612|          \u003cdiv id=\"pmListChecks\" class=\"space-y-1.5 max-h-40 overflow-y-auto pr-1\"\u003e\u003c/div\u003e\n   613|        \u003c/div\u003e\n   614|      \u003c/div\u003e\n   615|      \u003cdiv class=\"p-6 border-t border-white/[0.08] flex gap-3\"\u003e\n   616|        \u003cbutton onclick=\"closePermModal()\" class=\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\"\u003eCancel\u003c/button\u003e\n   617|        \u003cbutton onclick=\"savePermissions()\" class=\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\"\u003eSave\u003c/button\u003e\n   618|      \u003c/div\u003e\n   619|    \u003c/div\u003e\n   620|  \u003c/div\u003e\n   621|{{end}}\n   622|\n   623|{{define \"scripts\"}}\n   624|  \u003cscript\u003e\n   625|    const projectID = location.pathname.split('/').pop();\n   626|    function fmtDate(ms) { if (!ms) return ''; const d = new Date(ms); return d.toLocaleDateString('en-US', {month:'short',day:'numeric'}); }\n   627|\n   628|    // Badge helpers\n   629|    const priStyles = { critical:'background:rgba(239,68,68,.15);color:#f87171', high:'background:rgba(251,146,60,.15);color:#fb923c', medium:'background:rgba(250,204,21,.15);color:#facc15', low:'background:rgba(148,163,184,.15);color:#b0bec5' };\n   630|    const statStyles = { open:'background:rgba(96,165,250,.15);color:#60a5fa', in_process:'background:rgba(251,191,36,.15);color:#fbbf24', partial:'background:rgba(168,85,247,.15);color:#a855f7', complete:'background:rgba(74,222,128,.15);color:#4ade80' };\n   631|    const statLabels = { open:'Open', in_process:'In Process', partial:'Partial', complete:'Complete' };\n   632|\n   633|    function priorityBadge(p) {\n   634|      return `\u003cspan class=\"badge\" style=\"${priStyles[p]||priStyles.medium}\"\u003e${escHtml(p||'medium')}\u003c/span\u003e`;\n   635|    }\n   636|    function statusBadge(s) {\n   637|      return `\u003cspan class=\"badge\" style=\"${statStyles[s]||statStyles.open}\"\u003e${statLabels[s]||escHtml(s||'open')}\u003c/span\u003e`;\n   638|    }\n   639|\n   640|    function labelPill(label) {\n   641|      if (!label) return '';\n   642|      const colors = [\n   643|        {bg:'rgba(96,165,250,.15)',fg:'#60a5fa'},{bg:'rgba(244,114,182,.15)',fg:'#f472b6'},\n   644|        {bg:'rgba(167,139,250,.15)',fg:'#a78bfa'},{bg:'rgba(251,146,60,.15)',fg:'#fb923c'},\n   645|        {bg:'rgba(74,222,128,.15)',fg:'#4ade80'},{bg:'rgba(250,204,21,.15)',fg:'#facc15'}\n   646|      ];\n   647|      let hash = 0;\n   648|      for (let i = 0; i \u003c label.length; i++) hash = ((hash \u003c\u003c 5) - hash) + label.charCodeAt(i);\n   649|      const c = colors[Math.abs(hash) % colors.length];\n   650|      return `\u003cspan style=\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:11px;background:${c.bg};color:${c.fg}\"\u003e${escHtml(label)}\u003c/span\u003e`;\n   651|    }\n   652|\n   653|    function fmtDueDate(v) {\n   654|      if (!v) return '\\u2014';\n   655|      if (typeof v === 'number') return fmtDate(v);\n   656|      return escHtml(String(v));\n   657|    }\n   658|\n   659|    const statusColors = { active: 'bg-green-500/20 text-green-300', draft: 'bg-gray-500/20 text-gray-300', closed: 'bg-red-500/20 text-red-300' };\n   660|    const roleColors = { seller: 'bg-blue-500/20 text-blue-300', buyer: 'bg-green-500/20 text-green-300', ib: 'bg-[#c9a84c]/20 text-[#c9a84c]', advisor: 'bg-purple-500/20 text-purple-300' };\n   661|\n   662|    // ---- State ----\n   663|    let treeData = [];\n   664|    let collapsed = new Set();\n   665|    let numberMap = {};\n   666|    let openMenuId = null;\n   667|    let attachRequestId = null;\n   668|    let detailMode = false;\n   669|\n   670|    // ---- Detail toggle ----\n   671|    function toggleDetailMode() {\n   672|      detailMode = !detailMode;\n   673|      document.getElementById('detailToggle').textContent = detailMode ? 'Detailed View' : 'Summary View';\n   674|      renderTree();\n   675|    }\n   676|\n   677|    // ---- Project loading ----\n   678|    async function loadProject() {\n   679|      try {\n   680|        const res = await fetchAPI('/api/projects/' + projectID);\n   681|        if (!res.ok) { document.getElementById('projectTitle').textContent = 'Not found'; return; }\n   682|        const raw = await res.json();\n   683|        const p = raw.project || raw;\n   684|        const d = parseData(p.data_text);\n   685|        const name = d.name || p.summary_text || p.summary || 'Untitled';\n   686|        document.title = name + ' — Dealspace';\n   687|        document.getElementById('projectName').textContent = name;\n   688|        fetchAPI('/api/projects').then(r=\u003er.json()).then(projects=\u003e{\n   689|          const menu = document.getElementById('projectMenu');\n   690|          menu.innerHTML = (projects||[]).map(p=\u003e{\n   691|            const d = p.data_text ? (()=\u003e{try{return JSON.parse(p.data_text)}catch{return{}}})() : {};\n   692|            const pname = d.name || p.search_key || p.entry_id;\n   693|            const active = p.entry_id === projectID;\n   694|            return `\u003cdiv onclick=\"switchProject('${p.entry_id}')\" class=\"px-4 py-2.5 cursor-pointer transition text-sm\" style=\"color:${active?'var(--ds-ac)':'var(--ds-tx)'};background:${active?'rgba(255,255,255,.04)':'transparent'}\" onmouseover=\"this.style.background='rgba(255,255,255,.06)'\" onmouseout=\"this.style.background='${active?'rgba(255,255,255,.04)':'transparent'}'\"\u003e\n   695|              ${escHtml(pname)}\n   696|            \u003c/div\u003e`;\n   697|          }).join('');\n   698|        }).catch(()=\u003e{});\n   699|        // projectTitle removed — name shown in header breadcrumb only\n   700|        document.getElementById('projectDesc').textContent = d.description || '';\n   701|        const status = d.status || 'active';\n   702|        const sc = statusColors[status] || 'bg-gray-500/20 text-gray-300';\n   703|        document.getElementById('projectStatus').className = 'px-2.5 py-0.5 rounded-full text-xs font-medium capitalize ' + sc;\n   704|        document.getElementById('projectStatus').textContent = status;\n   705|      } catch(e) {}\n   706|    }\n   707|\n   708|    // ---- Tree loading ----\n   709|    async function loadRequestTree() {\n   710|      try {\n   711|        const res = await fetchAPI('/api/projects/' + projectID + '/requests/tree');\n   712|        treeData = await res.json();\n   713|        if (!treeData || treeData.length === 0) {\n   714|          document.getElementById('requestEmpty').classList.remove('hidden');\n   715|          document.getElementById('treeWrap').classList.add('hidden');\n   716|          return;\n   717|        }\n   718|        document.getElementById('requestEmpty').classList.add('hidden');\n   719|        document.getElementById('treeWrap').classList.remove('hidden');\n   720|        buildNumbering();\n   721|        renderTree();\n   722|      } catch(e) {\n   723|        document.getElementById('treeBody').innerHTML = '\u003ctr\u003e\u003ctd colspan=\"13\" class=\"text-red-400 text-sm py-4\"\u003eFailed to load requests.\u003c/td\u003e\u003c/tr\u003e';\n   724|      }\n   725|    }\n   726|\n   727|    // Build hierarchical numbering (1, 1.1, 1.1.1, etc.)\n   728|    function buildNumbering() {\n   729|      numberMap = {};\n   730|      const childrenOf = {};\n   731|      for (const item of treeData) {\n   732|        if (!childrenOf[item.parent_id]) childrenOf[item.parent_id] = [];\n   733|        childrenOf[item.parent_id].push(item.entry_id);\n   734|      }\n   735|      const counters = {};\n   736|      for (const item of treeData) {\n   737|        if (!counters[item.parent_id]) counters[item.parent_id] = 0;\n   738|        counters[item.parent_id]++;\n   739|        const parentNum = numberMap[item.parent_id];\n   740|        if (item.type === 'request_list') {\n   741|          numberMap[item.entry_id] = '' + counters[item.parent_id];\n   742|        } else {\n   743|          numberMap[item.entry_id] = (parentNum ? parentNum + '.' : '') + counters[item.parent_id];\n   744|        }\n   745|      }\n   746|    }\n   747|\n   748|    // Check if an item or any ancestor is collapsed\n   749|    function isHidden(item) {\n   750|      const parentMap = {};\n   751|      for (const t of treeData) parentMap[t.entry_id] = t.parent_id;\n   752|      let pid = item.parent_id;\n   753|      while (pid \u0026\u0026 pid !== projectID) {\n   754|        if (collapsed.has(pid)) return true;\n   755|        pid = parentMap[pid];\n   756|      }\n   757|      return false;\n   758|    }\n   759|\n   760|    // Auto-summarize long request text for Summary View.\n   761|    // Short text (≤8 words or ≤60 chars) is returned as-is.\n   762|    // Longer text: extract the first complete sentence; if still long, trim to ~80 chars at a word boundary.\n   763|    function summarize(text) {\n   764|      if (!text) return text;\n   765|      const wordCount = text.trim().split(/\\s+/).length;\n   766|      if (wordCount \u003c= 8 || text.length \u003c= 60) return text;\n   767|      // Try to find a sentence boundary\n   768|      const m = text.match(/^(.+?[.!?])(?:\\s|$)/);\n   769|      if (m \u0026\u0026 m[1].length \u003c= 120) return m[1];\n   770|      // No clean sentence — trim to ~80 chars at last word boundary\n   771|      if (text.length \u003c= 80) return text;\n   772|      const cut = text.lastIndexOf(' ', 80);\n   773|      return text.substring(0, cut \u003e 20 ? cut : 80) + '…';\n   774|    }\n   775|\n   776|    function renderTree() {\n   777|      const tbody = document.getElementById('treeBody');\n   778|      let html = '';\n   779|      for (const item of treeData) {\n   780|        if (isHidden(item)) continue;\n   781|        const d = item.data || {};\n   782|        const num = numberMap[item.entry_id] || '';\n   783|        const indent = Math.max(0, item.depth - 1) * 20;\n   784|        const isCollapsible = item.type === 'request_list' || item.type === 'section';\n   785|        const isCollapsed = collapsed.has(item.entry_id);\n   786|        const chevron = isCollapsible\n   787|          ? `\u003cspan class=\"collapse-btn ${isCollapsed?'collapsed':''}\" onclick=\"toggleCollapse('${item.entry_id}')\"\u003e\\u25BC\u003c/span\u003e`\n   788|          : '';\n   789|\n   790|        if (item.type === 'request_list') {\n   791|          const name = d.name || 'Request List';\n   792|          const childCount = item.children_count || 0;\n   793|          const visOrgId = d.visibility_org_id || '';\n   794|          const visBadge = visOrgId\n   795|            ? `\u003cspan style=\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:10px;background:rgba(251,146,60,.15);color:#fb923c;margin-left:8px\"\u003eRestricted\u003c/span\u003e`\n   796|            : `\u003cspan style=\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:10px;background:rgba(74,222,128,.12);color:#4ade80;margin-left:8px\"\u003eAll\u003c/span\u003e`;\n   797|          html += `\u003ctr class=\"row-list\" data-id=\"${item.entry_id}\" data-type=\"${item.type}\" data-parent=\"${item.parent_id}\" data-depth=\"${item.depth}\"\u003e\n   798|            \u003ctd\u003e\u003c/td\u003e\n   799|            \u003ctd colspan=\"11\" style=\"padding-left:${indent+10}px\"\u003e\n   800|              ${chevron}\n   801|              \u003cstrong style=\"color:var(--ds-tx);margin-left:4px;font-size:13px;font-weight:600\"\u003e${escHtml(name)}\u003c/strong\u003e\n   802|              ${visBadge}\n   803|              \u003cspan style=\"color:var(--ds-tx3);margin-left:8px;font-size:11px\"\u003e${childCount} items\u003c/span\u003e\n   804|              \u003cbutton onclick=\"editVisibility('${item.entry_id}')\" style=\"margin-left:6px;background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 4px\" title=\"Change visibility\"\u003e\u0026#9881;\u003c/button\u003e\n   805|              \u003cbutton onclick=\"startInlineAdd('section','${item.entry_id}')\" style=\"margin-left:10px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\"\u003e+ Section\u003c/button\u003e\n   806|              \u003cbutton onclick=\"startInlineAdd('request','${item.entry_id}')\" style=\"margin-left:4px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\"\u003e+ Request\u003c/button\u003e\n   807|            \u003c/td\u003e\n   808|            \u003ctd style=\"position:relative;text-align:right\"\u003e\n   809|              \u003cbutton onclick=\"toggleMenu(event,'${item.entry_id}')\" style=\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\"\u003e\u0026hellip;\u003c/button\u003e\n   810|              \u003cdiv id=\"menu-${item.entry_id}\" class=\"actions-menu\" style=\"display:none\"\u003e\n   811|                \u003cbutton onclick=\"startInlineAdd('section','${item.entry_id}')\"\u003e+ Add Section\u003c/button\u003e\n   812|                \u003cbutton onclick=\"startInlineAdd('request','${item.entry_id}')\"\u003e+ Add Request\u003c/button\u003e\n   813|                \u003cbutton onclick=\"editVisibility('${item.entry_id}')\"\u003eChange Visibility\u003c/button\u003e\n   814|                \u003cbutton onclick=\"renameListOrSection('${item.entry_id}','list')\" style=\"border-top:1px solid var(--ds-bd);margin-top:2px;padding-top:6px\"\u003eRename\u003c/button\u003e\n   815|                \u003cbutton onclick=\"deleteEntry('${item.entry_id}')\" style=\"color:#f87171\"\u003eDelete List\u003c/button\u003e\n   816|              \u003c/div\u003e\n   817|            \u003c/td\u003e\n   818|          \u003c/tr\u003e`;\n   819|        } else if (item.type === 'section') {\n   820|          const name = d.name || d.title || 'Section';\n   821|          html += `\u003ctr class=\"row-section\" data-id=\"${item.entry_id}\" data-type=\"${item.type}\" data-parent=\"${item.parent_id}\" data-depth=\"${item.depth}\"\n   822|                draggable=\"true\" ondragstart=\"onDragStart(event)\" ondragover=\"onDragOver(event)\" ondragleave=\"onDragLeave(event)\" ondrop=\"onDrop(event)\"\u003e\n   823|            \u003ctd class=\"drag-handle\" style=\"color:var(--ds-tx3);font-size:11px;cursor:grab\"\u003e\\u2630\u003c/td\u003e\n   824|            \u003ctd class=\"row-num\" style=\"padding-left:${indent}px\"\u003e${escHtml(num)}\u003c/td\u003e\n   825|            \u003ctd colspan=\"10\" style=\"padding-left:${indent}px\"\u003e\n   826|              ${chevron}\n   827|              \u003cspan style=\"color:var(--ds-tx);font-weight:600;font-size:13px;margin-left:4px\"\u003e${escHtml(name)}\u003c/span\u003e\n   828|              \u003cspan style=\"color:var(--ds-tx3);margin-left:8px;font-size:11px\"\u003e${item.children_count||0} requests\u003c/span\u003e\n   829|              \u003cbutton onclick=\"startInlineAdd('request','${item.entry_id}')\" style=\"margin-left:10px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\"\u003e+ Request\u003c/button\u003e\n   830|            \u003c/td\u003e\n   831|            \u003ctd style=\"position:relative;text-align:right\"\u003e\n   832|              \u003cbutton onclick=\"toggleMenu(event,'${item.entry_id}')\" style=\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\"\u003e\u0026hellip;\u003c/button\u003e\n   833|              \u003cdiv id=\"menu-${item.entry_id}\" class=\"actions-menu\" style=\"display:none\"\u003e\n   834|                \u003cbutton onclick=\"startInlineAdd('request','${item.entry_id}')\"\u003e+ Add Request\u003c/button\u003e\n   835|                \u003cbutton onclick=\"renameListOrSection('${item.entry_id}','section')\" style=\"border-top:1px solid var(--ds-bd);margin-top:2px;padding-top:6px\"\u003eRename\u003c/button\u003e\n   836|                \u003cbutton onclick=\"deleteEntry('${item.entry_id}')\" style=\"color:#f87171\"\u003eDelete Section\u003c/button\u003e\n   837|              \u003c/div\u003e\n   838|            \u003c/td\u003e\n   839|          \u003c/tr\u003e`;\n   840|        } else {\n   841|          // request row\n   842|          const title = d.title || d.description || 'Untitled';\n   843|          const buyerComment = d.buyer_comment || '';\n   844|          const sellerComment = d.seller_comment || '';\n   845|          const assignee = d.assignee || d.assigned_to || '';\n   846|          const dueDate = d.due_date || d.completed_date || '';\n   847|          const label = d.label || '';\n   848|          const curPri = d.priority || 'medium';\n   849|          const curStat = d.status || 'open';\n   850|          const eid = item.entry_id;\n   851|\n   852|          // Title with detail mode\n   853|          let titleHtml;\n   854|          if (detailMode) {\n   855|            titleHtml = `\u003ca href=\"/app/requests/${eid}\" style=\"color:var(--ds-tx);text-decoration:none\" class=\"hover:underline\"\u003e${escHtml(title)}\u003c/a\u003e`;\n   856|            if (d.description) {\n   857|              titleHtml += `\u003cdiv style=\"color:var(--ds-tx3);font-size:12px;margin-top:2px;line-height:1.4\"\u003e${escHtml(d.description)}\u003c/div\u003e`;\n   858|            }\n   859|          } else {\n   860|            titleHtml = `\u003ca href=\"/app/requests/${eid}\" style=\"color:var(--ds-tx);text-decoration:none;line-height:1.4\" class=\"hover:underline\"\u003e${escHtml(summarize(title))}\u003c/a\u003e`;\n   861|          }\n   862|\n   863|          // Priority select\n   864|          const priOpts = ['critical','high','medium','low'].map(o =\u003e\n   865|            `\u003coption value=\"${o}\" ${o===curPri?'selected':''}\u003e${o}\u003c/option\u003e`\n   866|          ).join('');\n   867|          const prioritySelect = `\u003cselect class=\"badge\" style=\"${priStyles[curPri]||priStyles.medium};border:none;cursor:pointer;font-size:11px;padding:2px 14px 2px 6px;border-radius:9999px;-webkit-appearance:none;appearance:none;outline:none;font-weight:500\" onchange=\"updateField('${eid}','priority',this.value)\"\u003e${priOpts}\u003c/select\u003e`;\n   868|\n   869|          // Status select\n   870|          const statOpts = ['open','in_process','partial','complete'].map(o =\u003e\n   871|            `\u003coption value=\"${o}\" ${o===curStat?'selected':''}\u003e${statLabels[o]}\u003c/option\u003e`\n   872|          ).join('');\n   873|          const statusSelect = `\u003cselect class=\"badge\" style=\"${statStyles[curStat]||statStyles.open};border:none;cursor:pointer;font-size:11px;padding:2px 14px 2px 6px;border-radius:9999px;-webkit-appearance:none;appearance:none;outline:none;font-weight:500\" onchange=\"updateField('${eid}','status',this.value)\"\u003e${statOpts}\u003c/select\u003e`;\n   874|\n   875|          html += `\u003ctr class=\"row-request\" data-id=\"${eid}\" data-type=\"${item.type}\" data-parent=\"${item.parent_id}\" data-depth=\"${item.depth}\"\n   876|                draggable=\"true\" ondragstart=\"onDragStart(event)\" ondragover=\"onDragOver(event)\" ondragleave=\"onDragLeave(event)\" ondrop=\"onDrop(event)\"\u003e\n   877|            \u003ctd class=\"drag-handle\" style=\"color:var(--ds-tx3);font-size:11px\"\u003e\\u2630\u003c/td\u003e\n   878|            \u003ctd class=\"row-num\" style=\"padding-left:${indent}px\"\u003e${escHtml(num)}\u003c/td\u003e\n   879|            \u003ctd class=\"req-title-cell\" style=\"white-space:normal;max-width:400px;padding-left:${indent}px\"\u003e\n   880|              ${titleHtml}\n   881|            \u003c/td\u003e\n   882|            \u003ctd\u003e${prioritySelect}\u003c/td\u003e\n   883|            \u003ctd\u003e${statusSelect}\u003c/td\u003e\n   884|            \u003ctd style=\"max-width:120px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\"\u003e${assignee ? escHtml(assignee) : '\\u2014'}\u003c/td\u003e\n   885|            \u003ctd style=\"color:var(--ds-tx2);font-size:12px\"\u003e${dueDate ? fmtDueDate(dueDate) : '\\u2014'}\u003c/td\u003e\n   886|            \u003ctd\u003e${labelPill(label)}\u003c/td\u003e\n   887|            \u003ctd style=\"text-align:center\"\u003e\n   888|              ${item.answer_count \u003e 0\n   889|                ? `\u003cspan class=\"badge\" style=\"background:rgba(74,222,128,.15);color:#4ade80;cursor:pointer\" onclick=\"showAttachModalById('${eid}')\"\u003e${item.answer_count}\u003c/span\u003e`\n   890|                : `\u003cspan style=\"color:var(--ds-tx3);cursor:pointer;font-size:12px\" onclick=\"showAttachModalById('${eid}')\"\u003e\u0026mdash;\u003c/span\u003e`}\n   891|            \u003c/td\u003e\n   892|            \u003ctd style=\"max-width:140px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\" title=\"${escHtml(buyerComment)}\"\u003e${escHtml(buyerComment)}\u003c/td\u003e\n   893|            \u003ctd style=\"max-width:140px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\" title=\"${escHtml(sellerComment)}\"\u003e${escHtml(sellerComment)}\u003c/td\u003e\n   894|            \u003ctd style=\"color:var(--ds-tx3);font-size:12px\"\u003e${fmtDate(item.created_at)}\u003c/td\u003e\n   895|            \u003ctd style=\"position:relative\"\u003e\n   896|              \u003cbutton onclick=\"toggleMenu(event,'${eid}')\" style=\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\"\u003e\u0026hellip;\u003c/button\u003e\n   897|              \u003cdiv id=\"menu-${eid}\" class=\"actions-menu\" style=\"display:none\"\u003e\n   898|                \u003cbutton onclick=\"showAttachModalById('${eid}')\"\u003eAttach Answer\u003c/button\u003e\n   899|                \u003cbutton onclick=\"editRequest('${eid}', event)\"\u003eEdit\u003c/button\u003e\n   900|                \u003cbutton onclick=\"deleteEntry('${eid}')\"\u003eDelete\u003c/button\u003e\n   901|              \u003c/div\u003e\n   902|            \u003c/td\u003e\n   903|          \u003c/tr\u003e`;\n   904|        }\n   905|      }\n   906|      if (!html) html = '\u003ctr\u003e\u003ctd colspan=\"13\" class=\"text-[#b0bec5] text-sm py-4\"\u003eNo requests found.\u003c/td\u003e\u003c/tr\u003e';\n   907|      tbody.innerHTML = html;\n   908|    }\n   909|\n   910|    // ---- Inline add section / request ----\n   911|    let inlineAddRow = null;\n   912|\n   913|    function startInlineAdd(type, parentId) {\n   914|      // Remove any existing inline row\n   915|      const existing = document.getElementById('inlineAddRow');\n   916|      if (existing) existing.remove();\n   917|\n   918|      const label = type === 'section' ? 'Section name' : 'Request text';\n   919|      const placeholder = type === 'section' ? 'e.g. Legal, Financial, Operations' : 'Enter the full request text — Dealspace will automatically show a concise summary in Summary View';\n   920|\n   921|      const tr = document.createElement('tr');\n   922|      tr.id = 'inlineAddRow';\n   923|      tr.setAttribute('data-parent', parentId);\n   924|      tr.setAttribute('data-addtype', type);\n   925|      tr.innerHTML = `\n   926|        \u003ctd\u003e\u003c/td\u003e\n   927|        \u003ctd colspan=\"12\" style=\"padding:8px 10px 8px 40px\"\u003e\n   928|          \u003cdiv style=\"display:flex;align-items:center;gap:8px\"\u003e\n   929|            \u003cinput id=\"inlineAddInput\" type=\"text\" placeholder=\"${placeholder}\"\n   930|              style=\"flex:1;padding:6px 10px;border-radius:6px;border:1px solid var(--ds-ac);background:var(--ds-bg);color:var(--ds-tx);font-size:13px;outline:none\"\n   931|              onkeydown=\"if(event.key==='Enter')submitInlineAdd();if(event.key==='Escape')cancelInlineAdd()\"\u003e\n   932|            \u003cbutton onclick=\"submitInlineAdd()\" style=\"padding:5px 14px;border-radius:6px;border:none;background:var(--ds-ac);color:var(--ds-act);font-size:12px;font-weight:600;cursor:pointer\"\u003eAdd\u003c/button\u003e\n   933|            \u003cbutton onclick=\"cancelInlineAdd()\" style=\"padding:5px 10px;border-radius:6px;border:1px solid var(--ds-bd);background:none;color:var(--ds-tx3);font-size:12px;cursor:pointer\"\u003eCancel\u003c/button\u003e\n   934|          \u003c/div\u003e\n   935|        \u003c/td\u003e`;\n   936|\n   937|      // Insert after the parent row\n   938|      const parentRow = document.querySelector(`tr[data-id=\"${parentId}\"]`);\n   939|      if (parentRow \u0026\u0026 parentRow.nextSibling) {\n   940|        parentRow.parentNode.insertBefore(tr, parentRow.nextSibling);\n   941|      } else if (parentRow) {\n   942|        parentRow.parentNode.appendChild(tr);\n   943|      }\n   944|\n   945|      document.getElementById('inlineAddInput').focus();\n   946|    }\n   947|\n   948|    function cancelInlineAdd() {\n   949|      const el = document.getElementById('inlineAddRow');\n   950|      if (el) el.remove();\n   951|    }\n   952|\n   953|    async function submitInlineAdd() {\n   954|      const tr = document.getElementById('inlineAddRow');\n   955|      if (!tr) return;\n   956|      const type = tr.getAttribute('data-addtype');\n   957|      const parentId = tr.getAttribute('data-parent');\n   958|      const text = document.getElementById('inlineAddInput').value.trim();\n   959|      if (!text) return;\n   960|\n   961|      try {\n   962|        const endpoint = type === 'section'\n   963|          ? `/api/projects/${projectID}/sections`\n   964|          : `/api/projects/${projectID}/requests/new`;\n   965|\n   966|        const res = await fetchAPI(endpoint, {\n   967|          method: 'POST',\n   968|          headers: {'Content-Type': 'application/json'},\n   969|          body: JSON.stringify({ name: text, title: text, parent_id: parentId })\n   970|        });\n   971|        if (!res.ok) throw new Error('Failed');\n   972|        cancelInlineAdd();\n   973|        await loadRequestTree();\n   974|      } catch(e) {\n   975|        document.getElementById('inlineAddInput').style.borderColor = 'red';\n   976|      }\n   977|    }\n   978|\n   979|    async function renameListOrSection(id, type) {\n   980|      // Close the menu\n   981|      const menu = document.getElementById('menu-' + id);\n   982|      if (menu) menu.style.display = 'none';\n   983|      openMenuId = null;\n   984|\n   985|      const item = treeData.find(t =\u003e t.entry_id === id);\n   986|      if (!item) return;\n   987|      const d = item.data || {};\n   988|      const currentName = d.name || d.title || '';\n   989|      const newName = prompt('Rename ' + (type === 'list' ? 'list' : 'section') + ':', currentName);\n   990|      if (!newName || !newName.trim() || newName.trim() === currentName) return;\n   991|\n   992|      const updated = { ...d, name: newName.trim(), title: newName.trim() };\n   993|      item.data = updated;\n   994|      try {\n   995|        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + id, {\n   996|          method: 'PUT',\n   997|          body: JSON.stringify({ data: JSON.stringify(updated), version: item.version || 0 })\n   998|        });\n   999|        if (res.ok) {\n  1000|          const result = await res.json();\n  1001|          if (result.version) item.version = result.version;\n  1002|          renderTree();\n  1003|        }\n  1004|      } catch(e) {\n  1005|        loadRequestTree();\n  1006|      }\n  1007|    }\n  1008|\n  1009|    function toggleCollapse(id) {\n  1010|      if (collapsed.has(id)) collapsed.delete(id); else collapsed.add(id);\n  1011|      renderTree();\n  1012|    }\n  1013|\n  1014|    // ---- Inline field update ----\n  1015|    async function updateField(entryId, field, value) {\n  1016|      const item = treeData.find(t =\u003e t.entry_id === entryId);\n  1017|      if (!item) return;\n  1018|      const d = { ...(item.data || {}), [field]: value };\n  1019|      item.data = d;\n  1020|      try {\n  1021|        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + entryId, {\n  1022|          method: 'PUT',\n  1023|          body: JSON.stringify({ data: JSON.stringify(d), version: item.version || 0 })\n  1024|        });\n  1025|        if (res.ok) {\n  1026|          const result = await res.json();\n  1027|          if (result.version) item.version = result.version;\n  1028|          renderTree();\n  1029|        }\n  1030|      } catch(e) {\n  1031|        console.error('Update failed:', e);\n  1032|        loadRequestTree();\n  1033|      }\n  1034|    }\n  1035|\n  1036|    // ---- Inline edit request title ----\n  1037|    function editRequest(id, e) {\n  1038|      if (e) e.stopPropagation();\n  1039|      if (openMenuId) {\n  1040|        const m = document.getElementById('menu-' + openMenuId);\n  1041|        if (m) m.style.display = 'none';\n  1042|        openMenuId = null;\n  1043|      }\n  1044|      const item = treeData.find(t =\u003e t.entry_id === id);\n  1045|      if (!item) return;\n  1046|      const d = item.data || {};\n  1047|      const title = d.title || d.description || 'Untitled';\n  1048|      const row = document.querySelector(`tr[data-id=\"${id}\"]`);\n  1049|      if (!row) return;\n  1050|      const titleCell = row.querySelector('.req-title-cell');\n  1051|      if (!titleCell) return;\n  1052|      row.dataset.originalTitleHtml = titleCell.innerHTML;\n  1053|      titleCell.innerHTML = `\u003cdiv style=\"display:flex;flex-direction:column;gap:4px;width:100%\"\u003e\n  1054|        \u003ctextarea rows=\"2\" style=\"width:100%;padding:6px 8px;background:#0a1628;border:1px solid rgba(201,168,76,.4);border-radius:6px;color:white;font-size:13px;outline:none;resize:vertical;line-height:1.4;font-family:inherit\" id=\"edit-title-${id}\"\u003e\u003c/textarea\u003e\n  1055|        \u003cdiv style=\"display:flex;gap:4px\"\u003e\n  1056|          \u003cbutton onclick=\"saveEditRequest('${id}')\" style=\"padding:3px 12px;background:#c9a84c;color:#0a1628;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer\"\u003eSave\u003c/button\u003e\n  1057|          \u003cbutton onclick=\"cancelEditRequest('${id}')\" style=\"padding:3px 10px;background:rgba(255,255,255,.08);color:white;border:none;border-radius:4px;font-size:11px;cursor:pointer\"\u003eCancel\u003c/button\u003e\n  1058|        \u003c/div\u003e\n  1059|      \u003c/div\u003e`;\n  1060|      const input = document.getElementById('edit-title-' + id);\n  1061|      if (input) { input.value = title; input.focus(); input.setSelectionRange(input.value.length, input.value.length); }\n  1062|    }\n  1063|\n  1064|    function cancelEditRequest(id) {\n  1065|      const row = document.querySelector(`tr[data-id=\"${id}\"]`);\n  1066|      if (!row) return;\n  1067|      const titleCell = row.querySelector('.req-title-cell');\n  1068|      if (titleCell \u0026\u0026 row.dataset.originalTitleHtml) titleCell.innerHTML = row.dataset.originalTitleHtml;\n  1069|    }\n  1070|\n  1071|    async function saveEditRequest(id) {\n  1072|      const input = document.getElementById('edit-title-' + id);\n  1073|      if (!input) return;\n  1074|      const newTitle = input.value.trim();\n  1075|      if (!newTitle) return;\n  1076|      const item = treeData.find(t =\u003e t.entry_id === id);\n  1077|      if (!item) return;\n  1078|      const d = { ...(item.data || {}), title: newTitle };\n  1079|      item.data = d;\n  1080|      try {\n  1081|        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + id, {\n  1082|          method: 'PUT',\n  1083|          body: JSON.stringify({ data: JSON.stringify(d), version: item.version || 0 })\n  1084|        });\n  1085|        if (res.ok) {\n  1086|          const result = await res.json();\n  1087|          if (result.version) item.version = result.version;\n  1088|          renderTree();\n  1089|        }\n  1090|      } catch(e) {\n  1091|        console.error('Save failed:', e);\n  1092|        cancelEditRequest(id);\n  1093|      }\n  1094|    }\n  1095|\n  1096|    // ---- Actions menu ----\n  1097|    function toggleMenu(e, id) {\n  1098|      e.stopPropagation();\n  1099|      if (openMenuId \u0026\u0026 openMenuId !== id) {\n  1100|        const prev = document.getElementById('menu-' + openMenuId);\n  1101|        if (prev) prev.style.display = 'none';\n  1102|      }\n  1103|      const menu = document.getElementById('menu-' + id);\n  1104|      if (menu) {\n  1105|        menu.style.display = menu.style.display === 'none' ? 'block' : 'none';\n  1106|        openMenuId = menu.style.display === 'block' ? id : null;\n  1107|      }\n  1108|    }\n  1109|    document.addEventListener('click', () =\u003e {\n  1110|      if (openMenuId) {\n  1111|        const m = document.getElementById('menu-' + openMenuId);\n  1112|        if (m) m.style.display = 'none';\n  1113|        openMenuId = null;\n  1114|      }\n  1115|    });\n  1116|\n  1117|    async function deleteEntry(entryID) {\n  1118|      if (!confirm('Delete this entry?')) return;\n  1119|      await fetchAPI('/api/projects/' + projectID + '/entries/' + entryID, { method: 'DELETE' });\n  1120|      loadRequestTree();\n  1121|    }\n  1122|\n  1123|    // ---- Drag \u0026 Drop ----\n  1124|    let dragEntryId = null;\n  1125|\n  1126|    function onDragStart(e) {\n  1127|      dragEntryId = e.currentTarget.dataset.id;\n  1128|      e.dataTransfer.effectAllowed = 'move';\n  1129|      e.currentTarget.style.opacity = '0.5';\n  1130|    }\n  1131|\n  1132|    function onDragOver(e) {\n  1133|      e.preventDefault();\n  1134|      e.dataTransfer.dropEffect = 'move';\n  1135|      const tr = e.currentTarget;\n  1136|      tr.classList.remove('drop-above', 'drop-below', 'drop-into');\n  1137|      const rect = tr.getBoundingClientRect();\n  1138|      const y = e.clientY - rect.top;\n  1139|      const pct = y / rect.height;\n  1140|      if (tr.dataset.type === 'section' \u0026\u0026 pct \u003e 0.25 \u0026\u0026 pct \u003c 0.75) {\n  1141|        tr.classList.add('drop-into');\n  1142|      } else if (pct \u003c 0.5) {\n  1143|        tr.classList.add('drop-above');\n  1144|      } else {\n  1145|        tr.classList.add('drop-below');\n  1146|      }\n  1147|    }\n  1148|\n  1149|    function onDragLeave(e) {\n  1150|      e.currentTarget.classList.remove('drop-above', 'drop-below', 'drop-into');\n  1151|    }\n  1152|\n  1153|    async function onDrop(e) {\n  1154|      e.preventDefault();\n  1155|      const tr = e.currentTarget;\n  1156|      tr.classList.remove('drop-above', 'drop-below', 'drop-into');\n  1157|      document.querySelectorAll('#reqTree tr').forEach(r =\u003e r.style.opacity = '');\n  1158|\n  1159|      if (!dragEntryId || dragEntryId === tr.dataset.id) return;\n  1160|\n  1161|      const targetId = tr.dataset.id;\n  1162|      const targetType = tr.dataset.type;\n  1163|      const rect = tr.getBoundingClientRect();\n  1164|      const y = e.clientY - rect.top;\n  1165|      const pct = y / rect.height;\n  1166|\n  1167|      let newParentId, position;\n  1168|\n  1169|      if (targetType === 'section' \u0026\u0026 pct \u003e 0.25 \u0026\u0026 pct \u003c 0.75) {\n  1170|        newParentId = targetId;\n  1171|        const children = treeData.filter(t =\u003e t.parent_id === targetId);\n  1172|        position = children.length;\n  1173|      } else {\n  1174|        newParentId = tr.dataset.parent;\n  1175|        const siblings = treeData.filter(t =\u003e t.parent_id === newParentId);\n  1176|        const targetIdx = siblings.findIndex(s =\u003e s.entry_id === targetId);\n  1177|        position = pct \u003c 0.5 ? targetIdx : targetIdx + 1;\n  1178|      }\n  1179|\n  1180|      try {\n  1181|        await fetchAPI('/api/projects/' + projectID + '/entries/' + dragEntryId + '/move', {\n  1182|          method: 'POST',\n  1183|          body: JSON.stringify({ parent_id: newParentId, position })\n  1184|        });\n  1185|        await loadRequestTree();\n  1186|      } catch(e) {\n  1187|        console.error('Move failed:', e);\n  1188|      }\n  1189|      dragEntryId = null;\n  1190|    }\n  1191|\n  1192|    // ---- Attach answer modal ----\n  1193|    function showAttachModalById(reqId) {\n  1194|      const item = treeData.find(t =\u003e t.entry_id === reqId);\n  1195|      const d = item ? (item.data || {}) : {};\n  1196|      const title = d.title || d.description || 'Untitled';\n  1197|      showAttachModal(reqId, title);\n  1198|    }\n  1199|\n  1200|    function showAttachModal(reqId, reqTitle) {\n  1201|      attachRequestId = reqId;\n  1202|      document.getElementById('attachReqTitle').textContent = reqTitle;\n  1203|      document.getElementById('attachModal').style.display = 'flex';\n  1204|      document.getElementById('attachSearch').value = '';\n  1205|      document.getElementById('attachFile').value = '';\n  1206|      loadAnswersForPicker('');\n  1207|      loadLinkedAnswers(reqId);\n  1208|    }\n  1209|    function closeAttachModal() { document.getElementById('attachModal').style.display = 'none'; attachRequestId = null; }\n  1210|\n  1211|    let searchTimeout;\n  1212|    function searchAnswers(q) {\n  1213|      clearTimeout(searchTimeout);\n  1214|      searchTimeout = setTimeout(() =\u003e loadAnswersForPicker(q), 200);\n  1215|    }\n  1216|\n  1217|    async function loadAnswersForPicker(q) {\n  1218|      const list = document.getElementById('attachList');\n  1219|      try {\n  1220|        const res = await fetchAPI('/api/projects/' + projectID + '/answers?q=' + encodeURIComponent(q));\n  1221|        const answers = await res.json();\n  1222|        if (!answers || answers.length === 0) {\n  1223|          list.innerHTML = '\u003cdiv class=\"text-[#b0bec5] text-sm py-2\"\u003eNo documents found.\u003c/div\u003e';\n  1224|          return;\n  1225|        }\n  1226|        list.innerHTML = answers.map(a =\u003e {\n  1227|          const d = a.data || {};\n  1228|          const name = d.filename || d.name || d.title || 'Document';\n  1229|          const objectID = d.object_id || '';\n  1230|          const previewUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '/preview?filename=' + encodeURIComponent(name) : '';\n  1231|          return `\u003cdiv class=\"flex items-center justify-between px-3 py-2 rounded-lg hover:bg-white/[0.04] transition\"\u003e\n  1232|            \u003cspan class=\"text-white text-sm truncate flex-1\"\u003e${escHtml(name)}\u003c/span\u003e\n  1233|            \u003cdiv class=\"flex items-center gap-1.5 shrink-0 ml-2\"\u003e\n  1234|              ${previewUrl ? `\u003ca href=\"${previewUrl}\" target=\"_blank\" class=\"px-2 py-1 bg-white/[0.06] text-[#b0bec5] hover:text-white rounded text-xs font-medium transition\"\u003ePreview\u003c/a\u003e` : ''}\n  1235|              \u003cbutton onclick=\"linkAnswer('${a.entry_id}')\" class=\"px-3 py-1 bg-[#c9a84c]/20 text-[#c9a84c] rounded text-xs font-medium hover:bg-[#c9a84c]/30 transition\"\u003eLink\u003c/button\u003e\n  1236|            \u003c/div\u003e\n  1237|          \u003c/div\u003e`;\n  1238|        }).join('');\n  1239|      } catch(e) {\n  1240|        list.innerHTML = '\u003cdiv class=\"text-red-400 text-sm py-2\"\u003eFailed to load.\u003c/div\u003e';\n  1241|      }\n  1242|    }\n  1243|\n  1244|    async function loadLinkedAnswers(reqId) {\n  1245|      try {\n  1246|        const res = await fetchAPI('/api/projects/' + projectID + '/requests/' + reqId + '/links');\n  1247|        const links = await res.json();\n  1248|        if (links \u0026\u0026 links.length \u003e 0) {\n  1249|          const linkedHtml = links.map(l =\u003e {\n  1250|            const d = l.data || {};\n  1251|            const name = d.filename || d.name || 'Document';\n  1252|            return `\u003cdiv class=\"flex items-center justify-between px-3 py-2 rounded-lg bg-green-500/5 border border-green-500/10\"\u003e\n  1253|              \u003cspan class=\"text-green-300 text-sm truncate flex-1\"\u003e${escHtml(name)}\u003c/span\u003e\n  1254|              \u003cbutton onclick=\"unlinkAnswer('${l.answer_id}')\" class=\"ml-2 px-3 py-1 bg-red-500/20 text-red-300 rounded text-xs font-medium hover:bg-red-500/30 transition\"\u003eUnlink\u003c/button\u003e\n  1255|            \u003c/div\u003e`;\n  1256|          }).join('');\n  1257|          const list = document.getElementById('attachList');\n  1258|          list.innerHTML = '\u003cdiv class=\"text-[#b0bec5] text-xs font-medium mb-1 mt-1\"\u003eLINKED\u003c/div\u003e' + linkedHtml + '\u003cdiv class=\"border-t border-white/[0.08] my-2\"\u003e\u003c/div\u003e\u003cdiv class=\"text-[#b0bec5] text-xs font-medium mb-1\"\u003eAVAILABLE\u003c/div\u003e' + list.innerHTML;\n  1259|        }\n  1260|      } catch(e) {}\n  1261|    }\n  1262|\n  1263|    async function linkAnswer(answerId) {\n  1264|      if (!attachRequestId) return;\n  1265|      await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links', {\n  1266|        method: 'POST', body: JSON.stringify({ answer_id: answerId })\n  1267|      });\n  1268|      closeAttachModal();\n  1269|      loadRequestTree();\n  1270|    }\n  1271|\n  1272|    async function unlinkAnswer(answerId) {\n  1273|      if (!attachRequestId) return;\n  1274|      await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links/' + answerId, { method: 'DELETE' });\n  1275|      showAttachModal(attachRequestId, document.getElementById('attachReqTitle').textContent);\n  1276|    }\n  1277|\n  1278|    async function uploadAndLink() {\n  1279|      const fileInput = document.getElementById('attachFile');\n  1280|      const file = fileInput.files[0];\n  1281|      if (!file || !attachRequestId) return;\n  1282|\n  1283|      const fd = new FormData();\n  1284|      fd.append('file', file);\n  1285|      const uploadRes = await fetch('/api/projects/' + projectID + '/objects', {\n  1286|        method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: fd\n  1287|      });\n  1288|      const uploadData = await uploadRes.json();\n  1289|      if (!uploadRes.ok) { alert('Upload failed'); return; }\n  1290|\n  1291|      const answerData = JSON.stringify({ filename: file.name, object_id: uploadData.object_id, name: file.name });\n  1292|      const entryRes = await fetchAPI('/api/projects/' + projectID + '/entries', {\n  1293|        method: 'POST',\n  1294|        body: JSON.stringify({ project_id: projectID, parent_id: projectID, type: 'answer', depth: 1, data: answerData, stage: 'dataroom' })\n  1295|      });\n  1296|      const entry = await entryRes.json();\n  1297|\n  1298|      if (entry.entry_id) {\n  1299|        await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links', {\n  1300|          method: 'POST', body: JSON.stringify({ answer_id: entry.entry_id })\n  1301|        });\n  1302|      }\n  1303|\n  1304|      closeAttachModal();\n  1305|      loadRequestTree();\n  1306|    }\n  1307|\n  1308|    // ---- Orgs + Team ----\n  1309|    // ---- Organizations ----\n  1310|    let allGlobalOrgs = [];\n  1311|\n  1312|    let _dealOrgsMap = {};\n  1313|    function imgErr(img) {\n  1314|      const w = img.parentElement;\n  1315|      w.className = 'w-11 h-11 rounded-full shrink-0 overflow-hidden bg-[#c9a84c]/20 flex items-center justify-center';\n  1316|      w.innerHTML = '\u003cspan class=\"text-[#c9a84c] font-semibold\"\u003e' + escHtml(img.dataset.initial || '?') + '\u003c/span\u003e';\n  1317|    }\n  1318|    function renderMemberCard(dealOrgId, m, mi) {\n  1319|      const initial = (m.name || m.email || '?')[0].toUpperCase();\n  1320|      let avatarHtml;\n  1321|      if (m.photo) {\n  1322|        avatarHtml = '\u003cdiv class=\"w-11 h-11 rounded-full shrink-0 overflow-hidden\"\u003e'\n  1323|          + '\u003cimg src=\"' + escHtml(m.photo) + '\" class=\"w-full h-full object-cover\" data-initial=\"' + escHtml(initial) + '\" onerror=\"imgErr(this)\"\u003e'\n  1324|          + '\u003c/div\u003e';\n  1325|      } else {\n  1326|        avatarHtml = '\u003cdiv class=\"w-11 h-11 rounded-full shrink-0 overflow-hidden bg-[#c9a84c]/20 flex items-center justify-center\"\u003e'\n  1327|          + '\u003cspan class=\"text-[#c9a84c] font-semibold\"\u003e' + escHtml(initial) + '\u003c/span\u003e\u003c/div\u003e';\n  1328|      }\n  1329|      const nameHtml = '\u003cdiv class=\"text-sm font-medium truncate\" style=\"color:var(--ds-tx)\"\u003e' + escHtml(m.name || m.email || '') + '\u003c/div\u003e';\n  1330|      const titleHtml = m.title ? '\u003cdiv class=\"text-xs truncate\" style=\"color:var(--ds-tx3)\"\u003e' + escHtml(m.title) + '\u003c/div\u003e' : '';\n  1331|      return '\u003cdiv class=\"flex items-center gap-3 cursor-pointer hover:opacity-80 transition\" onclick=\"openPersonPanel(\\'' + dealOrgId + '\\', ' + mi + ')\"\u003e'\n  1332|        + avatarHtml + '\u003cdiv class=\"min-w-0\"\u003e' + nameHtml + titleHtml + '\u003c/div\u003e\u003c/div\u003e';\n  1333|    }\n  1334|\n  1335|    async function loadOrgs() {\n  1336|      try {\n  1337|        const res = await fetchAPI('/api/projects/' + projectID + '/orgs');\n  1338|        const orgs = await res.json();\n  1339|        _dealOrgsMap = {};\n  1340|        if (Array.isArray(orgs)) orgs.forEach(o =\u003e {\n  1341|          o._raw = { org_id: o.org_id, role: o.role, domain_lock: o.domain_lock, permissions: o.permissions, members: o.members };\n  1342|          // Store org detail fields for edit modal\n  1343|          o.org_details = {\n  1344|            name: o.org_name || '', description: o.org_description || '', role: o.role || '',\n  1345|            industry: o.org_industry || '', website: o.org_website || '', phone: o.org_phone || '',\n  1346|            address: o.org_address || '', city: o.org_city || '', state: o.org_state || '',\n  1347|            founded: o.org_founded || '', linkedin: o.org_linkedin || '', logo: o.org_logo || '',\n  1348|          };\n  1349|          _dealOrgsMap[o.deal_org_id] = o;\n  1350|        });\n  1351|        const list = document.getElementById('orgList');\n  1352|        if (!orgs || orgs.length === 0) {\n  1353|          list.innerHTML = '\u003cdiv class=\"text-[#b0bec5] text-sm\"\u003eNo organizations added yet. Use \"+ Add Org\" to add parties to this deal.\u003c/div\u003e';\n  1354|          return;\n  1355|        }\n  1356|        list.innerHTML = orgs.map(o =\u003e {\n  1357|          const rc = roleColors[o.role] || 'bg-gray-500/20 text-gray-300';\n  1358|          const roleLabel = { buyer: 'Buyer', seller: 'Seller', ib: 'IB Advisor', advisor: 'Advisor' }[o.role] || o.role || '?';\n  1359|          const perms = o.permissions || {};\n  1360|          const members = o.members || [];\n  1361|          const dlBadge = { full: '📥 Full download', watermark: '📥 Watermarked', none: '🚫 No download' }[perms.download] || '📥 Watermarked';\n  1362|          const orgDesc = o.org_description || '';\n  1363|          const orgLogo = o.org_logo || '';\n  1364|          const orgIndustry = o.org_industry || '';\n  1365|          return `\u003cdiv class=\"rounded-2xl overflow-hidden\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd)\"\u003e\n  1366|            \u003c!-- Org header --\u003e\n  1367|            \u003cdiv class=\"px-6 py-5\"\u003e\n  1368|              \u003cdiv class=\"flex items-start gap-4\"\u003e\n  1369|                ${orgLogo ? `\u003cimg src=\"${escHtml(orgLogo)}\" class=\"w-14 h-14 rounded-xl object-contain shrink-0 bg-white p-1.5\" onerror=\"this.style.display='none'\"\u003e` : `\u003cdiv class=\"w-14 h-14 rounded-xl shrink-0 flex items-center justify-center text-2xl font-black\" style=\"background:var(--ds-ac);color:var(--ds-act)\"\u003e${escHtml((o.org_name||'?')[0])}\u003c/div\u003e`}\n  1370|                \u003cdiv class=\"flex-1 min-w-0\"\u003e\n  1371|                  \u003cdiv class=\"flex items-center gap-3\"\u003e\n  1372|                    \u003ch2 class=\"text-2xl font-black tracking-tight\" style=\"color:var(--ds-tx)\"\u003e${escHtml(o.org_name || 'Unknown')}\u003c/h2\u003e\n  1373|                    \u003cspan class=\"px-2.5 py-1 rounded-full text-xs font-semibold ${rc}\"\u003e${roleLabel}\u003c/span\u003e\n  1374|                  \u003c/div\u003e\n  1375|                  ${orgDesc ? `\u003cp class=\"text-sm mt-1 line-clamp-2\" style=\"color:var(--ds-tx2)\"\u003e${escHtml(orgDesc)}\u003c/p\u003e` : ''}\n  1376|                  \u003cdiv class=\"flex flex-wrap gap-3 text-xs mt-2\" style=\"color:var(--ds-tx3)\"\u003e\n  1377|                    \u003cspan\u003e${dlBadge}\u003c/span\u003e\n  1378|                    ${perms.upload ? '\u003cspan\u003e📤 Upload\u003c/span\u003e' : ''}\n  1379|                    \u003cspan\u003e${(() =\u003e {\n  1380|                      const vl = perms.visible_lists;\n  1381|                      if (!vl || vl === 'all') return '📂 All lists';\n  1382|                      if (Array.isArray(vl) \u0026\u0026 vl.length === 0) return '📂 No lists';\n  1383|                      if (Array.isArray(vl)) return '📂 ' + vl.length + ' list' + (vl.length \u003e 1 ? 's' : '');\n  1384|                      return '📂 All lists';\n  1385|                    })()}\u003c/span\u003e\n  1386|                  \u003c/div\u003e\n  1387|                \u003c/div\u003e\n  1388|                \u003cdiv class=\"flex items-center gap-2 shrink-0\"\u003e\n  1389|                  \u003cbutton onclick=\"openPermModalById('${o.deal_org_id}')\"\n  1390|                    class=\"px-3 py-1.5 rounded-lg text-xs font-medium transition\" style=\"background:var(--ds-hv);color:var(--ds-tx2)\"\u003ePermissions\u003c/button\u003e\n  1391|                  \u003cdiv class=\"relative\" id=\"orgMenu_${o.deal_org_id}\"\u003e\n  1392|                    \u003cbutton onclick=\"toggleOrgMenu('${o.deal_org_id}')\"\n  1393|                      class=\"px-2 py-1.5 rounded-lg text-sm font-medium transition\" style=\"background:var(--ds-hv);color:var(--ds-tx2)\" title=\"More options\"\u003e⋯\u003c/button\u003e\n  1394|                    \u003cdiv id=\"orgMenuDrop_${o.deal_org_id}\" class=\"hidden absolute right-0 top-full mt-1 rounded-lg shadow-xl z-30 py-1 min-w-[160px]\" style=\"background:var(--ds-card);border:1px solid var(--ds-bd)\"\u003e\n  1395|                      \u003cbutton onclick=\"openEditOrgModal('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')\"\n  1396|                        class=\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\" style=\"color:var(--ds-tx)\"\u003eEdit details\u003c/button\u003e\n  1397|                      \u003cbutton onclick=\"openPermModalById('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')\"\n  1398|                        class=\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\" style=\"color:var(--ds-tx)\"\u003ePermissions\u003c/button\u003e\n  1399|                      \u003cdiv style=\"height:1px;background:var(--ds-bd);margin:4px 0\"\u003e\u003c/div\u003e\n  1400|                      \u003cbutton onclick=\"removeDealOrg('${o.deal_org_id}', '${escHtml(orgName)}')\"\n  1401|                        class=\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\" style=\"color:#ef4444\"\u003eRemove from deal\u003c/button\u003e\n  1402|                    \u003c/div\u003e\n  1403|                  \u003c/div\u003e\n  1404|                \u003c/div\u003e\n  1405|              \u003c/div\u003e\n  1406|            \u003c/div\u003e\n  1407|            \u003c!-- Members --\u003e\n  1408|            \u003cdiv class=\"border-t px-6 py-4\" style=\"border-color:var(--ds-bd)\"\u003e\n  1409|              \u003cdiv class=\"grid gap-3\" style=\"grid-template-columns: repeat(auto-fill, minmax(180px, 1fr))\"\u003e\n  1410|                ${members.map((m, mi) =\u003e renderMemberCard(o.deal_org_id, m, mi)).join('')}\n  1411|                \u003cdiv class=\"flex items-center gap-3 cursor-pointer hover:opacity-80 transition\" onclick=\"openDealMemberPicker('' + o.deal_org_id + '', '' + escHtml(o.org_id) + '', '' + escHtml(o.org_name||'') + '')\"\u003e\n  1412|                  \u003cdiv class=\"w-11 h-11 rounded-full shrink-0 flex items-center justify-center text-xl\" style=\"background:var(--ds-hv);color:var(--ds-tx2)\"\u003e+\u003c/div\u003e\n  1413|                  \u003cspan class=\"text-sm\" style=\"color:var(--ds-tx3)\"\u003eAdd person\u003c/span\u003e\n  1414|                \u003c/div\u003e\n  1415|              \u003c/div\u003e\n  1416|            \u003c/div\u003e\n  1417|          \u003c/div\u003e`;\n  1418|        }).join('');\n  1419|      } catch(e) { console.error(e); }\n  1420|    }\n  1421|\n  1422|    // ---- Add Party Modal (3-step scrape flow) ----\n  1423|    let scrapedData = null;\n  1424|\n  1425|    function resetAddOrgForm() {\n  1426|      scrapedData = null;\n  1427|      // Step 1\n  1428|      document.getElementById('addOrgEmail').value = '';\n  1429|      document.getElementById('scrapeError').classList.add('hidden');\n  1430|      document.getElementById('scrapeLoading').classList.add('hidden');\n  1431|      document.getElementById('scrapeBtn').disabled = false;\n  1432|      // Hide wizard-only fields\n  1433|      document.getElementById('wizProjectNameWrap').classList.add('hidden');\n  1434|      document.getElementById('wizProjectName').value = '';\n  1435|      // Step 2 — clear all org fields\n  1436|      const clearIds = ['orgName','orgDesc','orgRole','orgIndustry','orgWebsite','orgPhone',\n  1437|                        'orgAddress','orgCity','orgState','orgFounded','orgLinkedIn'];\n  1438|      clearIds.forEach(id =\u003e { const el = document.getElementById(id); if (el) el.value = ''; });\n  1439|      document.getElementById('orgLogoWrap').classList.add('hidden');\n  1440|      document.getElementById('orgLogoImg').src = '';\n  1441|      // Step 3 — clear people\n  1442|      const pl = document.getElementById('peopleList'); if (pl) pl.innerHTML = '';\n  1443|      const np = document.getElementById('noPeople'); if (np) np.classList.remove('hidden');\n  1444|      // Required field errors\n  1445|      requiredFields.forEach(id =\u003e document.getElementById(id).classList.remove('field-error'));\n  1446|    }\n  1447|\n  1448|    function openAddOrgModal() {\n  1449|      resetAddOrgForm();\n  1450|      document.getElementById('addOrgTitle').textContent = 'Add Party';\n  1451|      document.getElementById('addOrgHint').textContent = \"Enter an email address or domain name \\u2014 we'll look up the organization automatically.\";\n  1452|      document.getElementById('addOrgSubmitBtn').textContent = 'Add to Deal';\n  1453|      showAddOrgStep(1);\n  1454|      document.getElementById('addOrgModal').classList.remove('hidden');\n  1455|      setTimeout(() =\u003e document.getElementById('addOrgEmail').focus(), 100);\n  1456|    }\n  1457|\n  1458|    function switchProject(pid) {\n  1459|      document.getElementById('projectMenu').classList.add('hidden');\n  1460|      if (pid \u0026\u0026 pid !== projectID) window.location.href = '/app/projects/' + pid;\n  1461|    }\n  1462|    function toggleProjectMenu() {\n  1463|      document.getElementById('projectMenu').classList.toggle('hidden');\n  1464|    }\n  1465|    document.addEventListener('click', e =\u003e {\n  1466|      if (!document.getElementById('projectSwitcher')?.contains(e.target))\n  1467|        document.getElementById('projectMenu')?.classList.add('hidden');\n  1468|    });\n  1469|\n  1470|    function tmplChanged(el) {\n  1471|      if (el.value === 'none' \u0026\u0026 el.checked) {\n  1472|        // \"Start empty\" unchecks the others\n  1473|        document.querySelectorAll('.tmpl-cb').forEach(cb =\u003e { if (cb.value !== 'none') cb.checked = false; });\n  1474|      } else if (el.value !== 'none' \u0026\u0026 el.checked) {\n  1475|        // Selecting a template unchecks \"Start empty\"\n  1476|        document.querySelector('.tmpl-cb[value=\"none\"]').checked = false;\n  1477|      }\n  1478|    }\n  1479|\n  1480|    // ---- New Project Wizard: Create Everything ----\n  1481|    async function createNewProject() {\n  1482|      const btn = document.getElementById('wizCreateBtn');\n  1483|      const projectName = document.getElementById('wizProjectName').value.trim();\n  1484|      const wizErr = document.getElementById('wizError');\n  1485|      if (wizErr) wizErr.classList.add('hidden');\n  1486|      const projNameEl = document.getElementById('wizProjectName');\n  1487|      if (!projectName) {\n  1488|        if (projNameEl) projNameEl.classList.add('field-error');\n  1489|        if (wizErr) { wizErr.textContent = 'Deal name is required.'; wizErr.classList.remove('hidden'); }\n  1490|        return;\n  1491|      }\n  1492|      if (projNameEl) projNameEl.classList.remove('field-error');\n  1493|      if (!validateStep2()) {\n  1494|        if (wizErr) { wizErr.textContent = 'Please fill in all required org fields. Go back to step 2.'; wizErr.classList.remove('hidden'); }\n  1495|        return;\n  1496|      }\n  1497|\n  1498|      btn.disabled = true; btn.textContent = 'Creating...';\n  1499|\n  1500|      try {\n  1501|        // 1. Create project\n  1502|        const projRes = await fetchAPI('/api/projects', {\n  1503|          method: 'POST',\n  1504|          body: JSON.stringify({ name: projectName, deal_type: 'sell_side' })\n  1505|        });\n  1506|        const projData = await projRes.json();\n  1507|        if (!projRes.ok) throw new Error(projData.error || 'Failed to create project');\n  1508|        const newProjectID = projData.project_id || projData.entry_id;\n  1509|\n  1510|        // 2. Add org + members to project\n  1511|        const name = document.getElementById('orgName').value.trim();\n  1512|        const role = document.getElementById('orgRole').value;\n  1513|        // Derive domain: prefer scrapedData.domain, fallback to website field\n  1514|      let domain = scrapedData?.domain || '';\n  1515|      if (!domain) {\n  1516|        const ws = document.getElementById('orgWebsite').value.trim();\n  1517|        if (ws) {\n  1518|          domain = ws.replace(/^https?:\\/\\//, '').replace(/^www\\./, '').split('/')[0];\n  1519|        }\n  1520|      }\n  1521|        const people = scrapedData?.people || [];\n  1522|        const selectedMembers = [];\n  1523|        document.querySelectorAll('.person-cb:checked').forEach(cb =\u003e {\n  1524|          const p = people[parseInt(cb.dataset.idx)];\n  1525|          if (p) selectedMembers.push({ name: p.name, email: p.email || '', title: p.title || '', phone: p.phone || '', photo: p.photo || '', bio: p.bio || '', linkedin: p.linkedin || '' });\n  1526|        });\n  1527|\n  1528|        const orgRes = await fetchAPI('/api/projects/' + newProjectID + '/orgs/add', {\n  1529|          method: 'POST',\n  1530|          body: JSON.stringify({\n  1531|            name,\n  1532|            domains: domain ? [domain] : [],\n  1533|            role,\n  1534|            logo: scrapedData?.logo || '',\n  1535|            website: document.getElementById('orgWebsite').value,\n  1536|            description: document.getElementById('orgDesc').value,\n  1537|            industry: document.getElementById('orgIndustry').value,\n  1538|            phone: document.getElementById('orgPhone').value,\n  1539|            address: document.getElementById('orgAddress').value,\n  1540|            city: document.getElementById('orgCity').value,\n  1541|            state: document.getElementById('orgState').value,\n  1542|            founded: document.getElementById('orgFounded').value,\n  1543|            linkedin: document.getElementById('orgLinkedIn').value,\n  1544|            members: selectedMembers,\n  1545|            domain_lock: true,\n  1546|          })\n  1547|        });\n  1548|        if (!orgRes.ok) { const e = await orgRes.json(); throw new Error(e.error || 'Failed to add org'); }\n  1549|\n  1550|        // 3. Import selected request list templates\n  1551|        const selectedTemplates = [...document.querySelectorAll('.tmpl-cb:checked')].map(cb =\u003e cb.value).filter(v =\u003e v !== 'none');\n  1552|        for (const tmpl of selectedTemplates) {\n  1553|          try {\n  1554|            await fetchAPI('/api/projects/' + newProjectID + '/requests/import-template', {\n  1555|              method: 'POST',\n  1556|              body: JSON.stringify({ template_id: tmpl })\n  1557|            });\n  1558|          } catch(e) { console.error('Template import error:', tmpl, e); }\n  1559|        }\n  1560|\n  1561|        // 4. Show email preview and redirect\n  1562|        closeAddOrgModal();\n  1563|        if (selectedMembers.length \u003e 0) {\n  1564|          showEmailPreview(selectedMembers, name);\n  1565|        }\n  1566|        // Redirect to the new project\n  1567|        window.location.href = '/app/projects/' + newProjectID;\n  1568|\n  1569|      } catch(e) {\n  1570|        alert(e.message);\n  1571|        btn.disabled = false;\n  1572|        btn.textContent = 'Create Project';\n  1573|      }\n  1574|    }\n  1575|\n  1576|    // ---- Email Preview ----\n  1577|    const EMAIL_WHITELIST = ['@muskepo.com', '@jongsma.me'];\n  1578|    function isWhitelisted(email) {\n  1579|      return EMAIL_WHITELIST.some(d =\u003e email.toLowerCase().endsWith(d));\n  1580|    }\n  1581|\n  1582|    function showEmailPreview(members, orgName) {\n  1583|      const list = document.getElementById('emailPreviewList');\n  1584|      if (!members.length) { return; }\n  1585|      list.innerHTML = members.map(m =\u003e {\n  1586|        const whitelisted = m.email \u0026\u0026 isWhitelisted(m.email);\n  1587|        return `\u003cdiv class=\"flex items-center gap-3 p-3 rounded-lg\" style=\"background:var(--ds-hv)\"\u003e\n  1588|          \u003cdiv class=\"flex-1 min-w-0\"\u003e\n  1589|            \u003cdiv class=\"text-sm font-medium\" style=\"color:var(--ds-tx)\"\u003e${escHtml(m.name || m.email || '?')}\u003c/div\u003e\n  1590|            \u003cdiv class=\"text-xs\" style=\"color:var(--ds-tx2)\"\u003e${escHtml(m.email || 'no email')}\u003c/div\u003e\n  1591|          \u003c/div\u003e\n  1592|          ${m.email ? (whitelisted\n  1593|            ? '\u003cspan class=\"text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-300 shrink-0\"\u003eWill send\u003c/span\u003e'\n  1594|            : '\u003cspan class=\"text-xs px-2 py-0.5 rounded-full bg-yellow-500/20 text-yellow-300 shrink-0\"\u003eBlocked (not whitelisted)\u003c/span\u003e'\n  1595|          ) : '\u003cspan class=\"text-xs px-2 py-0.5 rounded-full bg-gray-500/20 text-gray-300 shrink-0\"\u003eNo email\u003c/span\u003e'}\n  1596|        \u003c/div\u003e`;\n  1597|      }).join('');\n  1598|      document.getElementById('emailPreviewModal').classList.remove('hidden');\n  1599|    }\n  1600|\n  1601|    function closeEmailPreview() {\n  1602|      document.getElementById('emailPreviewModal').classList.add('hidden');\n  1603|    }\n  1604|\n  1605|    function closeAddOrgModal() {\n  1606|      document.getElementById('addOrgModal').classList.add('hidden');\n  1607|      resetAddOrgForm();\n  1608|    }\n  1609|\n  1610|    function showAddOrgStep(n) {\n  1611|      document.getElementById('addOrgStep1').classList.toggle('hidden', n !== 1);\n  1612|      document.getElementById('addOrgStep2').classList.toggle('hidden', n !== 2);\n  1613|      document.getElementById('addOrgStep3').classList.toggle('hidden', n !== 3);\n  1614|      document.getElementById('addOrgStep4').classList.toggle('hidden', n !== 4);\n  1615|      // Clear error banners when navigating\n  1616|      ['step2Error','step3Error','wizError'].forEach(id =\u003e { const el=document.getElementById(id); if(el) el.classList.add('hidden'); });\n  1617|      if (n === 3) renderPeople();\n  1618|      if (n === 4) {\n  1619|        // Pre-fill project name from org name\n  1620|        const orgName = document.getElementById('orgName').value.trim();\n  1621|        document.getElementById('wizProjectName').value = orgName;\n  1622|      }\n  1623|    }\n  1624|\n  1625|    const requiredFields = ['orgName', 'orgRole'];\n  1626|\n  1627|    function highlightRequired() {\n  1628|      requiredFields.forEach(id =\u003e {\n  1629|        const el = document.getElementById(id);\n  1630|        if (!el.value.trim()) {\n  1631|          el.classList.add('field-error');\n  1632|        } else {\n  1633|          el.classList.remove('field-error');\n  1634|        }\n  1635|      });\n  1636|    }\n  1637|\n  1638|    function validateStep2() {\n  1639|      highlightRequired();\n  1640|      return requiredFields.every(id =\u003e document.getElementById(id).value.trim());\n  1641|    }\n  1642|\n  1643|    function showStep2Error(msg) {\n  1644|      const el = document.getElementById('step2Error');\n  1645|      if (!el) return;\n  1646|      el.textContent = msg || 'Please fill in all required fields.';\n  1647|      el.classList.remove('hidden');\n  1648|    }\n  1649|\n  1650|    function hideStep2Error() {\n  1651|      const el = document.getElementById('step2Error');\n  1652|      if (el) el.classList.add('hidden');\n  1653|    }\n  1654|\n  1655|    function advanceFromStep2() {\n  1656|      if (!validateStep2()) {\n  1657|        showStep2Error('Please fill in all required fields (highlighted in red).');\n  1658|        return;\n  1659|      }\n  1660|      hideStep2Error();\n  1661|      showAddOrgStep(3);\n  1662|    }\n  1663|\n  1664|    // Clear red border on input/change\n  1665|    [...requiredFields, 'wizProjectName'].forEach(id =\u003e {\n  1666|      const el = document.getElementById(id);\n  1667|      if (el) {\n  1668|        const clear = () =\u003e el.classList.remove('field-error');\n  1669|        el.addEventListener('input', clear);\n  1670|        el.addEventListener('change', clear);\n  1671|      }\n  1672|    });\n  1673|\n  1674|    function ensureEmailPerson(email) {\n  1675|      if (!scrapedData) scrapedData = { domain: '', people: [] };\n  1676|      if (!scrapedData.people) scrapedData.people = [];\n  1677|      const found = scrapedData.people.find(p =\u003e p.email \u0026\u0026 p.email.toLowerCase() === email.toLowerCase());\n  1678|      if (!found) {\n  1679|        scrapedData.people.unshift({ name: '', email: email, title: '', phone: '', photo: '', bio: '', linkedin: '' });\n  1680|      }\n  1681|    }\n  1682|\n  1683|    function skipScrape() {\n  1684|      const input = document.getElementById('addOrgEmail').value.trim();\n  1685|      const isEmail = input.includes('@');\n  1686|      const domain = isEmail ? input.split('@')[1] : input.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');\n  1687|      const people = isEmail ? [{ name: '', email: input, title: '', phone: '', photo: '', bio: '', linkedin: '' }] : [];\n  1688|      scrapedData = { domain, people };\n  1689|      document.getElementById('orgLogoWrap').classList.add('hidden');\n  1690|      document.getElementById('orgName').value = '';\n  1691|      document.getElementById('orgDesc').value = '';\n  1692|      document.getElementById('orgIndustry').value = '';\n  1693|      document.getElementById('orgWebsite').value = domain ? 'https://' + domain : '';\n  1694|      document.getElementById('orgPhone').value = '';\n  1695|      document.getElementById('orgAddress').value = '';\n  1696|      document.getElementById('orgCity').value = '';\n  1697|      document.getElementById('orgState').value = '';\n  1698|      document.getElementById('orgFounded').value = '';\n  1699|      document.getElementById('orgLinkedIn').value = '';\n  1700|      highlightRequired();\n  1701|      showAddOrgStep(2);\n  1702|    }\n  1703|\n  1704|    async function scrapeOrg() {\n  1705|      let input = document.getElementById('addOrgEmail').value.trim();\n  1706|      if (!input) {\n  1707|        document.getElementById('scrapeErrorMsg').textContent = 'Enter an email address or domain name.';\n  1708|        document.getElementById('scrapeError').classList.remove('hidden');\n  1709|        return;\n  1710|      }\n  1711|      // If no @, treat as domain — fabricate an email for the API\n  1712|      const email = input.includes('@') ? input : 'info@' + input.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '');\n  1713|      document.getElementById('scrapeError').classList.add('hidden');\n  1714|      document.getElementById('scrapeLoading').classList.remove('hidden');\n  1715|      document.getElementById('scrapeBtn').disabled = true;\n  1716|      try {\n  1717|        const res = await fetchAPI('/api/scrape/org', { method: 'POST', body: JSON.stringify({ email }) });\n  1718|        const data = await res.json();\n  1719|        if (!res.ok) throw new Error(data.error || 'Scrape failed');\n  1720|        scrapedData = data;\n  1721|        if (data.logo) {\n  1722|          document.getElementById('orgLogoImg').src = data.logo;\n  1723|          document.getElementById('orgLogoWrap').classList.remove('hidden');\n  1724|        } else {\n  1725|          document.getElementById('orgLogoWrap').classList.add('hidden');\n  1726|        }\n  1727|        document.getElementById('orgName').value = data.name || '';\n  1728|        document.getElementById('orgDesc').value = data.description || '';\n  1729|        document.getElementById('orgIndustry').value = data.industry || '';\n  1730|        document.getElementById('orgWebsite').value = data.website || '';\n  1731|        document.getElementById('orgPhone').value = data.phone || '';\n  1732|        document.getElementById('orgAddress').value = data.address || '';\n  1733|        document.getElementById('orgCity').value = data.city || '';\n  1734|        document.getElementById('orgState').value = data.state || '';\n  1735|        document.getElementById('orgFounded').value = data.founded || '';\n  1736|        document.getElementById('orgLinkedIn').value = data.linkedin || '';\n  1737|        // Ensure the original email person is in the people list (only if real email, not fabricated)\n  1738|        if (input.includes('@')) ensureEmailPerson(email);\n  1739|        highlightRequired();\n  1740|        showAddOrgStep(2);\n  1741|      } catch (err) {\n  1742|        document.getElementById('scrapeErrorMsg').textContent = err.message;\n  1743|        document.getElementById('scrapeError').classList.remove('hidden');\n  1744|      } finally {\n  1745|        document.getElementById('scrapeLoading').classList.add('hidden');\n  1746|        document.getElementById('scrapeBtn').disabled = false;\n  1747|      }\n  1748|    }\n  1749|\n  1750|    function renderPeople() {\n  1751|      try {\n  1752|      const people = scrapedData?.people || [];\n  1753|      const list = document.getElementById('peopleList');\n  1754|      const none = document.getElementById('noPeople');\n  1755|      if (!list) { console.error('peopleList element not found'); return; }\n  1756|      if (people.length === 0) { list.innerHTML = ''; if(none) none.classList.remove('hidden'); return; }\n  1757|      if(none) none.classList.add('hidden');\n  1758|      list.innerHTML = people.map((p, i) =\u003e {\n  1759|        const hasDetail = p.name \u0026\u0026 p.title;\n  1760|        return `\n  1761|        \u003cdiv class=\"flex items-center gap-3 p-3 rounded-xl bg-white/[0.03] border border-white/[0.06] transition\"\u003e\n  1762|          \u003cinput type=\"checkbox\" class=\"person-cb accent-[#c9a84c] rounded shrink-0\" data-idx=\"${i}\" checked onchange=\"updateSelectedCount()\"\u003e\n  1763|          \u003cdiv class=\"w-10 h-10 rounded-full bg-[#0a1628] shrink-0 overflow-hidden border border-white/[0.08]\"\u003e\n  1764|            ${p.photo ? `\u003cimg src=\"${escHtml(p.photo)}\" class=\"w-full h-full object-cover\" onerror=\"this.parentElement.innerHTML='\u003cdiv class=\\\\'w-full h-full flex items-center justify-center text-[#c9a84c] font-semibold\\\\'\u003e${escHtml((p.name||'?')[0])}\u003c/div\u003e'\"\u003e` : `\u003cdiv class=\"w-full h-full flex items-center justify-center text-[#c9a84c] font-semibold\"\u003e${escHtml((p.name||'?')[0])}\u003c/div\u003e`}\n  1765|          \u003c/div\u003e\n  1766|          \u003cdiv class=\"flex-1 min-w-0 grid grid-cols-3 gap-2\"\u003e\n  1767|            \u003cinput type=\"text\" value=\"${escHtml(p.name)}\" placeholder=\"Name\" data-idx=\"${i}\" data-field=\"name\" onchange=\"updatePerson(this)\"\n  1768|              class=\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-white text-sm focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\"\u003e\n  1769|            \u003cinput type=\"text\" value=\"${escHtml(p.email)}\" placeholder=\"Email\" data-idx=\"${i}\" data-field=\"email\" onchange=\"updatePerson(this)\"\n  1770|              class=\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-[#b0bec5] text-xs focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\"\u003e\n  1771|            \u003cinput type=\"text\" value=\"${escHtml(p.title)}\" placeholder=\"Title\" data-idx=\"${i}\" data-field=\"title\" onchange=\"updatePerson(this)\"\n  1772|              class=\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-[#b0bec5] text-xs focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\"\u003e\n  1773|          \u003c/div\u003e\n  1774|        \u003c/div\u003e`;\n  1775|      }).join('');\n  1776|      const sa = document.getElementById('selectAllPeople');\n  1777|      if(sa) sa.checked = true;\n  1778|      updateSelectedCount();\n  1779|      } catch(err) { alert('renderPeople error: ' + err.message + '\\n' + err.stack); }\n  1780|    }\n  1781|\n  1782|    function updatePerson(el) {\n  1783|      const idx = parseInt(el.dataset.idx);\n  1784|      const field = el.dataset.field;\n  1785|      if (scrapedData?.people?.[idx]) {\n  1786|        scrapedData.people[idx][field] = el.value.trim();\n  1787|      }\n  1788|    }\n  1789|\n  1790|    function addManualPerson() {\n  1791|      try {\n  1792|        const name = document.getElementById('manualName').value.trim();\n  1793|        const email = document.getElementById('manualEmail').value.trim();\n  1794|        const title = document.getElementById('manualTitle').value.trim();\n  1795|        if (!name \u0026\u0026 !email) {\n  1796|          const nameEl = document.getElementById('manualName');\n  1797|          nameEl.classList.add('field-error');\n  1798|          nameEl.placeholder = 'Enter a name or email';\n  1799|          setTimeout(() =\u003e { nameEl.classList.remove('field-error'); nameEl.placeholder = 'Name'; }, 2500);\n  1800|          nameEl.focus();\n  1801|          return;\n  1802|        }\n  1803|        if (!scrapedData) scrapedData = { domain: '', people: [] };\n  1804|        if (!scrapedData.people) scrapedData.people = [];\n  1805|        scrapedData.people.push({ name: name || email, email, title, phone: '', photo: '', bio: '', linkedin: '' });\n  1806|        document.getElementById('manualName').value = '';\n  1807|        document.getElementById('manualEmail').value = '';\n  1808|        document.getElementById('manualTitle').value = '';\n  1809|        renderPeople();\n  1810|        document.getElementById('manualName').focus();\n  1811|      } catch(err) {\n  1812|        alert('addManualPerson error: ' + err.message + '\\n' + err.stack);\n  1813|      }\n  1814|    }\n  1815|\n  1816|    function toggleAllPeople(checked) {\n  1817|      document.querySelectorAll('.person-cb').forEach(cb =\u003e cb.checked = checked);\n  1818|      updateSelectedCount();\n  1819|    }\n  1820|\n  1821|    function updateSelectedCount() {\n  1822|      const checked = document.querySelectorAll('.person-cb:checked').length;\n  1823|      const total = document.querySelectorAll('.person-cb').length;\n  1824|      document.getElementById('selectedCount').textContent = checked + ' of ' + total + ' selected';\n  1825|      document.getElementById('selectAllPeople').checked = (checked === total);\n  1826|    }\n  1827|\n  1828|    function proceedFromStep3() {\n  1829|      if (_wizardMode) {\n  1830|        showAddOrgStep(4);\n  1831|      } else {\n  1832|        submitAddOrg();\n  1833|      }\n  1834|    }\n  1835|\n  1836|    async function submitAddOrg() {\n  1837|      const s3err = document.getElementById('step3Error');\n  1838|      if (s3err) s3err.classList.add('hidden');\n  1839|      if (!validateStep2()) {\n  1840|        if (s3err) { s3err.textContent = 'Required fields missing: go back and fill in Name and Role.'; s3err.classList.remove('hidden'); }\n  1841|        return;\n  1842|      }\n  1843|      const name = document.getElementById('orgName').value.trim();\n  1844|      const role = document.getElementById('orgRole').value;\n  1845|\n  1846|      // Derive domain: prefer scrapedData.domain, fallback to website field\n  1847|      let domain = scrapedData?.domain || '';\n  1848|      if (!domain) {\n  1849|        const ws = document.getElementById('orgWebsite').value.trim();\n  1850|        if (ws) {\n  1851|          domain = ws.replace(/^https?:\\/\\//, '').replace(/^www\\./, '').split('/')[0];\n  1852|        }\n  1853|      }\n  1854|      const people = scrapedData?.people || [];\n  1855|      const selectedMembers = [];\n  1856|      document.querySelectorAll('.person-cb:checked').forEach(cb =\u003e {\n  1857|        const p = people[parseInt(cb.dataset.idx)];\n  1858|        if (p) selectedMembers.push({ name: p.name, email: p.email || '', title: p.title || '', phone: p.phone || '', photo: p.photo || '', bio: p.bio || '', linkedin: p.linkedin || '' });\n  1859|      });\n  1860|\n  1861|      const btn = document.getElementById('addOrgSubmitBtn');\n  1862|      btn.disabled = true; btn.textContent = 'Adding...';\n  1863|\n  1864|      try {\n  1865|        const res = await fetchAPI('/api/projects/' + projectID + '/orgs/add', {\n  1866|          method: 'POST',\n  1867|          body: JSON.stringify({\n  1868|            name,\n  1869|            domains: domain ? [domain] : [],\n  1870|            role,\n  1871|            logo: scrapedData?.logo || '',\n  1872|            website: document.getElementById('orgWebsite').value,\n  1873|            description: document.getElementById('orgDesc').value,\n  1874|            industry: document.getElementById('orgIndustry').value,\n  1875|            phone: document.getElementById('orgPhone').value,\n  1876|            address: document.getElementById('orgAddress').value,\n  1877|            city: document.getElementById('orgCity').value,\n  1878|            state: document.getElementById('orgState').value,\n  1879|            founded: document.getElementById('orgFounded').value,\n  1880|            linkedin: document.getElementById('orgLinkedIn').value,\n  1881|            members: selectedMembers,\n  1882|            domain_lock: true,\n  1883|          })\n  1884|        });\n  1885|        const data = await res.json();\n  1886|        if (!res.ok) throw new Error(data.error || 'Failed to add organization');\n  1887|        closeAddOrgModal();\n  1888|        loadOrgs();\n  1889|        // Show email preview if there are members with emails\n  1890|        if (selectedMembers.length \u003e 0) {\n  1891|          showEmailPreview(selectedMembers, name);\n  1892|        }\n  1893|      } catch (err) { alert(err.message); }\n  1894|      finally { btn.disabled = false; btn.textContent = 'Add to Deal'; }\n  1895|    }\n  1896|\n  1897|    // ---- Person Panel ----\n  1898|    function openPersonPanel(dealOrgId, memberIdx) {\n  1899|      const o = _dealOrgsMap[dealOrgId];\n  1900|      if (!o) return;\n  1901|      const members = o.members || [];\n  1902|      const isNew = memberIdx \u003c 0;\n  1903|      const m = isNew ? { name: '', email: '', title: '', phone: '', photo: '', bio: '', linkedin: '' } : members[memberIdx];\n  1904|      if (!m) return;\n  1905|\n  1906|      document.getElementById('ppDealOrgId').value = dealOrgId;\n  1907|      document.getElementById('ppMemberIdx').value = memberIdx;\n  1908|      document.getElementById('ppName').value = m.name || '';\n  1909|      document.getElementById('ppEmail').value = m.email || '';\n  1910|      document.getElementById('ppTitle').value = m.title || '';\n  1911|      document.getElementById('ppPhone').value = m.phone || '';\n  1912|      document.getElementById('ppPhoto').value = m.photo || '';\n  1913|      document.getElementById('ppLinkedIn').value = m.linkedin || '';\n  1914|      document.getElementById('ppBio').value = m.bio || '';\n  1915|      document.getElementById('personPanelTitle').textContent = isNew ? 'Add Person' : 'Edit Person';\n  1916|      document.getElementById('ppRemoveBtn').classList.toggle('hidden', isNew);\n  1917|\n  1918|      previewPersonPhoto();\n  1919|      document.getElementById('ppPhotoUrlRow').classList.toggle('hidden', !!m.photo);\n  1920|\n  1921|      document.getElementById('personOverlay').classList.remove('hidden');\n  1922|      document.getElementById('personPanel').classList.remove('translate-x-full');\n  1923|    }\n  1924|\n  1925|    function closePersonPanel() {\n  1926|      document.getElementById('personPanel').classList.add('translate-x-full');\n  1927|      document.getElementById('personOverlay').classList.add('hidden');\n  1928|    }\n  1929|\n  1930|    function previewPersonPhoto() {\n  1931|      const url = document.getElementById('ppPhoto').value.trim();\n  1932|      const img = document.getElementById('ppPhotoImg');\n  1933|      const letter = document.getElementById('ppPhotoLetter');\n  1934|      if (url) {\n  1935|        img.src = url;\n  1936|        img.classList.remove('hidden');\n  1937|        img.onerror = () =\u003e { img.classList.add('hidden'); letter.classList.remove('hidden'); };\n  1938|        letter.classList.add('hidden');\n  1939|      } else {\n  1940|        img.classList.add('hidden');\n  1941|        letter.classList.remove('hidden');\n  1942|        const name = document.getElementById('ppName').value || document.getElementById('ppEmail').value || '?';\n  1943|        letter.textContent = name[0].toUpperCase();\n  1944|      }\n  1945|    }\n  1946|\n  1947|    async function savePersonPanel() {\n  1948|      const dealOrgId = document.getElementById('ppDealOrgId').value;\n  1949|      const memberIdx = parseInt(document.getElementById('ppMemberIdx').value);\n  1950|      const o = _dealOrgsMap[dealOrgId];\n  1951|      if (!o) return;\n  1952|\n  1953|      const person = {\n  1954|        name: document.getElementById('ppName').value.trim(),\n  1955|        email: document.getElementById('ppEmail').value.trim(),\n  1956|        title: document.getElementById('ppTitle').value.trim(),\n  1957|        phone: document.getElementById('ppPhone').value.trim(),\n  1958|        photo: document.getElementById('ppPhoto').value.trim(),\n  1959|        linkedin: document.getElementById('ppLinkedIn').value.trim(),\n  1960|        bio: document.getElementById('ppBio').value.trim(),\n  1961|      };\n  1962|\n  1963|      if (!person.name \u0026\u0026 !person.email) return;\n  1964|\n  1965|      const members = [...(o.members || [])];\n  1966|      if (memberIdx \u003c 0) {\n  1967|        members.push(person);\n  1968|      } else {\n  1969|        members[memberIdx] = person;\n  1970|      }\n  1971|\n  1972|      try {\n  1973|        await fetchAPI('/api/projects/' + projectID + '/entries/' + dealOrgId, {\n  1974|          method: 'PUT',\n  1975|          body: JSON.stringify({\n  1976|            data: JSON.stringify({ ...o._raw, members }),\n  1977|            version: o.version || 1\n  1978|          })\n  1979|        });\n  1980|        closePersonPanel();\n  1981|        loadOrgs();\n  1982|      } catch (e) { alert(e.message); }\n  1983|    }\n  1984|\n  1985|    async function removePersonFromDeal() {\n  1986|      const dealOrgId = document.getElementById('ppDealOrgId').value;\n  1987|      const memberIdx = parseInt(document.getElementById('ppMemberIdx').value);\n  1988|      const o = _dealOrgsMap[dealOrgId];\n  1989|      if (!o || memberIdx \u003c 0) return;\n  1990|\n  1991|      const name = (o.members || [])[memberIdx]?.name || 'this person';\n  1992|      if (!confirm('Remove ' + name + ' from this deal?')) return;\n  1993|\n  1994|      const members = [...(o.members || [])];\n  1995|      members.splice(memberIdx, 1);\n  1996|\n  1997|      try {\n  1998|        await fetchAPI('/api/projects/' + projectID + '/entries/' + dealOrgId, {\n  1999|          method: 'PUT',\n  2000|          body: JSON.stringify({\n\n(File has more lines. Use 'offset' parameter to read beyond line 2000)\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/home/johan/dev/dealspace/portal/templates/app/project.html\",\"content\":\"{{define \\\"header-left\\\"}}\\n    \\u003cdiv class=\\\"flex items-center gap-3\\\"\\u003e\\n      \\u003ca href=\\\"/app/projects\\\" class=\\\"text-2xl font-bold text-white tracking-tight\\\"\\u003e\\u003cspan class=\\\"text-[#c9a84c]\\\"\\u003eDeal\\u003c/span\\u003espace\\u003c/a\\u003e\\n      \\u003cspan class=\\\"text-white/20 text-lg\\\"\\u003e/\\u003c/span\\u003e\\n      \\u003cdiv id=\\\"projectSwitcher\\\" class=\\\"relative\\\"\\u003e\\n        \\u003cbutton id=\\\"projectSwitcherBtn\\\" onclick=\\\"toggleProjectMenu()\\\" class=\\\"flex items-center gap-1.5 text-xl font-semibold text-white transition focus:outline-none\\\"\\u003e\\n          \\u003cspan id=\\\"projectName\\\"\\u003eLoading...\\u003c/span\\u003e\\n          \\u003cspan style=\\\"font-size:10px;opacity:.5;margin-top:2px\\\"\\u003e▾\\u003c/span\\u003e\\n        \\u003c/button\\u003e\\n        \\u003cdiv id=\\\"projectMenu\\\" class=\\\"hidden absolute left-0 top-full mt-2 min-w-[220px] rounded-lg border shadow-xl z-50\\\" style=\\\"background:var(--ds-sf);border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n{{end}}\\n\\n{{define \\\"content\\\"}}\\n    \\u003cdiv class=\\\"px-8 pt-4 pb-8\\\" style=\\\"max-width:calc(100vw - 14rem)\\\"\\u003e\\n      \\u003cp id=\\\"projectDesc\\\" class=\\\"text-[#b0bec5] text-sm mb-2\\\"\\u003e\\u003c/p\\u003e\\n\\n      \\u003c!-- Tabs + actions on one line --\\u003e\\n      \\u003cdiv class=\\\"flex items-end gap-6 border-b border-white/[0.08] mb-6\\\"\\u003e\\n        \\u003cbutton class=\\\"tab active pb-3 text-base font-medium transition\\\" onclick=\\\"switchTab('requests', this)\\\"\\u003eRequests\\u003c/button\\u003e\\n        \\u003cbutton class=\\\"tab pb-3 text-base font-medium text-[#b0bec5] transition\\\" onclick=\\\"switchTab('orgs', this)\\\"\\u003eParties\\u003c/button\\u003e\\n        \\u003cdiv class=\\\"ml-auto flex items-center gap-2 pb-2\\\"\\u003e\\n          \\u003cspan id=\\\"projectStatus\\\" class=\\\"px-2 py-0.5 rounded text-xs font-medium\\\"\\u003e\\u003c/span\\u003e\\n          \\u003cbutton onclick=\\\"openCreateListModal()\\\" class=\\\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\\\"\\u003e+ New List\\u003c/button\\u003e\\n          \\u003cbutton onclick=\\\"openTemplateModal()\\\" class=\\\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\\\"\\u003eUse Template\\u003c/button\\u003e\\n          \\u003cbutton onclick=\\\"openImportModal()\\\" class=\\\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-white rounded text-sm font-medium transition\\\"\\u003eImport List\\u003c/button\\u003e\\n          \\u003cbutton id=\\\"detailToggle\\\" onclick=\\\"toggleDetailMode()\\\" class=\\\"px-3.5 py-1.5 bg-white/[0.07] hover:bg-white/[0.12] text-[#cbd5e1] hover:text-white rounded text-sm font-medium transition\\\"\\u003eSummary View\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n      \\u003c!-- Requests Tab --\\u003e\\n      \\u003cdiv id=\\\"tab-requests\\\"\\u003e\\n        \\u003cdiv id=\\\"requestEmpty\\\" class=\\\"hidden text-center py-16\\\"\\u003e\\n          \\u003ch3 class=\\\"text-lg font-semibold text-white mb-1\\\"\\u003eNo requests yet\\u003c/h3\\u003e\\n          \\u003cp class=\\\"text-[#b0bec5] text-sm mb-4\\\"\\u003eImport a diligence checklist to get started.\\u003c/p\\u003e\\n          \\u003cbutton onclick=\\\"openImportModal()\\\" class=\\\"px-4 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eImport Checklist\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"treeWrap\\\" class=\\\"overflow-x-auto\\\"\\u003e\\n          \\u003ctable id=\\\"reqTree\\\"\\u003e\\n            \\u003cthead\\u003e\\n              \\u003ctr\\u003e\\n                \\u003cth style=\\\"width:28px\\\" title=\\\"Drag to reorder\\\"\\u003e\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:60px\\\"\\u003e#\\u003c/th\\u003e\\n                \\u003cth style=\\\"min-width:280px\\\"\\u003eRequest Item\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:80px\\\"\\u003ePriority\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:90px\\\"\\u003eStatus\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:120px\\\"\\u003eAssignee\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:90px\\\"\\u003eDue Date\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:90px\\\"\\u003eLabel\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:50px\\\"\\u003eFiles\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:160px\\\"\\u003eBuyer Comment\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:160px\\\"\\u003eSeller Comment\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:90px\\\"\\u003eCreated\\u003c/th\\u003e\\n                \\u003cth style=\\\"width:40px\\\"\\u003e\\u003c/th\\u003e\\n              \\u003c/tr\\u003e\\n            \\u003c/thead\\u003e\\n            \\u003ctbody id=\\\"treeBody\\\"\\u003e\\n              \\u003ctr\\u003e\\u003ctd colspan=\\\"13\\\" class=\\\"text-[#b0bec5] text-sm py-4\\\"\\u003eLoading requests...\\u003c/td\\u003e\\u003c/tr\\u003e\\n            \\u003c/tbody\\u003e\\n          \\u003c/table\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n      \\u003c!-- Orgs Tab --\\u003e\\n      \\u003cdiv id=\\\"tab-orgs\\\" class=\\\"hidden\\\"\\u003e\\n        \\u003cdiv class=\\\"flex justify-between items-center mb-6\\\"\\u003e\\n          \\u003cp class=\\\"text-[#b0bec5] text-sm\\\"\\u003eParties participating in this deal and their permissions.\\u003c/p\\u003e\\n          \\u003cbutton onclick=\\\"openAddOrgModal()\\\" class=\\\"px-3 py-1.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003e+ Add Org\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"orgList\\\" class=\\\"space-y-3\\\"\\u003e\\u003cdiv class=\\\"text-[#b0bec5] text-sm\\\"\\u003eLoading...\\u003c/div\\u003e\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n\\n  \\u003c!-- Edit Org Modal --\\u003e\\n  \\u003cdiv id=\\\"editOrgModal\\\" class=\\\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto\\\"\\u003e\\n      \\u003cdiv class=\\\"flex items-center justify-between px-6 pt-5 pb-4 border-b border-white/[0.08]\\\"\\u003e\\n        \\u003ch2 class=\\\"text-lg font-semibold text-white\\\"\\u003eEdit Organization\\u003c/h2\\u003e\\n        \\u003cbutton onclick=\\\"closeEditOrgModal()\\\" class=\\\"text-[#b0bec5] hover:text-white text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"p-6\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"editOrgDealOrgId\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"editOrgVersion\\\"\\u003e\\n        \\u003cdiv class=\\\"grid grid-cols-2 gap-4\\\"\\u003e\\n          \\u003cdiv class=\\\"col-span-2 flex items-start gap-4\\\"\\u003e\\n            \\u003cdiv id=\\\"editOrgLogoWrap\\\" class=\\\"hidden shrink-0 w-16 h-16 rounded-lg border border-white/[0.08] overflow-hidden bg-white flex items-center justify-center\\\"\\u003e\\n              \\u003cimg id=\\\"editOrgLogoImg\\\" src=\\\"\\\" class=\\\"max-w-full max-h-full object-contain\\\"\\u003e\\n            \\u003c/div\\u003e\\n            \\u003cdiv class=\\\"flex-1\\\"\\u003e\\n              \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eName \\u003cspan class=\\\"text-red-400\\\"\\u003e*\\u003c/span\\u003e\\u003c/label\\u003e\\n              \\u003cinput type=\\\"text\\\" id=\\\"editOrgName\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"col-span-2\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eDescription\\u003c/label\\u003e\\n            \\u003ctextarea id=\\\"editOrgDesc\\\" rows=\\\"3\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] resize-none\\\"\\u003e\\u003c/textarea\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eRole in deal\\u003c/label\\u003e\\n            \\u003cselect id=\\\"editOrgRole\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n              \\u003coption value=\\\"seller\\\"\\u003eSeller\\u003c/option\\u003e\\n              \\u003coption value=\\\"buyer\\\"\\u003eBuyer\\u003c/option\\u003e\\n              \\u003coption value=\\\"ib\\\"\\u003eInvestment Bank\\u003c/option\\u003e\\n              \\u003coption value=\\\"advisor\\\"\\u003eAdvisor\\u003c/option\\u003e\\n            \\u003c/select\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eIndustry\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgIndustry\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eWebsite\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgWebsite\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003ePhone\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgPhone\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eLinkedIn\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgLinkedIn\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eFounded\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgFounded\\\" placeholder=\\\"e.g. 2010\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"col-span-2\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eAddress\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgAddress\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eCity\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgCity\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eState\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgState\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"col-span-2\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eLogo URL\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"editOrgLogo\\\" placeholder=\\\"https://...\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\n              oninput=\\\"previewEditOrgLogo()\\\"\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"editOrgError\\\" class=\\\"hidden mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003e\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"flex gap-3 px-6 pb-6\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeEditOrgModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton id=\\\"editOrgSaveBtn\\\" onclick=\\\"saveEditOrg()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eSave Changes\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Deal Member Picker Modal --\\u003e\\n  \\u003cdiv id=\\\"dealMemberPickerModal\\\" class=\\\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-lg flex flex-col\\\" style=\\\"max-height:85vh\\\"\\u003e\\n      \\u003cdiv class=\\\"flex items-center justify-between px-5 pt-5 pb-3 shrink-0\\\"\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003ch2 class=\\\"text-base font-semibold text-white\\\"\\u003eAdd people to deal\\u003c/h2\\u003e\\n          \\u003cp id=\\\"dmpOrgName\\\" class=\\\"text-xs mt-0.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003e\\u003c/p\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cbutton onclick=\\\"closeDealMemberPicker()\\\" class=\\\"text-[#94a3b8] hover:text-white text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Search --\\u003e\\n      \\u003cdiv class=\\\"px-5 pb-3 shrink-0\\\"\\u003e\\n        \\u003cinput type=\\\"text\\\" id=\\\"dmpSearch\\\" placeholder=\\\"Search by name or email...\\\" oninput=\\\"filterDmpList()\\\"\\n          class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\\\"\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Existing members from global org --\\u003e\\n      \\u003cdiv id=\\\"dmpList\\\" class=\\\"overflow-y-auto flex-1 px-5 space-y-1.5\\\"\\u003e\\u003c/div\\u003e\\n      \\u003c!-- Add new person inline --\\u003e\\n      \\u003cdiv class=\\\"px-5 py-4 border-t shrink-0\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cp class=\\\"text-xs mb-2 font-medium\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eNot listed? Add a new person:\\u003c/p\\u003e\\n        \\u003cdiv class=\\\"grid gap-2\\\" style=\\\"grid-template-columns:1fr 1fr 1fr auto\\\"\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"dmpNewName\\\" placeholder=\\\"Name\\\" class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\\\"\\u003e\\n          \\u003cinput type=\\\"email\\\" id=\\\"dmpNewEmail\\\" placeholder=\\\"Email\\\" class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\\\"\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"dmpNewTitle\\\" placeholder=\\\"Title\\\" onkeydown=\\\"if(event.key==='Enter'){event.preventDefault();addNewDealMember();}\\\" class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#64748b]\\\"\\u003e\\n          \\u003cbutton onclick=\\\"addNewDealMember()\\\" class=\\\"px-3 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-bold rounded-lg text-sm transition\\\"\\u003e+\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"dmpAddError\\\" class=\\\"hidden mt-1.5 text-xs text-red-400\\\"\\u003e\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Footer --\\u003e\\n      \\u003cdiv class=\\\"flex gap-3 px-5 pb-5 shrink-0\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeDealMemberPicker()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton id=\\\"dmpAddBtn\\\" onclick=\\\"confirmDealMemberAdd()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eAdd selected\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Template Picker Modal --\\u003e\\n  \\u003cdiv id=\\\"templateModal\\\" style=\\\"display:none\\\" class=\\\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] rounded-xl w-full max-w-lg border border-white/[0.08]\\\"\\u003e\\n      \\u003cdiv class=\\\"flex items-center justify-between px-6 pt-5 pb-4 border-b border-white/[0.08]\\\"\\u003e\\n        \\u003ch2 class=\\\"text-lg font-semibold text-white\\\"\\u003eUse a Template\\u003c/h2\\u003e\\n        \\u003cbutton onclick=\\\"closeTemplateModal()\\\" class=\\\"text-[#94a3b8] hover:text-white text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv id=\\\"templateList\\\" class=\\\"px-4 py-3 space-y-2 max-h-80 overflow-y-auto\\\"\\u003e\\n        \\u003cdiv class=\\\"text-sm text-[#94a3b8]\\\"\\u003eLoading templates...\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4 border-t border-white/[0.08]\\\"\\u003e\\n        \\u003clabel class=\\\"block text-xs text-[#94a3b8] mb-1.5\\\"\\u003eList name (optional — uses template name by default)\\u003c/label\\u003e\\n        \\u003cinput type=\\\"text\\\" id=\\\"templateListName\\\" placeholder=\\\"Leave blank to use template name\\\"\\n          class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] mb-4\\\"\\n          onkeydown=\\\"if(event.key==='Enter')submitTemplateImport()\\\"\\u003e\\n        \\u003cdiv id=\\\"templateImportError\\\" class=\\\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003e\\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex gap-3\\\"\\u003e\\n          \\u003cbutton onclick=\\\"closeTemplateModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n          \\u003cbutton id=\\\"templateImportBtn\\\" onclick=\\\"submitTemplateImport()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eAdd to Deal\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Create List Modal --\\u003e\\n  \\u003cdiv id=\\\"createListModal\\\" style=\\\"display:none\\\" class=\\\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] rounded-xl p-6 w-full max-w-md border border-white/[0.08]\\\"\\u003e\\n      \\u003ch2 class=\\\"text-lg font-semibold text-white mb-4\\\"\\u003eNew Request List\\u003c/h2\\u003e\\n      \\u003cdiv id=\\\"createListError\\\" class=\\\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003e\\u003c/div\\u003e\\n      \\u003cdiv class=\\\"mb-4\\\"\\u003e\\n        \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eList name\\u003c/label\\u003e\\n        \\u003cinput type=\\\"text\\\" id=\\\"createListName\\\" placeholder=\\\"e.g. Legal Due Diligence, Financial Review\\\"\\n          class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white placeholder-[#8899a6] focus:outline-none focus:border-[#c9a84c] text-sm\\\"\\n          onkeydown=\\\"if(event.key==='Enter')submitCreateList()\\\"\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"flex gap-3\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeCreateListModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton id=\\\"createListBtn\\\" onclick=\\\"submitCreateList()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eCreate\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Import Modal --\\u003e\\n  \\u003cdiv id=\\\"importModal\\\" style=\\\"display:none\\\" class=\\\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl p-8 w-full max-w-md\\\"\\u003e\\n      \\u003ch2 class=\\\"text-xl font-semibold text-white mb-6\\\"\\u003eImport Diligence Checklist\\u003c/h2\\u003e\\n      \\u003cdiv id=\\\"importError\\\" class=\\\"hidden mb-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003e\\u003c/div\\u003e\\n      \\u003cdiv class=\\\"space-y-4\\\"\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eRequest List Name\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"importListName\\\" placeholder=\\\"e.g. Blackstone DDQ, General Checklist\\\"\\n            class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] focus:ring-1 focus:ring-[#c9a84c]\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eFile (CSV or XLSX)\\u003c/label\\u003e\\n          \\u003cinput type=\\\"file\\\" id=\\\"importFile\\\" accept=\\\".csv,.xlsx,.xls\\\" required\\n            class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] focus:ring-1 focus:ring-[#c9a84c]\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eImport Mode\\u003c/label\\u003e\\n          \\u003cselect id=\\\"importMode\\\" class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n            \\u003coption value=\\\"add\\\"\\u003eAdd new request list\\u003c/option\\u003e\\n            \\u003coption value=\\\"replace\\\"\\u003eReplace all request lists\\u003c/option\\u003e\\n          \\u003c/select\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eVisible to\\u003c/label\\u003e\\n          \\u003cselect id=\\\"importVisibility\\\" class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n            \\u003coption value=\\\"\\\"\\u003eEveryone (General List)\\u003c/option\\u003e\\n          \\u003c/select\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"flex gap-3 mt-6\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeImportModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton id=\\\"importBtn\\\" onclick=\\\"submitImport()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eImport List\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Attach Answer Modal --\\u003e\\n  \\u003cdiv id=\\\"attachModal\\\" style=\\\"display:none\\\" class=\\\"fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl p-8 w-full max-w-lg\\\"\\u003e\\n      \\u003ch2 class=\\\"text-xl font-semibold text-white mb-2\\\"\\u003eAttach Answer Document\\u003c/h2\\u003e\\n      \\u003cp id=\\\"attachReqTitle\\\" class=\\\"text-[#b0bec5] text-sm mb-2\\\"\\u003e\\u003c/p\\u003e\\n      \\u003cdiv class=\\\"mb-4\\\"\\u003e\\n        \\u003cinput type=\\\"text\\\" id=\\\"attachSearch\\\" placeholder=\\\"Search existing documents...\\\"\\n          class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\n          oninput=\\\"searchAnswers(this.value)\\\"\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv id=\\\"attachList\\\" class=\\\"max-h-64 overflow-y-auto space-y-1 mb-4\\\"\\u003e\\u003c/div\\u003e\\n      \\u003cdiv class=\\\"border-t border-white/[0.08] pt-4\\\"\\u003e\\n        \\u003clabel class=\\\"block text-sm font-medium text-[#b0bec5] mb-1.5\\\"\\u003eOr upload new document\\u003c/label\\u003e\\n        \\u003cinput type=\\\"file\\\" id=\\\"attachFile\\\" class=\\\"w-full px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm\\\"\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"flex gap-3 mt-4\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeAttachModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton onclick=\\\"uploadAndLink()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eUpload \\u0026amp; Link\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Add Party Modal (3-step: email → org details → colleagues) --\\u003e\\n  \\u003cdiv id=\\\"addOrgModal\\\" class=\\\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto\\\"\\u003e\\n\\n      \\u003c!-- Step 1: Email lookup --\\u003e\\n      \\u003cdiv id=\\\"addOrgStep1\\\" class=\\\"p-6\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between mb-1\\\"\\u003e\\n          \\u003ch2 id=\\\"addOrgTitle\\\" class=\\\"text-lg font-semibold text-white\\\"\\u003eAdd Party\\u003c/h2\\u003e\\n          \\u003cbutton onclick=\\\"closeAddOrgModal()\\\" class=\\\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cp id=\\\"addOrgHint\\\" class=\\\"text-[#b0bec5] text-sm mb-5\\\"\\u003eEnter an email address or domain name — we'll look up the organization automatically.\\u003c/p\\u003e\\n        \\u003c!-- Project name — only shown in wizard mode --\\u003e\\n        \\u003cdiv id=\\\"wizProjectNameWrap\\\" class=\\\"hidden mb-4\\\"\\u003e\\n          \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eDeal name \\u003cspan class=\\\"text-red-400\\\"\\u003e*\\u003c/span\\u003e\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"wizProjectName\\\" placeholder=\\\"e.g. Project Falcon\\\"\\n            class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\n            onkeydown=\\\"if(event.key==='Enter')scrapeOrg()\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex gap-3\\\"\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"addOrgEmail\\\" placeholder=\\\"e.g. john.smith@example.com or example.com\\\" autocomplete=\\\"off\\\"\\n            class=\\\"flex-1 px-4 py-2.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\\\"\\n            onkeydown=\\\"if(event.key==='Enter')scrapeOrg()\\\"\\u003e\\n          \\u003cbutton onclick=\\\"scrapeOrg()\\\" id=\\\"scrapeBtn\\\" class=\\\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition whitespace-nowrap\\\"\\u003eLook up\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"scrapeError\\\" class=\\\"hidden mt-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm flex items-center justify-between gap-3\\\"\\u003e\\n          \\u003cspan id=\\\"scrapeErrorMsg\\\"\\u003e\\u003c/span\\u003e\\n          \\u003cbutton onclick=\\\"skipScrape()\\\" class=\\\"shrink-0 px-3 py-1 bg-white/[0.08] hover:bg-white/[0.12] text-white rounded text-xs font-medium transition\\\"\\u003eContinue manually\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"scrapeLoading\\\" class=\\\"hidden mt-4 flex items-center gap-3 text-[#b0bec5] text-sm\\\"\\u003e\\n          \\u003csvg class=\\\"animate-spin w-4 h-4\\\" fill=\\\"none\\\" viewBox=\\\"0 0 24 24\\\"\\u003e\\u003ccircle class=\\\"opacity-25\\\" cx=\\\"12\\\" cy=\\\"12\\\" r=\\\"10\\\" stroke=\\\"currentColor\\\" stroke-width=\\\"4\\\"/\\u003e\\u003cpath class=\\\"opacity-75\\\" fill=\\\"currentColor\\\" d=\\\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\\\"/\\u003e\\u003c/svg\\u003e\\n          Scanning website...\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n      \\u003c!-- Step 2: Org details (prefilled from scrape) --\\u003e\\n      \\u003cdiv id=\\\"addOrgStep2\\\" class=\\\"hidden p-6\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between mb-4\\\"\\u003e\\n          \\u003ch2 class=\\\"text-lg font-semibold text-white\\\"\\u003eOrganization Details\\u003c/h2\\u003e\\n          \\u003cbutton onclick=\\\"closeAddOrgModal()\\\" class=\\\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"grid grid-cols-2 gap-4 mb-5\\\"\\u003e\\n          \\u003cdiv class=\\\"col-span-2 flex items-start gap-4\\\"\\u003e\\n            \\u003cdiv id=\\\"orgLogoWrap\\\" class=\\\"hidden shrink-0 w-16 h-16 rounded-lg border border-white/[0.08] overflow-hidden bg-white flex items-center justify-center\\\"\\u003e\\n              \\u003cimg id=\\\"orgLogoImg\\\" src=\\\"\\\" class=\\\"max-w-full max-h-full object-contain\\\"\\u003e\\n            \\u003c/div\\u003e\\n            \\u003cdiv class=\\\"flex-1\\\"\\u003e\\n              \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eName \\u003cspan class=\\\"text-red-400\\\"\\u003e*\\u003c/span\\u003e\\u003c/label\\u003e\\n              \\u003cinput type=\\\"text\\\" id=\\\"orgName\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"col-span-2\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eDescription\\u003c/label\\u003e\\n            \\u003ctextarea id=\\\"orgDesc\\\" rows=\\\"4\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] resize-none\\\"\\u003e\\u003c/textarea\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eRole in deal \\u003cspan class=\\\"text-red-400\\\"\\u003e*\\u003c/span\\u003e\\u003c/label\\u003e\\n            \\u003cselect id=\\\"orgRole\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n              \\u003coption value=\\\"\\\"\\u003eSelect...\\u003c/option\\u003e\\n              \\u003coption value=\\\"seller\\\"\\u003eSeller\\u003c/option\\u003e\\n              \\u003coption value=\\\"buyer\\\"\\u003eBuyer\\u003c/option\\u003e\\n              \\u003coption value=\\\"ib\\\"\\u003eInvestment Bank\\u003c/option\\u003e\\n              \\u003coption value=\\\"advisor\\\"\\u003eAdvisor\\u003c/option\\u003e\\n            \\u003c/select\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eIndustry\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgIndustry\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eWebsite\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgWebsite\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003ePhone\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgPhone\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"col-span-2\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eAddress\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgAddress\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eCity\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgCity\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eState\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgState\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eFounded\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgFounded\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\n            \\u003clabel class=\\\"block text-xs text-[#b0bec5] mb-1\\\"\\u003eLinkedIn\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"orgLinkedIn\\\" class=\\\"w-full px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex justify-between\\\"\\u003e\\n          \\u003cbutton onclick=\\\"showAddOrgStep(1)\\\" class=\\\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\\\"\\u003eBack\\u003c/button\\u003e\\n          \\u003cdiv id=\\\"step2Error\\\" class=\\\"hidden mb-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003ePlease fill in all required fields.\\u003c/div\\u003e\\n          \\u003cbutton onclick=\\\"advanceFromStep2()\\\" class=\\\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eNext: Select People\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n      \\u003c!-- Step 3: Colleagues --\\u003e\\n      \\u003cdiv id=\\\"addOrgStep3\\\" class=\\\"hidden p-6\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between mb-1\\\"\\u003e\\n          \\u003ch2 class=\\\"text-lg font-semibold text-white\\\"\\u003eSelect Colleagues\\u003c/h2\\u003e\\n          \\u003cbutton onclick=\\\"closeAddOrgModal()\\\" class=\\\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cp class=\\\"text-[#b0bec5] text-sm mb-4\\\"\\u003eChoose who to add to this deal.\\u003c/p\\u003e\\n        \\u003cdiv class=\\\"flex items-center gap-3 mb-4\\\"\\u003e\\n          \\u003clabel class=\\\"flex items-center gap-2 cursor-pointer text-sm text-[#b0bec5]\\\"\\u003e\\n            \\u003cinput type=\\\"checkbox\\\" id=\\\"selectAllPeople\\\" onchange=\\\"toggleAllPeople(this.checked)\\\" class=\\\"rounded accent-[#c9a84c]\\\"\\u003e\\n            Select all\\n          \\u003c/label\\u003e\\n          \\u003cspan id=\\\"selectedCount\\\" class=\\\"text-xs text-[#8899a6]\\\"\\u003e0 selected\\u003c/span\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"peopleList\\\" class=\\\"space-y-2 mb-4 max-h-[40vh] overflow-y-auto\\\"\\u003e\\u003c/div\\u003e\\n        \\u003cdiv id=\\\"noPeople\\\" class=\\\"hidden text-[#b0bec5] text-sm py-4 text-center\\\"\\u003eNo team members found on the website.\\u003c/div\\u003e\\n        \\u003c!-- Manual add row --\\u003e\\n        \\u003cdiv class=\\\"border-t border-white/[0.08] pt-3 mt-3\\\"\\u003e\\n          \\u003cp class=\\\"text-xs text-[#b0bec5] mb-2\\\"\\u003eAdd manually\\u003c/p\\u003e\\n          \\u003cdiv class=\\\"grid grid-cols-[1fr_1fr_1fr_auto] gap-2\\\"\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"manualName\\\" placeholder=\\\"Name\\\"\\n              class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\\\"\\u003e\\n            \\u003cinput type=\\\"email\\\" id=\\\"manualEmail\\\" placeholder=\\\"Email\\\"\\n              class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\\\"\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"manualTitle\\\" placeholder=\\\"Title\\\"\\n              onkeydown=\\\"if(event.key==='Enter'){event.preventDefault();addManualPerson();}\\\"\\n              class=\\\"px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-sm focus:outline-none focus:border-[#c9a84c] placeholder-[#8899a6]\\\"\\u003e\\n            \\u003cbutton onclick=\\\"addManualPerson()\\\" class=\\\"px-3 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-bold rounded-lg text-sm transition\\\"\\u003e+\\u003c/button\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex justify-between\\\"\\u003e\\n          \\u003cbutton onclick=\\\"showAddOrgStep(2)\\\" class=\\\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\\\"\\u003eBack\\u003c/button\\u003e\\n          \\u003cdiv class=\\\"flex flex-col items-end gap-2\\\"\\u003e\\n            \\u003cdiv id=\\\"step3Error\\\" class=\\\"hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm text-right\\\"\\u003e\\u003c/div\\u003e\\n            \\u003cbutton onclick=\\\"proceedFromStep3()\\\" id=\\\"addOrgSubmitBtn\\\" class=\\\"px-5 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eAdd to Deal\\u003c/button\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n      \\u003c!-- Step 4: Request List Templates (wizard mode only) --\\u003e\\n      \\u003cdiv id=\\\"addOrgStep4\\\" class=\\\"hidden p-6\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between mb-1\\\"\\u003e\\n          \\u003ch2 class=\\\"text-lg font-semibold text-white\\\"\\u003eRequest Lists\\u003c/h2\\u003e\\n          \\u003cbutton onclick=\\\"closeAddOrgModal()\\\" class=\\\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cp class=\\\"text-[#b0bec5] text-sm mb-4\\\"\\u003eChoose which request list templates to include in this project.\\u003c/p\\u003e\\n\\n        \\u003cdiv class=\\\"space-y-3 mb-6\\\" id=\\\"wizTemplatePicker\\\"\\u003e\\n          \\u003clabel class=\\\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\\\" style=\\\"background:var(--ds-hv);border:1px solid var(--ds-bd)\\\"\\u003e\\n            \\u003cinput type=\\\"checkbox\\\" class=\\\"tmpl-cb mt-0.5 accent-[#c9a84c]\\\" value=\\\"core-8\\\" checked onchange=\\\"tmplChanged(this)\\\"\\u003e\\n            \\u003cdiv\\u003e\\n              \\u003cdiv class=\\\"text-sm font-semibold\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eCore Due Diligence\\u003c/div\\u003e\\n              \\u003cdiv class=\\\"text-xs mt-0.5\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e8 essential items — financials, legal, commercial, HR, tax. The minimum every deal needs.\\u003c/div\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/label\\u003e\\n          \\u003clabel class=\\\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\\\" style=\\\"background:var(--ds-hv);border:1px solid var(--ds-bd)\\\"\\u003e\\n            \\u003cinput type=\\\"checkbox\\\" class=\\\"tmpl-cb mt-0.5 accent-[#c9a84c]\\\" value=\\\"comprehensive-100\\\" onchange=\\\"tmplChanged(this)\\\"\\u003e\\n            \\u003cdiv\\u003e\\n              \\u003cdiv class=\\\"text-sm font-semibold\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eComprehensive Due Diligence\\u003c/div\\u003e\\n              \\u003cdiv class=\\\"text-xs mt-0.5\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e104 items across 18 sections — financial, legal, tax, commercial, HR, IP, IT, environmental, insurance. Full-scope M\\u0026A checklist.\\u003c/div\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/label\\u003e\\n          \\u003clabel class=\\\"flex items-start gap-3 p-4 rounded-xl cursor-pointer transition\\\" style=\\\"background:var(--ds-hv);border:1px solid var(--ds-bd)\\\"\\u003e\\n            \\u003cinput type=\\\"checkbox\\\" class=\\\"tmpl-cb mt-0.5 accent-[#c9a84c]\\\" value=\\\"none\\\" onchange=\\\"tmplChanged(this)\\\"\\u003e\\n            \\u003cdiv\\u003e\\n              \\u003cdiv class=\\\"text-sm font-semibold\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eStart empty\\u003c/div\\u003e\\n              \\u003cdiv class=\\\"text-xs mt-0.5\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eNo request lists — import or create your own later.\\u003c/div\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/label\\u003e\\n        \\u003c/div\\u003e\\n\\n        \\u003cdiv class=\\\"flex justify-between\\\"\\u003e\\n          \\u003cbutton onclick=\\\"showAddOrgStep(3)\\\" class=\\\"px-4 py-2 text-[#b0bec5] hover:text-white text-sm transition\\\"\\u003eBack\\u003c/button\\u003e\\n\\u003cdiv class=\\\"w-full mb-2\\\"\\u003e\\u003cdiv id=\\\"wizError\\\" class=\\\"hidden p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-red-400 text-sm\\\"\\u003e\\u003c/div\\u003e\\u003c/div\\u003e\\n          \\u003cbutton onclick=\\\"createNewProject()\\\" id=\\\"wizCreateBtn\\\" class=\\\"px-6 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eCreate Project\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Email Preview Modal --\\u003e\\n  \\u003cdiv id=\\\"emailPreviewModal\\\" class=\\\"hidden fixed inset-0 bg-black/40 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"rounded-xl w-full max-w-lg max-h-[80vh] overflow-y-auto\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd)\\\"\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4 border-b flex items-center justify-between\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003ch3 class=\\\"text-lg font-semibold\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eInvite Emails\\u003c/h3\\u003e\\n        \\u003cbutton onclick=\\\"closeEmailPreview()\\\" class=\\\"text-2xl leading-none\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4\\\"\\u003e\\n        \\u003cdiv id=\\\"emailPreviewList\\\" class=\\\"space-y-2\\\"\\u003e\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4 border-t\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closeEmailPreview()\\\" class=\\\"w-full py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eOK\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Person Slide-out Panel --\\u003e\\n  \\u003cdiv id=\\\"personOverlay\\\" class=\\\"hidden fixed inset-0 bg-black/40 z-50\\\" onclick=\\\"closePersonPanel()\\\"\\u003e\\u003c/div\\u003e\\n  \\u003cdiv id=\\\"personPanel\\\" class=\\\"fixed top-0 right-0 h-full w-full max-w-md z-50 transform translate-x-full transition-transform duration-200 ease-out\\\"\\n    style=\\\"background:var(--ds-sf, #0d1f3c); border-left: 1px solid var(--ds-bd, rgba(255,255,255,.08));\\\"\\u003e\\n    \\u003cdiv class=\\\"h-full flex flex-col\\\"\\u003e\\n      \\u003c!-- Header --\\u003e\\n      \\u003cdiv class=\\\"flex items-center justify-between px-6 py-4 border-b\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003ch3 id=\\\"personPanelTitle\\\" class=\\\"text-lg font-semibold\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eEdit Person\\u003c/h3\\u003e\\n        \\u003cbutton onclick=\\\"closePersonPanel()\\\" class=\\\"text-2xl leading-none\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Body --\\u003e\\n      \\u003cdiv class=\\\"flex-1 overflow-y-auto min-h-0 px-6 py-5 space-y-4\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"ppDealOrgId\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"ppMemberIdx\\\"\\u003e\\n        \\u003c!-- Photo + Name --\\u003e\\n        \\u003cdiv class=\\\"flex items-center gap-4\\\"\\u003e\\n          \\u003cdiv id=\\\"ppPhotoWrap\\\" class=\\\"w-20 h-20 rounded-full overflow-hidden shrink-0 bg-[#c9a84c]/20 flex items-center justify-center cursor-pointer relative group\\\"\\n            onclick=\\\"document.getElementById('ppPhotoUrlRow').classList.toggle('hidden')\\\" title=\\\"Click to change photo\\\"\\u003e\\n            \\u003cimg id=\\\"ppPhotoImg\\\" src=\\\"\\\" class=\\\"w-full h-full object-cover hidden\\\"\\u003e\\n            \\u003cspan id=\\\"ppPhotoLetter\\\" class=\\\"text-[#c9a84c] text-2xl font-semibold\\\"\\u003e\\u003c/span\\u003e\\n            \\u003cdiv class=\\\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center rounded-full\\\"\\u003e\\n              \\u003cspan class=\\\"text-white text-xs\\\"\\u003eEdit\\u003c/span\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"flex-1\\\"\\u003e\\n            \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eName\\u003c/label\\u003e\\n            \\u003cinput type=\\\"text\\\" id=\\\"ppName\\\" class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n              style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\n              oninput=\\\"previewPersonPhoto()\\\"\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"ppPhotoUrlRow\\\" class=\\\"hidden\\\"\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003ePhoto URL\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"ppPhoto\\\" placeholder=\\\"https://...\\\"\\n            class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\n            oninput=\\\"previewPersonPhoto()\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eEmail\\u003c/label\\u003e\\n          \\u003cinput type=\\\"email\\\" id=\\\"ppEmail\\\" class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eTitle\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"ppTitle\\\" class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003ePhone\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"ppPhone\\\" class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eLinkedIn\\u003c/label\\u003e\\n          \\u003cinput type=\\\"text\\\" id=\\\"ppLinkedIn\\\" placeholder=\\\"\\\"\\n            class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003clabel class=\\\"block text-xs mb-1\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eBio\\u003c/label\\u003e\\n          \\u003ctextarea id=\\\"ppBio\\\" rows=\\\"5\\\" class=\\\"w-full px-3 py-2 rounded-lg text-sm focus:outline-none resize-none\\\"\\n            style=\\\"background:var(--ds-inp);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\u003c/textarea\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Footer --\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4 pb-16 border-t flex items-center gap-3 shrink-0\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cbutton onclick=\\\"removePersonFromDeal()\\\" id=\\\"ppRemoveBtn\\\"\\n          class=\\\"px-3 py-2 rounded-lg text-sm font-medium transition\\\"\\n          style=\\\"background:rgba(239,68,68,.1);color:#f87171;border:1px solid rgba(239,68,68,.2)\\\"\\u003eRemove\\u003c/button\\u003e\\n        \\u003cdiv class=\\\"flex-1\\\"\\u003e\\u003c/div\\u003e\\n        \\u003cbutton onclick=\\\"closePersonPanel()\\\" class=\\\"px-4 py-2 rounded-lg text-sm font-medium transition\\\"\\n          style=\\\"background:var(--ds-hv);color:var(--ds-tx)\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton onclick=\\\"savePersonPanel()\\\" class=\\\"px-4 py-2 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eSave\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n\\n  \\u003c!-- Permissions Modal --\\u003e\\n  \\u003cdiv id=\\\"permModal\\\" class=\\\"hidden fixed inset-0 bg-black/60 z-50 flex items-center justify-center p-4\\\"\\u003e\\n    \\u003cdiv class=\\\"bg-[#0d1f3c] border border-white/[0.08] rounded-xl w-full max-w-md\\\"\\u003e\\n      \\u003cdiv class=\\\"p-6 border-b border-white/[0.08] flex items-center justify-between\\\"\\u003e\\n        \\u003ch2 class=\\\"text-lg font-semibold text-white\\\" id=\\\"permModalTitle\\\"\\u003ePermissions\\u003c/h2\\u003e\\n        \\u003cbutton onclick=\\\"closePermModal()\\\" class=\\\"text-[#b0bec5] hover:text-white transition text-2xl leading-none\\\"\\u003e\\u0026times;\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"p-6 space-y-4\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"permModalDealOrgId\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"permModalVersion\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"permModalOrgId\\\"\\u003e\\n        \\u003cinput type=\\\"hidden\\\" id=\\\"permModalRole\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between\\\"\\u003e\\n          \\u003cspan class=\\\"text-sm text-[#b0bec5]\\\"\\u003eDownload documents\\u003c/span\\u003e\\n          \\u003cselect id=\\\"pmDownload\\\" class=\\\"px-3 py-1.5 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white text-xs focus:outline-none focus:border-[#c9a84c]\\\"\\u003e\\n            \\u003coption value=\\\"watermark\\\"\\u003eWatermarked only\\u003c/option\\u003e\\n            \\u003coption value=\\\"full\\\"\\u003eFull (no watermark)\\u003c/option\\u003e\\n            \\u003coption value=\\\"none\\\"\\u003eNo access\\u003c/option\\u003e\\n          \\u003c/select\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between\\\"\\u003e\\n          \\u003cspan class=\\\"text-sm text-[#b0bec5]\\\"\\u003eUpload documents\\u003c/span\\u003e\\n          \\u003cinput type=\\\"checkbox\\\" id=\\\"pmUpload\\\" class=\\\"w-4 h-4 accent-[#c9a84c]\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between\\\"\\u003e\\n          \\u003cspan class=\\\"text-sm text-[#b0bec5]\\\"\\u003eAdd request lists\\u003c/span\\u003e\\n          \\u003cinput type=\\\"checkbox\\\" id=\\\"pmAddLists\\\" class=\\\"w-4 h-4 accent-[#c9a84c]\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex items-center justify-between\\\"\\u003e\\n          \\u003cspan class=\\\"text-sm text-[#b0bec5]\\\"\\u003eEdit request lists\\u003c/span\\u003e\\n          \\u003cinput type=\\\"checkbox\\\" id=\\\"pmEditLists\\\" class=\\\"w-4 h-4 accent-[#c9a84c]\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv\\u003e\\n          \\u003cdiv class=\\\"flex items-center justify-between mb-2\\\"\\u003e\\n            \\u003cspan class=\\\"text-sm text-[#b0bec5]\\\"\\u003eVisible request lists\\u003c/span\\u003e\\n            \\u003clabel class=\\\"flex items-center gap-1.5 text-xs text-[#b0bec5] cursor-pointer\\\"\\u003e\\n              \\u003cinput type=\\\"checkbox\\\" id=\\\"pmAllLists\\\" class=\\\"w-3.5 h-3.5 accent-[#c9a84c]\\\" onchange=\\\"toggleAllLists(this.checked)\\\"\\u003e\\n              All lists\\n            \\u003c/label\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv id=\\\"pmListChecks\\\" class=\\\"space-y-1.5 max-h-40 overflow-y-auto pr-1\\\"\\u003e\\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"p-6 border-t border-white/[0.08] flex gap-3\\\"\\u003e\\n        \\u003cbutton onclick=\\\"closePermModal()\\\" class=\\\"flex-1 py-2.5 bg-white/[0.05] hover:bg-white/[0.08] text-white rounded-lg text-sm font-medium transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003cbutton onclick=\\\"savePermissions()\\\" class=\\\"flex-1 py-2.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-sm transition\\\"\\u003eSave\\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n  \\u003c/div\\u003e\\n{{end}}\\n\\n{{define \\\"scripts\\\"}}\\n  \\u003cscript\\u003e\\n    const projectID = location.pathname.split('/').pop();\\n    function fmtDate(ms) { if (!ms) return ''; const d = new Date(ms); return d.toLocaleDateString('en-US', {month:'short',day:'numeric'}); }\\n\\n    // Badge helpers\\n    const priStyles = { critical:'background:rgba(239,68,68,.15);color:#f87171', high:'background:rgba(251,146,60,.15);color:#fb923c', medium:'background:rgba(250,204,21,.15);color:#facc15', low:'background:rgba(148,163,184,.15);color:#b0bec5' };\\n    const statStyles = { open:'background:rgba(96,165,250,.15);color:#60a5fa', in_process:'background:rgba(251,191,36,.15);color:#fbbf24', partial:'background:rgba(168,85,247,.15);color:#a855f7', complete:'background:rgba(74,222,128,.15);color:#4ade80' };\\n    const statLabels = { open:'Open', in_process:'In Process', partial:'Partial', complete:'Complete' };\\n\\n    function priorityBadge(p) {\\n      return `\\u003cspan class=\\\"badge\\\" style=\\\"${priStyles[p]||priStyles.medium}\\\"\\u003e${escHtml(p||'medium')}\\u003c/span\\u003e`;\\n    }\\n    function statusBadge(s) {\\n      return `\\u003cspan class=\\\"badge\\\" style=\\\"${statStyles[s]||statStyles.open}\\\"\\u003e${statLabels[s]||escHtml(s||'open')}\\u003c/span\\u003e`;\\n    }\\n\\n    function labelPill(label) {\\n      if (!label) return '';\\n      const colors = [\\n        {bg:'rgba(96,165,250,.15)',fg:'#60a5fa'},{bg:'rgba(244,114,182,.15)',fg:'#f472b6'},\\n        {bg:'rgba(167,139,250,.15)',fg:'#a78bfa'},{bg:'rgba(251,146,60,.15)',fg:'#fb923c'},\\n        {bg:'rgba(74,222,128,.15)',fg:'#4ade80'},{bg:'rgba(250,204,21,.15)',fg:'#facc15'}\\n      ];\\n      let hash = 0;\\n      for (let i = 0; i \\u003c label.length; i++) hash = ((hash \\u003c\\u003c 5) - hash) + label.charCodeAt(i);\\n      const c = colors[Math.abs(hash) % colors.length];\\n      return `\\u003cspan style=\\\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:11px;background:${c.bg};color:${c.fg}\\\"\\u003e${escHtml(label)}\\u003c/span\\u003e`;\\n    }\\n\\n    function fmtDueDate(v) {\\n      if (!v) return '\\\\u2014';\\n      if (typeof v === 'number') return fmtDate(v);\\n      return escHtml(String(v));\\n    }\\n\\n    const statusColors = { active: 'bg-green-500/20 text-green-300', draft: 'bg-gray-500/20 text-gray-300', closed: 'bg-red-500/20 text-red-300' };\\n    const roleColors = { seller: 'bg-blue-500/20 text-blue-300', buyer: 'bg-green-500/20 text-green-300', ib: 'bg-[#c9a84c]/20 text-[#c9a84c]', advisor: 'bg-purple-500/20 text-purple-300' };\\n\\n    // ---- State ----\\n    let treeData = [];\\n    let collapsed = new Set();\\n    let numberMap = {};\\n    let openMenuId = null;\\n    let attachRequestId = null;\\n    let detailMode = false;\\n\\n    // ---- Detail toggle ----\\n    function toggleDetailMode() {\\n      detailMode = !detailMode;\\n      document.getElementById('detailToggle').textContent = detailMode ? 'Detailed View' : 'Summary View';\\n      renderTree();\\n    }\\n\\n    // ---- Project loading ----\\n    async function loadProject() {\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID);\\n        if (!res.ok) { document.getElementById('projectTitle').textContent = 'Not found'; return; }\\n        const raw = await res.json();\\n        const p = raw.project || raw;\\n        const d = parseData(p.data_text);\\n        const name = d.name || p.summary_text || p.summary || 'Untitled';\\n        document.title = name + ' — Dealspace';\\n        document.getElementById('projectName').textContent = name;\\n        fetchAPI('/api/projects').then(r=\\u003er.json()).then(projects=\\u003e{\\n          const menu = document.getElementById('projectMenu');\\n          menu.innerHTML = (projects||[]).map(p=\\u003e{\\n            const d = p.data_text ? (()=\\u003e{try{return JSON.parse(p.data_text)}catch{return{}}})() : {};\\n            const pname = d.name || p.search_key || p.entry_id;\\n            const active = p.entry_id === projectID;\\n            return `\\u003cdiv onclick=\\\"switchProject('${p.entry_id}')\\\" class=\\\"px-4 py-2.5 cursor-pointer transition text-sm\\\" style=\\\"color:${active?'var(--ds-ac)':'var(--ds-tx)'};background:${active?'rgba(255,255,255,.04)':'transparent'}\\\" onmouseover=\\\"this.style.background='rgba(255,255,255,.06)'\\\" onmouseout=\\\"this.style.background='${active?'rgba(255,255,255,.04)':'transparent'}'\\\"\\u003e\\n              ${escHtml(pname)}\\n            \\u003c/div\\u003e`;\\n          }).join('');\\n        }).catch(()=\\u003e{});\\n        // projectTitle removed — name shown in header breadcrumb only\\n        document.getElementById('projectDesc').textContent = d.description || '';\\n        const status = d.status || 'active';\\n        const sc = statusColors[status] || 'bg-gray-500/20 text-gray-300';\\n        document.getElementById('projectStatus').className = 'px-2.5 py-0.5 rounded-full text-xs font-medium capitalize ' + sc;\\n        document.getElementById('projectStatus').textContent = status;\\n      } catch(e) {}\\n    }\\n\\n    // ---- Tree loading ----\\n    async function loadRequestTree() {\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/requests/tree');\\n        treeData = await res.json();\\n        if (!treeData || treeData.length === 0) {\\n          document.getElementById('requestEmpty').classList.remove('hidden');\\n          document.getElementById('treeWrap').classList.add('hidden');\\n          return;\\n        }\\n        document.getElementById('requestEmpty').classList.add('hidden');\\n        document.getElementById('treeWrap').classList.remove('hidden');\\n        buildNumbering();\\n        renderTree();\\n      } catch(e) {\\n        document.getElementById('treeBody').innerHTML = '\\u003ctr\\u003e\\u003ctd colspan=\\\"13\\\" class=\\\"text-red-400 text-sm py-4\\\"\\u003eFailed to load requests.\\u003c/td\\u003e\\u003c/tr\\u003e';\\n      }\\n    }\\n\\n    // Build hierarchical numbering (1, 1.1, 1.1.1, etc.)\\n    function buildNumbering() {\\n      numberMap = {};\\n      const childrenOf = {};\\n      for (const item of treeData) {\\n        if (!childrenOf[item.parent_id]) childrenOf[item.parent_id] = [];\\n        childrenOf[item.parent_id].push(item.entry_id);\\n      }\\n      const counters = {};\\n      for (const item of treeData) {\\n        if (!counters[item.parent_id]) counters[item.parent_id] = 0;\\n        counters[item.parent_id]++;\\n        const parentNum = numberMap[item.parent_id];\\n        if (item.type === 'request_list') {\\n          numberMap[item.entry_id] = '' + counters[item.parent_id];\\n        } else {\\n          numberMap[item.entry_id] = (parentNum ? parentNum + '.' : '') + counters[item.parent_id];\\n        }\\n      }\\n    }\\n\\n    // Check if an item or any ancestor is collapsed\\n    function isHidden(item) {\\n      const parentMap = {};\\n      for (const t of treeData) parentMap[t.entry_id] = t.parent_id;\\n      let pid = item.parent_id;\\n      while (pid \\u0026\\u0026 pid !== projectID) {\\n        if (collapsed.has(pid)) return true;\\n        pid = parentMap[pid];\\n      }\\n      return false;\\n    }\\n\\n    // Auto-summarize long request text for Summary View.\\n    // Short text (≤8 words or ≤60 chars) is returned as-is.\\n    // Longer text: extract the first complete sentence; if still long, trim to ~80 chars at a word boundary.\\n    function summarize(text) {\\n      if (!text) return text;\\n      const wordCount = text.trim().split(/\\\\s+/).length;\\n      if (wordCount \\u003c= 8 || text.length \\u003c= 60) return text;\\n      // Try to find a sentence boundary\\n      const m = text.match(/^(.+?[.!?])(?:\\\\s|$)/);\\n      if (m \\u0026\\u0026 m[1].length \\u003c= 120) return m[1];\\n      // No clean sentence — trim to ~80 chars at last word boundary\\n      if (text.length \\u003c= 80) return text;\\n      const cut = text.lastIndexOf(' ', 80);\\n      return text.substring(0, cut \\u003e 20 ? cut : 80) + '…';\\n    }\\n\\n    function renderTree() {\\n      const tbody = document.getElementById('treeBody');\\n      let html = '';\\n      for (const item of treeData) {\\n        if (isHidden(item)) continue;\\n        const d = item.data || {};\\n        const num = numberMap[item.entry_id] || '';\\n        const indent = Math.max(0, item.depth - 1) * 20;\\n        const isCollapsible = item.type === 'request_list' || item.type === 'section';\\n        const isCollapsed = collapsed.has(item.entry_id);\\n        const chevron = isCollapsible\\n          ? `\\u003cspan class=\\\"collapse-btn ${isCollapsed?'collapsed':''}\\\" onclick=\\\"toggleCollapse('${item.entry_id}')\\\"\\u003e\\\\u25BC\\u003c/span\\u003e`\\n          : '';\\n\\n        if (item.type === 'request_list') {\\n          const name = d.name || 'Request List';\\n          const childCount = item.children_count || 0;\\n          const visOrgId = d.visibility_org_id || '';\\n          const visBadge = visOrgId\\n            ? `\\u003cspan style=\\\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:10px;background:rgba(251,146,60,.15);color:#fb923c;margin-left:8px\\\"\\u003eRestricted\\u003c/span\\u003e`\\n            : `\\u003cspan style=\\\"display:inline-block;padding:1px 8px;border-radius:9999px;font-size:10px;background:rgba(74,222,128,.12);color:#4ade80;margin-left:8px\\\"\\u003eAll\\u003c/span\\u003e`;\\n          html += `\\u003ctr class=\\\"row-list\\\" data-id=\\\"${item.entry_id}\\\" data-type=\\\"${item.type}\\\" data-parent=\\\"${item.parent_id}\\\" data-depth=\\\"${item.depth}\\\"\\u003e\\n            \\u003ctd\\u003e\\u003c/td\\u003e\\n            \\u003ctd colspan=\\\"11\\\" style=\\\"padding-left:${indent+10}px\\\"\\u003e\\n              ${chevron}\\n              \\u003cstrong style=\\\"color:var(--ds-tx);margin-left:4px;font-size:13px;font-weight:600\\\"\\u003e${escHtml(name)}\\u003c/strong\\u003e\\n              ${visBadge}\\n              \\u003cspan style=\\\"color:var(--ds-tx3);margin-left:8px;font-size:11px\\\"\\u003e${childCount} items\\u003c/span\\u003e\\n              \\u003cbutton onclick=\\\"editVisibility('${item.entry_id}')\\\" style=\\\"margin-left:6px;background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 4px\\\" title=\\\"Change visibility\\\"\\u003e\\u0026#9881;\\u003c/button\\u003e\\n              \\u003cbutton onclick=\\\"startInlineAdd('section','${item.entry_id}')\\\" style=\\\"margin-left:10px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\\\"\\u003e+ Section\\u003c/button\\u003e\\n              \\u003cbutton onclick=\\\"startInlineAdd('request','${item.entry_id}')\\\" style=\\\"margin-left:4px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\\\"\\u003e+ Request\\u003c/button\\u003e\\n            \\u003c/td\\u003e\\n            \\u003ctd style=\\\"position:relative;text-align:right\\\"\\u003e\\n              \\u003cbutton onclick=\\\"toggleMenu(event,'${item.entry_id}')\\\" style=\\\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\\\"\\u003e\\u0026hellip;\\u003c/button\\u003e\\n              \\u003cdiv id=\\\"menu-${item.entry_id}\\\" class=\\\"actions-menu\\\" style=\\\"display:none\\\"\\u003e\\n                \\u003cbutton onclick=\\\"startInlineAdd('section','${item.entry_id}')\\\"\\u003e+ Add Section\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"startInlineAdd('request','${item.entry_id}')\\\"\\u003e+ Add Request\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"editVisibility('${item.entry_id}')\\\"\\u003eChange Visibility\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"renameListOrSection('${item.entry_id}','list')\\\" style=\\\"border-top:1px solid var(--ds-bd);margin-top:2px;padding-top:6px\\\"\\u003eRename\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"deleteEntry('${item.entry_id}')\\\" style=\\\"color:#f87171\\\"\\u003eDelete List\\u003c/button\\u003e\\n              \\u003c/div\\u003e\\n            \\u003c/td\\u003e\\n          \\u003c/tr\\u003e`;\\n        } else if (item.type === 'section') {\\n          const name = d.name || d.title || 'Section';\\n          html += `\\u003ctr class=\\\"row-section\\\" data-id=\\\"${item.entry_id}\\\" data-type=\\\"${item.type}\\\" data-parent=\\\"${item.parent_id}\\\" data-depth=\\\"${item.depth}\\\"\\n                draggable=\\\"true\\\" ondragstart=\\\"onDragStart(event)\\\" ondragover=\\\"onDragOver(event)\\\" ondragleave=\\\"onDragLeave(event)\\\" ondrop=\\\"onDrop(event)\\\"\\u003e\\n            \\u003ctd class=\\\"drag-handle\\\" style=\\\"color:var(--ds-tx3);font-size:11px;cursor:grab\\\"\\u003e\\\\u2630\\u003c/td\\u003e\\n            \\u003ctd class=\\\"row-num\\\" style=\\\"padding-left:${indent}px\\\"\\u003e${escHtml(num)}\\u003c/td\\u003e\\n            \\u003ctd colspan=\\\"10\\\" style=\\\"padding-left:${indent}px\\\"\\u003e\\n              ${chevron}\\n              \\u003cspan style=\\\"color:var(--ds-tx);font-weight:600;font-size:13px;margin-left:4px\\\"\\u003e${escHtml(name)}\\u003c/span\\u003e\\n              \\u003cspan style=\\\"color:var(--ds-tx3);margin-left:8px;font-size:11px\\\"\\u003e${item.children_count||0} requests\\u003c/span\\u003e\\n              \\u003cbutton onclick=\\\"startInlineAdd('request','${item.entry_id}')\\\" style=\\\"margin-left:10px;background:none;border:1px solid var(--ds-bd);border-radius:4px;cursor:pointer;color:var(--ds-tx3);font-size:11px;padding:1px 8px\\\"\\u003e+ Request\\u003c/button\\u003e\\n            \\u003c/td\\u003e\\n            \\u003ctd style=\\\"position:relative;text-align:right\\\"\\u003e\\n              \\u003cbutton onclick=\\\"toggleMenu(event,'${item.entry_id}')\\\" style=\\\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\\\"\\u003e\\u0026hellip;\\u003c/button\\u003e\\n              \\u003cdiv id=\\\"menu-${item.entry_id}\\\" class=\\\"actions-menu\\\" style=\\\"display:none\\\"\\u003e\\n                \\u003cbutton onclick=\\\"startInlineAdd('request','${item.entry_id}')\\\"\\u003e+ Add Request\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"renameListOrSection('${item.entry_id}','section')\\\" style=\\\"border-top:1px solid var(--ds-bd);margin-top:2px;padding-top:6px\\\"\\u003eRename\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"deleteEntry('${item.entry_id}')\\\" style=\\\"color:#f87171\\\"\\u003eDelete Section\\u003c/button\\u003e\\n              \\u003c/div\\u003e\\n            \\u003c/td\\u003e\\n          \\u003c/tr\\u003e`;\\n        } else {\\n          // request row\\n          const title = d.title || d.description || 'Untitled';\\n          const buyerComment = d.buyer_comment || '';\\n          const sellerComment = d.seller_comment || '';\\n          const assignee = d.assignee || d.assigned_to || '';\\n          const dueDate = d.due_date || d.completed_date || '';\\n          const label = d.label || '';\\n          const curPri = d.priority || 'medium';\\n          const curStat = d.status || 'open';\\n          const eid = item.entry_id;\\n\\n          // Title with detail mode\\n          let titleHtml;\\n          if (detailMode) {\\n            titleHtml = `\\u003ca href=\\\"/app/requests/${eid}\\\" style=\\\"color:var(--ds-tx);text-decoration:none\\\" class=\\\"hover:underline\\\"\\u003e${escHtml(title)}\\u003c/a\\u003e`;\\n            if (d.description) {\\n              titleHtml += `\\u003cdiv style=\\\"color:var(--ds-tx3);font-size:12px;margin-top:2px;line-height:1.4\\\"\\u003e${escHtml(d.description)}\\u003c/div\\u003e`;\\n            }\\n          } else {\\n            titleHtml = `\\u003ca href=\\\"/app/requests/${eid}\\\" style=\\\"color:var(--ds-tx);text-decoration:none;line-height:1.4\\\" class=\\\"hover:underline\\\"\\u003e${escHtml(summarize(title))}\\u003c/a\\u003e`;\\n          }\\n\\n          // Priority select\\n          const priOpts = ['critical','high','medium','low'].map(o =\\u003e\\n            `\\u003coption value=\\\"${o}\\\" ${o===curPri?'selected':''}\\u003e${o}\\u003c/option\\u003e`\\n          ).join('');\\n          const prioritySelect = `\\u003cselect class=\\\"badge\\\" style=\\\"${priStyles[curPri]||priStyles.medium};border:none;cursor:pointer;font-size:11px;padding:2px 14px 2px 6px;border-radius:9999px;-webkit-appearance:none;appearance:none;outline:none;font-weight:500\\\" onchange=\\\"updateField('${eid}','priority',this.value)\\\"\\u003e${priOpts}\\u003c/select\\u003e`;\\n\\n          // Status select\\n          const statOpts = ['open','in_process','partial','complete'].map(o =\\u003e\\n            `\\u003coption value=\\\"${o}\\\" ${o===curStat?'selected':''}\\u003e${statLabels[o]}\\u003c/option\\u003e`\\n          ).join('');\\n          const statusSelect = `\\u003cselect class=\\\"badge\\\" style=\\\"${statStyles[curStat]||statStyles.open};border:none;cursor:pointer;font-size:11px;padding:2px 14px 2px 6px;border-radius:9999px;-webkit-appearance:none;appearance:none;outline:none;font-weight:500\\\" onchange=\\\"updateField('${eid}','status',this.value)\\\"\\u003e${statOpts}\\u003c/select\\u003e`;\\n\\n          html += `\\u003ctr class=\\\"row-request\\\" data-id=\\\"${eid}\\\" data-type=\\\"${item.type}\\\" data-parent=\\\"${item.parent_id}\\\" data-depth=\\\"${item.depth}\\\"\\n                draggable=\\\"true\\\" ondragstart=\\\"onDragStart(event)\\\" ondragover=\\\"onDragOver(event)\\\" ondragleave=\\\"onDragLeave(event)\\\" ondrop=\\\"onDrop(event)\\\"\\u003e\\n            \\u003ctd class=\\\"drag-handle\\\" style=\\\"color:var(--ds-tx3);font-size:11px\\\"\\u003e\\\\u2630\\u003c/td\\u003e\\n            \\u003ctd class=\\\"row-num\\\" style=\\\"padding-left:${indent}px\\\"\\u003e${escHtml(num)}\\u003c/td\\u003e\\n            \\u003ctd class=\\\"req-title-cell\\\" style=\\\"white-space:normal;max-width:400px;padding-left:${indent}px\\\"\\u003e\\n              ${titleHtml}\\n            \\u003c/td\\u003e\\n            \\u003ctd\\u003e${prioritySelect}\\u003c/td\\u003e\\n            \\u003ctd\\u003e${statusSelect}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"max-width:120px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\\\"\\u003e${assignee ? escHtml(assignee) : '\\\\u2014'}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"color:var(--ds-tx2);font-size:12px\\\"\\u003e${dueDate ? fmtDueDate(dueDate) : '\\\\u2014'}\\u003c/td\\u003e\\n            \\u003ctd\\u003e${labelPill(label)}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"text-align:center\\\"\\u003e\\n              ${item.answer_count \\u003e 0\\n                ? `\\u003cspan class=\\\"badge\\\" style=\\\"background:rgba(74,222,128,.15);color:#4ade80;cursor:pointer\\\" onclick=\\\"showAttachModalById('${eid}')\\\"\\u003e${item.answer_count}\\u003c/span\\u003e`\\n                : `\\u003cspan style=\\\"color:var(--ds-tx3);cursor:pointer;font-size:12px\\\" onclick=\\\"showAttachModalById('${eid}')\\\"\\u003e\\u0026mdash;\\u003c/span\\u003e`}\\n            \\u003c/td\\u003e\\n            \\u003ctd style=\\\"max-width:140px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\\\" title=\\\"${escHtml(buyerComment)}\\\"\\u003e${escHtml(buyerComment)}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"max-width:140px;overflow:hidden;text-overflow:ellipsis;color:var(--ds-tx2);font-size:12px\\\" title=\\\"${escHtml(sellerComment)}\\\"\\u003e${escHtml(sellerComment)}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"color:var(--ds-tx3);font-size:12px\\\"\\u003e${fmtDate(item.created_at)}\\u003c/td\\u003e\\n            \\u003ctd style=\\\"position:relative\\\"\\u003e\\n              \\u003cbutton onclick=\\\"toggleMenu(event,'${eid}')\\\" style=\\\"background:none;border:none;cursor:pointer;color:var(--ds-tx3);font-size:16px;padding:2px 6px\\\"\\u003e\\u0026hellip;\\u003c/button\\u003e\\n              \\u003cdiv id=\\\"menu-${eid}\\\" class=\\\"actions-menu\\\" style=\\\"display:none\\\"\\u003e\\n                \\u003cbutton onclick=\\\"showAttachModalById('${eid}')\\\"\\u003eAttach Answer\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"editRequest('${eid}', event)\\\"\\u003eEdit\\u003c/button\\u003e\\n                \\u003cbutton onclick=\\\"deleteEntry('${eid}')\\\"\\u003eDelete\\u003c/button\\u003e\\n              \\u003c/div\\u003e\\n            \\u003c/td\\u003e\\n          \\u003c/tr\\u003e`;\\n        }\\n      }\\n      if (!html) html = '\\u003ctr\\u003e\\u003ctd colspan=\\\"13\\\" class=\\\"text-[#b0bec5] text-sm py-4\\\"\\u003eNo requests found.\\u003c/td\\u003e\\u003c/tr\\u003e';\\n      tbody.innerHTML = html;\\n    }\\n\\n    // ---- Inline add section / request ----\\n    let inlineAddRow = null;\\n\\n    function startInlineAdd(type, parentId) {\\n      // Remove any existing inline row\\n      const existing = document.getElementById('inlineAddRow');\\n      if (existing) existing.remove();\\n\\n      const label = type === 'section' ? 'Section name' : 'Request text';\\n      const placeholder = type === 'section' ? 'e.g. Legal, Financial, Operations' : 'Enter the full request text — Dealspace will automatically show a concise summary in Summary View';\\n\\n      const tr = document.createElement('tr');\\n      tr.id = 'inlineAddRow';\\n      tr.setAttribute('data-parent', parentId);\\n      tr.setAttribute('data-addtype', type);\\n      tr.innerHTML = `\\n        \\u003ctd\\u003e\\u003c/td\\u003e\\n        \\u003ctd colspan=\\\"12\\\" style=\\\"padding:8px 10px 8px 40px\\\"\\u003e\\n          \\u003cdiv style=\\\"display:flex;align-items:center;gap:8px\\\"\\u003e\\n            \\u003cinput id=\\\"inlineAddInput\\\" type=\\\"text\\\" placeholder=\\\"${placeholder}\\\"\\n              style=\\\"flex:1;padding:6px 10px;border-radius:6px;border:1px solid var(--ds-ac);background:var(--ds-bg);color:var(--ds-tx);font-size:13px;outline:none\\\"\\n              onkeydown=\\\"if(event.key==='Enter')submitInlineAdd();if(event.key==='Escape')cancelInlineAdd()\\\"\\u003e\\n            \\u003cbutton onclick=\\\"submitInlineAdd()\\\" style=\\\"padding:5px 14px;border-radius:6px;border:none;background:var(--ds-ac);color:var(--ds-act);font-size:12px;font-weight:600;cursor:pointer\\\"\\u003eAdd\\u003c/button\\u003e\\n            \\u003cbutton onclick=\\\"cancelInlineAdd()\\\" style=\\\"padding:5px 10px;border-radius:6px;border:1px solid var(--ds-bd);background:none;color:var(--ds-tx3);font-size:12px;cursor:pointer\\\"\\u003eCancel\\u003c/button\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/td\\u003e`;\\n\\n      // Insert after the parent row\\n      const parentRow = document.querySelector(`tr[data-id=\\\"${parentId}\\\"]`);\\n      if (parentRow \\u0026\\u0026 parentRow.nextSibling) {\\n        parentRow.parentNode.insertBefore(tr, parentRow.nextSibling);\\n      } else if (parentRow) {\\n        parentRow.parentNode.appendChild(tr);\\n      }\\n\\n      document.getElementById('inlineAddInput').focus();\\n    }\\n\\n    function cancelInlineAdd() {\\n      const el = document.getElementById('inlineAddRow');\\n      if (el) el.remove();\\n    }\\n\\n    async function submitInlineAdd() {\\n      const tr = document.getElementById('inlineAddRow');\\n      if (!tr) return;\\n      const type = tr.getAttribute('data-addtype');\\n      const parentId = tr.getAttribute('data-parent');\\n      const text = document.getElementById('inlineAddInput').value.trim();\\n      if (!text) return;\\n\\n      try {\\n        const endpoint = type === 'section'\\n          ? `/api/projects/${projectID}/sections`\\n          : `/api/projects/${projectID}/requests/new`;\\n\\n        const res = await fetchAPI(endpoint, {\\n          method: 'POST',\\n          headers: {'Content-Type': 'application/json'},\\n          body: JSON.stringify({ name: text, title: text, parent_id: parentId })\\n        });\\n        if (!res.ok) throw new Error('Failed');\\n        cancelInlineAdd();\\n        await loadRequestTree();\\n      } catch(e) {\\n        document.getElementById('inlineAddInput').style.borderColor = 'red';\\n      }\\n    }\\n\\n    async function renameListOrSection(id, type) {\\n      // Close the menu\\n      const menu = document.getElementById('menu-' + id);\\n      if (menu) menu.style.display = 'none';\\n      openMenuId = null;\\n\\n      const item = treeData.find(t =\\u003e t.entry_id === id);\\n      if (!item) return;\\n      const d = item.data || {};\\n      const currentName = d.name || d.title || '';\\n      const newName = prompt('Rename ' + (type === 'list' ? 'list' : 'section') + ':', currentName);\\n      if (!newName || !newName.trim() || newName.trim() === currentName) return;\\n\\n      const updated = { ...d, name: newName.trim(), title: newName.trim() };\\n      item.data = updated;\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + id, {\\n          method: 'PUT',\\n          body: JSON.stringify({ data: JSON.stringify(updated), version: item.version || 0 })\\n        });\\n        if (res.ok) {\\n          const result = await res.json();\\n          if (result.version) item.version = result.version;\\n          renderTree();\\n        }\\n      } catch(e) {\\n        loadRequestTree();\\n      }\\n    }\\n\\n    function toggleCollapse(id) {\\n      if (collapsed.has(id)) collapsed.delete(id); else collapsed.add(id);\\n      renderTree();\\n    }\\n\\n    // ---- Inline field update ----\\n    async function updateField(entryId, field, value) {\\n      const item = treeData.find(t =\\u003e t.entry_id === entryId);\\n      if (!item) return;\\n      const d = { ...(item.data || {}), [field]: value };\\n      item.data = d;\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + entryId, {\\n          method: 'PUT',\\n          body: JSON.stringify({ data: JSON.stringify(d), version: item.version || 0 })\\n        });\\n        if (res.ok) {\\n          const result = await res.json();\\n          if (result.version) item.version = result.version;\\n          renderTree();\\n        }\\n      } catch(e) {\\n        console.error('Update failed:', e);\\n        loadRequestTree();\\n      }\\n    }\\n\\n    // ---- Inline edit request title ----\\n    function editRequest(id, e) {\\n      if (e) e.stopPropagation();\\n      if (openMenuId) {\\n        const m = document.getElementById('menu-' + openMenuId);\\n        if (m) m.style.display = 'none';\\n        openMenuId = null;\\n      }\\n      const item = treeData.find(t =\\u003e t.entry_id === id);\\n      if (!item) return;\\n      const d = item.data || {};\\n      const title = d.title || d.description || 'Untitled';\\n      const row = document.querySelector(`tr[data-id=\\\"${id}\\\"]`);\\n      if (!row) return;\\n      const titleCell = row.querySelector('.req-title-cell');\\n      if (!titleCell) return;\\n      row.dataset.originalTitleHtml = titleCell.innerHTML;\\n      titleCell.innerHTML = `\\u003cdiv style=\\\"display:flex;flex-direction:column;gap:4px;width:100%\\\"\\u003e\\n        \\u003ctextarea rows=\\\"2\\\" style=\\\"width:100%;padding:6px 8px;background:#0a1628;border:1px solid rgba(201,168,76,.4);border-radius:6px;color:white;font-size:13px;outline:none;resize:vertical;line-height:1.4;font-family:inherit\\\" id=\\\"edit-title-${id}\\\"\\u003e\\u003c/textarea\\u003e\\n        \\u003cdiv style=\\\"display:flex;gap:4px\\\"\\u003e\\n          \\u003cbutton onclick=\\\"saveEditRequest('${id}')\\\" style=\\\"padding:3px 12px;background:#c9a84c;color:#0a1628;border:none;border-radius:4px;font-size:11px;font-weight:600;cursor:pointer\\\"\\u003eSave\\u003c/button\\u003e\\n          \\u003cbutton onclick=\\\"cancelEditRequest('${id}')\\\" style=\\\"padding:3px 10px;background:rgba(255,255,255,.08);color:white;border:none;border-radius:4px;font-size:11px;cursor:pointer\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e`;\\n      const input = document.getElementById('edit-title-' + id);\\n      if (input) { input.value = title; input.focus(); input.setSelectionRange(input.value.length, input.value.length); }\\n    }\\n\\n    function cancelEditRequest(id) {\\n      const row = document.querySelector(`tr[data-id=\\\"${id}\\\"]`);\\n      if (!row) return;\\n      const titleCell = row.querySelector('.req-title-cell');\\n      if (titleCell \\u0026\\u0026 row.dataset.originalTitleHtml) titleCell.innerHTML = row.dataset.originalTitleHtml;\\n    }\\n\\n    async function saveEditRequest(id) {\\n      const input = document.getElementById('edit-title-' + id);\\n      if (!input) return;\\n      const newTitle = input.value.trim();\\n      if (!newTitle) return;\\n      const item = treeData.find(t =\\u003e t.entry_id === id);\\n      if (!item) return;\\n      const d = { ...(item.data || {}), title: newTitle };\\n      item.data = d;\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + id, {\\n          method: 'PUT',\\n          body: JSON.stringify({ data: JSON.stringify(d), version: item.version || 0 })\\n        });\\n        if (res.ok) {\\n          const result = await res.json();\\n          if (result.version) item.version = result.version;\\n          renderTree();\\n        }\\n      } catch(e) {\\n        console.error('Save failed:', e);\\n        cancelEditRequest(id);\\n      }\\n    }\\n\\n    // ---- Actions menu ----\\n    function toggleMenu(e, id) {\\n      e.stopPropagation();\\n      if (openMenuId \\u0026\\u0026 openMenuId !== id) {\\n        const prev = document.getElementById('menu-' + openMenuId);\\n        if (prev) prev.style.display = 'none';\\n      }\\n      const menu = document.getElementById('menu-' + id);\\n      if (menu) {\\n        menu.style.display = menu.style.display === 'none' ? 'block' : 'none';\\n        openMenuId = menu.style.display === 'block' ? id : null;\\n      }\\n    }\\n    document.addEventListener('click', () =\\u003e {\\n      if (openMenuId) {\\n        const m = document.getElementById('menu-' + openMenuId);\\n        if (m) m.style.display = 'none';\\n        openMenuId = null;\\n      }\\n    });\\n\\n    async function deleteEntry(entryID) {\\n      if (!confirm('Delete this entry?')) return;\\n      await fetchAPI('/api/projects/' + projectID + '/entries/' + entryID, { method: 'DELETE' });\\n      loadRequestTree();\\n    }\\n\\n    // ---- Drag \\u0026 Drop ----\\n    let dragEntryId = null;\\n\\n    function onDragStart(e) {\\n      dragEntryId = e.currentTarget.dataset.id;\\n      e.dataTransfer.effectAllowed = 'move';\\n      e.currentTarget.style.opacity = '0.5';\\n    }\\n\\n    function onDragOver(e) {\\n      e.preventDefault();\\n      e.dataTransfer.dropEffect = 'move';\\n      const tr = e.currentTarget;\\n      tr.classList.remove('drop-above', 'drop-below', 'drop-into');\\n      const rect = tr.getBoundingClientRect();\\n      const y = e.clientY - rect.top;\\n      const pct = y / rect.height;\\n      if (tr.dataset.type === 'section' \\u0026\\u0026 pct \\u003e 0.25 \\u0026\\u0026 pct \\u003c 0.75) {\\n        tr.classList.add('drop-into');\\n      } else if (pct \\u003c 0.5) {\\n        tr.classList.add('drop-above');\\n      } else {\\n        tr.classList.add('drop-below');\\n      }\\n    }\\n\\n    function onDragLeave(e) {\\n      e.currentTarget.classList.remove('drop-above', 'drop-below', 'drop-into');\\n    }\\n\\n    async function onDrop(e) {\\n      e.preventDefault();\\n      const tr = e.currentTarget;\\n      tr.classList.remove('drop-above', 'drop-below', 'drop-into');\\n      document.querySelectorAll('#reqTree tr').forEach(r =\\u003e r.style.opacity = '');\\n\\n      if (!dragEntryId || dragEntryId === tr.dataset.id) return;\\n\\n      const targetId = tr.dataset.id;\\n      const targetType = tr.dataset.type;\\n      const rect = tr.getBoundingClientRect();\\n      const y = e.clientY - rect.top;\\n      const pct = y / rect.height;\\n\\n      let newParentId, position;\\n\\n      if (targetType === 'section' \\u0026\\u0026 pct \\u003e 0.25 \\u0026\\u0026 pct \\u003c 0.75) {\\n        newParentId = targetId;\\n        const children = treeData.filter(t =\\u003e t.parent_id === targetId);\\n        position = children.length;\\n      } else {\\n        newParentId = tr.dataset.parent;\\n        const siblings = treeData.filter(t =\\u003e t.parent_id === newParentId);\\n        const targetIdx = siblings.findIndex(s =\\u003e s.entry_id === targetId);\\n        position = pct \\u003c 0.5 ? targetIdx : targetIdx + 1;\\n      }\\n\\n      try {\\n        await fetchAPI('/api/projects/' + projectID + '/entries/' + dragEntryId + '/move', {\\n          method: 'POST',\\n          body: JSON.stringify({ parent_id: newParentId, position })\\n        });\\n        await loadRequestTree();\\n      } catch(e) {\\n        console.error('Move failed:', e);\\n      }\\n      dragEntryId = null;\\n    }\\n\\n    // ---- Attach answer modal ----\\n    function showAttachModalById(reqId) {\\n      const item = treeData.find(t =\\u003e t.entry_id === reqId);\\n      const d = item ? (item.data || {}) : {};\\n      const title = d.title || d.description || 'Untitled';\\n      showAttachModal(reqId, title);\\n    }\\n\\n    function showAttachModal(reqId, reqTitle) {\\n      attachRequestId = reqId;\\n      document.getElementById('attachReqTitle').textContent = reqTitle;\\n      document.getElementById('attachModal').style.display = 'flex';\\n      document.getElementById('attachSearch').value = '';\\n      document.getElementById('attachFile').value = '';\\n      loadAnswersForPicker('');\\n      loadLinkedAnswers(reqId);\\n    }\\n    function closeAttachModal() { document.getElementById('attachModal').style.display = 'none'; attachRequestId = null; }\\n\\n    let searchTimeout;\\n    function searchAnswers(q) {\\n      clearTimeout(searchTimeout);\\n      searchTimeout = setTimeout(() =\\u003e loadAnswersForPicker(q), 200);\\n    }\\n\\n    async function loadAnswersForPicker(q) {\\n      const list = document.getElementById('attachList');\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/answers?q=' + encodeURIComponent(q));\\n        const answers = await res.json();\\n        if (!answers || answers.length === 0) {\\n          list.innerHTML = '\\u003cdiv class=\\\"text-[#b0bec5] text-sm py-2\\\"\\u003eNo documents found.\\u003c/div\\u003e';\\n          return;\\n        }\\n        list.innerHTML = answers.map(a =\\u003e {\\n          const d = a.data || {};\\n          const name = d.filename || d.name || d.title || 'Document';\\n          const objectID = d.object_id || '';\\n          const previewUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '/preview?filename=' + encodeURIComponent(name) : '';\\n          return `\\u003cdiv class=\\\"flex items-center justify-between px-3 py-2 rounded-lg hover:bg-white/[0.04] transition\\\"\\u003e\\n            \\u003cspan class=\\\"text-white text-sm truncate flex-1\\\"\\u003e${escHtml(name)}\\u003c/span\\u003e\\n            \\u003cdiv class=\\\"flex items-center gap-1.5 shrink-0 ml-2\\\"\\u003e\\n              ${previewUrl ? `\\u003ca href=\\\"${previewUrl}\\\" target=\\\"_blank\\\" class=\\\"px-2 py-1 bg-white/[0.06] text-[#b0bec5] hover:text-white rounded text-xs font-medium transition\\\"\\u003ePreview\\u003c/a\\u003e` : ''}\\n              \\u003cbutton onclick=\\\"linkAnswer('${a.entry_id}')\\\" class=\\\"px-3 py-1 bg-[#c9a84c]/20 text-[#c9a84c] rounded text-xs font-medium hover:bg-[#c9a84c]/30 transition\\\"\\u003eLink\\u003c/button\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/div\\u003e`;\\n        }).join('');\\n      } catch(e) {\\n        list.innerHTML = '\\u003cdiv class=\\\"text-red-400 text-sm py-2\\\"\\u003eFailed to load.\\u003c/div\\u003e';\\n      }\\n    }\\n\\n    async function loadLinkedAnswers(reqId) {\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/requests/' + reqId + '/links');\\n        const links = await res.json();\\n        if (links \\u0026\\u0026 links.length \\u003e 0) {\\n          const linkedHtml = links.map(l =\\u003e {\\n            const d = l.data || {};\\n            const name = d.filename || d.name || 'Document';\\n            return `\\u003cdiv class=\\\"flex items-center justify-between px-3 py-2 rounded-lg bg-green-500/5 border border-green-500/10\\\"\\u003e\\n              \\u003cspan class=\\\"text-green-300 text-sm truncate flex-1\\\"\\u003e${escHtml(name)}\\u003c/span\\u003e\\n              \\u003cbutton onclick=\\\"unlinkAnswer('${l.answer_id}')\\\" class=\\\"ml-2 px-3 py-1 bg-red-500/20 text-red-300 rounded text-xs font-medium hover:bg-red-500/30 transition\\\"\\u003eUnlink\\u003c/button\\u003e\\n            \\u003c/div\\u003e`;\\n          }).join('');\\n          const list = document.getElementById('attachList');\\n          list.innerHTML = '\\u003cdiv class=\\\"text-[#b0bec5] text-xs font-medium mb-1 mt-1\\\"\\u003eLINKED\\u003c/div\\u003e' + linkedHtml + '\\u003cdiv class=\\\"border-t border-white/[0.08] my-2\\\"\\u003e\\u003c/div\\u003e\\u003cdiv class=\\\"text-[#b0bec5] text-xs font-medium mb-1\\\"\\u003eAVAILABLE\\u003c/div\\u003e' + list.innerHTML;\\n        }\\n      } catch(e) {}\\n    }\\n\\n    async function linkAnswer(answerId) {\\n      if (!attachRequestId) return;\\n      await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links', {\\n        method: 'POST', body: JSON.stringify({ answer_id: answerId })\\n      });\\n      closeAttachModal();\\n      loadRequestTree();\\n    }\\n\\n    async function unlinkAnswer(answerId) {\\n      if (!attachRequestId) return;\\n      await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links/' + answerId, { method: 'DELETE' });\\n      showAttachModal(attachRequestId, document.getElementById('attachReqTitle').textContent);\\n    }\\n\\n    async function uploadAndLink() {\\n      const fileInput = document.getElementById('attachFile');\\n      const file = fileInput.files[0];\\n      if (!file || !attachRequestId) return;\\n\\n      const fd = new FormData();\\n      fd.append('file', file);\\n      const uploadRes = await fetch('/api/projects/' + projectID + '/objects', {\\n        method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: fd\\n      });\\n      const uploadData = await uploadRes.json();\\n      if (!uploadRes.ok) { alert('Upload failed'); return; }\\n\\n      const answerData = JSON.stringify({ filename: file.name, object_id: uploadData.object_id, name: file.name });\\n      const entryRes = await fetchAPI('/api/projects/' + projectID + '/entries', {\\n        method: 'POST',\\n        body: JSON.stringify({ project_id: projectID, parent_id: projectID, type: 'answer', depth: 1, data: answerData, stage: 'dataroom' })\\n      });\\n      const entry = await entryRes.json();\\n\\n      if (entry.entry_id) {\\n        await fetchAPI('/api/projects/' + projectID + '/requests/' + attachRequestId + '/links', {\\n          method: 'POST', body: JSON.stringify({ answer_id: entry.entry_id })\\n        });\\n      }\\n\\n      closeAttachModal();\\n      loadRequestTree();\\n    }\\n\\n    // ---- Orgs + Team ----\\n    // ---- Organizations ----\\n    let allGlobalOrgs = [];\\n\\n    let _dealOrgsMap = {};\\n    function imgErr(img) {\\n      const w = img.parentElement;\\n      w.className = 'w-11 h-11 rounded-full shrink-0 overflow-hidden bg-[#c9a84c]/20 flex items-center justify-center';\\n      w.innerHTML = '\\u003cspan class=\\\"text-[#c9a84c] font-semibold\\\"\\u003e' + escHtml(img.dataset.initial || '?') + '\\u003c/span\\u003e';\\n    }\\n    function renderMemberCard(dealOrgId, m, mi) {\\n      const initial = (m.name || m.email || '?')[0].toUpperCase();\\n      let avatarHtml;\\n      if (m.photo) {\\n        avatarHtml = '\\u003cdiv class=\\\"w-11 h-11 rounded-full shrink-0 overflow-hidden\\\"\\u003e'\\n          + '\\u003cimg src=\\\"' + escHtml(m.photo) + '\\\" class=\\\"w-full h-full object-cover\\\" data-initial=\\\"' + escHtml(initial) + '\\\" onerror=\\\"imgErr(this)\\\"\\u003e'\\n          + '\\u003c/div\\u003e';\\n      } else {\\n        avatarHtml = '\\u003cdiv class=\\\"w-11 h-11 rounded-full shrink-0 overflow-hidden bg-[#c9a84c]/20 flex items-center justify-center\\\"\\u003e'\\n          + '\\u003cspan class=\\\"text-[#c9a84c] font-semibold\\\"\\u003e' + escHtml(initial) + '\\u003c/span\\u003e\\u003c/div\\u003e';\\n      }\\n      const nameHtml = '\\u003cdiv class=\\\"text-sm font-medium truncate\\\" style=\\\"color:var(--ds-tx)\\\"\\u003e' + escHtml(m.name || m.email || '') + '\\u003c/div\\u003e';\\n      const titleHtml = m.title ? '\\u003cdiv class=\\\"text-xs truncate\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003e' + escHtml(m.title) + '\\u003c/div\\u003e' : '';\\n      return '\\u003cdiv class=\\\"flex items-center gap-3 cursor-pointer hover:opacity-80 transition\\\" onclick=\\\"openPersonPanel(\\\\'' + dealOrgId + '\\\\', ' + mi + ')\\\"\\u003e'\\n        + avatarHtml + '\\u003cdiv class=\\\"min-w-0\\\"\\u003e' + nameHtml + titleHtml + '\\u003c/div\\u003e\\u003c/div\\u003e';\\n    }\\n\\n    async function loadOrgs() {\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/orgs');\\n        const orgs = await res.json();\\n        _dealOrgsMap = {};\\n        if (Array.isArray(orgs)) orgs.forEach(o =\\u003e {\\n          o._raw = { org_id: o.org_id, role: o.role, domain_lock: o.domain_lock, permissions: o.permissions, members: o.members };\\n          // Store org detail fields for edit modal\\n          o.org_details = {\\n            name: o.org_name || '', description: o.org_description || '', role: o.role || '',\\n            industry: o.org_industry || '', website: o.org_website || '', phone: o.org_phone || '',\\n            address: o.org_address || '', city: o.org_city || '', state: o.org_state || '',\\n            founded: o.org_founded || '', linkedin: o.org_linkedin || '', logo: o.org_logo || '',\\n          };\\n          _dealOrgsMap[o.deal_org_id] = o;\\n        });\\n        const list = document.getElementById('orgList');\\n        if (!orgs || orgs.length === 0) {\\n          list.innerHTML = '\\u003cdiv class=\\\"text-[#b0bec5] text-sm\\\"\\u003eNo organizations added yet. Use \\\"+ Add Org\\\" to add parties to this deal.\\u003c/div\\u003e';\\n          return;\\n        }\\n        list.innerHTML = orgs.map(o =\\u003e {\\n          const rc = roleColors[o.role] || 'bg-gray-500/20 text-gray-300';\\n          const roleLabel = { buyer: 'Buyer', seller: 'Seller', ib: 'IB Advisor', advisor: 'Advisor' }[o.role] || o.role || '?';\\n          const perms = o.permissions || {};\\n          const members = o.members || [];\\n          const dlBadge = { full: '📥 Full download', watermark: '📥 Watermarked', none: '🚫 No download' }[perms.download] || '📥 Watermarked';\\n          const orgDesc = o.org_description || '';\\n          const orgLogo = o.org_logo || '';\\n          const orgIndustry = o.org_industry || '';\\n          return `\\u003cdiv class=\\\"rounded-2xl overflow-hidden\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd)\\\"\\u003e\\n            \\u003c!-- Org header --\\u003e\\n            \\u003cdiv class=\\\"px-6 py-5\\\"\\u003e\\n              \\u003cdiv class=\\\"flex items-start gap-4\\\"\\u003e\\n                ${orgLogo ? `\\u003cimg src=\\\"${escHtml(orgLogo)}\\\" class=\\\"w-14 h-14 rounded-xl object-contain shrink-0 bg-white p-1.5\\\" onerror=\\\"this.style.display='none'\\\"\\u003e` : `\\u003cdiv class=\\\"w-14 h-14 rounded-xl shrink-0 flex items-center justify-center text-2xl font-black\\\" style=\\\"background:var(--ds-ac);color:var(--ds-act)\\\"\\u003e${escHtml((o.org_name||'?')[0])}\\u003c/div\\u003e`}\\n                \\u003cdiv class=\\\"flex-1 min-w-0\\\"\\u003e\\n                  \\u003cdiv class=\\\"flex items-center gap-3\\\"\\u003e\\n                    \\u003ch2 class=\\\"text-2xl font-black tracking-tight\\\" style=\\\"color:var(--ds-tx)\\\"\\u003e${escHtml(o.org_name || 'Unknown')}\\u003c/h2\\u003e\\n                    \\u003cspan class=\\\"px-2.5 py-1 rounded-full text-xs font-semibold ${rc}\\\"\\u003e${roleLabel}\\u003c/span\\u003e\\n                  \\u003c/div\\u003e\\n                  ${orgDesc ? `\\u003cp class=\\\"text-sm mt-1 line-clamp-2\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e${escHtml(orgDesc)}\\u003c/p\\u003e` : ''}\\n                  \\u003cdiv class=\\\"flex flex-wrap gap-3 text-xs mt-2\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003e\\n                    \\u003cspan\\u003e${dlBadge}\\u003c/span\\u003e\\n                    ${perms.upload ? '\\u003cspan\\u003e📤 Upload\\u003c/span\\u003e' : ''}\\n                    \\u003cspan\\u003e${(() =\\u003e {\\n                      const vl = perms.visible_lists;\\n                      if (!vl || vl === 'all') return '📂 All lists';\\n                      if (Array.isArray(vl) \\u0026\\u0026 vl.length === 0) return '📂 No lists';\\n                      if (Array.isArray(vl)) return '📂 ' + vl.length + ' list' + (vl.length \\u003e 1 ? 's' : '');\\n                      return '📂 All lists';\\n                    })()}\\u003c/span\\u003e\\n                  \\u003c/div\\u003e\\n                \\u003c/div\\u003e\\n                \\u003cdiv class=\\\"flex items-center gap-2 shrink-0\\\"\\u003e\\n                  \\u003cbutton onclick=\\\"openPermModalById('${o.deal_org_id}')\\\"\\n                    class=\\\"px-3 py-1.5 rounded-lg text-xs font-medium transition\\\" style=\\\"background:var(--ds-hv);color:var(--ds-tx2)\\\"\\u003ePermissions\\u003c/button\\u003e\\n                  \\u003cdiv class=\\\"relative\\\" id=\\\"orgMenu_${o.deal_org_id}\\\"\\u003e\\n                    \\u003cbutton onclick=\\\"toggleOrgMenu('${o.deal_org_id}')\\\"\\n                      class=\\\"px-2 py-1.5 rounded-lg text-sm font-medium transition\\\" style=\\\"background:var(--ds-hv);color:var(--ds-tx2)\\\" title=\\\"More options\\\"\\u003e⋯\\u003c/button\\u003e\\n                    \\u003cdiv id=\\\"orgMenuDrop_${o.deal_org_id}\\\" class=\\\"hidden absolute right-0 top-full mt-1 rounded-lg shadow-xl z-30 py-1 min-w-[160px]\\\" style=\\\"background:var(--ds-card);border:1px solid var(--ds-bd)\\\"\\u003e\\n                      \\u003cbutton onclick=\\\"openEditOrgModal('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')\\\"\\n                        class=\\\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eEdit details\\u003c/button\\u003e\\n                      \\u003cbutton onclick=\\\"openPermModalById('${o.deal_org_id}');closeOrgMenu('${o.deal_org_id}')\\\"\\n                        class=\\\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\\\" style=\\\"color:var(--ds-tx)\\\"\\u003ePermissions\\u003c/button\\u003e\\n                      \\u003cdiv style=\\\"height:1px;background:var(--ds-bd);margin:4px 0\\\"\\u003e\\u003c/div\\u003e\\n                      \\u003cbutton onclick=\\\"removeDealOrg('${o.deal_org_id}', '${escHtml(orgName)}')\\\"\\n                        class=\\\"w-full text-left px-4 py-2 text-sm transition hover:opacity-80\\\" style=\\\"color:#ef4444\\\"\\u003eRemove from deal\\u003c/button\\u003e\\n                    \\u003c/div\\u003e\\n                  \\u003c/div\\u003e\\n                \\u003c/div\\u003e\\n              \\u003c/div\\u003e\\n            \\u003c/div\\u003e\\n            \\u003c!-- Members --\\u003e\\n            \\u003cdiv class=\\\"border-t px-6 py-4\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n              \\u003cdiv class=\\\"grid gap-3\\\" style=\\\"grid-template-columns: repeat(auto-fill, minmax(180px, 1fr))\\\"\\u003e\\n                ${members.map((m, mi) =\\u003e renderMemberCard(o.deal_org_id, m, mi)).join('')}\\n                \\u003cdiv class=\\\"flex items-center gap-3 cursor-pointer hover:opacity-80 transition\\\" onclick=\\\"openDealMemberPicker('' + o.deal_org_id + '', '' + escHtml(o.org_id) + '', '' + escHtml(o.org_name||'') + '')\\\"\\u003e\\n                  \\u003cdiv class=\\\"w-11 h-11 rounded-full shrink-0 flex items-center justify-center text-xl\\\" style=\\\"background:var(--ds-hv);color:var(--ds-tx2)\\\"\\u003e+\\u003c/div\\u003e\\n                  \\u003cspan class=\\\"text-sm\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eAdd person\\u003c/span\\u003e\\n                \\u003c/div\\u003e\\n              \\u003c/div\\u003e\\n            \\u003c/div\\u003e\\n          \\u003c/div\\u003e`;\\n        }).join('');\\n      } catch(e) { console.error(e); }\\n    }\\n\\n    // ---- Add Party Modal (3-step scrape flow) ----\\n    let scrapedData = null;\\n\\n    function resetAddOrgForm() {\\n      scrapedData = null;\\n      // Step 1\\n      document.getElementById('addOrgEmail').value = '';\\n      document.getElementById('scrapeError').classList.add('hidden');\\n      document.getElementById('scrapeLoading').classList.add('hidden');\\n      document.getElementById('scrapeBtn').disabled = false;\\n      // Hide wizard-only fields\\n      document.getElementById('wizProjectNameWrap').classList.add('hidden');\\n      document.getElementById('wizProjectName').value = '';\\n      // Step 2 — clear all org fields\\n      const clearIds = ['orgName','orgDesc','orgRole','orgIndustry','orgWebsite','orgPhone',\\n                        'orgAddress','orgCity','orgState','orgFounded','orgLinkedIn'];\\n      clearIds.forEach(id =\\u003e { const el = document.getElementById(id); if (el) el.value = ''; });\\n      document.getElementById('orgLogoWrap').classList.add('hidden');\\n      document.getElementById('orgLogoImg').src = '';\\n      // Step 3 — clear people\\n      const pl = document.getElementById('peopleList'); if (pl) pl.innerHTML = '';\\n      const np = document.getElementById('noPeople'); if (np) np.classList.remove('hidden');\\n      // Required field errors\\n      requiredFields.forEach(id =\\u003e document.getElementById(id).classList.remove('field-error'));\\n    }\\n\\n    function openAddOrgModal() {\\n      resetAddOrgForm();\\n      document.getElementById('addOrgTitle').textContent = 'Add Party';\\n      document.getElementById('addOrgHint').textContent = \\\"Enter an email address or domain name \\\\u2014 we'll look up the organization automatically.\\\";\\n      document.getElementById('addOrgSubmitBtn').textContent = 'Add to Deal';\\n      showAddOrgStep(1);\\n      document.getElementById('addOrgModal').classList.remove('hidden');\\n      setTimeout(() =\\u003e document.getElementById('addOrgEmail').focus(), 100);\\n    }\\n\\n    function switchProject(pid) {\\n      document.getElementById('projectMenu').classList.add('hidden');\\n      if (pid \\u0026\\u0026 pid !== projectID) window.location.href = '/app/projects/' + pid;\\n    }\\n    function toggleProjectMenu() {\\n      document.getElementById('projectMenu').classList.toggle('hidden');\\n    }\\n    document.addEventListener('click', e =\\u003e {\\n      if (!document.getElementById('projectSwitcher')?.contains(e.target))\\n        document.getElementById('projectMenu')?.classList.add('hidden');\\n    });\\n\\n    function tmplChanged(el) {\\n      if (el.value === 'none' \\u0026\\u0026 el.checked) {\\n        // \\\"Start empty\\\" unchecks the others\\n        document.querySelectorAll('.tmpl-cb').forEach(cb =\\u003e { if (cb.value !== 'none') cb.checked = false; });\\n      } else if (el.value !== 'none' \\u0026\\u0026 el.checked) {\\n        // Selecting a template unchecks \\\"Start empty\\\"\\n        document.querySelector('.tmpl-cb[value=\\\"none\\\"]').checked = false;\\n      }\\n    }\\n\\n    // ---- New Project Wizard: Create Everything ----\\n    async function createNewProject() {\\n      const btn = document.getElementById('wizCreateBtn');\\n      const projectName = document.getElementById('wizProjectName').value.trim();\\n      const wizErr = document.getElementById('wizError');\\n      if (wizErr) wizErr.classList.add('hidden');\\n      const projNameEl = document.getElementById('wizProjectName');\\n      if (!projectName) {\\n        if (projNameEl) projNameEl.classList.add('field-error');\\n        if (wizErr) { wizErr.textContent = 'Deal name is required.'; wizErr.classList.remove('hidden'); }\\n        return;\\n      }\\n      if (projNameEl) projNameEl.classList.remove('field-error');\\n      if (!validateStep2()) {\\n        if (wizErr) { wizErr.textContent = 'Please fill in all required org fields. Go back to step 2.'; wizErr.classList.remove('hidden'); }\\n        return;\\n      }\\n\\n      btn.disabled = true; btn.textContent = 'Creating...';\\n\\n      try {\\n        // 1. Create project\\n        const projRes = await fetchAPI('/api/projects', {\\n          method: 'POST',\\n          body: JSON.stringify({ name: projectName, deal_type: 'sell_side' })\\n        });\\n        const projData = await projRes.json();\\n        if (!projRes.ok) throw new Error(projData.error || 'Failed to create project');\\n        const newProjectID = projData.project_id || projData.entry_id;\\n\\n        // 2. Add org + members to project\\n        const name = document.getElementById('orgName').value.trim();\\n        const role = document.getElementById('orgRole').value;\\n        // Derive domain: prefer scrapedData.domain, fallback to website field\\n      let domain = scrapedData?.domain || '';\\n      if (!domain) {\\n        const ws = document.getElementById('orgWebsite').value.trim();\\n        if (ws) {\\n          domain = ws.replace(/^https?:\\\\/\\\\//, '').replace(/^www\\\\./, '').split('/')[0];\\n        }\\n      }\\n        const people = scrapedData?.people || [];\\n        const selectedMembers = [];\\n        document.querySelectorAll('.person-cb:checked').forEach(cb =\\u003e {\\n          const p = people[parseInt(cb.dataset.idx)];\\n          if (p) selectedMembers.push({ name: p.name, email: p.email || '', title: p.title || '', phone: p.phone || '', photo: p.photo || '', bio: p.bio || '', linkedin: p.linkedin || '' });\\n        });\\n\\n        const orgRes = await fetchAPI('/api/projects/' + newProjectID + '/orgs/add', {\\n          method: 'POST',\\n          body: JSON.stringify({\\n            name,\\n            domains: domain ? [domain] : [],\\n            role,\\n            logo: scrapedData?.logo || '',\\n            website: document.getElementById('orgWebsite').value,\\n            description: document.getElementById('orgDesc').value,\\n            industry: document.getElementById('orgIndustry').value,\\n            phone: document.getElementById('orgPhone').value,\\n            address: document.getElementById('orgAddress').value,\\n            city: document.getElementById('orgCity').value,\\n            state: document.getElementById('orgState').value,\\n            founded: document.getElementById('orgFounded').value,\\n            linkedin: document.getElementById('orgLinkedIn').value,\\n            members: selectedMembers,\\n            domain_lock: true,\\n          })\\n        });\\n        if (!orgRes.ok) { const e = await orgRes.json(); throw new Error(e.error || 'Failed to add org'); }\\n\\n        // 3. Import selected request list templates\\n        const selectedTemplates = [...document.querySelectorAll('.tmpl-cb:checked')].map(cb =\\u003e cb.value).filter(v =\\u003e v !== 'none');\\n        for (const tmpl of selectedTemplates) {\\n          try {\\n            await fetchAPI('/api/projects/' + newProjectID + '/requests/import-template', {\\n              method: 'POST',\\n              body: JSON.stringify({ template_id: tmpl })\\n            });\\n          } catch(e) { console.error('Template import error:', tmpl, e); }\\n        }\\n\\n        // 4. Show email preview and redirect\\n        closeAddOrgModal();\\n        if (selectedMembers.length \\u003e 0) {\\n          showEmailPreview(selectedMembers, name);\\n        }\\n        // Redirect to the new project\\n        window.location.href = '/app/projects/' + newProjectID;\\n\\n      } catch(e) {\\n        alert(e.message);\\n        btn.disabled = false;\\n        btn.textContent = 'Create Project';\\n      }\\n    }\\n\\n    // ---- Email Preview ----\\n    const EMAIL_WHITELIST = ['@muskepo.com', '@jongsma.me'];\\n    function isWhitelisted(email) {\\n      return EMAIL_WHITELIST.some(d =\\u003e email.toLowerCase().endsWith(d));\\n    }\\n\\n    function showEmailPreview(members, orgName) {\\n      const list = document.getElementById('emailPreviewList');\\n      if (!members.length) { return; }\\n      list.innerHTML = members.map(m =\\u003e {\\n        const whitelisted = m.email \\u0026\\u0026 isWhitelisted(m.email);\\n        return `\\u003cdiv class=\\\"flex items-center gap-3 p-3 rounded-lg\\\" style=\\\"background:var(--ds-hv)\\\"\\u003e\\n          \\u003cdiv class=\\\"flex-1 min-w-0\\\"\\u003e\\n            \\u003cdiv class=\\\"text-sm font-medium\\\" style=\\\"color:var(--ds-tx)\\\"\\u003e${escHtml(m.name || m.email || '?')}\\u003c/div\\u003e\\n            \\u003cdiv class=\\\"text-xs\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e${escHtml(m.email || 'no email')}\\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          ${m.email ? (whitelisted\\n            ? '\\u003cspan class=\\\"text-xs px-2 py-0.5 rounded-full bg-green-500/20 text-green-300 shrink-0\\\"\\u003eWill send\\u003c/span\\u003e'\\n            : '\\u003cspan class=\\\"text-xs px-2 py-0.5 rounded-full bg-yellow-500/20 text-yellow-300 shrink-0\\\"\\u003eBlocked (not whitelisted)\\u003c/span\\u003e'\\n          ) : '\\u003cspan class=\\\"text-xs px-2 py-0.5 rounded-full bg-gray-500/20 text-gray-300 shrink-0\\\"\\u003eNo email\\u003c/span\\u003e'}\\n        \\u003c/div\\u003e`;\\n      }).join('');\\n      document.getElementById('emailPreviewModal').classList.remove('hidden');\\n    }\\n\\n    function closeEmailPreview() {\\n      document.getElementById('emailPreviewModal').classList.add('hidden');\\n    }\\n\\n    function closeAddOrgModal() {\\n      document.getElementById('addOrgModal').classList.add('hidden');\\n      resetAddOrgForm();\\n    }\\n\\n    function showAddOrgStep(n) {\\n      document.getElementById('addOrgStep1').classList.toggle('hidden', n !== 1);\\n      document.getElementById('addOrgStep2').classList.toggle('hidden', n !== 2);\\n      document.getElementById('addOrgStep3').classList.toggle('hidden', n !== 3);\\n      document.getElementById('addOrgStep4').classList.toggle('hidden', n !== 4);\\n      // Clear error banners when navigating\\n      ['step2Error','step3Error','wizError'].forEach(id =\\u003e { const el=document.getElementById(id); if(el) el.classList.add('hidden'); });\\n      if (n === 3) renderPeople();\\n      if (n === 4) {\\n        // Pre-fill project name from org name\\n        const orgName = document.getElementById('orgName').value.trim();\\n        document.getElementById('wizProjectName').value = orgName;\\n      }\\n    }\\n\\n    const requiredFields = ['orgName', 'orgRole'];\\n\\n    function highlightRequired() {\\n      requiredFields.forEach(id =\\u003e {\\n        const el = document.getElementById(id);\\n        if (!el.value.trim()) {\\n          el.classList.add('field-error');\\n        } else {\\n          el.classList.remove('field-error');\\n        }\\n      });\\n    }\\n\\n    function validateStep2() {\\n      highlightRequired();\\n      return requiredFields.every(id =\\u003e document.getElementById(id).value.trim());\\n    }\\n\\n    function showStep2Error(msg) {\\n      const el = document.getElementById('step2Error');\\n      if (!el) return;\\n      el.textContent = msg || 'Please fill in all required fields.';\\n      el.classList.remove('hidden');\\n    }\\n\\n    function hideStep2Error() {\\n      const el = document.getElementById('step2Error');\\n      if (el) el.classList.add('hidden');\\n    }\\n\\n    function advanceFromStep2() {\\n      if (!validateStep2()) {\\n        showStep2Error('Please fill in all required fields (highlighted in red).');\\n        return;\\n      }\\n      hideStep2Error();\\n      showAddOrgStep(3);\\n    }\\n\\n    // Clear red border on input/change\\n    [...requiredFields, 'wizProjectName'].forEach(id =\\u003e {\\n      const el = document.getElementById(id);\\n      if (el) {\\n        const clear = () =\\u003e el.classList.remove('field-error');\\n        el.addEventListener('input', clear);\\n        el.addEventListener('change', clear);\\n      }\\n    });\\n\\n    function ensureEmailPerson(email) {\\n      if (!scrapedData) scrapedData = { domain: '', people: [] };\\n      if (!scrapedData.people) scrapedData.people = [];\\n      const found = scrapedData.people.find(p =\\u003e p.email \\u0026\\u0026 p.email.toLowerCase() === email.toLowerCase());\\n      if (!found) {\\n        scrapedData.people.unshift({ name: '', email: email, title: '', phone: '', photo: '', bio: '', linkedin: '' });\\n      }\\n    }\\n\\n    function skipScrape() {\\n      const input = document.getElementById('addOrgEmail').value.trim();\\n      const isEmail = input.includes('@');\\n      const domain = isEmail ? input.split('@')[1] : input.replace(/^https?:\\\\/\\\\//, '').replace(/\\\\/.*$/, '');\\n      const people = isEmail ? [{ name: '', email: input, title: '', phone: '', photo: '', bio: '', linkedin: '' }] : [];\\n      scrapedData = { domain, people };\\n      document.getElementById('orgLogoWrap').classList.add('hidden');\\n      document.getElementById('orgName').value = '';\\n      document.getElementById('orgDesc').value = '';\\n      document.getElementById('orgIndustry').value = '';\\n      document.getElementById('orgWebsite').value = domain ? 'https://' + domain : '';\\n      document.getElementById('orgPhone').value = '';\\n      document.getElementById('orgAddress').value = '';\\n      document.getElementById('orgCity').value = '';\\n      document.getElementById('orgState').value = '';\\n      document.getElementById('orgFounded').value = '';\\n      document.getElementById('orgLinkedIn').value = '';\\n      highlightRequired();\\n      showAddOrgStep(2);\\n    }\\n\\n    async function scrapeOrg() {\\n      let input = document.getElementById('addOrgEmail').value.trim();\\n      if (!input) {\\n        document.getElementById('scrapeErrorMsg').textContent = 'Enter an email address or domain name.';\\n        document.getElementById('scrapeError').classList.remove('hidden');\\n        return;\\n      }\\n      // If no @, treat as domain — fabricate an email for the API\\n      const email = input.includes('@') ? input : 'info@' + input.replace(/^https?:\\\\/\\\\//, '').replace(/\\\\/.*$/, '');\\n      document.getElementById('scrapeError').classList.add('hidden');\\n      document.getElementById('scrapeLoading').classList.remove('hidden');\\n      document.getElementById('scrapeBtn').disabled = true;\\n      try {\\n        const res = await fetchAPI('/api/scrape/org', { method: 'POST', body: JSON.stringify({ email }) });\\n        const data = await res.json();\\n        if (!res.ok) throw new Error(data.error || 'Scrape failed');\\n        scrapedData = data;\\n        if (data.logo) {\\n          document.getElementById('orgLogoImg').src = data.logo;\\n          document.getElementById('orgLogoWrap').classList.remove('hidden');\\n        } else {\\n          document.getElementById('orgLogoWrap').classList.add('hidden');\\n        }\\n        document.getElementById('orgName').value = data.name || '';\\n        document.getElementById('orgDesc').value = data.description || '';\\n        document.getElementById('orgIndustry').value = data.industry || '';\\n        document.getElementById('orgWebsite').value = data.website || '';\\n        document.getElementById('orgPhone').value = data.phone || '';\\n        document.getElementById('orgAddress').value = data.address || '';\\n        document.getElementById('orgCity').value = data.city || '';\\n        document.getElementById('orgState').value = data.state || '';\\n        document.getElementById('orgFounded').value = data.founded || '';\\n        document.getElementById('orgLinkedIn').value = data.linkedin || '';\\n        // Ensure the original email person is in the people list (only if real email, not fabricated)\\n        if (input.includes('@')) ensureEmailPerson(email);\\n        highlightRequired();\\n        showAddOrgStep(2);\\n      } catch (err) {\\n        document.getElementById('scrapeErrorMsg').textContent = err.message;\\n        document.getElementById('scrapeError').classList.remove('hidden');\\n      } finally {\\n        document.getElementById('scrapeLoading').classList.add('hidden');\\n        document.getElementById('scrapeBtn').disabled = false;\\n      }\\n    }\\n\\n    function renderPeople() {\\n      try {\\n      const people = scrapedData?.people || [];\\n      const list = document.getElementById('peopleList');\\n      const none = document.getElementById('noPeople');\\n      if (!list) { console.error('peopleList element not found'); return; }\\n      if (people.length === 0) { list.innerHTML = ''; if(none) none.classList.remove('hidden'); return; }\\n      if(none) none.classList.add('hidden');\\n      list.innerHTML = people.map((p, i) =\\u003e {\\n        const hasDetail = p.name \\u0026\\u0026 p.title;\\n        return `\\n        \\u003cdiv class=\\\"flex items-center gap-3 p-3 rounded-xl bg-white/[0.03] border border-white/[0.06] transition\\\"\\u003e\\n          \\u003cinput type=\\\"checkbox\\\" class=\\\"person-cb accent-[#c9a84c] rounded shrink-0\\\" data-idx=\\\"${i}\\\" checked onchange=\\\"updateSelectedCount()\\\"\\u003e\\n          \\u003cdiv class=\\\"w-10 h-10 rounded-full bg-[#0a1628] shrink-0 overflow-hidden border border-white/[0.08]\\\"\\u003e\\n            ${p.photo ? `\\u003cimg src=\\\"${escHtml(p.photo)}\\\" class=\\\"w-full h-full object-cover\\\" onerror=\\\"this.parentElement.innerHTML='\\u003cdiv class=\\\\\\\\'w-full h-full flex items-center justify-center text-[#c9a84c] font-semibold\\\\\\\\'\\u003e${escHtml((p.name||'?')[0])}\\u003c/div\\u003e'\\\"\\u003e` : `\\u003cdiv class=\\\"w-full h-full flex items-center justify-center text-[#c9a84c] font-semibold\\\"\\u003e${escHtml((p.name||'?')[0])}\\u003c/div\\u003e`}\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"flex-1 min-w-0 grid grid-cols-3 gap-2\\\"\\u003e\\n            \\u003cinput type=\\\"text\\\" value=\\\"${escHtml(p.name)}\\\" placeholder=\\\"Name\\\" data-idx=\\\"${i}\\\" data-field=\\\"name\\\" onchange=\\\"updatePerson(this)\\\"\\n              class=\\\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-white text-sm focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\\\"\\u003e\\n            \\u003cinput type=\\\"text\\\" value=\\\"${escHtml(p.email)}\\\" placeholder=\\\"Email\\\" data-idx=\\\"${i}\\\" data-field=\\\"email\\\" onchange=\\\"updatePerson(this)\\\"\\n              class=\\\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-[#b0bec5] text-xs focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\\\"\\u003e\\n            \\u003cinput type=\\\"text\\\" value=\\\"${escHtml(p.title)}\\\" placeholder=\\\"Title\\\" data-idx=\\\"${i}\\\" data-field=\\\"title\\\" onchange=\\\"updatePerson(this)\\\"\\n              class=\\\"px-2 py-1 bg-transparent border border-transparent hover:border-white/[0.08] focus:border-[#c9a84c] rounded text-[#b0bec5] text-xs focus:outline-none ${hasDetail ? '' : 'border-white/[0.08]'}\\\"\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e`;\\n      }).join('');\\n      const sa = document.getElementById('selectAllPeople');\\n      if(sa) sa.checked = true;\\n      updateSelectedCount();\\n      } catch(err) { alert('renderPeople error: ' + err.message + '\\\\n' + err.stack); }\\n    }\\n\\n    function updatePerson(el) {\\n      const idx = parseInt(el.dataset.idx);\\n      const field = el.dataset.field;\\n      if (scrapedData?.people?.[idx]) {\\n        scrapedData.people[idx][field] = el.value.trim();\\n      }\\n    }\\n\\n    function addManualPerson() {\\n      try {\\n        const name = document.getElementById('manualName').value.trim();\\n        const email = document.getElementById('manualEmail').value.trim();\\n        const title = document.getElementById('manualTitle').value.trim();\\n        if (!name \\u0026\\u0026 !email) {\\n          const nameEl = document.getElementById('manualName');\\n          nameEl.classList.add('field-error');\\n          nameEl.placeholder = 'Enter a name or email';\\n          setTimeout(() =\\u003e { nameEl.classList.remove('field-error'); nameEl.placeholder = 'Name'; }, 2500);\\n          nameEl.focus();\\n          return;\\n        }\\n        if (!scrapedData) scrapedData = { domain: '', people: [] };\\n        if (!scrapedData.people) scrapedData.people = [];\\n        scrapedData.people.push({ name: name || email, email, title, phone: '', photo: '', bio: '', linkedin: '' });\\n        document.getElementById('manualName').value = '';\\n        document.getElementById('manualEmail').value = '';\\n        document.getElementById('manualTitle').value = '';\\n        renderPeople();\\n        document.getElementById('manualName').focus();\\n      } catch(err) {\\n        alert('addManualPerson error: ' + err.message + '\\\\n' + err.stack);\\n      }\\n    }\\n\\n    function toggleAllPeople(checked) {\\n      document.querySelectorAll('.person-cb').forEach(cb =\\u003e cb.checked = checked);\\n      updateSelectedCount();\\n    }\\n\\n    function updateSelectedCount() {\\n      const checked = document.querySelectorAll('.person-cb:checked').length;\\n      const total = document.querySelectorAll('.person-cb').length;\\n      document.getElementById('selectedCount').textContent = checked + ' of ' + total + ' selected';\\n      document.getElementById('selectAllPeople').checked = (checked === total);\\n    }\\n\\n    function proceedFromStep3() {\\n      if (_wizardMode) {\\n        showAddOrgStep(4);\\n      } else {\\n        submitAddOrg();\\n      }\\n    }\\n\\n    async function submitAddOrg() {\\n      const s3err = document.getElementById('step3Error');\\n      if (s3err) s3err.classList.add('hidden');\\n      if (!validateStep2()) {\\n        if (s3err) { s3err.textContent = 'Required fields missing: go back and fill in Name and Role.'; s3err.classList.remove('hidden'); }\\n        return;\\n      }\\n      const name = document.getElementById('orgName').value.trim();\\n      const role = document.getElementById('orgRole').value;\\n\\n      // Derive domain: prefer scrapedData.domain, fallback to website field\\n      let domain = scrapedData?.domain || '';\\n      if (!domain) {\\n        const ws = document.getElementById('orgWebsite').value.trim();\\n        if (ws) {\\n          domain = ws.replace(/^https?:\\\\/\\\\//, '').replace(/^www\\\\./, '').split('/')[0];\\n        }\\n      }\\n      const people = scrapedData?.people || [];\\n      const selectedMembers = [];\\n      document.querySelectorAll('.person-cb:checked').forEach(cb =\\u003e {\\n        const p = people[parseInt(cb.dataset.idx)];\\n        if (p) selectedMembers.push({ name: p.name, email: p.email || '', title: p.title || '', phone: p.phone || '', photo: p.photo || '', bio: p.bio || '', linkedin: p.linkedin || '' });\\n      });\\n\\n      const btn = document.getElementById('addOrgSubmitBtn');\\n      btn.disabled = true; btn.textContent = 'Adding...';\\n\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/orgs/add', {\\n          method: 'POST',\\n          body: JSON.stringify({\\n            name,\\n            domains: domain ? [domain] : [],\\n            role,\\n            logo: scrapedData?.logo || '',\\n            website: document.getElementById('orgWebsite').value,\\n            description: document.getElementById('orgDesc').value,\\n            industry: document.getElementById('orgIndustry').value,\\n            phone: document.getElementById('orgPhone').value,\\n            address: document.getElementById('orgAddress').value,\\n            city: document.getElementById('orgCity').value,\\n            state: document.getElementById('orgState').value,\\n            founded: document.getElementById('orgFounded').value,\\n            linkedin: document.getElementById('orgLinkedIn').value,\\n            members: selectedMembers,\\n            domain_lock: true,\\n          })\\n        });\\n        const data = await res.json();\\n        if (!res.ok) throw new Error(data.error || 'Failed to add organization');\\n        closeAddOrgModal();\\n        loadOrgs();\\n        // Show email preview if there are members with emails\\n        if (selectedMembers.length \\u003e 0) {\\n          showEmailPreview(selectedMembers, name);\\n        }\\n      } catch (err) { alert(err.message); }\\n      finally { btn.disabled = false; btn.textContent = 'Add to Deal'; }\\n    }\\n\\n    // ---- Person Panel ----\\n    function openPersonPanel(dealOrgId, memberIdx) {\\n      const o = _dealOrgsMap[dealOrgId];\\n      if (!o) return;\\n      const members = o.members || [];\\n      const isNew = memberIdx \\u003c 0;\\n      const m = isNew ? { name: '', email: '', title: '', phone: '', photo: '', bio: '', linkedin: '' } : members[memberIdx];\\n      if (!m) return;\\n\\n      document.getElementById('ppDealOrgId').value = dealOrgId;\\n      document.getElementById('ppMemberIdx').value = memberIdx;\\n      document.getElementById('ppName').value = m.name || '';\\n      document.getElementById('ppEmail').value = m.email || '';\\n      document.getElementById('ppTitle').value = m.title || '';\\n      document.getElementById('ppPhone').value = m.phone || '';\\n      document.getElementById('ppPhoto').value = m.photo || '';\\n      document.getElementById('ppLinkedIn').value = m.linkedin || '';\\n      document.getElementById('ppBio').value = m.bio || '';\\n      document.getElementById('personPanelTitle').textContent = isNew ? 'Add Person' : 'Edit Person';\\n      document.getElementById('ppRemoveBtn').classList.toggle('hidden', isNew);\\n\\n      previewPersonPhoto();\\n      document.getElementById('ppPhotoUrlRow').classList.toggle('hidden', !!m.photo);\\n\\n      document.getElementById('personOverlay').classList.remove('hidden');\\n      document.getElementById('personPanel').classList.remove('translate-x-full');\\n    }\\n\\n    function closePersonPanel() {\\n      document.getElementById('personPanel').classList.add('translate-x-full');\\n      document.getElementById('personOverlay').classList.add('hidden');\\n    }\\n\\n    function previewPersonPhoto() {\\n      const url = document.getElementById('ppPhoto').value.trim();\\n      const img = document.getElementById('ppPhotoImg');\\n      const letter = document.getElementById('ppPhotoLetter');\\n      if (url) {\\n        img.src = url;\\n        img.classList.remove('hidden');\\n        img.onerror = () =\\u003e { img.classList.add('hidden'); letter.classList.remove('hidden'); };\\n        letter.classList.add('hidden');\\n      } else {\\n        img.classList.add('hidden');\\n        letter.classList.remove('hidden');\\n        const name = document.getElementById('ppName').value || document.getElementById('ppEmail').value || '?';\\n        letter.textContent = name[0].toUpperCase();\\n      }\\n    }\\n\\n    async function savePersonPanel() {\\n      const dealOrgId = document.getElementById('ppDealOrgId').value;\\n      const memberIdx = parseInt(document.getElementById('ppMemberIdx').value);\\n      const o = _dealOrgsMap[dealOrgId];\\n      if (!o) return;\\n\\n      const person = {\\n        name: document.getElementById('ppName').value.trim(),\\n        email: document.getElementById('ppEmail').value.trim(),\\n        title: document.getElementById('ppTitle').value.trim(),\\n        phone: document.getElementById('ppPhone').value.trim(),\\n        photo: document.getElementById('ppPhoto').value.trim(),\\n        linkedin: document.getElementById('ppLinkedIn').value.trim(),\\n        bio: document.getElementById('ppBio').value.trim(),\\n      };\\n\\n      if (!person.name \\u0026\\u0026 !person.email) return;\\n\\n      const members = [...(o.members || [])];\\n      if (memberIdx \\u003c 0) {\\n        members.push(person);\\n      } else {\\n        members[memberIdx] = person;\\n      }\\n\\n      try {\\n        await fetchAPI('/api/projects/' + projectID + '/entries/' + dealOrgId, {\\n          method: 'PUT',\\n          body: JSON.stringify({\\n            data: JSON.stringify({ ...o._raw, members }),\\n            version: o.version || 1\\n          })\\n        });\\n        closePersonPanel();\\n        loadOrgs();\\n      } catch (e) { alert(e.message); }\\n    }\\n\\n    async function removePersonFromDeal() {\\n      const dealOrgId = document.getElementById('ppDealOrgId').value;\\n      const memberIdx = parseInt(document.getElementById('ppMemberIdx').value);\\n      const o = _dealOrgsMap[dealOrgId];\\n      if (!o || memberIdx \\u003c 0) return;\\n\\n      const name = (o.members || [])[memberIdx]?.name || 'this person';\\n      if (!confirm('Remove ' + name + ' from this deal?')) return;\\n\\n      const members = [...(o.members || [])];\\n      members.splice(memberIdx, 1);\\n\\n      try {\\n        await fetchAPI('/api/projects/' + projectID + '/entries/' + dealOrgId, {\\n          method: 'PUT',\\n          body: JSON.stringify({\"}","is_error":false}},{"type":"tool_result","data":{"tool_call_id":"functions.view:8","name":"","content":"\u003cfile\u003e\n     1|{{define \"header-left\"}}\n     2|    \u003cdiv class=\"flex items-center gap-3\"\u003e\n     3|      \u003ca href=\"/app/projects\" class=\"text-2xl font-bold text-white tracking-tight\"\u003e\u003cspan class=\"text-[#c9a84c]\"\u003eDeal\u003c/span\u003espace\u003c/a\u003e\n     4|      \u003cspan class=\"text-white/20\"\u003e/\u003c/span\u003e\n     5|      \u003ca id=\"backToProject\" href=\"/app/projects\" class=\"text-sm text-[#b0bec5] hover:text-white transition\"\u003eProject\u003c/a\u003e\n     6|      \u003cspan class=\"text-white/20\"\u003e/\u003c/span\u003e\n     7|      \u003cspan id=\"reqRef\" class=\"text-sm text-white font-medium\"\u003e—\u003c/span\u003e\n     8|    \u003c/div\u003e\n     9|{{end}}\n    10|\n    11|{{define \"content\"}}\n    12|  \u003cdiv class=\"max-w-4xl mx-auto px-6 py-8 space-y-6\"\u003e\n    13|\n    14|    \u003c!-- REQUEST CARD --\u003e\n    15|    \u003cdiv class=\"rounded-xl overflow-hidden\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd)\"\u003e\n    16|      \u003c!-- Section header with meta --\u003e\n    17|      \u003cdiv class=\"px-6 pt-5 pb-4 border-b\" style=\"border-color:var(--ds-bd)\"\u003e\n    18|        \u003cdiv class=\"flex items-start justify-between gap-4\"\u003e\n    19|          \u003cdiv class=\"flex flex-wrap items-center gap-2\"\u003e\n    20|            \u003cspan id=\"reqItemBadge\" class=\"hidden px-2.5 py-1 rounded text-xs font-semibold tracking-wide\" style=\"background:var(--ds-ac);color:var(--ds-act)\"\u003e\u003c/span\u003e\n    21|            \u003cspan id=\"reqStatus\" class=\"px-2.5 py-1 rounded-full text-xs font-medium capitalize\"\u003e\u003c/span\u003e\n    22|            \u003cspan id=\"reqPriority\" class=\"hidden px-2.5 py-1 rounded-full text-xs font-medium\"\u003e\u003c/span\u003e\n    23|            \u003cspan id=\"reqDue\" class=\"hidden text-xs font-medium\" style=\"color:var(--ds-tx3)\"\u003e\u003c/span\u003e\n    24|          \u003c/div\u003e\n    25|          \u003cbutton id=\"editReqBtn\" onclick=\"startEditRequest()\" class=\"shrink-0 p-1.5 rounded hover:bg-white/[0.08] transition\" style=\"color:var(--ds-tx3)\" title=\"Edit request\"\u003e\n    26|            \u003csvg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\"/\u003e\u003c/svg\u003e\n    27|          \u003c/button\u003e\n    28|        \u003c/div\u003e\n    29|      \u003c/div\u003e\n    30|      \u003c!-- Request body --\u003e\n    31|      \u003cdiv class=\"px-6 py-5\"\u003e\n    32|        \u003cp id=\"reqTitle\" class=\"text-base leading-relaxed font-medium\" style=\"color:var(--ds-tx)\"\u003eLoading...\u003c/p\u003e\n    33|        \u003cp id=\"reqDesc\" class=\"text-sm leading-relaxed mt-3\" style=\"color:var(--ds-tx2)\"\u003e\u003c/p\u003e\n    34|        \u003c!-- Edit mode --\u003e\n    35|        \u003cdiv id=\"reqEditMode\" class=\"hidden mt-5 space-y-3 p-4 rounded-lg\" style=\"background:var(--ds-bg);border:1px solid var(--ds-bd)\"\u003e\n    36|          \u003cdiv\u003e\u003clabel class=\"block text-xs font-medium mb-1.5\" style=\"color:var(--ds-tx3)\"\u003eRequest text\u003c/label\u003e\n    37|            \u003ctextarea id=\"editReqTitle\" rows=\"4\" class=\"w-full px-3 py-2 rounded text-sm focus:outline-none\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\u003c/textarea\u003e\u003c/div\u003e\n    38|          \u003cdiv class=\"flex gap-3\"\u003e\n    39|            \u003cdiv class=\"flex-1\"\u003e\u003clabel class=\"block text-xs font-medium mb-1.5\" style=\"color:var(--ds-tx3)\"\u003ePriority\u003c/label\u003e\n    40|              \u003cselect id=\"editReqPriority\" class=\"w-full px-3 py-2 rounded text-sm focus:outline-none\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n    41|                \u003coption value=\"critical\"\u003eCritical\u003c/option\u003e\u003coption value=\"high\"\u003eHigh\u003c/option\u003e\u003coption value=\"medium\"\u003eMedium\u003c/option\u003e\u003coption value=\"low\"\u003eLow\u003c/option\u003e\n    42|              \u003c/select\u003e\u003c/div\u003e\n    43|            \u003cdiv class=\"flex-1\"\u003e\u003clabel class=\"block text-xs font-medium mb-1.5\" style=\"color:var(--ds-tx3)\"\u003eStatus\u003c/label\u003e\n    44|              \u003cselect id=\"editReqStatus\" class=\"w-full px-3 py-2 rounded text-sm focus:outline-none\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\n    45|                \u003coption value=\"open\"\u003eOpen\u003c/option\u003e\u003coption value=\"in_process\"\u003eIn Process\u003c/option\u003e\u003coption value=\"partial\"\u003ePartial\u003c/option\u003e\u003coption value=\"complete\"\u003eComplete\u003c/option\u003e\n    46|              \u003c/select\u003e\u003c/div\u003e\n    47|          \u003c/div\u003e\n    48|          \u003cdiv\u003e\u003clabel class=\"block text-xs font-medium mb-1.5\" style=\"color:var(--ds-tx3)\"\u003eDescription (optional)\u003c/label\u003e\n    49|            \u003ctextarea id=\"editReqDesc\" rows=\"2\" class=\"w-full px-3 py-2 rounded text-sm focus:outline-none\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\"\u003e\u003c/textarea\u003e\u003c/div\u003e\n    50|          \u003cdiv class=\"flex gap-2 mt-1\"\u003e\n    51|            \u003cbutton onclick=\"saveEditRequestDetail()\" class=\"px-4 py-2 rounded text-sm font-semibold transition\" style=\"background:var(--ds-ac);color:var(--ds-act)\"\u003eSave\u003c/button\u003e\n    52|            \u003cbutton onclick=\"cancelEditRequestDetail()\" class=\"px-4 py-2 rounded text-sm transition\" style=\"background:var(--ds-hv);color:var(--ds-tx)\"\u003eCancel\u003c/button\u003e\n    53|          \u003c/div\u003e\n    54|        \u003c/div\u003e\n    55|      \u003c/div\u003e\n    56|    \u003c/div\u003e\n    57|\n    58|    \u003c!-- RESPONSE CARD --\u003e\n    59|    \u003cdiv class=\"rounded-xl overflow-hidden\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd)\"\u003e\n    60|      \u003cdiv class=\"px-6 pt-4 pb-3 border-b flex items-center justify-between\" style=\"border-color:var(--ds-bd)\"\u003e\n    61|        \u003cspan class=\"text-xs font-semibold uppercase tracking-wider\" style=\"color:var(--ds-tx3)\"\u003eFiles \u0026amp; Response\u003c/span\u003e\n    62|        \u003cdiv id=\"answeredBanner\" class=\"hidden px-3 py-1 rounded-full text-xs font-semibold bg-green-500/15 text-green-300\"\u003e\u0026#10003; Answered\u003c/div\u003e\n    63|      \u003c/div\u003e\n    64|      \u003cdiv class=\"px-6 py-4\"\u003e\n    65|        \u003cdiv id=\"answers\" class=\"space-y-2 mb-4\"\u003e\u003c/div\u003e\n    66|        \u003c!-- empty state shown by JS if no answers --\u003e\n    67|        \u003cdiv id=\"answersEmpty\" class=\"hidden py-6 text-center text-sm\" style=\"color:var(--ds-tx3)\"\u003eNo files submitted yet.\u003c/div\u003e\n    68|        \u003cdiv id=\"uploadArea\" class=\"mt-2 border border-dashed rounded-lg px-5 py-6 text-center cursor-pointer transition\" style=\"border-color:var(--ds-bd)\"\n    69|          onclick=\"document.getElementById('fileInput').click()\"\n    70|          ondragover=\"event.preventDefault();this.style.borderColor='var(--ds-ac)'\"\n    71|          ondragleave=\"this.style.borderColor='var(--ds-bd)'\"\n    72|          ondrop=\"event.preventDefault();this.style.borderColor='var(--ds-bd)';uploadFiles(event.dataTransfer.files)\"\u003e\n    73|          \u003csvg class=\"w-6 h-6 mx-auto mb-2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" style=\"color:var(--ds-tx3)\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12\"/\u003e\u003c/svg\u003e\n    74|          \u003cp class=\"text-sm font-medium\" style=\"color:var(--ds-tx2)\"\u003eDrop files or click to upload\u003c/p\u003e\n    75|          \u003cp class=\"text-xs mt-0.5\" style=\"color:var(--ds-tx3)\"\u003ePDF, DOCX, XLSX, images accepted\u003c/p\u003e\n    76|          \u003cinput id=\"fileInput\" type=\"file\" multiple class=\"hidden\" onchange=\"uploadFiles(this.files)\"\u003e\n    77|        \u003c/div\u003e\n    78|        \u003cdiv id=\"uploadStatus\" class=\"mt-3 text-sm\" style=\"color:var(--ds-tx2)\"\u003e\u003c/div\u003e\n    79|        \u003cbutton id=\"markAnsweredBtn\" onclick=\"markAnswered()\" class=\"hidden mt-4 px-5 py-2.5 rounded text-sm font-semibold bg-green-600 hover:bg-green-700 text-white transition\"\u003e\n    80|          \u0026#10003; Mark as Answered\n    81|        \u003c/button\u003e\n    82|      \u003c/div\u003e\n    83|    \u003c/div\u003e\n    84|\n    85|    \u003c!-- DISCUSSION CARD --\u003e\n    86|    \u003cdiv class=\"rounded-xl overflow-hidden\" style=\"background:var(--ds-sf);border:1px solid var(--ds-bd)\"\u003e\n    87|      \u003cdiv id=\"channelTabs\" class=\"flex items-center overflow-x-auto border-b\" style=\"border-color:var(--ds-bd)\"\u003e\n    88|        \u003cdiv class=\"px-5 py-3 text-xs\" style=\"color:var(--ds-tx3)\"\u003eLoading channels...\u003c/div\u003e\n    89|      \u003c/div\u003e\n    90|      \u003cdiv id=\"channelComments\" class=\"px-6 py-4 space-y-4 min-h-[80px] max-h-[400px] overflow-y-auto\"\u003e\n    91|        \u003cp class=\"text-sm\" style=\"color:var(--ds-tx3)\"\u003eSelect a channel above.\u003c/p\u003e\n    92|      \u003c/div\u003e\n    93|      \u003cdiv id=\"commentComposer\" class=\"border-t px-5 py-4\" style=\"border-color:var(--ds-bd)\"\u003e\n    94|        \u003cdiv id=\"composeReadOnly\" class=\"hidden py-1 text-xs text-center\" style=\"color:var(--ds-tx3)\"\u003eRead only in this channel.\u003c/div\u003e\n    95|        \u003cdiv id=\"composeForm\" class=\"flex gap-3 items-end\"\u003e\n    96|          \u003ctextarea id=\"commentText\" rows=\"2\" placeholder=\"Post an announcement...\"\n    97|            class=\"flex-1 px-3 py-2.5 rounded-lg text-sm resize-none focus:outline-none focus:ring-1\"\n    98|            style=\"background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx);--tw-ring-color:var(--ds-ac)\"\u003e\u003c/textarea\u003e\n    99|          \u003cbutton onclick=\"postComment(null)\" class=\"px-4 py-2.5 rounded-lg text-sm font-semibold transition shrink-0\" style=\"background:var(--ds-ac);color:var(--ds-act)\"\u003ePost\u003c/button\u003e\n   100|        \u003c/div\u003e\n   101|      \u003c/div\u003e\n   102|    \u003c/div\u003e\n   103|\n   104|  \u003c/div\u003e\n   105|{{end}}\n   106|\n   107|{{define \"scripts\"}}\n   108|\u003cscript\u003e\n   109|    const reqID = location.pathname.split('/').pop();\n   110|    function isBuyerRole() {\n   111|      const r = localStorage.getItem('ds_test_role') || '';\n   112|      return r === 'buyer' || r === 'seller' || r === 'advisor';\n   113|    }\n   114|\n   115|    const statusColors = { open: 'bg-yellow-500/20 text-yellow-300', answered: 'bg-green-500/20 text-green-300', closed: 'bg-gray-500/20 text-gray-300', 'under-review': 'bg-blue-500/20 text-blue-300' };\n   116|    const priorityColors = { high: 'bg-red-500/20 text-red-300', medium: 'bg-yellow-500/20 text-yellow-300', low: 'bg-blue-500/20 text-blue-300' };\n   117|\n   118|    let currentRequest = null;\n   119|    let currentData = null;\n   120|    let currentAnswers = [];\n   121|\n   122|    async function applyRoleRestrictions() {\n   123|      try {\n   124|        const res = await fetchAPI('/api/auth/me');\n   125|        const me = res.ok ? await res.json() : {};\n   126|        // Use server role first, fall back to localStorage (survives service restarts)\n   127|        const role = me.test_role || localStorage.getItem('ds_test_role') || '';\n   128|        const isReadOnly = role === 'buyer' || role === 'seller' || role === 'advisor';\n   129|        if (isReadOnly) {\n   130|          const ua = document.getElementById('uploadArea');\n   131|          if (ua) ua.style.display = 'none';\n   132|          const mb = document.getElementById('markAnsweredBtn');\n   133|          if (mb) mb.style.display = 'none';\n   134|          const eb = document.getElementById('editReqBtn');\n   135|          if (eb) eb.style.display = 'none';\n   136|          const cc = document.getElementById('commentComposer');\n   137|          if (cc) cc.style.display = 'none';\n   138|        }\n   139|      } catch(e) {}\n   140|    }\n   141|\n   142|    async function loadAll() {\n   143|      try {\n   144|        const res = await fetchAPI('/api/requests/' + reqID);\n   145|        if (!res.ok) { document.getElementById('reqTitle').textContent = 'Not found'; return; }\n   146|        const json = await res.json();\n   147|        currentRequest = json.request;\n   148|        currentData = parseData(currentRequest.data_text);\n   149|        const children = json.children || [];\n   150|\n   151|        renderRequest(currentRequest, currentData);\n   152|        currentAnswers = children.filter(c =\u003e c.type === 'answer' || c.type === 'document');\n   153|        renderAnswers(currentAnswers);\n   154|        allComments = children.filter(c =\u003e c.type === 'comment');\n   155|        await initChannels();\n   156|      } catch(e) {\n   157|        document.getElementById('reqTitle').textContent = 'Error loading request';\n   158|      }\n   159|    }\n   160|\n   161|    function renderRequest(req, d) {\n   162|      const title = d.title || req.summary || 'Untitled';\n   163|      document.title = title + ' \\u2014 Dealspace';\n   164|      document.getElementById('reqRef').textContent = d.item_number || title;\n   165|      document.getElementById('reqTitle').textContent = title;\n   166|      // Don't repeat description if it's the same as the title\n   167|      const desc = d.description || '';\n   168|      document.getElementById('reqDesc').textContent = desc === title ? '' : desc;\n   169|\n   170|      // Apply role-based UI restrictions\n   171|      applyRoleRestrictions();\n   172|\n   173|      if (req.project_id) {\n   174|        document.getElementById('backToProject').href = '/app/projects/' + req.project_id;\n   175|        // Fetch project name for breadcrumb\n   176|        fetchAPI('/api/projects/' + req.project_id).then(r=\u003er.json()).then(p=\u003e{\n   177|          const pname = (p.data_text ? (()=\u003e{try{return JSON.parse(p.data_text)}catch{return{}}})() : {}).name || 'Project';\n   178|          document.getElementById('backToProject').textContent = pname;\n   179|        }).catch(()=\u003e{});\n   180|      }\n   181|\n   182|      const badge = document.getElementById('reqItemBadge');\n   183|      const badgeParts = [d.section, d.item_number].filter(Boolean);\n   184|      if (badgeParts.length) { badge.textContent = badgeParts.join(' \\u00b7 '); badge.classList.remove('hidden'); }\n   185|\n   186|      const prioEl = document.getElementById('reqPriority');\n   187|      if (d.priority) {\n   188|        const pc = priorityColors[d.priority] || 'bg-gray-500/20 text-gray-300';\n   189|        prioEl.className = 'px-2.5 py-1 rounded-full text-xs font-medium capitalize ' + pc;\n   190|        prioEl.textContent = d.priority + ' priority';\n   191|        prioEl.classList.remove('hidden');\n   192|      }\n   193|\n   194|      if (d.due_date) {\n   195|        const dueEl = document.getElementById('reqDue');\n   196|        dueEl.textContent = '\\ud83d\\udcc5 Due: ' + d.due_date;\n   197|        dueEl.classList.remove('hidden');\n   198|      }\n   199|\n   200|      const status = d.status || 'open';\n   201|      const sc = statusColors[status] || 'bg-gray-500/20 text-gray-300';\n   202|      const statusEl = document.getElementById('reqStatus');\n   203|      statusEl.className = 'px-2.5 py-1 rounded-full text-xs font-medium capitalize ' + sc;\n   204|      statusEl.textContent = status;\n   205|\n   206|      if (status === 'answered') {\n   207|        document.getElementById('answeredBanner').classList.remove('hidden');\n   208|        document.getElementById('markAnsweredBtn').classList.add('hidden');\n   209|      } else {\n   210|        document.getElementById('answeredBanner').classList.add('hidden');\n   211|        document.getElementById('markAnsweredBtn').classList.remove('hidden');\n   212|      }\n   213|    }\n   214|\n   215|    // ---- Edit request detail ----\n   216|    function startEditRequest() {\n   217|      if (!currentData) return;\n   218|      document.getElementById('editReqTitle').value = currentData.title || '';\n   219|      document.getElementById('editReqDesc').value = currentData.description || '';\n   220|      document.getElementById('editReqPriority').value = currentData.priority || 'medium';\n   221|      document.getElementById('editReqStatus').value = currentData.status || 'open';\n   222|      document.getElementById('reqTitle').classList.add('hidden');\n   223|      document.getElementById('reqDesc').classList.add('hidden');\n   224|      document.getElementById('editReqBtn').classList.add('hidden');\n   225|      document.getElementById('reqMetaBar').classList.add('hidden');\n   226|      document.getElementById('reqEditMode').classList.remove('hidden');\n   227|    }\n   228|\n   229|    function cancelEditRequestDetail() {\n   230|      document.getElementById('reqTitle').classList.remove('hidden');\n   231|      document.getElementById('reqDesc').classList.remove('hidden');\n   232|      document.getElementById('editReqBtn').classList.remove('hidden');\n   233|      document.getElementById('reqMetaBar').classList.remove('hidden');\n   234|      document.getElementById('reqEditMode').classList.add('hidden');\n   235|    }\n   236|\n   237|    async function saveEditRequestDetail() {\n   238|      if (!currentRequest || !currentData) return;\n   239|      const projectID = currentRequest.project_id;\n   240|      const updatedData = {\n   241|        ...currentData,\n   242|        title: document.getElementById('editReqTitle').value.trim(),\n   243|        description: document.getElementById('editReqDesc').value.trim(),\n   244|        priority: document.getElementById('editReqPriority').value,\n   245|        status: document.getElementById('editReqStatus').value\n   246|      };\n   247|      try {\n   248|        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + reqID, {\n   249|          method: 'PUT',\n   250|          body: JSON.stringify({ data: JSON.stringify(updatedData), version: currentRequest.version })\n   251|        });\n   252|        if (res.ok) {\n   253|          cancelEditRequestDetail();\n   254|          loadAll();\n   255|        }\n   256|      } catch(e) {\n   257|        console.error('Save failed:', e);\n   258|      }\n   259|    }\n   260|\n   261|    // ---- Answers / Files ----\n   262|    function renderAnswers(docs) {\n   263|      const el = document.getElementById('answers');\n   264|      const emptyEl = document.getElementById('answersEmpty');\n   265|      if (!docs || docs.length === 0) {\n   266|        el.innerHTML = '';\n   267|        if (emptyEl) emptyEl.classList.remove('hidden');\n   268|        return;\n   269|      }\n   270|      if (emptyEl) emptyEl.classList.add('hidden');\n   271|      const projectID = currentRequest.project_id;\n   272|      el.innerHTML = docs.map(a =\u003e {\n   273|        const d = parseData(a.data_text);\n   274|        const name = d.filename || d.name || a.summary || 'Document';\n   275|        const icon = name.endsWith('.pdf') ? '\\ud83d\\udcc4' : name.match(/\\.(jpg|jpeg|png|gif)$/i) ? '\\ud83d\\uddbc\\ufe0f' : name.match(/\\.(xlsx?|csv)$/i) ? '\\ud83d\\udcca' : '\\ud83d\\udcce';\n   276|        const objectID = d.object_id || '';\n   277|        const downloadUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '?filename=' + encodeURIComponent(name) : '#';\n   278|        const previewUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '/preview?filename=' + encodeURIComponent(name) : '';\n   279|        const isVideo = /\\.(mp4|mov|avi|mkv|webm)$/i.test(name);\n   280|        return `\u003cdiv class=\"flex items-center gap-3 px-3 py-2.5 rounded-lg bg-[#0a1628] border border-white/[0.05]\"\u003e\n   281|          \u003cspan class=\"text-xl\"\u003e${icon}\u003c/span\u003e\n   282|          \u003cdiv class=\"flex-1 min-w-0\"\u003e\n   283|            \u003cdiv class=\"flex items-center gap-1\"\u003e\n   284|              \u003cspan class=\"text-white text-sm font-medium truncate\" id=\"fname-${a.entry_id}\"\u003e${escHtml(name)}\u003c/span\u003e\n   285|              \u003cbutton onclick=\"startRenameFile('${a.entry_id}')\" class=\"shrink-0 p-0.5 rounded hover:bg-white/[0.08] text-[#b0bec5] hover:text-white transition\" title=\"Rename\"\u003e\n   286|                \u003csvg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\u003e\u003cpath stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\"/\u003e\u003c/svg\u003e\n   287|              \u003c/button\u003e\n   288|            \u003c/div\u003e\n   289|            \u003cdiv class=\"text-[#8899a6] text-xs\"\u003e${a.created_at ? new Date(a.created_at * 1000).toLocaleString() : ''}\u003c/div\u003e\n   290|          \u003c/div\u003e\n   291|          \u003cdiv class=\"flex items-center gap-2 shrink-0\"\u003e\n   292|            ${objectID \u0026\u0026 previewUrl ? `\u003ca href=\"${previewUrl}\" target=\"_blank\" class=\"px-2 py-1 rounded text-xs font-medium bg-white/[0.06] text-[#b0bec5] hover:text-white hover:bg-white/[0.1] transition\"\u003e${isVideo ? 'Play' : (isBuyerRole() ? 'View' : 'Preview')}\u003c/a\u003e` : ''}\n   293|            ${objectID \u0026\u0026 !isBuyerRole() ? `\u003ca href=\"${downloadUrl}\" class=\"text-sm hover:underline\" style=\"color:var(--ds-ac)\"\u003eDownload\u003c/a\u003e` : ''}\n   294|          \u003c/div\u003e\n   295|        \u003c/div\u003e`;\n   296|      }).join('');\n   297|    }\n   298|\n   299|    // ---- Rename file ----\n   300|    function startRenameFile(answerId) {\n   301|      const span = document.getElementById('fname-' + answerId);\n   302|      if (!span) return;\n   303|      const currentName = span.textContent;\n   304|      const container = span.parentElement;\n   305|      container.innerHTML = `\u003cinput type=\"text\" value=\"\" id=\"rename-input-${answerId}\"\n   306|        style=\"flex:1;padding:2px 6px;background:#0a1628;border:1px solid rgba(255,255,255,.12);border-radius:4px;color:white;font-size:13px;outline:none\"\n   307|        onkeydown=\"handleRenameKey(event,'${answerId}')\" onblur=\"saveRenameFile('${answerId}')\"\u003e`;\n   308|      const input = document.getElementById('rename-input-' + answerId);\n   309|      if (input) { input.value = currentName; input.focus(); input.select(); }\n   310|    }\n   311|\n   312|    function handleRenameKey(e, answerId) {\n   313|      if (e.key === 'Enter') { e.preventDefault(); e.target.blur(); }\n   314|      if (e.key === 'Escape') { e.target.dataset.cancelled = 'true'; e.target.blur(); }\n   315|    }\n   316|\n   317|    async function saveRenameFile(answerId) {\n   318|      const input = document.getElementById('rename-input-' + answerId);\n   319|      if (!input || input.dataset.cancelled === 'true') { loadAll(); return; }\n   320|      const newName = input.value.trim();\n   321|      if (!newName) { loadAll(); return; }\n   322|\n   323|      const answer = currentAnswers.find(a =\u003e a.entry_id === answerId);\n   324|      if (!answer) { loadAll(); return; }\n   325|\n   326|      const projectID = currentRequest.project_id;\n   327|      const d = parseData(answer.data_text);\n   328|      d.filename = newName;\n   329|      d.name = newName;\n   330|\n   331|      try {\n   332|        await fetchAPI('/api/projects/' + projectID + '/entries/' + answerId, {\n   333|          method: 'PUT',\n   334|          body: JSON.stringify({ data: JSON.stringify(d), version: answer.version || 0 })\n   335|        });\n   336|      } catch(e) {\n   337|        console.error('Rename failed:', e);\n   338|      }\n   339|      loadAll();\n   340|    }\n   341|\n   342|    // renderComments replaced by channel system below\n   343|\n   344|    async function uploadFiles(files) {\n   345|      if (!currentRequest) return;\n   346|      const projectID = currentRequest.project_id;\n   347|      const status = document.getElementById('uploadStatus');\n   348|      for (const file of files) {\n   349|        status.textContent = 'Uploading ' + file.name + '...';\n   350|        const fd = new FormData();\n   351|        fd.append('file', file);\n   352|        try {\n   353|          const uploadRes = await fetchAPI('/api/projects/' + projectID + '/objects', { method: 'POST', body: fd });\n   354|          if (!uploadRes.ok) { status.textContent = 'Upload failed for ' + file.name; continue; }\n   355|          const uploadData = await uploadRes.json();\n   356|\n   357|          const entryBody = {\n   358|            project_id: projectID,\n   359|            parent_id: reqID,\n   360|            type: 'document',\n   361|            data: JSON.stringify({ filename: file.name, name: file.name, content_type: file.type, object_id: uploadData.object_id })\n   362|          };\n   363|          const entryRes = await fetchAPI('/api/projects/' + projectID + '/entries', { method: 'POST', body: JSON.stringify(entryBody) });\n   364|          if (entryRes.ok) { status.textContent = file.name + ' uploaded.'; }\n   365|          else { status.textContent = 'Failed to save ' + file.name; }\n   366|        } catch(e) { status.textContent = 'Error: ' + e.message; }\n   367|      }\n   368|      setTimeout(() =\u003e status.textContent = '', 3000);\n   369|      loadAll();\n   370|    }\n   371|\n   372|    // ---- Channel-based comment system ----\n   373|    let activeChannel = 'announcements';\n   374|    let allComments = [];\n   375|    let dealOrgs = [];\n   376|    let replyingToId = null;\n   377|\n   378|    async function initChannels() {\n   379|      if (!currentRequest) return;\n   380|      try {\n   381|        const res = await fetchAPI('/api/projects/' + currentRequest.project_id + '/orgs');\n   382|        dealOrgs = await res.json() || [];\n   383|      } catch(e) { dealOrgs = []; }\n   384|      renderChannelTabs();\n   385|      switchChannel('announcements');\n   386|    }\n   387|\n   388|    function renderChannelTabs() {\n   389|      const container = document.getElementById('channelTabs');\n   390|      const tabs = [\n   391|        { key: 'announcements', label: '📢 Announcements' },\n   392|        ...dealOrgs.map(o =\u003e {\n   393|          const d = parseData(o.data_text);\n   394|          return { key: 'org:' + o.entry_id, label: '🔒 ' + escHtml(d.org_name || d.name || 'Org') };\n   395|        })\n   396|      ];\n   397|      container.innerHTML = tabs.map(t =\u003e\n   398|        `\u003cbutton onclick=\"switchChannel(${JSON.stringify(t.key)})\"\n   399|          class=\"px-4 py-3 text-sm font-medium whitespace-nowrap border-b-2 -mb-px transition\n   400|            ${activeChannel === t.key ? 'border-[#c9a84c] text-white' : 'border-transparent text-[#b0bec5] hover:text-white'}\"\u003e\n   401|          ${t.label}\n   402|        \u003c/button\u003e`\n   403|      ).join('');\n   404|    }\n   405|\n   406|    function switchChannel(key) {\n   407|      activeChannel = key;\n   408|      cancelReply();\n   409|      renderChannelTabs();\n   410|      renderChannelComments();\n   411|      const ta = document.getElementById('commentText');\n   412|      ta.placeholder = key === 'announcements' ? 'Post an announcement...' : 'Message this channel...';\n   413|    }\n   414|\n   415|    function renderChannelComments() {\n   416|      const el = document.getElementById('channelComments');\n   417|      const ch = allComments.filter(c =\u003e (parseData(c.data_text).channel || 'announcements') === activeChannel);\n   418|      const top = ch.filter(c =\u003e !parseData(c.data_text).parent_comment_id);\n   419|      const nested = ch.filter(c =\u003e !!parseData(c.data_text).parent_comment_id);\n   420|      if (top.length === 0) {\n   421|        el.innerHTML = '\u003cp class=\"text-[#8899a6] text-sm\"\u003eNo messages yet.\u003c/p\u003e';\n   422|        return;\n   423|      }\n   424|      el.innerHTML = top.map(c =\u003e {\n   425|        const d = parseData(c.data_text);\n   426|        const replies = nested.filter(r =\u003e parseData(r.data_text).parent_comment_id === c.entry_id);\n   427|        return renderOneComment(c, d, false) + replies.map(r =\u003e renderOneComment(r, parseData(r.data_text), true)).join('');\n   428|      }).join('');\n   429|      el.scrollTop = el.scrollHeight;\n   430|    }\n   431|\n   432|    function renderOneComment(c, d, isReply) {\n   433|      const av = (d.author || '?')[0].toUpperCase();\n   434|      const ts = c.created_at ? new Date(c.created_at * 1000).toLocaleString('en-US', {month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) : '';\n   435|      const replyBtn = isReply ? '' : `\u003cbutton onclick=\"startReply('${c.entry_id}')\" class=\"text-xs text-[#8899a6] hover:text-[#b0bec5] ml-2 transition\"\u003eReply\u003c/button\u003e`;\n   436|      const indent = isReply ? 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30' : '';\n   437|      return `\u003cdiv class=\"flex gap-3 ${indent}\" data-comment-id=\"${c.entry_id}\"\u003e\n   438|        \u003cdiv class=\"w-7 h-7 rounded-full bg-[#c9a84c]/20 flex items-center justify-center text-[#c9a84c] text-xs font-semibold shrink-0 mt-0.5\"\u003e${av}\u003c/div\u003e\n   439|        \u003cdiv class=\"flex-1 min-w-0\"\u003e\n   440|          \u003cdiv class=\"flex items-baseline gap-2 mb-1\"\u003e\n   441|            \u003cspan class=\"text-white text-sm font-medium\"\u003e${escHtml(d.author || 'Unknown')}\u003c/span\u003e\n   442|            \u003cspan class=\"text-[#8899a6] text-xs\"\u003e${ts}\u003c/span\u003e\n   443|            ${replyBtn}\n   444|          \u003c/div\u003e\n   445|          \u003cp class=\"text-[#b0bec5] text-sm leading-relaxed\"\u003e${escHtml(d.text || '')}\u003c/p\u003e\n   446|        \u003c/div\u003e\n   447|      \u003c/div\u003e`;\n   448|    }\n   449|\n   450|    function startReply(commentId) {\n   451|      cancelReply();\n   452|      replyingToId = commentId;\n   453|      const el = document.querySelector(`[data-comment-id=\"${commentId}\"]`);\n   454|      if (!el) return;\n   455|      const form = document.createElement('div');\n   456|      form.id = 'replyForm';\n   457|      form.className = 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30 mt-2';\n   458|      form.innerHTML = `\u003cdiv class=\"flex gap-2 items-end\"\u003e\n   459|        \u003ctextarea id=\"replyText\" rows=\"2\" placeholder=\"Reply...\" class=\"flex-1 px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white placeholder-[#8899a6] focus:outline-none focus:border-[#c9a84c] text-sm resize-none\"\u003e\u003c/textarea\u003e\n   460|        \u003cdiv class=\"flex flex-col gap-1\"\u003e\n   461|          \u003cbutton onclick=\"postComment('${commentId}')\" class=\"px-3 py-1.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-xs transition\"\u003eReply\u003c/button\u003e\n   462|          \u003cbutton onclick=\"cancelReply()\" class=\"px-3 py-1.5 bg-white/[0.05] hover:bg-white/[0.08] text-[#b0bec5] rounded-lg text-xs transition\"\u003eCancel\u003c/button\u003e\n   463|        \u003c/div\u003e\n   464|      \u003c/div\u003e`;\n   465|      el.insertAdjacentElement('afterend', form);\n   466|      document.getElementById('replyText').focus();\n   467|    }\n   468|\n   469|    function cancelReply() {\n   470|      replyingToId = null;\n   471|      const f = document.getElementById('replyForm');\n   472|      if (f) f.remove();\n   473|    }\n   474|\n   475|    async function postComment(parentCommentId) {\n   476|      if (!currentRequest) return;\n   477|      const text = parentCommentId\n   478|        ? (document.getElementById('replyText')?.value || '').trim()\n   479|        : (document.getElementById('commentText')?.value || '').trim();\n   480|      if (!text) return;\n   481|      const projectID = currentRequest.project_id;\n   482|      const body = {\n   483|        project_id: projectID,\n   484|        parent_id: reqID,\n   485|        type: 'comment',\n   486|        data: JSON.stringify({ text, author: user.name || user.email, channel: activeChannel, parent_comment_id: parentCommentId || null })\n   487|      };\n   488|      try {\n   489|        const res = await fetchAPI('/api/projects/' + projectID + '/entries', { method: 'POST', body: JSON.stringify(body) });\n   490|        if (res.ok) {\n   491|          if (parentCommentId) cancelReply(); else document.getElementById('commentText').value = '';\n   492|          await loadAll();\n   493|        }\n   494|      } catch(e) {}\n   495|    }\n   496|\n   497|    async function markAnswered() {\n   498|      if (!currentRequest || !currentData) return;\n   499|      const projectID = currentRequest.project_id;\n   500|      const updatedData = { ...currentData, status: 'answered' };\n   501|      const body = {\n   502|        data: JSON.stringify(updatedData),\n   503|        version: currentRequest.version\n   504|      };\n   505|      try {\n   506|        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + reqID, { method: 'PUT', body: JSON.stringify(body) });\n   507|        if (res.ok) loadAll();\n   508|      } catch(e) {}\n   509|    }\n   510|\n   511|    // Drag and drop\n   512|    const ua = document.getElementById('uploadArea');\n   513|    ua.addEventListener('dragover', e =\u003e { e.preventDefault(); ua.classList.add('border-[#c9a84c]/60'); });\n   514|    ua.addEventListener('dragleave', () =\u003e ua.classList.remove('border-[#c9a84c]/60'));\n   515|    ua.addEventListener('drop', e =\u003e { e.preventDefault(); ua.classList.remove('border-[#c9a84c]/60'); uploadFiles(e.dataTransfer.files); });\n   516|\n   517|    loadAll();\n   518|\u003c/script\u003e\n   519|{{end}}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/home/johan/dev/dealspace/portal/templates/app/request.html\",\"content\":\"{{define \\\"header-left\\\"}}\\n    \\u003cdiv class=\\\"flex items-center gap-3\\\"\\u003e\\n      \\u003ca href=\\\"/app/projects\\\" class=\\\"text-2xl font-bold text-white tracking-tight\\\"\\u003e\\u003cspan class=\\\"text-[#c9a84c]\\\"\\u003eDeal\\u003c/span\\u003espace\\u003c/a\\u003e\\n      \\u003cspan class=\\\"text-white/20\\\"\\u003e/\\u003c/span\\u003e\\n      \\u003ca id=\\\"backToProject\\\" href=\\\"/app/projects\\\" class=\\\"text-sm text-[#b0bec5] hover:text-white transition\\\"\\u003eProject\\u003c/a\\u003e\\n      \\u003cspan class=\\\"text-white/20\\\"\\u003e/\\u003c/span\\u003e\\n      \\u003cspan id=\\\"reqRef\\\" class=\\\"text-sm text-white font-medium\\\"\\u003e—\\u003c/span\\u003e\\n    \\u003c/div\\u003e\\n{{end}}\\n\\n{{define \\\"content\\\"}}\\n  \\u003cdiv class=\\\"max-w-4xl mx-auto px-6 py-8 space-y-6\\\"\\u003e\\n\\n    \\u003c!-- REQUEST CARD --\\u003e\\n    \\u003cdiv class=\\\"rounded-xl overflow-hidden\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd)\\\"\\u003e\\n      \\u003c!-- Section header with meta --\\u003e\\n      \\u003cdiv class=\\\"px-6 pt-5 pb-4 border-b\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cdiv class=\\\"flex items-start justify-between gap-4\\\"\\u003e\\n          \\u003cdiv class=\\\"flex flex-wrap items-center gap-2\\\"\\u003e\\n            \\u003cspan id=\\\"reqItemBadge\\\" class=\\\"hidden px-2.5 py-1 rounded text-xs font-semibold tracking-wide\\\" style=\\\"background:var(--ds-ac);color:var(--ds-act)\\\"\\u003e\\u003c/span\\u003e\\n            \\u003cspan id=\\\"reqStatus\\\" class=\\\"px-2.5 py-1 rounded-full text-xs font-medium capitalize\\\"\\u003e\\u003c/span\\u003e\\n            \\u003cspan id=\\\"reqPriority\\\" class=\\\"hidden px-2.5 py-1 rounded-full text-xs font-medium\\\"\\u003e\\u003c/span\\u003e\\n            \\u003cspan id=\\\"reqDue\\\" class=\\\"hidden text-xs font-medium\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003e\\u003c/span\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cbutton id=\\\"editReqBtn\\\" onclick=\\\"startEditRequest()\\\" class=\\\"shrink-0 p-1.5 rounded hover:bg-white/[0.08] transition\\\" style=\\\"color:var(--ds-tx3)\\\" title=\\\"Edit request\\\"\\u003e\\n            \\u003csvg class=\\\"w-4 h-4\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" viewBox=\\\"0 0 24 24\\\"\\u003e\\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" stroke-width=\\\"2\\\" d=\\\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\\\"/\\u003e\\u003c/svg\\u003e\\n          \\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003c!-- Request body --\\u003e\\n      \\u003cdiv class=\\\"px-6 py-5\\\"\\u003e\\n        \\u003cp id=\\\"reqTitle\\\" class=\\\"text-base leading-relaxed font-medium\\\" style=\\\"color:var(--ds-tx)\\\"\\u003eLoading...\\u003c/p\\u003e\\n        \\u003cp id=\\\"reqDesc\\\" class=\\\"text-sm leading-relaxed mt-3\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e\\u003c/p\\u003e\\n        \\u003c!-- Edit mode --\\u003e\\n        \\u003cdiv id=\\\"reqEditMode\\\" class=\\\"hidden mt-5 space-y-3 p-4 rounded-lg\\\" style=\\\"background:var(--ds-bg);border:1px solid var(--ds-bd)\\\"\\u003e\\n          \\u003cdiv\\u003e\\u003clabel class=\\\"block text-xs font-medium mb-1.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eRequest text\\u003c/label\\u003e\\n            \\u003ctextarea id=\\\"editReqTitle\\\" rows=\\\"4\\\" class=\\\"w-full px-3 py-2 rounded text-sm focus:outline-none\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\u003c/textarea\\u003e\\u003c/div\\u003e\\n          \\u003cdiv class=\\\"flex gap-3\\\"\\u003e\\n            \\u003cdiv class=\\\"flex-1\\\"\\u003e\\u003clabel class=\\\"block text-xs font-medium mb-1.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003ePriority\\u003c/label\\u003e\\n              \\u003cselect id=\\\"editReqPriority\\\" class=\\\"w-full px-3 py-2 rounded text-sm focus:outline-none\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n                \\u003coption value=\\\"critical\\\"\\u003eCritical\\u003c/option\\u003e\\u003coption value=\\\"high\\\"\\u003eHigh\\u003c/option\\u003e\\u003coption value=\\\"medium\\\"\\u003eMedium\\u003c/option\\u003e\\u003coption value=\\\"low\\\"\\u003eLow\\u003c/option\\u003e\\n              \\u003c/select\\u003e\\u003c/div\\u003e\\n            \\u003cdiv class=\\\"flex-1\\\"\\u003e\\u003clabel class=\\\"block text-xs font-medium mb-1.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eStatus\\u003c/label\\u003e\\n              \\u003cselect id=\\\"editReqStatus\\\" class=\\\"w-full px-3 py-2 rounded text-sm focus:outline-none\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\n                \\u003coption value=\\\"open\\\"\\u003eOpen\\u003c/option\\u003e\\u003coption value=\\\"in_process\\\"\\u003eIn Process\\u003c/option\\u003e\\u003coption value=\\\"partial\\\"\\u003ePartial\\u003c/option\\u003e\\u003coption value=\\\"complete\\\"\\u003eComplete\\u003c/option\\u003e\\n              \\u003c/select\\u003e\\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv\\u003e\\u003clabel class=\\\"block text-xs font-medium mb-1.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eDescription (optional)\\u003c/label\\u003e\\n            \\u003ctextarea id=\\\"editReqDesc\\\" rows=\\\"2\\\" class=\\\"w-full px-3 py-2 rounded text-sm focus:outline-none\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd);color:var(--ds-tx)\\\"\\u003e\\u003c/textarea\\u003e\\u003c/div\\u003e\\n          \\u003cdiv class=\\\"flex gap-2 mt-1\\\"\\u003e\\n            \\u003cbutton onclick=\\\"saveEditRequestDetail()\\\" class=\\\"px-4 py-2 rounded text-sm font-semibold transition\\\" style=\\\"background:var(--ds-ac);color:var(--ds-act)\\\"\\u003eSave\\u003c/button\\u003e\\n            \\u003cbutton onclick=\\\"cancelEditRequestDetail()\\\" class=\\\"px-4 py-2 rounded text-sm transition\\\" style=\\\"background:var(--ds-hv);color:var(--ds-tx)\\\"\\u003eCancel\\u003c/button\\u003e\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n\\n    \\u003c!-- RESPONSE CARD --\\u003e\\n    \\u003cdiv class=\\\"rounded-xl overflow-hidden\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd)\\\"\\u003e\\n      \\u003cdiv class=\\\"px-6 pt-4 pb-3 border-b flex items-center justify-between\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cspan class=\\\"text-xs font-semibold uppercase tracking-wider\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eFiles \\u0026amp; Response\\u003c/span\\u003e\\n        \\u003cdiv id=\\\"answeredBanner\\\" class=\\\"hidden px-3 py-1 rounded-full text-xs font-semibold bg-green-500/15 text-green-300\\\"\\u003e\\u0026#10003; Answered\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv class=\\\"px-6 py-4\\\"\\u003e\\n        \\u003cdiv id=\\\"answers\\\" class=\\\"space-y-2 mb-4\\\"\\u003e\\u003c/div\\u003e\\n        \\u003c!-- empty state shown by JS if no answers --\\u003e\\n        \\u003cdiv id=\\\"answersEmpty\\\" class=\\\"hidden py-6 text-center text-sm\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eNo files submitted yet.\\u003c/div\\u003e\\n        \\u003cdiv id=\\\"uploadArea\\\" class=\\\"mt-2 border border-dashed rounded-lg px-5 py-6 text-center cursor-pointer transition\\\" style=\\\"border-color:var(--ds-bd)\\\"\\n          onclick=\\\"document.getElementById('fileInput').click()\\\"\\n          ondragover=\\\"event.preventDefault();this.style.borderColor='var(--ds-ac)'\\\"\\n          ondragleave=\\\"this.style.borderColor='var(--ds-bd)'\\\"\\n          ondrop=\\\"event.preventDefault();this.style.borderColor='var(--ds-bd)';uploadFiles(event.dataTransfer.files)\\\"\\u003e\\n          \\u003csvg class=\\\"w-6 h-6 mx-auto mb-2\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" viewBox=\\\"0 0 24 24\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003e\\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" stroke-width=\\\"1.5\\\" d=\\\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12\\\"/\\u003e\\u003c/svg\\u003e\\n          \\u003cp class=\\\"text-sm font-medium\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003eDrop files or click to upload\\u003c/p\\u003e\\n          \\u003cp class=\\\"text-xs mt-0.5\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003ePDF, DOCX, XLSX, images accepted\\u003c/p\\u003e\\n          \\u003cinput id=\\\"fileInput\\\" type=\\\"file\\\" multiple class=\\\"hidden\\\" onchange=\\\"uploadFiles(this.files)\\\"\\u003e\\n        \\u003c/div\\u003e\\n        \\u003cdiv id=\\\"uploadStatus\\\" class=\\\"mt-3 text-sm\\\" style=\\\"color:var(--ds-tx2)\\\"\\u003e\\u003c/div\\u003e\\n        \\u003cbutton id=\\\"markAnsweredBtn\\\" onclick=\\\"markAnswered()\\\" class=\\\"hidden mt-4 px-5 py-2.5 rounded text-sm font-semibold bg-green-600 hover:bg-green-700 text-white transition\\\"\\u003e\\n          \\u0026#10003; Mark as Answered\\n        \\u003c/button\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n\\n    \\u003c!-- DISCUSSION CARD --\\u003e\\n    \\u003cdiv class=\\\"rounded-xl overflow-hidden\\\" style=\\\"background:var(--ds-sf);border:1px solid var(--ds-bd)\\\"\\u003e\\n      \\u003cdiv id=\\\"channelTabs\\\" class=\\\"flex items-center overflow-x-auto border-b\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cdiv class=\\\"px-5 py-3 text-xs\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eLoading channels...\\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv id=\\\"channelComments\\\" class=\\\"px-6 py-4 space-y-4 min-h-[80px] max-h-[400px] overflow-y-auto\\\"\\u003e\\n        \\u003cp class=\\\"text-sm\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eSelect a channel above.\\u003c/p\\u003e\\n      \\u003c/div\\u003e\\n      \\u003cdiv id=\\\"commentComposer\\\" class=\\\"border-t px-5 py-4\\\" style=\\\"border-color:var(--ds-bd)\\\"\\u003e\\n        \\u003cdiv id=\\\"composeReadOnly\\\" class=\\\"hidden py-1 text-xs text-center\\\" style=\\\"color:var(--ds-tx3)\\\"\\u003eRead only in this channel.\\u003c/div\\u003e\\n        \\u003cdiv id=\\\"composeForm\\\" class=\\\"flex gap-3 items-end\\\"\\u003e\\n          \\u003ctextarea id=\\\"commentText\\\" rows=\\\"2\\\" placeholder=\\\"Post an announcement...\\\"\\n            class=\\\"flex-1 px-3 py-2.5 rounded-lg text-sm resize-none focus:outline-none focus:ring-1\\\"\\n            style=\\\"background:var(--ds-bg);border:1px solid var(--ds-bd);color:var(--ds-tx);--tw-ring-color:var(--ds-ac)\\\"\\u003e\\u003c/textarea\\u003e\\n          \\u003cbutton onclick=\\\"postComment(null)\\\" class=\\\"px-4 py-2.5 rounded-lg text-sm font-semibold transition shrink-0\\\" style=\\\"background:var(--ds-ac);color:var(--ds-act)\\\"\\u003ePost\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e\\n    \\u003c/div\\u003e\\n\\n  \\u003c/div\\u003e\\n{{end}}\\n\\n{{define \\\"scripts\\\"}}\\n\\u003cscript\\u003e\\n    const reqID = location.pathname.split('/').pop();\\n    function isBuyerRole() {\\n      const r = localStorage.getItem('ds_test_role') || '';\\n      return r === 'buyer' || r === 'seller' || r === 'advisor';\\n    }\\n\\n    const statusColors = { open: 'bg-yellow-500/20 text-yellow-300', answered: 'bg-green-500/20 text-green-300', closed: 'bg-gray-500/20 text-gray-300', 'under-review': 'bg-blue-500/20 text-blue-300' };\\n    const priorityColors = { high: 'bg-red-500/20 text-red-300', medium: 'bg-yellow-500/20 text-yellow-300', low: 'bg-blue-500/20 text-blue-300' };\\n\\n    let currentRequest = null;\\n    let currentData = null;\\n    let currentAnswers = [];\\n\\n    async function applyRoleRestrictions() {\\n      try {\\n        const res = await fetchAPI('/api/auth/me');\\n        const me = res.ok ? await res.json() : {};\\n        // Use server role first, fall back to localStorage (survives service restarts)\\n        const role = me.test_role || localStorage.getItem('ds_test_role') || '';\\n        const isReadOnly = role === 'buyer' || role === 'seller' || role === 'advisor';\\n        if (isReadOnly) {\\n          const ua = document.getElementById('uploadArea');\\n          if (ua) ua.style.display = 'none';\\n          const mb = document.getElementById('markAnsweredBtn');\\n          if (mb) mb.style.display = 'none';\\n          const eb = document.getElementById('editReqBtn');\\n          if (eb) eb.style.display = 'none';\\n          const cc = document.getElementById('commentComposer');\\n          if (cc) cc.style.display = 'none';\\n        }\\n      } catch(e) {}\\n    }\\n\\n    async function loadAll() {\\n      try {\\n        const res = await fetchAPI('/api/requests/' + reqID);\\n        if (!res.ok) { document.getElementById('reqTitle').textContent = 'Not found'; return; }\\n        const json = await res.json();\\n        currentRequest = json.request;\\n        currentData = parseData(currentRequest.data_text);\\n        const children = json.children || [];\\n\\n        renderRequest(currentRequest, currentData);\\n        currentAnswers = children.filter(c =\\u003e c.type === 'answer' || c.type === 'document');\\n        renderAnswers(currentAnswers);\\n        allComments = children.filter(c =\\u003e c.type === 'comment');\\n        await initChannels();\\n      } catch(e) {\\n        document.getElementById('reqTitle').textContent = 'Error loading request';\\n      }\\n    }\\n\\n    function renderRequest(req, d) {\\n      const title = d.title || req.summary || 'Untitled';\\n      document.title = title + ' \\\\u2014 Dealspace';\\n      document.getElementById('reqRef').textContent = d.item_number || title;\\n      document.getElementById('reqTitle').textContent = title;\\n      // Don't repeat description if it's the same as the title\\n      const desc = d.description || '';\\n      document.getElementById('reqDesc').textContent = desc === title ? '' : desc;\\n\\n      // Apply role-based UI restrictions\\n      applyRoleRestrictions();\\n\\n      if (req.project_id) {\\n        document.getElementById('backToProject').href = '/app/projects/' + req.project_id;\\n        // Fetch project name for breadcrumb\\n        fetchAPI('/api/projects/' + req.project_id).then(r=\\u003er.json()).then(p=\\u003e{\\n          const pname = (p.data_text ? (()=\\u003e{try{return JSON.parse(p.data_text)}catch{return{}}})() : {}).name || 'Project';\\n          document.getElementById('backToProject').textContent = pname;\\n        }).catch(()=\\u003e{});\\n      }\\n\\n      const badge = document.getElementById('reqItemBadge');\\n      const badgeParts = [d.section, d.item_number].filter(Boolean);\\n      if (badgeParts.length) { badge.textContent = badgeParts.join(' \\\\u00b7 '); badge.classList.remove('hidden'); }\\n\\n      const prioEl = document.getElementById('reqPriority');\\n      if (d.priority) {\\n        const pc = priorityColors[d.priority] || 'bg-gray-500/20 text-gray-300';\\n        prioEl.className = 'px-2.5 py-1 rounded-full text-xs font-medium capitalize ' + pc;\\n        prioEl.textContent = d.priority + ' priority';\\n        prioEl.classList.remove('hidden');\\n      }\\n\\n      if (d.due_date) {\\n        const dueEl = document.getElementById('reqDue');\\n        dueEl.textContent = '\\\\ud83d\\\\udcc5 Due: ' + d.due_date;\\n        dueEl.classList.remove('hidden');\\n      }\\n\\n      const status = d.status || 'open';\\n      const sc = statusColors[status] || 'bg-gray-500/20 text-gray-300';\\n      const statusEl = document.getElementById('reqStatus');\\n      statusEl.className = 'px-2.5 py-1 rounded-full text-xs font-medium capitalize ' + sc;\\n      statusEl.textContent = status;\\n\\n      if (status === 'answered') {\\n        document.getElementById('answeredBanner').classList.remove('hidden');\\n        document.getElementById('markAnsweredBtn').classList.add('hidden');\\n      } else {\\n        document.getElementById('answeredBanner').classList.add('hidden');\\n        document.getElementById('markAnsweredBtn').classList.remove('hidden');\\n      }\\n    }\\n\\n    // ---- Edit request detail ----\\n    function startEditRequest() {\\n      if (!currentData) return;\\n      document.getElementById('editReqTitle').value = currentData.title || '';\\n      document.getElementById('editReqDesc').value = currentData.description || '';\\n      document.getElementById('editReqPriority').value = currentData.priority || 'medium';\\n      document.getElementById('editReqStatus').value = currentData.status || 'open';\\n      document.getElementById('reqTitle').classList.add('hidden');\\n      document.getElementById('reqDesc').classList.add('hidden');\\n      document.getElementById('editReqBtn').classList.add('hidden');\\n      document.getElementById('reqMetaBar').classList.add('hidden');\\n      document.getElementById('reqEditMode').classList.remove('hidden');\\n    }\\n\\n    function cancelEditRequestDetail() {\\n      document.getElementById('reqTitle').classList.remove('hidden');\\n      document.getElementById('reqDesc').classList.remove('hidden');\\n      document.getElementById('editReqBtn').classList.remove('hidden');\\n      document.getElementById('reqMetaBar').classList.remove('hidden');\\n      document.getElementById('reqEditMode').classList.add('hidden');\\n    }\\n\\n    async function saveEditRequestDetail() {\\n      if (!currentRequest || !currentData) return;\\n      const projectID = currentRequest.project_id;\\n      const updatedData = {\\n        ...currentData,\\n        title: document.getElementById('editReqTitle').value.trim(),\\n        description: document.getElementById('editReqDesc').value.trim(),\\n        priority: document.getElementById('editReqPriority').value,\\n        status: document.getElementById('editReqStatus').value\\n      };\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + reqID, {\\n          method: 'PUT',\\n          body: JSON.stringify({ data: JSON.stringify(updatedData), version: currentRequest.version })\\n        });\\n        if (res.ok) {\\n          cancelEditRequestDetail();\\n          loadAll();\\n        }\\n      } catch(e) {\\n        console.error('Save failed:', e);\\n      }\\n    }\\n\\n    // ---- Answers / Files ----\\n    function renderAnswers(docs) {\\n      const el = document.getElementById('answers');\\n      const emptyEl = document.getElementById('answersEmpty');\\n      if (!docs || docs.length === 0) {\\n        el.innerHTML = '';\\n        if (emptyEl) emptyEl.classList.remove('hidden');\\n        return;\\n      }\\n      if (emptyEl) emptyEl.classList.add('hidden');\\n      const projectID = currentRequest.project_id;\\n      el.innerHTML = docs.map(a =\\u003e {\\n        const d = parseData(a.data_text);\\n        const name = d.filename || d.name || a.summary || 'Document';\\n        const icon = name.endsWith('.pdf') ? '\\\\ud83d\\\\udcc4' : name.match(/\\\\.(jpg|jpeg|png|gif)$/i) ? '\\\\ud83d\\\\uddbc\\\\ufe0f' : name.match(/\\\\.(xlsx?|csv)$/i) ? '\\\\ud83d\\\\udcca' : '\\\\ud83d\\\\udcce';\\n        const objectID = d.object_id || '';\\n        const downloadUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '?filename=' + encodeURIComponent(name) : '#';\\n        const previewUrl = objectID ? '/api/projects/' + projectID + '/objects/' + objectID + '/preview?filename=' + encodeURIComponent(name) : '';\\n        const isVideo = /\\\\.(mp4|mov|avi|mkv|webm)$/i.test(name);\\n        return `\\u003cdiv class=\\\"flex items-center gap-3 px-3 py-2.5 rounded-lg bg-[#0a1628] border border-white/[0.05]\\\"\\u003e\\n          \\u003cspan class=\\\"text-xl\\\"\\u003e${icon}\\u003c/span\\u003e\\n          \\u003cdiv class=\\\"flex-1 min-w-0\\\"\\u003e\\n            \\u003cdiv class=\\\"flex items-center gap-1\\\"\\u003e\\n              \\u003cspan class=\\\"text-white text-sm font-medium truncate\\\" id=\\\"fname-${a.entry_id}\\\"\\u003e${escHtml(name)}\\u003c/span\\u003e\\n              \\u003cbutton onclick=\\\"startRenameFile('${a.entry_id}')\\\" class=\\\"shrink-0 p-0.5 rounded hover:bg-white/[0.08] text-[#b0bec5] hover:text-white transition\\\" title=\\\"Rename\\\"\\u003e\\n                \\u003csvg class=\\\"w-3 h-3\\\" fill=\\\"none\\\" stroke=\\\"currentColor\\\" viewBox=\\\"0 0 24 24\\\"\\u003e\\u003cpath stroke-linecap=\\\"round\\\" stroke-linejoin=\\\"round\\\" stroke-width=\\\"2\\\" d=\\\"M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z\\\"/\\u003e\\u003c/svg\\u003e\\n              \\u003c/button\\u003e\\n            \\u003c/div\\u003e\\n            \\u003cdiv class=\\\"text-[#8899a6] text-xs\\\"\\u003e${a.created_at ? new Date(a.created_at * 1000).toLocaleString() : ''}\\u003c/div\\u003e\\n          \\u003c/div\\u003e\\n          \\u003cdiv class=\\\"flex items-center gap-2 shrink-0\\\"\\u003e\\n            ${objectID \\u0026\\u0026 previewUrl ? `\\u003ca href=\\\"${previewUrl}\\\" target=\\\"_blank\\\" class=\\\"px-2 py-1 rounded text-xs font-medium bg-white/[0.06] text-[#b0bec5] hover:text-white hover:bg-white/[0.1] transition\\\"\\u003e${isVideo ? 'Play' : (isBuyerRole() ? 'View' : 'Preview')}\\u003c/a\\u003e` : ''}\\n            ${objectID \\u0026\\u0026 !isBuyerRole() ? `\\u003ca href=\\\"${downloadUrl}\\\" class=\\\"text-sm hover:underline\\\" style=\\\"color:var(--ds-ac)\\\"\\u003eDownload\\u003c/a\\u003e` : ''}\\n          \\u003c/div\\u003e\\n        \\u003c/div\\u003e`;\\n      }).join('');\\n    }\\n\\n    // ---- Rename file ----\\n    function startRenameFile(answerId) {\\n      const span = document.getElementById('fname-' + answerId);\\n      if (!span) return;\\n      const currentName = span.textContent;\\n      const container = span.parentElement;\\n      container.innerHTML = `\\u003cinput type=\\\"text\\\" value=\\\"\\\" id=\\\"rename-input-${answerId}\\\"\\n        style=\\\"flex:1;padding:2px 6px;background:#0a1628;border:1px solid rgba(255,255,255,.12);border-radius:4px;color:white;font-size:13px;outline:none\\\"\\n        onkeydown=\\\"handleRenameKey(event,'${answerId}')\\\" onblur=\\\"saveRenameFile('${answerId}')\\\"\\u003e`;\\n      const input = document.getElementById('rename-input-' + answerId);\\n      if (input) { input.value = currentName; input.focus(); input.select(); }\\n    }\\n\\n    function handleRenameKey(e, answerId) {\\n      if (e.key === 'Enter') { e.preventDefault(); e.target.blur(); }\\n      if (e.key === 'Escape') { e.target.dataset.cancelled = 'true'; e.target.blur(); }\\n    }\\n\\n    async function saveRenameFile(answerId) {\\n      const input = document.getElementById('rename-input-' + answerId);\\n      if (!input || input.dataset.cancelled === 'true') { loadAll(); return; }\\n      const newName = input.value.trim();\\n      if (!newName) { loadAll(); return; }\\n\\n      const answer = currentAnswers.find(a =\\u003e a.entry_id === answerId);\\n      if (!answer) { loadAll(); return; }\\n\\n      const projectID = currentRequest.project_id;\\n      const d = parseData(answer.data_text);\\n      d.filename = newName;\\n      d.name = newName;\\n\\n      try {\\n        await fetchAPI('/api/projects/' + projectID + '/entries/' + answerId, {\\n          method: 'PUT',\\n          body: JSON.stringify({ data: JSON.stringify(d), version: answer.version || 0 })\\n        });\\n      } catch(e) {\\n        console.error('Rename failed:', e);\\n      }\\n      loadAll();\\n    }\\n\\n    // renderComments replaced by channel system below\\n\\n    async function uploadFiles(files) {\\n      if (!currentRequest) return;\\n      const projectID = currentRequest.project_id;\\n      const status = document.getElementById('uploadStatus');\\n      for (const file of files) {\\n        status.textContent = 'Uploading ' + file.name + '...';\\n        const fd = new FormData();\\n        fd.append('file', file);\\n        try {\\n          const uploadRes = await fetchAPI('/api/projects/' + projectID + '/objects', { method: 'POST', body: fd });\\n          if (!uploadRes.ok) { status.textContent = 'Upload failed for ' + file.name; continue; }\\n          const uploadData = await uploadRes.json();\\n\\n          const entryBody = {\\n            project_id: projectID,\\n            parent_id: reqID,\\n            type: 'document',\\n            data: JSON.stringify({ filename: file.name, name: file.name, content_type: file.type, object_id: uploadData.object_id })\\n          };\\n          const entryRes = await fetchAPI('/api/projects/' + projectID + '/entries', { method: 'POST', body: JSON.stringify(entryBody) });\\n          if (entryRes.ok) { status.textContent = file.name + ' uploaded.'; }\\n          else { status.textContent = 'Failed to save ' + file.name; }\\n        } catch(e) { status.textContent = 'Error: ' + e.message; }\\n      }\\n      setTimeout(() =\\u003e status.textContent = '', 3000);\\n      loadAll();\\n    }\\n\\n    // ---- Channel-based comment system ----\\n    let activeChannel = 'announcements';\\n    let allComments = [];\\n    let dealOrgs = [];\\n    let replyingToId = null;\\n\\n    async function initChannels() {\\n      if (!currentRequest) return;\\n      try {\\n        const res = await fetchAPI('/api/projects/' + currentRequest.project_id + '/orgs');\\n        dealOrgs = await res.json() || [];\\n      } catch(e) { dealOrgs = []; }\\n      renderChannelTabs();\\n      switchChannel('announcements');\\n    }\\n\\n    function renderChannelTabs() {\\n      const container = document.getElementById('channelTabs');\\n      const tabs = [\\n        { key: 'announcements', label: '📢 Announcements' },\\n        ...dealOrgs.map(o =\\u003e {\\n          const d = parseData(o.data_text);\\n          return { key: 'org:' + o.entry_id, label: '🔒 ' + escHtml(d.org_name || d.name || 'Org') };\\n        })\\n      ];\\n      container.innerHTML = tabs.map(t =\\u003e\\n        `\\u003cbutton onclick=\\\"switchChannel(${JSON.stringify(t.key)})\\\"\\n          class=\\\"px-4 py-3 text-sm font-medium whitespace-nowrap border-b-2 -mb-px transition\\n            ${activeChannel === t.key ? 'border-[#c9a84c] text-white' : 'border-transparent text-[#b0bec5] hover:text-white'}\\\"\\u003e\\n          ${t.label}\\n        \\u003c/button\\u003e`\\n      ).join('');\\n    }\\n\\n    function switchChannel(key) {\\n      activeChannel = key;\\n      cancelReply();\\n      renderChannelTabs();\\n      renderChannelComments();\\n      const ta = document.getElementById('commentText');\\n      ta.placeholder = key === 'announcements' ? 'Post an announcement...' : 'Message this channel...';\\n    }\\n\\n    function renderChannelComments() {\\n      const el = document.getElementById('channelComments');\\n      const ch = allComments.filter(c =\\u003e (parseData(c.data_text).channel || 'announcements') === activeChannel);\\n      const top = ch.filter(c =\\u003e !parseData(c.data_text).parent_comment_id);\\n      const nested = ch.filter(c =\\u003e !!parseData(c.data_text).parent_comment_id);\\n      if (top.length === 0) {\\n        el.innerHTML = '\\u003cp class=\\\"text-[#8899a6] text-sm\\\"\\u003eNo messages yet.\\u003c/p\\u003e';\\n        return;\\n      }\\n      el.innerHTML = top.map(c =\\u003e {\\n        const d = parseData(c.data_text);\\n        const replies = nested.filter(r =\\u003e parseData(r.data_text).parent_comment_id === c.entry_id);\\n        return renderOneComment(c, d, false) + replies.map(r =\\u003e renderOneComment(r, parseData(r.data_text), true)).join('');\\n      }).join('');\\n      el.scrollTop = el.scrollHeight;\\n    }\\n\\n    function renderOneComment(c, d, isReply) {\\n      const av = (d.author || '?')[0].toUpperCase();\\n      const ts = c.created_at ? new Date(c.created_at * 1000).toLocaleString('en-US', {month:'short',day:'numeric',hour:'numeric',minute:'2-digit'}) : '';\\n      const replyBtn = isReply ? '' : `\\u003cbutton onclick=\\\"startReply('${c.entry_id}')\\\" class=\\\"text-xs text-[#8899a6] hover:text-[#b0bec5] ml-2 transition\\\"\\u003eReply\\u003c/button\\u003e`;\\n      const indent = isReply ? 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30' : '';\\n      return `\\u003cdiv class=\\\"flex gap-3 ${indent}\\\" data-comment-id=\\\"${c.entry_id}\\\"\\u003e\\n        \\u003cdiv class=\\\"w-7 h-7 rounded-full bg-[#c9a84c]/20 flex items-center justify-center text-[#c9a84c] text-xs font-semibold shrink-0 mt-0.5\\\"\\u003e${av}\\u003c/div\\u003e\\n        \\u003cdiv class=\\\"flex-1 min-w-0\\\"\\u003e\\n          \\u003cdiv class=\\\"flex items-baseline gap-2 mb-1\\\"\\u003e\\n            \\u003cspan class=\\\"text-white text-sm font-medium\\\"\\u003e${escHtml(d.author || 'Unknown')}\\u003c/span\\u003e\\n            \\u003cspan class=\\\"text-[#8899a6] text-xs\\\"\\u003e${ts}\\u003c/span\\u003e\\n            ${replyBtn}\\n          \\u003c/div\\u003e\\n          \\u003cp class=\\\"text-[#b0bec5] text-sm leading-relaxed\\\"\\u003e${escHtml(d.text || '')}\\u003c/p\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e`;\\n    }\\n\\n    function startReply(commentId) {\\n      cancelReply();\\n      replyingToId = commentId;\\n      const el = document.querySelector(`[data-comment-id=\\\"${commentId}\\\"]`);\\n      if (!el) return;\\n      const form = document.createElement('div');\\n      form.id = 'replyForm';\\n      form.className = 'ml-10 pl-4 border-l-2 border-[#c9a84c]/30 mt-2';\\n      form.innerHTML = `\\u003cdiv class=\\\"flex gap-2 items-end\\\"\\u003e\\n        \\u003ctextarea id=\\\"replyText\\\" rows=\\\"2\\\" placeholder=\\\"Reply...\\\" class=\\\"flex-1 px-3 py-2 bg-[#0a1628] border border-white/[0.08] rounded-lg text-white placeholder-[#8899a6] focus:outline-none focus:border-[#c9a84c] text-sm resize-none\\\"\\u003e\\u003c/textarea\\u003e\\n        \\u003cdiv class=\\\"flex flex-col gap-1\\\"\\u003e\\n          \\u003cbutton onclick=\\\"postComment('${commentId}')\\\" class=\\\"px-3 py-1.5 bg-[#c9a84c] hover:bg-[#b8973f] text-[#0a1628] font-semibold rounded-lg text-xs transition\\\"\\u003eReply\\u003c/button\\u003e\\n          \\u003cbutton onclick=\\\"cancelReply()\\\" class=\\\"px-3 py-1.5 bg-white/[0.05] hover:bg-white/[0.08] text-[#b0bec5] rounded-lg text-xs transition\\\"\\u003eCancel\\u003c/button\\u003e\\n        \\u003c/div\\u003e\\n      \\u003c/div\\u003e`;\\n      el.insertAdjacentElement('afterend', form);\\n      document.getElementById('replyText').focus();\\n    }\\n\\n    function cancelReply() {\\n      replyingToId = null;\\n      const f = document.getElementById('replyForm');\\n      if (f) f.remove();\\n    }\\n\\n    async function postComment(parentCommentId) {\\n      if (!currentRequest) return;\\n      const text = parentCommentId\\n        ? (document.getElementById('replyText')?.value || '').trim()\\n        : (document.getElementById('commentText')?.value || '').trim();\\n      if (!text) return;\\n      const projectID = currentRequest.project_id;\\n      const body = {\\n        project_id: projectID,\\n        parent_id: reqID,\\n        type: 'comment',\\n        data: JSON.stringify({ text, author: user.name || user.email, channel: activeChannel, parent_comment_id: parentCommentId || null })\\n      };\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries', { method: 'POST', body: JSON.stringify(body) });\\n        if (res.ok) {\\n          if (parentCommentId) cancelReply(); else document.getElementById('commentText').value = '';\\n          await loadAll();\\n        }\\n      } catch(e) {}\\n    }\\n\\n    async function markAnswered() {\\n      if (!currentRequest || !currentData) return;\\n      const projectID = currentRequest.project_id;\\n      const updatedData = { ...currentData, status: 'answered' };\\n      const body = {\\n        data: JSON.stringify(updatedData),\\n        version: currentRequest.version\\n      };\\n      try {\\n        const res = await fetchAPI('/api/projects/' + projectID + '/entries/' + reqID, { method: 'PUT', body: JSON.stringify(body) });\\n        if (res.ok) loadAll();\\n      } catch(e) {}\\n    }\\n\\n    // Drag and drop\\n    const ua = document.getElementById('uploadArea');\\n    ua.addEventListener('dragover', e =\\u003e { e.preventDefault(); ua.classList.add('border-[#c9a84c]/60'); });\\n    ua.addEventListener('dragleave', () =\\u003e ua.classList.remove('border-[#c9a84c]/60'));\\n    ua.addEventListener('drop', e =\\u003e { e.preventDefault(); ua.classList.remove('border-[#c9a84c]/60'); uploadFiles(e.dataTransfer.files); });\\n\\n    loadAll();\\n\\u003c/script\\u003e\\n{{end}}\"}","is_error":false}},{"type":"tool_result","data":{"tool_call_id":"functions.view:9","name":"","content":"\u003cfile\u003e\n     1|/* =============================================================================\n     2|   Dealspace Portal — app.css\n     3|   Single canonical stylesheet for all portal pages.\n     4|   ============================================================================= */\n     5|\n     6|/* ===== BASE ===== */\n     7|* { font-family: 'Inter', sans-serif; }\n     8|\n     9|/* ===== THEMES ===== */\n    10|:root,\n    11|html[data-theme=\"midnight\"] {\n    12|  font-size: 14px;\n    13|  --ds-bg: #0a1628;\n    14|  --ds-sf: #0f2240;\n    15|  --ds-tx: #fff;\n    16|  --ds-tx2: #c0cdd6;\n    17|  --ds-tx3: #8899a6;\n    18|  --ds-ac: #c9a84c;\n    19|  --ds-ac2: #b8973f;\n    20|  --ds-act: #0a1628;\n    21|  --ds-bd: rgba(255,255,255,.16);\n    22|  --ds-hv: rgba(255,255,255,.08);\n    23|  --ds-inp: #0a1628;\n    24|  --ds-hf: 'Inter', sans-serif;\n    25|}\n    26|\n    27|html[data-theme=\"light\"] {\n    28|  font-size: 14px;\n    29|  --ds-bg: #f0f2f5;\n    30|  --ds-sf: #fff;\n    31|  --ds-tx: #1a202c;\n    32|  --ds-tx2: #8899a6;\n    33|  --ds-tx3: #b0bec5;\n    34|  --ds-ac: #2563eb;\n    35|  --ds-ac2: #1d4ed8;\n    36|  --ds-act: #fff;\n    37|  --ds-bd: rgba(0,0,0,.08);\n    38|  --ds-hv: rgba(0,0,0,.05);\n    39|  --ds-inp: #f8fafc;\n    40|  --ds-hf: 'Inter', sans-serif;\n    41|}\n    42|\n    43|html[data-theme=\"slate\"] {\n    44|  font-size: 14px;\n    45|  --ds-bg: #1e293b;\n    46|  --ds-sf: #334155;\n    47|  --ds-tx: #f1f5f9;\n    48|  --ds-tx2: #b0bec5;\n    49|  --ds-tx3: #8899a6;\n    50|  --ds-ac: #14b8a6;\n    51|  --ds-ac2: #0d9488;\n    52|  --ds-act: #0f172a;\n    53|  --ds-bd: rgba(148,163,184,.12);\n    54|  --ds-hv: rgba(255,255,255,.05);\n    55|  --ds-inp: #1e293b;\n    56|  --ds-hf: 'Inter', sans-serif;\n    57|}\n    58|\n    59|html[data-theme=\"compact\"] {\n    60|  font-size: 13px;\n    61|  --ds-bg: #0a1628;\n    62|  --ds-sf: #0d1f3c;\n    63|  --ds-tx: #fff;\n    64|  --ds-tx2: #b0bec5;\n    65|  --ds-tx3: #8899a6;\n    66|  --ds-ac: #c9a84c;\n    67|  --ds-ac2: #b8973f;\n    68|  --ds-act: #0a1628;\n    69|  --ds-bd: rgba(255,255,255,.08);\n    70|  --ds-hv: rgba(255,255,255,.04);\n    71|  --ds-inp: #0a1628;\n    72|  --ds-hf: 'Inter', sans-serif;\n    73|}\n    74|\n    75|html[data-theme=\"executive\"] {\n    76|  font-size: 17px;\n    77|  --ds-bg: #1a1410;\n    78|  --ds-sf: #2a2218;\n    79|  --ds-tx: #f5f0e8;\n    80|  --ds-tx2: #c4b8a4;\n    81|  --ds-tx3: #8a7e6e;\n    82|  --ds-ac: #d4a843;\n    83|  --ds-ac2: #c49835;\n    84|  --ds-act: #1a1410;\n    85|  --ds-bd: rgba(196,184,164,.12);\n    86|  --ds-hv: rgba(212,168,67,.06);\n    87|  --ds-inp: #1a1410;\n    88|  --ds-hf: 'Georgia', 'Times New Roman', serif;\n    89|}\n    90|\n    91|/* ===== THEME OVERRIDES (Tailwind hardcoded → CSS vars) ===== */\n    92|body { background: var(--ds-bg) !important; }\n    93|h1, h2, h3 { font-family: var(--ds-hf) !important; }\n    94|html[data-theme=\"executive\"] * { font-family: 'Georgia', 'Times New Roman', serif !important; }\n    95|\n    96|/* ===== STEEL THEME ===== */\n    97|html[data-theme=\"steel\"] {\n    98|  font-size: 14px;\n    99|  --ds-bg: #09090b;\n   100|  --ds-sf: #1c1c22;\n   101|  --ds-tx: #fafafa;\n   102|  --ds-tx2: #b4bcc8;\n   103|  --ds-tx3: #8892a0;\n   104|  --ds-ac: #3b82f6;\n   105|  --ds-ac2: #2563eb;\n   106|  --ds-act: #fff;\n   107|  --ds-bd: rgba(255,255,255,.18);\n   108|  --ds-hv: rgba(59,130,246,.10);\n   109|  --ds-inp: #111115;\n   110|  --ds-hf: 'DM Sans', sans-serif;\n   111|}\n   112|html[data-theme=\"steel\"] * { font-family: 'DM Sans', sans-serif !important; }\n   113|html[data-theme=\"steel\"] [class*=\"text-[#c9a84c]\"] { color: #3b82f6 !important; }\n   114|html[data-theme=\"steel\"] [class*=\"bg-[#c9a84c]\"] { background: #3b82f6 !important; color: #fff !important; }\n   115|html[data-theme=\"steel\"] [class*=\"border-[#c9a84c]\"] { border-color: #3b82f6 !important; }\n   116|html[data-theme=\"steel\"] .sidebar-link.active { background: rgba(59,130,246,.12) !important; color: #3b82f6 !important; }\n   117|html[data-theme=\"steel\"] #ds-theme-bar button.active { background: #3b82f6 !important; color: #fff !important; }\n   118|\n   119|/* ===== BRUTALIST THEME ===== */\n   120|html[data-theme=\"brutalist\"] {\n   121|  font-size: 14px;\n   122|  --ds-bg: #111111;\n   123|  --ds-sf: #1c1c1c;\n   124|  --ds-tx: #ffffff;\n   125|  --ds-tx2: #999999;\n   126|  --ds-tx3: #666666;\n   127|  --ds-ac: #ff6600;\n   128|  --ds-ac2: #e05500;\n   129|  --ds-act: #fff;\n   130|  --ds-bd: rgba(255,255,255,.09);\n   131|  --ds-hv: rgba(255,102,0,.06);\n   132|  --ds-inp: #111111;\n   133|  --ds-hf: 'Space Mono', monospace;\n   134|}\n   135|html[data-theme=\"brutalist\"] * { font-family: 'Space Mono', monospace !important; }\n   136|html[data-theme=\"brutalist\"] [class*=\"text-[#c9a84c]\"] { color: #ff6600 !important; }\n   137|html[data-theme=\"brutalist\"] [class*=\"bg-[#c9a84c]\"] { background: #ff6600 !important; color: #fff !important; }\n   138|html[data-theme=\"brutalist\"] [class*=\"border-[#c9a84c]\"] { border-color: #ff6600 !important; }\n   139|html[data-theme=\"brutalist\"] .sidebar-link.active { background: rgba(255,102,0,.1) !important; color: #ff6600 !important; }\n   140|html[data-theme=\"brutalist\"] #ds-theme-bar button.active { background: #ff6600 !important; color: #fff !important; }\n   141|header { background: var(--ds-sf) !important; border-color: var(--ds-bd) !important; }\n   142|nav { background: var(--ds-sf) !important; border-color: var(--ds-bd) !important; }\n   143|.text-white { color: var(--ds-tx) !important; }\n   144|[class*=\"text-white/\"] { color: var(--ds-tx3) !important; }\n   145|[class*=\"bg-[#0d1f3c]\"] { background-color: var(--ds-sf) !important; }\n   146|[class*=\"bg-[#0a1628]\"] { background-color: var(--ds-bg) !important; }\n   147|[class*=\"text-[#c9a84c]\"] { color: var(--ds-ac) !important; }\n   148|[class*=\"text-[#b0bec5]\"] { color: var(--ds-tx2) !important; }\n   149|[class*=\"text-[#8899a6]\"] { color: var(--ds-tx3) !important; }\n   150|[class*=\"text-[#0a1628]\"] { color: var(--ds-act) !important; }\n   151|[class*=\"bg-[#c9a84c]\"] { background-color: var(--ds-ac) !important; }\n   152|[class*=\"hover:bg-[#b8973f]\"]:hover { background-color: var(--ds-ac2) !important; }\n   153|[class*=\"border-white\"] { border-color: var(--ds-bd) !important; }\n   154|[class*=\"bg-white/\"] { background-color: var(--ds-hv) !important; }\n   155|[class*=\"hover:bg-white/\"]:hover { background-color: var(--ds-hv) !important; }\n   156|input, textarea, select { background-color: var(--ds-inp) !important; color: var(--ds-tx) !important; border-color: var(--ds-bd) !important; }\n   157|input:focus, textarea:focus, select:focus { border-color: var(--ds-ac) !important; }\n   158|input.field-error, textarea.field-error, select.field-error { border-color: #ef4444 !important; }\n   159|[class*=\"bg-black/\"] { background-color: rgba(0,0,0,.6) !important; }\n   160|\n   161|/* ===== SIDEBAR ===== */\n   162|.sidebar-link.active {\n   163|  background: color-mix(in srgb, var(--ds-ac) 10%, transparent) !important;\n   164|  color: var(--ds-ac) !important;\n   165|  border-left-color: var(--ds-ac) !important;\n   166|  border-left: 3px solid var(--ds-ac);\n   167|}\n   168|.sidebar-link { color: var(--ds-tx2) !important; }\n   169|.sidebar-link:hover:not(.active) { background: var(--ds-hv) !important; color: var(--ds-tx) !important; }\n   170|\n   171|/* ===== TABS ===== */\n   172|.tab { border-bottom: 2px solid transparent; }\n   173|.tab.active { color: var(--ds-ac) !important; border-bottom-color: var(--ds-ac) !important; }\n   174|\n   175|/* ===== BADGES ===== */\n   176|.badge {\n   177|  display: inline-block;\n   178|  padding: 1px 8px;\n   179|  border-radius: 9999px;\n   180|  font-size: 11px;\n   181|  font-weight: 500;\n   182|  line-height: 18px;\n   183|}\n   184|.priority-high { background: #ef4444; }\n   185|.priority-normal { background: #c9a84c; }\n   186|.priority-low { background: #22c55e; }\n   187|\n   188|/* ===== CARDS ===== */\n   189|.card:hover { border-color: rgba(201,168,76,0.3); transform: translateY(-1px); }\n   190|.task-card:hover { border-color: rgba(201,168,76,0.3); transform: translateY(-1px); }\n   191|\n   192|/* ===== ACTIONS MENU ===== */\n   193|.actions-menu {\n   194|  position: absolute;\n   195|  right: 0;\n   196|  top: 100%;\n   197|  background: var(--ds-sf, #0d1f3c);\n   198|  border: 1px solid var(--ds-bd);\n   199|  border-radius: 8px;\n   200|  padding: 4px 0;\n   201|  min-width: 160px;\n   202|  z-index: 100;\n   203|  box-shadow: 0 8px 24px rgba(0,0,0,.4);\n   204|}\n   205|.actions-menu button {\n   206|  display: block;\n   207|  width: 100%;\n   208|  text-align: left;\n   209|  padding: 6px 14px;\n   210|  font-size: 13px;\n   211|  color: var(--ds-tx2, #b0bec5);\n   212|  background: none;\n   213|  border: none;\n   214|  cursor: pointer;\n   215|}\n   216|.actions-menu button:hover { background: var(--ds-hv); color: var(--ds-tx, #fff); }\n   217|\n   218|/* ===== TREE TABLE ===== */\n   219|#reqTree { border-collapse: collapse; width: 100%; font-size: 13px; }\n   220|\n   221|/* ===== TYPE HIERARCHY ===== */\n   222|/* Level 1 — Logo / Page title : 2xl (19.5px) — set inline */\n   223|/* Level 2 — Project name in header : xl (16.25px) — set inline */\n   224|/* Level 3 — Section / group labels : 13px bold — set inline in renderTree */\n   225|/* Level 4 — Body / table rows : 13px — base */\n   226|/* Level 5 — Metadata / counts / dates : 11px — set inline */\n   227|/* Tabs, sidebar links : text-base (13px) */\n   228|/* Badges, labels, pill text : 11px */\n   229|.tab { font-size: 13px; }\n   230|.sidebar-link { font-size: 13px; }\n   231|#reqTree th { font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }\n   232|#reqTree td { font-size: 13px; }\n   233|#reqTree th {\n   234|  text-align: left;\n   235|  padding: 6px 10px;\n   236|  font-weight: 600;\n   237|  font-size: 11px;\n   238|  text-transform: uppercase;\n   239|  letter-spacing: 0.05em;\n   240|  color: var(--ds-tx3, #8899a6);\n   241|  border-bottom: 1px solid var(--ds-bd, rgba(255,255,255,.08));\n   242|  user-select: none;\n   243|  white-space: nowrap;\n   244|}\n   245|#reqTree td {\n   246|  padding: 5px 10px;\n   247|  border-bottom: 1px solid var(--ds-bd, rgba(255,255,255,.08));\n   248|  vertical-align: middle;\n   249|  white-space: nowrap;\n   250|}\n   251|#reqTree tbody tr { transition: background .1s; }\n   252|#reqTree tbody tr:hover { background: var(--ds-hv, rgba(255,255,255,.04)); }\n   253|#reqTree tbody tr.row-list { background: var(--ds-sf, #0d1f3c); }\n   254|#reqTree tbody tr.row-list:hover { background: color-mix(in srgb, var(--ds-sf, #0d1f3c) 85%, var(--ds-tx, white)); }\n   255|#reqTree tbody tr.row-section { background: rgba(255,255,255,.02); }\n   256|#reqTree .row-num { font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ds-tx3, #8899a6); }\n   257|\n   258|/* ===== DRAG \u0026 DROP ===== */\n   259|#reqTree .drag-handle { cursor: grab; opacity: 0.3; transition: opacity .15s; }\n   260|#reqTree tr:hover .drag-handle { opacity: 0.8; }\n   261|#reqTree .collapse-btn {\n   262|  cursor: pointer;\n   263|  user-select: none;\n   264|  display: inline-block;\n   265|  width: 16px;\n   266|  text-align: center;\n   267|  font-size: 10px;\n   268|  color: var(--ds-tx3, #8899a6);\n   269|  transition: transform .15s;\n   270|}\n   271|#reqTree .collapse-btn.collapsed { transform: rotate(-90deg); }\n   272|.drop-above { box-shadow: 0 -2px 0 var(--ds-ac, #c9a84c) inset; }\n   273|.drop-below { box-shadow: 0 2px 0 var(--ds-ac, #c9a84c) inset; }\n   274|.drop-into  { box-shadow: inset 0 0 0 1px var(--ds-ac, #c9a84c); }\n   275|\n   276|/* ===== THEME BAR ===== */\n   277|#ds-theme-bar {\n   278|  position: fixed;\n   279|  bottom: 20px;\n   280|  right: 20px;\n   281|  z-index: 9999;\n   282|  display: flex;\n   283|  gap: 3px;\n   284|  padding: 4px;\n   285|  background: var(--ds-sf);\n   286|  border: 1px solid var(--ds-bd);\n   287|  border-radius: 12px;\n   288|  box-shadow: 0 4px 20px rgba(0,0,0,.3);\n   289|}\n   290|#ds-theme-bar button {\n   291|  padding: 5px 10px;\n   292|  border-radius: 8px;\n   293|  border: none;\n   294|  cursor: pointer;\n   295|  background: transparent;\n   296|  color: var(--ds-tx2);\n   297|  white-space: nowrap;\n   298|  transition: all .15s;\n   299|  font-size: 12px;\n   300|}\n   301|#ds-theme-bar button:hover { background: var(--ds-hv); }\n   302|#ds-theme-bar button.active { background: var(--ds-ac); color: var(--ds-act); font-weight: 600; }\n   303|#ds-theme-bar, #ds-theme-bar * { font-family: 'Inter', sans-serif !important; }\n   304|\n   305|/* ===== AUTH PAGES ===== */\n   306|.code-input {\n   307|  letter-spacing: 0.5em;\n   308|  text-align: center;\n   309|  font-size: 1.5rem;\n   310|  font-weight: 600;\n   311|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/home/johan/dev/dealspace/portal/static/app.css\",\"content\":\"/* =============================================================================\\n   Dealspace Portal — app.css\\n   Single canonical stylesheet for all portal pages.\\n   ============================================================================= */\\n\\n/* ===== BASE ===== */\\n* { font-family: 'Inter', sans-serif; }\\n\\n/* ===== THEMES ===== */\\n:root,\\nhtml[data-theme=\\\"midnight\\\"] {\\n  font-size: 14px;\\n  --ds-bg: #0a1628;\\n  --ds-sf: #0f2240;\\n  --ds-tx: #fff;\\n  --ds-tx2: #c0cdd6;\\n  --ds-tx3: #8899a6;\\n  --ds-ac: #c9a84c;\\n  --ds-ac2: #b8973f;\\n  --ds-act: #0a1628;\\n  --ds-bd: rgba(255,255,255,.16);\\n  --ds-hv: rgba(255,255,255,.08);\\n  --ds-inp: #0a1628;\\n  --ds-hf: 'Inter', sans-serif;\\n}\\n\\nhtml[data-theme=\\\"light\\\"] {\\n  font-size: 14px;\\n  --ds-bg: #f0f2f5;\\n  --ds-sf: #fff;\\n  --ds-tx: #1a202c;\\n  --ds-tx2: #8899a6;\\n  --ds-tx3: #b0bec5;\\n  --ds-ac: #2563eb;\\n  --ds-ac2: #1d4ed8;\\n  --ds-act: #fff;\\n  --ds-bd: rgba(0,0,0,.08);\\n  --ds-hv: rgba(0,0,0,.05);\\n  --ds-inp: #f8fafc;\\n  --ds-hf: 'Inter', sans-serif;\\n}\\n\\nhtml[data-theme=\\\"slate\\\"] {\\n  font-size: 14px;\\n  --ds-bg: #1e293b;\\n  --ds-sf: #334155;\\n  --ds-tx: #f1f5f9;\\n  --ds-tx2: #b0bec5;\\n  --ds-tx3: #8899a6;\\n  --ds-ac: #14b8a6;\\n  --ds-ac2: #0d9488;\\n  --ds-act: #0f172a;\\n  --ds-bd: rgba(148,163,184,.12);\\n  --ds-hv: rgba(255,255,255,.05);\\n  --ds-inp: #1e293b;\\n  --ds-hf: 'Inter', sans-serif;\\n}\\n\\nhtml[data-theme=\\\"compact\\\"] {\\n  font-size: 13px;\\n  --ds-bg: #0a1628;\\n  --ds-sf: #0d1f3c;\\n  --ds-tx: #fff;\\n  --ds-tx2: #b0bec5;\\n  --ds-tx3: #8899a6;\\n  --ds-ac: #c9a84c;\\n  --ds-ac2: #b8973f;\\n  --ds-act: #0a1628;\\n  --ds-bd: rgba(255,255,255,.08);\\n  --ds-hv: rgba(255,255,255,.04);\\n  --ds-inp: #0a1628;\\n  --ds-hf: 'Inter', sans-serif;\\n}\\n\\nhtml[data-theme=\\\"executive\\\"] {\\n  font-size: 17px;\\n  --ds-bg: #1a1410;\\n  --ds-sf: #2a2218;\\n  --ds-tx: #f5f0e8;\\n  --ds-tx2: #c4b8a4;\\n  --ds-tx3: #8a7e6e;\\n  --ds-ac: #d4a843;\\n  --ds-ac2: #c49835;\\n  --ds-act: #1a1410;\\n  --ds-bd: rgba(196,184,164,.12);\\n  --ds-hv: rgba(212,168,67,.06);\\n  --ds-inp: #1a1410;\\n  --ds-hf: 'Georgia', 'Times New Roman', serif;\\n}\\n\\n/* ===== THEME OVERRIDES (Tailwind hardcoded → CSS vars) ===== */\\nbody { background: var(--ds-bg) !important; }\\nh1, h2, h3 { font-family: var(--ds-hf) !important; }\\nhtml[data-theme=\\\"executive\\\"] * { font-family: 'Georgia', 'Times New Roman', serif !important; }\\n\\n/* ===== STEEL THEME ===== */\\nhtml[data-theme=\\\"steel\\\"] {\\n  font-size: 14px;\\n  --ds-bg: #09090b;\\n  --ds-sf: #1c1c22;\\n  --ds-tx: #fafafa;\\n  --ds-tx2: #b4bcc8;\\n  --ds-tx3: #8892a0;\\n  --ds-ac: #3b82f6;\\n  --ds-ac2: #2563eb;\\n  --ds-act: #fff;\\n  --ds-bd: rgba(255,255,255,.18);\\n  --ds-hv: rgba(59,130,246,.10);\\n  --ds-inp: #111115;\\n  --ds-hf: 'DM Sans', sans-serif;\\n}\\nhtml[data-theme=\\\"steel\\\"] * { font-family: 'DM Sans', sans-serif !important; }\\nhtml[data-theme=\\\"steel\\\"] [class*=\\\"text-[#c9a84c]\\\"] { color: #3b82f6 !important; }\\nhtml[data-theme=\\\"steel\\\"] [class*=\\\"bg-[#c9a84c]\\\"] { background: #3b82f6 !important; color: #fff !important; }\\nhtml[data-theme=\\\"steel\\\"] [class*=\\\"border-[#c9a84c]\\\"] { border-color: #3b82f6 !important; }\\nhtml[data-theme=\\\"steel\\\"] .sidebar-link.active { background: rgba(59,130,246,.12) !important; color: #3b82f6 !important; }\\nhtml[data-theme=\\\"steel\\\"] #ds-theme-bar button.active { background: #3b82f6 !important; color: #fff !important; }\\n\\n/* ===== BRUTALIST THEME ===== */\\nhtml[data-theme=\\\"brutalist\\\"] {\\n  font-size: 14px;\\n  --ds-bg: #111111;\\n  --ds-sf: #1c1c1c;\\n  --ds-tx: #ffffff;\\n  --ds-tx2: #999999;\\n  --ds-tx3: #666666;\\n  --ds-ac: #ff6600;\\n  --ds-ac2: #e05500;\\n  --ds-act: #fff;\\n  --ds-bd: rgba(255,255,255,.09);\\n  --ds-hv: rgba(255,102,0,.06);\\n  --ds-inp: #111111;\\n  --ds-hf: 'Space Mono', monospace;\\n}\\nhtml[data-theme=\\\"brutalist\\\"] * { font-family: 'Space Mono', monospace !important; }\\nhtml[data-theme=\\\"brutalist\\\"] [class*=\\\"text-[#c9a84c]\\\"] { color: #ff6600 !important; }\\nhtml[data-theme=\\\"brutalist\\\"] [class*=\\\"bg-[#c9a84c]\\\"] { background: #ff6600 !important; color: #fff !important; }\\nhtml[data-theme=\\\"brutalist\\\"] [class*=\\\"border-[#c9a84c]\\\"] { border-color: #ff6600 !important; }\\nhtml[data-theme=\\\"brutalist\\\"] .sidebar-link.active { background: rgba(255,102,0,.1) !important; color: #ff6600 !important; }\\nhtml[data-theme=\\\"brutalist\\\"] #ds-theme-bar button.active { background: #ff6600 !important; color: #fff !important; }\\nheader { background: var(--ds-sf) !important; border-color: var(--ds-bd) !important; }\\nnav { background: var(--ds-sf) !important; border-color: var(--ds-bd) !important; }\\n.text-white { color: var(--ds-tx) !important; }\\n[class*=\\\"text-white/\\\"] { color: var(--ds-tx3) !important; }\\n[class*=\\\"bg-[#0d1f3c]\\\"] { background-color: var(--ds-sf) !important; }\\n[class*=\\\"bg-[#0a1628]\\\"] { background-color: var(--ds-bg) !important; }\\n[class*=\\\"text-[#c9a84c]\\\"] { color: var(--ds-ac) !important; }\\n[class*=\\\"text-[#b0bec5]\\\"] { color: var(--ds-tx2) !important; }\\n[class*=\\\"text-[#8899a6]\\\"] { color: var(--ds-tx3) !important; }\\n[class*=\\\"text-[#0a1628]\\\"] { color: var(--ds-act) !important; }\\n[class*=\\\"bg-[#c9a84c]\\\"] { background-color: var(--ds-ac) !important; }\\n[class*=\\\"hover:bg-[#b8973f]\\\"]:hover { background-color: var(--ds-ac2) !important; }\\n[class*=\\\"border-white\\\"] { border-color: var(--ds-bd) !important; }\\n[class*=\\\"bg-white/\\\"] { background-color: var(--ds-hv) !important; }\\n[class*=\\\"hover:bg-white/\\\"]:hover { background-color: var(--ds-hv) !important; }\\ninput, textarea, select { background-color: var(--ds-inp) !important; color: var(--ds-tx) !important; border-color: var(--ds-bd) !important; }\\ninput:focus, textarea:focus, select:focus { border-color: var(--ds-ac) !important; }\\ninput.field-error, textarea.field-error, select.field-error { border-color: #ef4444 !important; }\\n[class*=\\\"bg-black/\\\"] { background-color: rgba(0,0,0,.6) !important; }\\n\\n/* ===== SIDEBAR ===== */\\n.sidebar-link.active {\\n  background: color-mix(in srgb, var(--ds-ac) 10%, transparent) !important;\\n  color: var(--ds-ac) !important;\\n  border-left-color: var(--ds-ac) !important;\\n  border-left: 3px solid var(--ds-ac);\\n}\\n.sidebar-link { color: var(--ds-tx2) !important; }\\n.sidebar-link:hover:not(.active) { background: var(--ds-hv) !important; color: var(--ds-tx) !important; }\\n\\n/* ===== TABS ===== */\\n.tab { border-bottom: 2px solid transparent; }\\n.tab.active { color: var(--ds-ac) !important; border-bottom-color: var(--ds-ac) !important; }\\n\\n/* ===== BADGES ===== */\\n.badge {\\n  display: inline-block;\\n  padding: 1px 8px;\\n  border-radius: 9999px;\\n  font-size: 11px;\\n  font-weight: 500;\\n  line-height: 18px;\\n}\\n.priority-high { background: #ef4444; }\\n.priority-normal { background: #c9a84c; }\\n.priority-low { background: #22c55e; }\\n\\n/* ===== CARDS ===== */\\n.card:hover { border-color: rgba(201,168,76,0.3); transform: translateY(-1px); }\\n.task-card:hover { border-color: rgba(201,168,76,0.3); transform: translateY(-1px); }\\n\\n/* ===== ACTIONS MENU ===== */\\n.actions-menu {\\n  position: absolute;\\n  right: 0;\\n  top: 100%;\\n  background: var(--ds-sf, #0d1f3c);\\n  border: 1px solid var(--ds-bd);\\n  border-radius: 8px;\\n  padding: 4px 0;\\n  min-width: 160px;\\n  z-index: 100;\\n  box-shadow: 0 8px 24px rgba(0,0,0,.4);\\n}\\n.actions-menu button {\\n  display: block;\\n  width: 100%;\\n  text-align: left;\\n  padding: 6px 14px;\\n  font-size: 13px;\\n  color: var(--ds-tx2, #b0bec5);\\n  background: none;\\n  border: none;\\n  cursor: pointer;\\n}\\n.actions-menu button:hover { background: var(--ds-hv); color: var(--ds-tx, #fff); }\\n\\n/* ===== TREE TABLE ===== */\\n#reqTree { border-collapse: collapse; width: 100%; font-size: 13px; }\\n\\n/* ===== TYPE HIERARCHY ===== */\\n/* Level 1 — Logo / Page title : 2xl (19.5px) — set inline */\\n/* Level 2 — Project name in header : xl (16.25px) — set inline */\\n/* Level 3 — Section / group labels : 13px bold — set inline in renderTree */\\n/* Level 4 — Body / table rows : 13px — base */\\n/* Level 5 — Metadata / counts / dates : 11px — set inline */\\n/* Tabs, sidebar links : text-base (13px) */\\n/* Badges, labels, pill text : 11px */\\n.tab { font-size: 13px; }\\n.sidebar-link { font-size: 13px; }\\n#reqTree th { font-size: 11px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; }\\n#reqTree td { font-size: 13px; }\\n#reqTree th {\\n  text-align: left;\\n  padding: 6px 10px;\\n  font-weight: 600;\\n  font-size: 11px;\\n  text-transform: uppercase;\\n  letter-spacing: 0.05em;\\n  color: var(--ds-tx3, #8899a6);\\n  border-bottom: 1px solid var(--ds-bd, rgba(255,255,255,.08));\\n  user-select: none;\\n  white-space: nowrap;\\n}\\n#reqTree td {\\n  padding: 5px 10px;\\n  border-bottom: 1px solid var(--ds-bd, rgba(255,255,255,.08));\\n  vertical-align: middle;\\n  white-space: nowrap;\\n}\\n#reqTree tbody tr { transition: background .1s; }\\n#reqTree tbody tr:hover { background: var(--ds-hv, rgba(255,255,255,.04)); }\\n#reqTree tbody tr.row-list { background: var(--ds-sf, #0d1f3c); }\\n#reqTree tbody tr.row-list:hover { background: color-mix(in srgb, var(--ds-sf, #0d1f3c) 85%, var(--ds-tx, white)); }\\n#reqTree tbody tr.row-section { background: rgba(255,255,255,.02); }\\n#reqTree .row-num { font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ds-tx3, #8899a6); }\\n\\n/* ===== DRAG \\u0026 DROP ===== */\\n#reqTree .drag-handle { cursor: grab; opacity: 0.3; transition: opacity .15s; }\\n#reqTree tr:hover .drag-handle { opacity: 0.8; }\\n#reqTree .collapse-btn {\\n  cursor: pointer;\\n  user-select: none;\\n  display: inline-block;\\n  width: 16px;\\n  text-align: center;\\n  font-size: 10px;\\n  color: var(--ds-tx3, #8899a6);\\n  transition: transform .15s;\\n}\\n#reqTree .collapse-btn.collapsed { transform: rotate(-90deg); }\\n.drop-above { box-shadow: 0 -2px 0 var(--ds-ac, #c9a84c) inset; }\\n.drop-below { box-shadow: 0 2px 0 var(--ds-ac, #c9a84c) inset; }\\n.drop-into  { box-shadow: inset 0 0 0 1px var(--ds-ac, #c9a84c); }\\n\\n/* ===== THEME BAR ===== */\\n#ds-theme-bar {\\n  position: fixed;\\n  bottom: 20px;\\n  right: 20px;\\n  z-index: 9999;\\n  display: flex;\\n  gap: 3px;\\n  padding: 4px;\\n  background: var(--ds-sf);\\n  border: 1px solid var(--ds-bd);\\n  border-radius: 12px;\\n  box-shadow: 0 4px 20px rgba(0,0,0,.3);\\n}\\n#ds-theme-bar button {\\n  padding: 5px 10px;\\n  border-radius: 8px;\\n  border: none;\\n  cursor: pointer;\\n  background: transparent;\\n  color: var(--ds-tx2);\\n  white-space: nowrap;\\n  transition: all .15s;\\n  font-size: 12px;\\n}\\n#ds-theme-bar button:hover { background: var(--ds-hv); }\\n#ds-theme-bar button.active { background: var(--ds-ac); color: var(--ds-act); font-weight: 600; }\\n#ds-theme-bar, #ds-theme-bar * { font-family: 'Inter', sans-serif !important; }\\n\\n/* ===== AUTH PAGES ===== */\\n.code-input {\\n  letter-spacing: 0.5em;\\n  text-align: center;\\n  font-size: 1.5rem;\\n  font-weight: 600;\\n}\"}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: a1fc98a1-75cd-47b6-a83c-e14e872b0d14
session_id: functions.agent:1
role: assistant
model: local.accounts/fireworks/models/kimi-k2p5
parts:
  [{"type":"text","data":{"text":"## Request-Centric Workflow Implementation Summary\n\n### 1. Database Tables\n\n**Core Table: `entries`** (single-table design)\n- Types: `request_list` → `section` → `request` → `answer`/`comment`\n- Key fields: `entry_id`, `project_id`, `parent_id`, `type`, `depth`, `stage`, `assignee_id`, `return_to_id`, `origin_id`, `sort_order`, `summary` (encrypted), `data` (encrypted JSON)\n\n**`answer_links`** - Many-to-many request↔answer links\n- Fields: `answer_id`, `request_id`, `status` (pending|confirmed|rejected), `linked_by`, `linked_at`, `reviewed_by`, `reviewed_at`, `reject_reason`\n\n**`entry_events`** - Workflow audit trail\n\n### 2. API Endpoints\n\n**Request CRUD:**\n- `GET /api/projects/{id}/requests` - List requests\n- `GET /api/projects/{id}/requests/tree` - Hierarchical tree\n- `POST /api/projects/{id}/requests/new` - Create request\n- `POST /api/projects/{id}/sections` - Create section\n- `PUT /api/projects/{id}/entries/{id}` - Update (status, priority, data)\n- `DELETE /api/projects/{id}/entries/{id}` - Delete\n- `POST /api/projects/{id}/entries/{id}/move` - Drag-drop reorder\n\n**Answers/Documents:**\n- `GET /api/requests/{id}` - Request detail with children\n- `GET /api/projects/{id}/requests/{id}/links` - List linked answers\n- `POST /api/projects/{id}/requests/{id}/links` - Link answer\n- `DELETE /api/projects/{id}/requests/{id}/links/{answerId}` - Unlink\n- `POST /api/projects/{id}/objects` - Upload file\n- `GET /api/projects/{id}/objects/{id}/preview` - Preview\n\n**Import/Templates:**\n- `POST /api/projects/{id}/requests/import` - CSV/XLSX import (smart header detection)\n- `POST /api/projects/{id}/requests/import-template` - Import from template\n- `GET /templates`, `POST /templates` - Template management\n\n### 3. UI Templates\n\n| Template | Purpose |\n|----------|---------|\n| `/portal/templates/app/project.html` | Request list view with tree table (columns: #, Request, Priority, Status, Assignee, Due, Label, Files, Comments) |\n| `/portal/templates/app/request.html` | Request detail view with file upload, discussion channels, edit mode |\n\n**Key UI Features:**\n- Inline priority/status dropdowns in list view\n- Drag \u0026 drop reordering with visual indicators\n- Collapsible sections\n- Summary/Detailed view toggle\n- Attach answer modal with search\n- Channel-based comments (Announcements + org-private)\n\n### 4. Current Status Model\n\n**Status values:** `open` | `in_process` | `partial` | `complete` | `answered`\n**Priority values:** `critical` | `high` | `medium` | `low`\n**Stage values:** `pre_dataroom` (IB/seller only) | `dataroom` (all parties) | `closed`\n\n### What's Implemented vs Missing\n\n| Feature | Status | Notes |\n|---------|--------|-------|\n| Request CRUD | ✅ | Full create, read, update, delete |\n| Hierarchical tree | ✅ | List → Section → Request |\n| Status/priority tracking | ✅ | Inline editing in UI |\n| File upload/linking | ✅ | Answer links with pending/confirmed/rejected |\n| Comments/discussion | ✅ | Channel-based (Announcements, org-private) |\n| Import/Export | ✅ | CSV/XLSX with smart detection, templates |\n| Drag \u0026 drop | ✅ | Reordering via `EntryMoveSort` |\n| **Assignment workflow** | ❌ | `assignee_id` exists, no UI or logic |\n| **Review/approval** | ❌ | `reviewed_by` fields exist, no workflow |\n| **Notifications** | ❌ | SMTP not configured |\n| **Due date tracking** | ❌ | Field exists, no reminders |\n| **Request forwarding** | ❌ | `return_to_id`/`origin_id` unused |\n| **Audit log UI** | ❌ | `entry_events` table exists, no display |\n| **Bulk operations** | ❌ | No multi-select actions |\n| **Search/filter** | ❌ | No status/priority filters in list |\n\nThe foundation is solid with RBAC enforced at the three choke points (`EntryRead`, `EntryWrite`, `EntryDelete`), but workflow automation (assignments, reviews, notifications) remains to be implemented."}},{"type":"finish","data":{"reason":"end_turn","time":1774088535}}]


id: e5bd07d4-dc1d-4af2-9502-2a7974659c2c
session_id: 69d4f621-0c76-43a4-b49f-a02ef9d97c02
role: tool
parts:
  [{"type":"tool_result","data":{"tool_call_id":"functions.agent:1","name":"","content":"## Request-Centric Workflow Implementation Summary\n\n### 1. Database Tables\n\n**Core Table: `entries`** (single-table design)\n- Types: `request_list` → `section` → `request` → `answer`/`comment`\n- Key fields: `entry_id`, `project_id`, `parent_id`, `type`, `depth`, `stage`, `assignee_id`, `return_to_id`, `origin_id`, `sort_order`, `summary` (encrypted), `data` (encrypted JSON)\n\n**`answer_links`** - Many-to-many request↔answer links\n- Fields: `answer_id`, `request_id`, `status` (pending|confirmed|rejected), `linked_by`, `linked_at`, `reviewed_by`, `reviewed_at`, `reject_reason`\n\n**`entry_events`** - Workflow audit trail\n\n### 2. API Endpoints\n\n**Request CRUD:**\n- `GET /api/projects/{id}/requests` - List requests\n- `GET /api/projects/{id}/requests/tree` - Hierarchical tree\n- `POST /api/projects/{id}/requests/new` - Create request\n- `POST /api/projects/{id}/sections` - Create section\n- `PUT /api/projects/{id}/entries/{id}` - Update (status, priority, data)\n- `DELETE /api/projects/{id}/entries/{id}` - Delete\n- `POST /api/projects/{id}/entries/{id}/move` - Drag-drop reorder\n\n**Answers/Documents:**\n- `GET /api/requests/{id}` - Request detail with children\n- `GET /api/projects/{id}/requests/{id}/links` - List linked answers\n- `POST /api/projects/{id}/requests/{id}/links` - Link answer\n- `DELETE /api/projects/{id}/requests/{id}/links/{answerId}` - Unlink\n- `POST /api/projects/{id}/objects` - Upload file\n- `GET /api/projects/{id}/objects/{id}/preview` - Preview\n\n**Import/Templates:**\n- `POST /api/projects/{id}/requests/import` - CSV/XLSX import (smart header detection)\n- `POST /api/projects/{id}/requests/import-template` - Import from template\n- `GET /templates`, `POST /templates` - Template management\n\n### 3. UI Templates\n\n| Template | Purpose |\n|----------|---------|\n| `/portal/templates/app/project.html` | Request list view with tree table (columns: #, Request, Priority, Status, Assignee, Due, Label, Files, Comments) |\n| `/portal/templates/app/request.html` | Request detail view with file upload, discussion channels, edit mode |\n\n**Key UI Features:**\n- Inline priority/status dropdowns in list view\n- Drag \u0026 drop reordering with visual indicators\n- Collapsible sections\n- Summary/Detailed view toggle\n- Attach answer modal with search\n- Channel-based comments (Announcements + org-private)\n\n### 4. Current Status Model\n\n**Status values:** `open` | `in_process` | `partial` | `complete` | `answered`\n**Priority values:** `critical` | `high` | `medium` | `low`\n**Stage values:** `pre_dataroom` (IB/seller only) | `dataroom` (all parties) | `closed`\n\n### What's Implemented vs Missing\n\n| Feature | Status | Notes |\n|---------|--------|-------|\n| Request CRUD | ✅ | Full create, read, update, delete |\n| Hierarchical tree | ✅ | List → Section → Request |\n| Status/priority tracking | ✅ | Inline editing in UI |\n| File upload/linking | ✅ | Answer links with pending/confirmed/rejected |\n| Comments/discussion | ✅ | Channel-based (Announcements, org-private) |\n| Import/Export | ✅ | CSV/XLSX with smart detection, templates |\n| Drag \u0026 drop | ✅ | Reordering via `EntryMoveSort` |\n| **Assignment workflow** | ❌ | `assignee_id` exists, no UI or logic |\n| **Review/approval** | ❌ | `reviewed_by` fields exist, no workflow |\n| **Notifications** | ❌ | SMTP not configured |\n| **Due date tracking** | ❌ | Field exists, no reminders |\n| **Request forwarding** | ❌ | `return_to_id`/`origin_id` unused |\n| **Audit log UI** | ❌ | `entry_events` table exists, no display |\n| **Bulk operations** | ❌ | No multi-select actions |\n| **Search/filter** | ❌ | No status/priority filters in list |\n\nThe foundation is solid with RBAC enforced at the three choke points (`EntryRead`, `EntryWrite`, `EntryDelete`), but workflow automation (assignments, reviews, notifications) remains to be implemented.","metadata":"","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


id: 6f2e0c42-2261-4388-b31b-1efd4b5d0499
session_id: 69d4f621-0c76-43a4-b49f-a02ef9d97c02
role: assistant
model: local.accounts/fireworks/models/kimi-k2p5
parts:
  [{"type":"text","data":{"text":"Let me check the current status model and see what needs to be updated to match the website description."}},{"type":"tool_call","data":{"id":"functions.view:2","name":"view","input":"{\"file_path\": \"/home/johan/dev/dealspace/lib/types.go\"}","type":"function","finished":true}},{"type":"tool_call","data":{"id":"functions.grep:3","name":"grep","input":"{\"pattern\": \"status.*=|Status.*string|type.*Status\", \"path\": \"/home/johan/dev/dealspace\", \"include\": \"*.go\"}","type":"function","finished":true}},{"type":"finish","data":{"reason":"tool_use","time":1774088537}}]


id: c58b04e1-d79c-44ae-b495-870b72eaea23
session_id: 69d4f621-0c76-43a4-b49f-a02ef9d97c02
role: tool
parts:
  [{"type":"tool_result","data":{"tool_call_id":"functions.view:2","name":"","content":"\u003cfile\u003e\n     1|package lib\n     2|\n     3|import \"database/sql\"\n     4|\n     5|// Entry is the single core data type. One table to rule them all.\n     6|type Entry struct {\n     7|\tEntryID    string `json:\"entry_id\"`\n     8|\tProjectID  string `json:\"project_id\"`\n     9|\tParentID   string `json:\"parent_id\"`\n    10|\tType       string `json:\"type\"`\n    11|\tDepth      int    `json:\"depth\"`\n    12|\tSortOrder  int    `json:\"sort_order\"`\n    13|\tSearchKey  []byte `json:\"-\"` // blind index (HMAC-SHA256)\n    14|\tSearchKey2 []byte `json:\"-\"` // blind index (HMAC-SHA256)\n    15|\tSummary    []byte `json:\"-\"` // packed: zstd + AES-256-GCM\n    16|\tData       []byte `json:\"-\"` // packed: zstd + AES-256-GCM\n    17|\tStage      string `json:\"stage\"`\n    18|\n    19|\t// Workflow routing (plain, indexed)\n    20|\tAssigneeID string `json:\"assignee_id\"`\n    21|\tReturnToID string `json:\"return_to_id\"`\n    22|\tOriginID   string `json:\"origin_id\"`\n    23|\n    24|\t// Optimistic locking\n    25|\tVersion int `json:\"version\"`\n    26|\n    27|\t// Soft delete\n    28|\tDeletedAt *int64  `json:\"deleted_at,omitempty\"`\n    29|\tDeletedBy *string `json:\"deleted_by,omitempty\"`\n    30|\n    31|\t// Key rotation\n    32|\tKeyVersion int `json:\"key_version\"`\n    33|\n    34|\t// Timestamps\n    35|\tCreatedAt int64  `json:\"created_at\"`\n    36|\tUpdatedAt int64  `json:\"updated_at\"`\n    37|\tCreatedBy string `json:\"created_by\"`\n    38|\n    39|\t// Unpacked fields (not stored directly, populated by lib)\n    40|\tSummaryText string `json:\"summary,omitempty\"`\n    41|\tDataText    string `json:\"data_text,omitempty\"`\n    42|}\n    43|\n    44|// Entry types\n    45|const (\n    46|\tTypeProject         = \"project\"\n    47|\tTypeWorkstream      = \"workstream\"\n    48|\tTypeRequestList     = \"request_list\"\n    49|\tTypeRequest         = \"request\"\n    50|\tTypeAnswer          = \"answer\"\n    51|\tTypeSection         = \"section\"\n    52|\tTypeComment         = \"comment\"\n    53|\tTypeOrganization    = \"organization\"\n    54|\tTypeDealOrg         = \"deal_org\"\n    55|\tTypeRequestTemplate = \"request_template\" // global template stored under TemplateProjectID\n    56|)\n    57|\n    58|// Stages\n    59|const (\n    60|\tStagePreDataroom = \"pre_dataroom\"\n    61|\tStageDataroom    = \"dataroom\"\n    62|\tStageClosed      = \"closed\"\n    63|)\n    64|\n    65|// OrgData is the JSON structure packed into an organization entry's Data field.\n    66|type OrgData struct {\n    67|\tName         string          `json:\"name\"`\n    68|\tDomains      []string        `json:\"domains\"`                 // required, e.g. [\"kaseya.com\",\"datto.com\"]\n    69|\tRole         string          `json:\"role\"`                    // seller | buyer | ib | advisor\n    70|\tLogo         string          `json:\"logo,omitempty\"`\n    71|\tWebsite      string          `json:\"website,omitempty\"`\n    72|\tDescription  string          `json:\"description,omitempty\"`\n    73|\tIndustry     string          `json:\"industry,omitempty\"`\n    74|\tPhone        string          `json:\"phone,omitempty\"`\n    75|\tFax          string          `json:\"fax,omitempty\"`\n    76|\tAddress      string          `json:\"address,omitempty\"`\n    77|\tCity         string          `json:\"city,omitempty\"`\n    78|\tState        string          `json:\"state,omitempty\"`\n    79|\tCountry      string          `json:\"country,omitempty\"`\n    80|\tFounded      string          `json:\"founded,omitempty\"`\n    81|\tLinkedIn     string          `json:\"linkedin,omitempty\"`\n    82|\tContactName  string          `json:\"contact_name,omitempty\"`\n    83|\tContactEmail string          `json:\"contact_email,omitempty\"`\n    84|\tMembers      []DealOrgMember `json:\"members,omitempty\"`       // global contact registry for this org\n    85|}\n    86|\n    87|// DealOrgData is the JSON structure packed into a deal_org entry's Data field.\n    88|// A deal_org entry links an organization into a specific deal (project).\n    89|type DealOrgData struct {\n    90|\tOrgID      string          `json:\"org_id\"`      // entry_id of the organization\n    91|\tRole       string          `json:\"role\"`        // seller | buyer | ib | advisor\n    92|\tDomainLock bool            `json:\"domain_lock\"` // if true, enforce domain check on invites\n    93|\tPermissions *DealOrgPerms  `json:\"permissions,omitempty\"`\n    94|\tMembers     []DealOrgMember `json:\"members,omitempty\"`\n    95|}\n    96|\n    97|// DealOrgPerms defines what an org can do in a deal.\n    98|type DealOrgPerms struct {\n    99|\tDownload         string      `json:\"download\"`           // \"full\" | \"watermark\" | \"none\"\n   100|\tUpload           bool        `json:\"upload\"`\n   101|\tAddRequestLists  bool        `json:\"add_request_lists\"`\n   102|\tEditRequestLists bool        `json:\"edit_request_lists\"`\n   103|\tFolderAccess     string      `json:\"folder_access\"`      // \"all\" | \"assigned\"\n   104|\tVisibleLists     interface{} `json:\"visible_lists,omitempty\"` // \"all\" | []string of entry_ids\n   105|}\n   106|\n   107|// DealOrgMember is a person associated with a deal org.\n   108|type DealOrgMember struct {\n   109|\tName          string `json:\"name\"`\n   110|\tEmail         string `json:\"email\"`\n   111|\tTitle         string `json:\"title,omitempty\"`\n   112|\tPhone         string `json:\"phone,omitempty\"`          // legacy / kept for compat\n   113|\tPhoneBusiness string `json:\"phone_business,omitempty\"`\n   114|\tPhonePersonal string `json:\"phone_personal,omitempty\"`\n   115|\tPhoto         string `json:\"photo,omitempty\"`\n   116|\tBio           string `json:\"bio,omitempty\"`\n   117|\tLinkedIn      string `json:\"linkedin,omitempty\"`\n   118|}\n   119|\n   120|// User represents an account.\n   121|type User struct {\n   122|\tUserID    string `json:\"user_id\"`\n   123|\tEmail     string `json:\"email\"`\n   124|\tName      string `json:\"name\"`\n   125|\tPassword  string `json:\"-\"` // bcrypt\n   126|\tOrgID     string `json:\"org_id\"`\n   127|\tOrgName   string `json:\"org_name\"`\n   128|\tMFASecret string `json:\"-\"`\n   129|\tActive    bool   `json:\"active\"`\n   130|\tCreatedAt int64  `json:\"created_at\"`\n   131|\tUpdatedAt int64  `json:\"updated_at\"`\n   132|}\n   133|\n   134|// Access is an RBAC grant.\n   135|type Access struct {\n   136|\tID           string `json:\"id\"`\n   137|\tProjectID    string `json:\"project_id\"`\n   138|\tWorkstreamID string `json:\"workstream_id,omitempty\"`\n   139|\tUserID       string `json:\"user_id\"`\n   140|\tRole         string `json:\"role\"`\n   141|\tOps          string `json:\"ops\"`\n   142|\tCanGrant     bool   `json:\"can_grant\"`\n   143|\tGrantedBy    string `json:\"granted_by\"`\n   144|\tGrantedAt    int64  `json:\"granted_at\"`\n   145|\tRevokedAt    *int64 `json:\"revoked_at,omitempty\"`\n   146|\tRevokedBy    string `json:\"revoked_by,omitempty\"`\n   147|}\n   148|\n   149|// Roles\n   150|const (\n   151|\tRoleSuperAdmin   = \"super_admin\"\n   152|\tRoleIBAdmin      = \"ib_admin\"\n   153|\tRoleIBMember     = \"ib_member\"\n   154|\tRoleSellerAdmin  = \"seller_admin\"\n   155|\tRoleSellerMember = \"seller_member\"\n   156|\tRoleBuyerAdmin   = \"buyer_admin\"\n   157|\tRoleBuyerMember  = \"buyer_member\"\n   158|\tRoleObserver     = \"observer\"\n   159|)\n   160|\n   161|// RoleHierarchy defines privilege levels. Higher = more privileged.\n   162|var RoleHierarchy = map[string]int{\n   163|\tRoleSuperAdmin:   200,\n   164|\tRoleIBAdmin:      100,\n   165|\tRoleIBMember:     80,\n   166|\tRoleSellerAdmin:  70,\n   167|\tRoleSellerMember: 50,\n   168|\tRoleBuyerAdmin:   40,\n   169|\tRoleBuyerMember:  30,\n   170|\tRoleObserver:     10,\n   171|}\n   172|\n   173|// AnswerLink connects answers to requests (many-to-many).\n   174|type AnswerLink struct {\n   175|\tAnswerID     string   `json:\"answer_id\"`\n   176|\tRequestID    string   `json:\"request_id\"`\n   177|\tLinkedBy     string   `json:\"linked_by\"`\n   178|\tLinkedAt     int64    `json:\"linked_at\"`\n   179|\tConfirmed    bool     `json:\"confirmed\"`\n   180|\tAIScore      *float64 `json:\"ai_score,omitempty\"`\n   181|\tStatus       string   `json:\"status\"`\n   182|\tReviewedBy   string   `json:\"reviewed_by,omitempty\"`\n   183|\tReviewedAt   *int64   `json:\"reviewed_at,omitempty\"`\n   184|\tRejectReason string   `json:\"reject_reason,omitempty\"`\n   185|}\n   186|\n   187|// EntryEvent is a workflow thread item.\n   188|type EntryEvent struct {\n   189|\tID      string `json:\"id\"`\n   190|\tEntryID string `json:\"entry_id\"`\n   191|\tActorID string `json:\"actor_id\"`\n   192|\tChannel string `json:\"channel\"`\n   193|\tAction  []byte `json:\"-\"` // packed\n   194|\tData    []byte `json:\"-\"` // packed\n   195|\tTs      int64  `json:\"ts\"`\n   196|\n   197|\tActionText string `json:\"action,omitempty\"`\n   198|\tDataText   string `json:\"data_text,omitempty\"`\n   199|}\n   200|\n   201|// AuditEntry is a security audit log row.\n   202|type AuditEntry struct {\n   203|\tID        string `json:\"id\"`\n   204|\tProjectID string `json:\"project_id\"`\n   205|\tActorID   string `json:\"actor_id\"`\n   206|\tAction    []byte `json:\"-\"`\n   207|\tTargetID  string `json:\"target_id,omitempty\"`\n   208|\tDetails   []byte `json:\"-\"`\n   209|\tIP        string `json:\"ip,omitempty\"`\n   210|\tTs        int64  `json:\"ts\"`\n   211|\n   212|\tActionText  string `json:\"action,omitempty\"`\n   213|\tDetailsText string `json:\"details,omitempty\"`\n   214|}\n   215|\n   216|// Session tracks active user sessions.\n   217|type Session struct {\n   218|\tID          string `json:\"id\"`\n   219|\tUserID      string `json:\"user_id\"`\n   220|\tFingerprint string `json:\"fingerprint\"`\n   221|\tCreatedAt   int64  `json:\"created_at\"`\n   222|\tExpiresAt   int64  `json:\"expires_at\"`\n   223|\tRevoked     bool   `json:\"revoked\"`\n   224|\tTestRole    string `json:\"test_role,omitempty\"` // super-admin test impersonation\n   225|}\n   226|\n   227|// Theme is a CSS custom properties bundle.\n   228|type Theme struct {\n   229|\tID         string `json:\"id\"`\n   230|\tName       string `json:\"name\"`\n   231|\tProjectID  string `json:\"project_id,omitempty\"`\n   232|\tProperties string `json:\"properties\"` // packed CSS vars as JSON\n   233|}\n   234|\n   235|// EntryFilter is used by EntryRead to query entries.\n   236|type EntryFilter struct {\n   237|\tProjectID  string\n   238|\tParentID   *string\n   239|\tType       string\n   240|\tStage      string\n   241|\tAssigneeID string\n   242|\tSearchKey  []byte // blind index to match\n   243|\tLimit      int\n   244|\tOffset     int\n   245|}\n   246|\n   247|// DB wraps the database connection.\n   248|type DB struct {\n   249|\tConn *sql.DB\n   250|}\n   251|\n   252|// Challenge represents an email OTP challenge for passwordless login.\n   253|type Challenge struct {\n   254|\tChallengeID string `json:\"challenge_id\"`\n   255|\tEmail       string `json:\"email\"`\n   256|\tCode        string `json:\"code\"`\n   257|\tCreatedAt   int64  `json:\"created_at\"`\n   258|\tExpiresAt   int64  `json:\"expires_at\"`\n   259|\tUsed        bool   `json:\"used\"`\n   260|}\n   261|\n   262|// Config holds application configuration.\n   263|type Config struct {\n   264|\tMasterKey      []byte\n   265|\tDBPath         string\n   266|\tStorePath      string\n   267|\tPort           string\n   268|\tEnv            string // \"development\" | \"production\"\n   269|\tJWTSecret      []byte\n   270|\tMailer         *Mailer\n   271|\tBackdoorCode   string // OTP backdoor for dev/testing\n   272|\tOpenRouterKey  string // OpenRouter API key for LLM features\n   273|}\n   274|\n   275|// RequestData is the JSON structure packed into a request entry's Data field.\n   276|// Represents a diligence request item imported from CSV/XLSX.\n   277|type RequestData struct {\n   278|\tTitle          string   `json:\"title\"`                      // human-readable description\n   279|\tItemNumber     string   `json:\"item_number\"`                // e.g. \"1.3\", \"A-12\"\n   280|\tSection        string   `json:\"section\"`                    // e.g. \"Financial\", \"Legal\"\n   281|\tDescription    string   `json:\"description\"`                // full detail / context\n   282|\tPriority       string   `json:\"priority\"`                   // critical | high | medium | low\n   283|\tStatus         string   `json:\"status\"`                     // open | in_process | partial | complete\n   284|\tAssigneeID     string   `json:\"assignee_id,omitempty\"`\n   285|\tAssigneeName   string   `json:\"assignee_name,omitempty\"`\n   286|\tReviewerID     string   `json:\"reviewer_id,omitempty\"`\n   287|\tDueDate        string   `json:\"due_date,omitempty\"`         // YYYY-MM-DD\n   288|\tCompletedAt    string   `json:\"completed_at,omitempty\"`     // YYYY-MM-DD\n   289|\tBuyerComment   string   `json:\"buyer_comment,omitempty\"`\n   290|\tSellerComment  string   `json:\"seller_comment,omitempty\"`\n   291|\tLabel          string   `json:\"label,omitempty\"`\n   292|\tTags           []string `json:\"tags,omitempty\"`\n   293|\tLinkedEntryIDs []string `json:\"linked_entry_ids,omitempty\"` // linked answer/file entries\n   294|}\n   295|\n   296|// SectionData is the JSON structure packed into a section entry's Data field.\n   297|type SectionData struct {\n   298|\tName string `json:\"name\"`\n   299|}\n   300|\n   301|// RequestListData is the JSON structure packed into a request_list entry's Data field.\n   302|type RequestListData struct {\n   303|\tName            string `json:\"name\"`\n   304|\tOrgID           string `json:\"org_id,omitempty\"`            // PE firm org entry_id\n   305|\tVisibilityOrgID string `json:\"visibility_org_id,omitempty\"` // restrict visibility to this org (+ IB)\n   306|}\n   307|\n   308|// RequestTemplateData is the JSON structure packed into a request_template entry's Data field.\n   309|type RequestTemplateData struct {\n   310|\tName        string              `json:\"name\"`\n   311|\tDescription string              `json:\"description,omitempty\"`\n   312|\tItems       []RequestTemplateItem `json:\"items\"`\n   313|}\n   314|\n   315|// RequestTemplateItem is a single item in a request template.\n   316|type RequestTemplateItem struct {\n   317|\tSection    string `json:\"section\"`\n   318|\tItemNumber string `json:\"item_number,omitempty\"`\n   319|\tTitle      string `json:\"title\"`\n   320|\tPriority   string `json:\"priority,omitempty\"`\n   321|}\n   322|\n   323|// WorkstreamData is the JSON structure packed into a workstream entry's Data field.\n   324|type WorkstreamData struct {\n   325|\tName        string `json:\"name\"`\n   326|\tDescription string `json:\"description,omitempty\"`\n   327|}\n   328|\n   329|// OAuthClient represents a registered OAuth 2.0 client application.\n   330|type OAuthClient struct {\n   331|\tClientID     string   `json:\"client_id\"`\n   332|\tClientName   string   `json:\"client_name\"`\n   333|\tRedirectURIs []string `json:\"redirect_uris\"`\n   334|\tCreatedAt    int64    `json:\"created_at\"`\n   335|}\n   336|\n   337|// OAuthCode represents an OAuth 2.0 authorization code.\n   338|type OAuthCode struct {\n   339|\tCode          string `json:\"code\"`\n   340|\tClientID      string `json:\"client_id\"`\n   341|\tUserID        string `json:\"user_id\"`\n   342|\tRedirectURI   string `json:\"redirect_uri\"`\n   343|\tCodeChallenge string `json:\"code_challenge\"`\n   344|\tScope         string `json:\"scope\"`\n   345|\tExpiresAt     int64  `json:\"expires_at\"`\n   346|\tUsed          bool   `json:\"used\"`\n   347|}\n   348|\n   349|// OAuthToken represents an OAuth 2.0 access token.\n   350|type OAuthToken struct {\n   351|\tToken     string `json:\"token\"`\n   352|\tClientID  string `json:\"client_id\"`\n   353|\tUserID    string `json:\"user_id\"`\n   354|\tScope     string `json:\"scope\"`\n   355|\tExpiresAt int64  `json:\"expires_at\"`\n   356|\tRevoked   bool   `json:\"revoked\"`\n   357|\tCreatedAt int64  `json:\"created_at\"`\n   358|}\n\u003c/file\u003e\n","metadata":"{\"file_path\":\"/home/johan/dev/dealspace/lib/types.go\",\"content\":\"package lib\\n\\nimport \\\"database/sql\\\"\\n\\n// Entry is the single core data type. One table to rule them all.\\ntype Entry struct {\\n\\tEntryID    string `json:\\\"entry_id\\\"`\\n\\tProjectID  string `json:\\\"project_id\\\"`\\n\\tParentID   string `json:\\\"parent_id\\\"`\\n\\tType       string `json:\\\"type\\\"`\\n\\tDepth      int    `json:\\\"depth\\\"`\\n\\tSortOrder  int    `json:\\\"sort_order\\\"`\\n\\tSearchKey  []byte `json:\\\"-\\\"` // blind index (HMAC-SHA256)\\n\\tSearchKey2 []byte `json:\\\"-\\\"` // blind index (HMAC-SHA256)\\n\\tSummary    []byte `json:\\\"-\\\"` // packed: zstd + AES-256-GCM\\n\\tData       []byte `json:\\\"-\\\"` // packed: zstd + AES-256-GCM\\n\\tStage      string `json:\\\"stage\\\"`\\n\\n\\t// Workflow routing (plain, indexed)\\n\\tAssigneeID string `json:\\\"assignee_id\\\"`\\n\\tReturnToID string `json:\\\"return_to_id\\\"`\\n\\tOriginID   string `json:\\\"origin_id\\\"`\\n\\n\\t// Optimistic locking\\n\\tVersion int `json:\\\"version\\\"`\\n\\n\\t// Soft delete\\n\\tDeletedAt *int64  `json:\\\"deleted_at,omitempty\\\"`\\n\\tDeletedBy *string `json:\\\"deleted_by,omitempty\\\"`\\n\\n\\t// Key rotation\\n\\tKeyVersion int `json:\\\"key_version\\\"`\\n\\n\\t// Timestamps\\n\\tCreatedAt int64  `json:\\\"created_at\\\"`\\n\\tUpdatedAt int64  `json:\\\"updated_at\\\"`\\n\\tCreatedBy string `json:\\\"created_by\\\"`\\n\\n\\t// Unpacked fields (not stored directly, populated by lib)\\n\\tSummaryText string `json:\\\"summary,omitempty\\\"`\\n\\tDataText    string `json:\\\"data_text,omitempty\\\"`\\n}\\n\\n// Entry types\\nconst (\\n\\tTypeProject         = \\\"project\\\"\\n\\tTypeWorkstream      = \\\"workstream\\\"\\n\\tTypeRequestList     = \\\"request_list\\\"\\n\\tTypeRequest         = \\\"request\\\"\\n\\tTypeAnswer          = \\\"answer\\\"\\n\\tTypeSection         = \\\"section\\\"\\n\\tTypeComment         = \\\"comment\\\"\\n\\tTypeOrganization    = \\\"organization\\\"\\n\\tTypeDealOrg         = \\\"deal_org\\\"\\n\\tTypeRequestTemplate = \\\"request_template\\\" // global template stored under TemplateProjectID\\n)\\n\\n// Stages\\nconst (\\n\\tStagePreDataroom = \\\"pre_dataroom\\\"\\n\\tStageDataroom    = \\\"dataroom\\\"\\n\\tStageClosed      = \\\"closed\\\"\\n)\\n\\n// OrgData is the JSON structure packed into an organization entry's Data field.\\ntype OrgData struct {\\n\\tName         string          `json:\\\"name\\\"`\\n\\tDomains      []string        `json:\\\"domains\\\"`                 // required, e.g. [\\\"kaseya.com\\\",\\\"datto.com\\\"]\\n\\tRole         string          `json:\\\"role\\\"`                    // seller | buyer | ib | advisor\\n\\tLogo         string          `json:\\\"logo,omitempty\\\"`\\n\\tWebsite      string          `json:\\\"website,omitempty\\\"`\\n\\tDescription  string          `json:\\\"description,omitempty\\\"`\\n\\tIndustry     string          `json:\\\"industry,omitempty\\\"`\\n\\tPhone        string          `json:\\\"phone,omitempty\\\"`\\n\\tFax          string          `json:\\\"fax,omitempty\\\"`\\n\\tAddress      string          `json:\\\"address,omitempty\\\"`\\n\\tCity         string          `json:\\\"city,omitempty\\\"`\\n\\tState        string          `json:\\\"state,omitempty\\\"`\\n\\tCountry      string          `json:\\\"country,omitempty\\\"`\\n\\tFounded      string          `json:\\\"founded,omitempty\\\"`\\n\\tLinkedIn     string          `json:\\\"linkedin,omitempty\\\"`\\n\\tContactName  string          `json:\\\"contact_name,omitempty\\\"`\\n\\tContactEmail string          `json:\\\"contact_email,omitempty\\\"`\\n\\tMembers      []DealOrgMember `json:\\\"members,omitempty\\\"`       // global contact registry for this org\\n}\\n\\n// DealOrgData is the JSON structure packed into a deal_org entry's Data field.\\n// A deal_org entry links an organization into a specific deal (project).\\ntype DealOrgData struct {\\n\\tOrgID      string          `json:\\\"org_id\\\"`      // entry_id of the organization\\n\\tRole       string          `json:\\\"role\\\"`        // seller | buyer | ib | advisor\\n\\tDomainLock bool            `json:\\\"domain_lock\\\"` // if true, enforce domain check on invites\\n\\tPermissions *DealOrgPerms  `json:\\\"permissions,omitempty\\\"`\\n\\tMembers     []DealOrgMember `json:\\\"members,omitempty\\\"`\\n}\\n\\n// DealOrgPerms defines what an org can do in a deal.\\ntype DealOrgPerms struct {\\n\\tDownload         string      `json:\\\"download\\\"`           // \\\"full\\\" | \\\"watermark\\\" | \\\"none\\\"\\n\\tUpload           bool        `json:\\\"upload\\\"`\\n\\tAddRequestLists  bool        `json:\\\"add_request_lists\\\"`\\n\\tEditRequestLists bool        `json:\\\"edit_request_lists\\\"`\\n\\tFolderAccess     string      `json:\\\"folder_access\\\"`      // \\\"all\\\" | \\\"assigned\\\"\\n\\tVisibleLists     interface{} `json:\\\"visible_lists,omitempty\\\"` // \\\"all\\\" | []string of entry_ids\\n}\\n\\n// DealOrgMember is a person associated with a deal org.\\ntype DealOrgMember struct {\\n\\tName          string `json:\\\"name\\\"`\\n\\tEmail         string `json:\\\"email\\\"`\\n\\tTitle         string `json:\\\"title,omitempty\\\"`\\n\\tPhone         string `json:\\\"phone,omitempty\\\"`          // legacy / kept for compat\\n\\tPhoneBusiness string `json:\\\"phone_business,omitempty\\\"`\\n\\tPhonePersonal string `json:\\\"phone_personal,omitempty\\\"`\\n\\tPhoto         string `json:\\\"photo,omitempty\\\"`\\n\\tBio           string `json:\\\"bio,omitempty\\\"`\\n\\tLinkedIn      string `json:\\\"linkedin,omitempty\\\"`\\n}\\n\\n// User represents an account.\\ntype User struct {\\n\\tUserID    string `json:\\\"user_id\\\"`\\n\\tEmail     string `json:\\\"email\\\"`\\n\\tName      string `json:\\\"name\\\"`\\n\\tPassword  string `json:\\\"-\\\"` // bcrypt\\n\\tOrgID     string `json:\\\"org_id\\\"`\\n\\tOrgName   string `json:\\\"org_name\\\"`\\n\\tMFASecret string `json:\\\"-\\\"`\\n\\tActive    bool   `json:\\\"active\\\"`\\n\\tCreatedAt int64  `json:\\\"created_at\\\"`\\n\\tUpdatedAt int64  `json:\\\"updated_at\\\"`\\n}\\n\\n// Access is an RBAC grant.\\ntype Access struct {\\n\\tID           string `json:\\\"id\\\"`\\n\\tProjectID    string `json:\\\"project_id\\\"`\\n\\tWorkstreamID string `json:\\\"workstream_id,omitempty\\\"`\\n\\tUserID       string `json:\\\"user_id\\\"`\\n\\tRole         string `json:\\\"role\\\"`\\n\\tOps          string `json:\\\"ops\\\"`\\n\\tCanGrant     bool   `json:\\\"can_grant\\\"`\\n\\tGrantedBy    string `json:\\\"granted_by\\\"`\\n\\tGrantedAt    int64  `json:\\\"granted_at\\\"`\\n\\tRevokedAt    *int64 `json:\\\"revoked_at,omitempty\\\"`\\n\\tRevokedBy    string `json:\\\"revoked_by,omitempty\\\"`\\n}\\n\\n// Roles\\nconst (\\n\\tRoleSuperAdmin   = \\\"super_admin\\\"\\n\\tRoleIBAdmin      = \\\"ib_admin\\\"\\n\\tRoleIBMember     = \\\"ib_member\\\"\\n\\tRoleSellerAdmin  = \\\"seller_admin\\\"\\n\\tRoleSellerMember = \\\"seller_member\\\"\\n\\tRoleBuyerAdmin   = \\\"buyer_admin\\\"\\n\\tRoleBuyerMember  = \\\"buyer_member\\\"\\n\\tRoleObserver     = \\\"observer\\\"\\n)\\n\\n// RoleHierarchy defines privilege levels. Higher = more privileged.\\nvar RoleHierarchy = map[string]int{\\n\\tRoleSuperAdmin:   200,\\n\\tRoleIBAdmin:      100,\\n\\tRoleIBMember:     80,\\n\\tRoleSellerAdmin:  70,\\n\\tRoleSellerMember: 50,\\n\\tRoleBuyerAdmin:   40,\\n\\tRoleBuyerMember:  30,\\n\\tRoleObserver:     10,\\n}\\n\\n// AnswerLink connects answers to requests (many-to-many).\\ntype AnswerLink struct {\\n\\tAnswerID     string   `json:\\\"answer_id\\\"`\\n\\tRequestID    string   `json:\\\"request_id\\\"`\\n\\tLinkedBy     string   `json:\\\"linked_by\\\"`\\n\\tLinkedAt     int64    `json:\\\"linked_at\\\"`\\n\\tConfirmed    bool     `json:\\\"confirmed\\\"`\\n\\tAIScore      *float64 `json:\\\"ai_score,omitempty\\\"`\\n\\tStatus       string   `json:\\\"status\\\"`\\n\\tReviewedBy   string   `json:\\\"reviewed_by,omitempty\\\"`\\n\\tReviewedAt   *int64   `json:\\\"reviewed_at,omitempty\\\"`\\n\\tRejectReason string   `json:\\\"reject_reason,omitempty\\\"`\\n}\\n\\n// EntryEvent is a workflow thread item.\\ntype EntryEvent struct {\\n\\tID      string `json:\\\"id\\\"`\\n\\tEntryID string `json:\\\"entry_id\\\"`\\n\\tActorID string `json:\\\"actor_id\\\"`\\n\\tChannel string `json:\\\"channel\\\"`\\n\\tAction  []byte `json:\\\"-\\\"` // packed\\n\\tData    []byte `json:\\\"-\\\"` // packed\\n\\tTs      int64  `json:\\\"ts\\\"`\\n\\n\\tActionText string `json:\\\"action,omitempty\\\"`\\n\\tDataText   string `json:\\\"data_text,omitempty\\\"`\\n}\\n\\n// AuditEntry is a security audit log row.\\ntype AuditEntry struct {\\n\\tID        string `json:\\\"id\\\"`\\n\\tProjectID string `json:\\\"project_id\\\"`\\n\\tActorID   string `json:\\\"actor_id\\\"`\\n\\tAction    []byte `json:\\\"-\\\"`\\n\\tTargetID  string `json:\\\"target_id,omitempty\\\"`\\n\\tDetails   []byte `json:\\\"-\\\"`\\n\\tIP        string `json:\\\"ip,omitempty\\\"`\\n\\tTs        int64  `json:\\\"ts\\\"`\\n\\n\\tActionText  string `json:\\\"action,omitempty\\\"`\\n\\tDetailsText string `json:\\\"details,omitempty\\\"`\\n}\\n\\n// Session tracks active user sessions.\\ntype Session struct {\\n\\tID          string `json:\\\"id\\\"`\\n\\tUserID      string `json:\\\"user_id\\\"`\\n\\tFingerprint string `json:\\\"fingerprint\\\"`\\n\\tCreatedAt   int64  `json:\\\"created_at\\\"`\\n\\tExpiresAt   int64  `json:\\\"expires_at\\\"`\\n\\tRevoked     bool   `json:\\\"revoked\\\"`\\n\\tTestRole    string `json:\\\"test_role,omitempty\\\"` // super-admin test impersonation\\n}\\n\\n// Theme is a CSS custom properties bundle.\\ntype Theme struct {\\n\\tID         string `json:\\\"id\\\"`\\n\\tName       string `json:\\\"name\\\"`\\n\\tProjectID  string `json:\\\"project_id,omitempty\\\"`\\n\\tProperties string `json:\\\"properties\\\"` // packed CSS vars as JSON\\n}\\n\\n// EntryFilter is used by EntryRead to query entries.\\ntype EntryFilter struct {\\n\\tProjectID  string\\n\\tParentID   *string\\n\\tType       string\\n\\tStage      string\\n\\tAssigneeID string\\n\\tSearchKey  []byte // blind index to match\\n\\tLimit      int\\n\\tOffset     int\\n}\\n\\n// DB wraps the database connection.\\ntype DB struct {\\n\\tConn *sql.DB\\n}\\n\\n// Challenge represents an email OTP challenge for passwordless login.\\ntype Challenge struct {\\n\\tChallengeID string `json:\\\"challenge_id\\\"`\\n\\tEmail       string `json:\\\"email\\\"`\\n\\tCode        string `json:\\\"code\\\"`\\n\\tCreatedAt   int64  `json:\\\"created_at\\\"`\\n\\tExpiresAt   int64  `json:\\\"expires_at\\\"`\\n\\tUsed        bool   `json:\\\"used\\\"`\\n}\\n\\n// Config holds application configuration.\\ntype Config struct {\\n\\tMasterKey      []byte\\n\\tDBPath         string\\n\\tStorePath      string\\n\\tPort           string\\n\\tEnv            string // \\\"development\\\" | \\\"production\\\"\\n\\tJWTSecret      []byte\\n\\tMailer         *Mailer\\n\\tBackdoorCode   string // OTP backdoor for dev/testing\\n\\tOpenRouterKey  string // OpenRouter API key for LLM features\\n}\\n\\n// RequestData is the JSON structure packed into a request entry's Data field.\\n// Represents a diligence request item imported from CSV/XLSX.\\ntype RequestData struct {\\n\\tTitle          string   `json:\\\"title\\\"`                      // human-readable description\\n\\tItemNumber     string   `json:\\\"item_number\\\"`                // e.g. \\\"1.3\\\", \\\"A-12\\\"\\n\\tSection        string   `json:\\\"section\\\"`                    // e.g. \\\"Financial\\\", \\\"Legal\\\"\\n\\tDescription    string   `json:\\\"description\\\"`                // full detail / context\\n\\tPriority       string   `json:\\\"priority\\\"`                   // critical | high | medium | low\\n\\tStatus         string   `json:\\\"status\\\"`                     // open | in_process | partial | complete\\n\\tAssigneeID     string   `json:\\\"assignee_id,omitempty\\\"`\\n\\tAssigneeName   string   `json:\\\"assignee_name,omitempty\\\"`\\n\\tReviewerID     string   `json:\\\"reviewer_id,omitempty\\\"`\\n\\tDueDate        string   `json:\\\"due_date,omitempty\\\"`         // YYYY-MM-DD\\n\\tCompletedAt    string   `json:\\\"completed_at,omitempty\\\"`     // YYYY-MM-DD\\n\\tBuyerComment   string   `json:\\\"buyer_comment,omitempty\\\"`\\n\\tSellerComment  string   `json:\\\"seller_comment,omitempty\\\"`\\n\\tLabel          string   `json:\\\"label,omitempty\\\"`\\n\\tTags           []string `json:\\\"tags,omitempty\\\"`\\n\\tLinkedEntryIDs []string `json:\\\"linked_entry_ids,omitempty\\\"` // linked answer/file entries\\n}\\n\\n// SectionData is the JSON structure packed into a section entry's Data field.\\ntype SectionData struct {\\n\\tName string `json:\\\"name\\\"`\\n}\\n\\n// RequestListData is the JSON structure packed into a request_list entry's Data field.\\ntype RequestListData struct {\\n\\tName            string `json:\\\"name\\\"`\\n\\tOrgID           string `json:\\\"org_id,omitempty\\\"`            // PE firm org entry_id\\n\\tVisibilityOrgID string `json:\\\"visibility_org_id,omitempty\\\"` // restrict visibility to this org (+ IB)\\n}\\n\\n// RequestTemplateData is the JSON structure packed into a request_template entry's Data field.\\ntype RequestTemplateData struct {\\n\\tName        string              `json:\\\"name\\\"`\\n\\tDescription string              `json:\\\"description,omitempty\\\"`\\n\\tItems       []RequestTemplateItem `json:\\\"items\\\"`\\n}\\n\\n// RequestTemplateItem is a single item in a request template.\\ntype RequestTemplateItem struct {\\n\\tSection    string `json:\\\"section\\\"`\\n\\tItemNumber string `json:\\\"item_number,omitempty\\\"`\\n\\tTitle      string `json:\\\"title\\\"`\\n\\tPriority   string `json:\\\"priority,omitempty\\\"`\\n}\\n\\n// WorkstreamData is the JSON structure packed into a workstream entry's Data field.\\ntype WorkstreamData struct {\\n\\tName        string `json:\\\"name\\\"`\\n\\tDescription string `json:\\\"description,omitempty\\\"`\\n}\\n\\n// OAuthClient represents a registered OAuth 2.0 client application.\\ntype OAuthClient struct {\\n\\tClientID     string   `json:\\\"client_id\\\"`\\n\\tClientName   string   `json:\\\"client_name\\\"`\\n\\tRedirectURIs []string `json:\\\"redirect_uris\\\"`\\n\\tCreatedAt    int64    `json:\\\"created_at\\\"`\\n}\\n\\n// OAuthCode represents an OAuth 2.0 authorization code.\\ntype OAuthCode struct {\\n\\tCode          string `json:\\\"code\\\"`\\n\\tClientID      string `json:\\\"client_id\\\"`\\n\\tUserID        string `json:\\\"user_id\\\"`\\n\\tRedirectURI   string `json:\\\"redirect_uri\\\"`\\n\\tCodeChallenge string `json:\\\"code_challenge\\\"`\\n\\tScope         string `json:\\\"scope\\\"`\\n\\tExpiresAt     int64  `json:\\\"expires_at\\\"`\\n\\tUsed          bool   `json:\\\"used\\\"`\\n}\\n\\n// OAuthToken represents an OAuth 2.0 access token.\\ntype OAuthToken struct {\\n\\tToken     string `json:\\\"token\\\"`\\n\\tClientID  string `json:\\\"client_id\\\"`\\n\\tUserID    string `json:\\\"user_id\\\"`\\n\\tScope     string `json:\\\"scope\\\"`\\n\\tExpiresAt int64  `json:\\\"expires_at\\\"`\\n\\tRevoked   bool   `json:\\\"revoked\\\"`\\n\\tCreatedAt int64  `json:\\\"created_at\\\"`\\n}\"}","is_error":false}},{"type":"tool_result","data":{"tool_call_id":"functions.grep:3","name":"","content":"Found 54 matches\n/home/johan/dev/dealspace/api/chat.go:\n  Line 351: \t\treturn \"\", fmt.Errorf(\"API error %d: %s\", resp.StatusCode, string(body))\n\n/home/johan/dev/dealspace/lib/llm.go:\n  Line 210: \t\treturn \"\", fmt.Errorf(\"OpenRouter API error %d: %s\", resp.StatusCode, string(body))\n\n/home/johan/dev/dealspace/api/handlers.go:\n  Line 2273: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"deleted\"})\n  Line 304: \t\tJSONResponse(w, http.StatusOK, map[string]string{\n  Line 3925: \tJSONResponse(w, http.StatusCreated, map[string]any{\n  Line 3743: \tJSONResponse(w, http.StatusOK, map[string]any{\"ok\": true, \"test_role\": req.Role})\n  Line 3620: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"unlinked\"})\n  Line 3600: \tJSONResponse(w, http.StatusCreated, map[string]string{\"status\": \"linked\"})\n  Line 3539: \t\tStatus    string `json:\"status\"`\n  Line 3488: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"moved\"})\n  Line 3250: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"updated\"})\n  Line 3188: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 800: \tJSONResponse(w, http.StatusCreated, map[string]string{\n  Line 2733: \tJSONResponse(w, http.StatusOK, map[string]any{\"entry_id\": entry.EntryID, \"title\": title})\n  Line 830: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 2695: \tJSONResponse(w, http.StatusOK, map[string]any{\"entry_id\": entry.EntryID, \"name\": name})\n  Line 2604: \t\tStatus     string          `json:\"status\"`\n  Line 46: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"ok\"})\n  Line 177: \tJSONResponse(w, http.StatusOK, map[string]any{\"entry_id\": existing.EntryID, \"version\": existing.Version})\n  Line 199: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"deleted\"})\n  Line 242: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"deleted\"})\n  Line 2425: \tJSONResponse(w, http.StatusOK, map[string]any{\"entry_id\": entry.EntryID, \"name\": data.Name})\n  Line 347: \tJSONResponse(w, http.StatusOK, map[string]string{\n  Line 431: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 454: \tJSONResponse(w, http.StatusOK, map[string]string{\"status\": \"ok\"})\n  Line 469: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 611: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 2780: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 2562: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 917: \tJSONResponse(w, http.StatusCreated, map[string]string{\n  Line 1216: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 1528: \tJSONResponse(w, http.StatusCreated, map[string]any{\n  Line 1602: \tJSONResponse(w, http.StatusOK, map[string]any{\"ok\": true, \"version\": org.Version})\n  Line 1651: \ttype MemberStatus struct {\n  Line 1653: \t\tStatus   string            `json:\"status\"` // \"existing\" | \"new\" | \"not_found\"\n  Line 1699: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 1837: \tJSONResponse(w, http.StatusOK, map[string]any{\"ok\": true})\n  Line 2036: \tJSONResponse(w, http.StatusOK, map[string]any{\"ok\": true})\n  Line 2240: \tJSONResponse(w, http.StatusCreated, map[string]any{\n\n/home/johan/dev/dealspace/lib/types.go:\n  Line 283: \tStatus         string   `json:\"status\"`                     // open | in_process | partial | complete\n  Line 181: \tStatus       string   `json:\"status\"`\n\n/home/johan/dev/dealspace/api/middleware.go:\n  Line 120: \tw.status = code\n\n/home/johan/dev/dealspace/api/integration_test.go:\n  Line 249: \tif healthResp[\"status\"] != \"ok\" {\n\n/home/johan/dev/dealspace/api/oauth.go:\n  Line 56: \tJSONResponse(w, http.StatusOK, map[string]any{\n  Line 41: \tJSONResponse(w, http.StatusOK, map[string]any{\n\n/home/johan/dev/dealspace/api/mcp.go:\n  Line 337: \t\tvalidStatuses := map[string]bool{\"open\": true, \"in_process\": true, \"partial\": true, \"complete\": true}\n  Line 332: \t\tstatus, err := req.RequireString(\"status\")\n  Line 309: \t\t\t\tstatus, _ := data[\"status\"].(string)\n  Line 224: \t\t\t\tstatus, _ := data[\"status\"].(string)\n\n/home/johan/dev/dealspace/api/requests_test.go:\n  Line 342: \t\tt.Fatalf(\"expected 200, got %d: %s\", resp.StatusCode, string(body))\n  Line 187: \t\tt.Fatalf(\"expected 200, got %d: %s\", resp.StatusCode, string(body))\n  Line 129: \t\tt.Fatalf(\"expected 200, got %d: %s\", resp.StatusCode, string(body))\n  Line 532: \t\tif r[\"status\"] == nil {\n\n/home/johan/dev/dealspace/lib/notify.go:\n  Line 14: \t// 1. Query answer_links WHERE answer_id = ? AND status = 'confirmed'\n","metadata":"{\"number_of_matches\":54,\"truncated\":false}","is_error":false}},{"type":"finish","data":{"reason":"stop","time":0}}]


