@gsep/core 0.8.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +12 -12
  2. package/dist/{PGA.d.ts → GSEP.d.ts} +120 -8
  3. package/dist/GSEP.d.ts.map +1 -0
  4. package/dist/{PGA.js → GSEP.js} +1248 -46
  5. package/dist/GSEP.js.map +1 -0
  6. package/dist/adapters/langchain.d.ts +52 -0
  7. package/dist/adapters/langchain.d.ts.map +1 -0
  8. package/dist/adapters/langchain.js +89 -0
  9. package/dist/adapters/langchain.js.map +1 -0
  10. package/dist/adapters/openclaw-plugin.d.ts +42 -0
  11. package/dist/adapters/openclaw-plugin.d.ts.map +1 -0
  12. package/dist/adapters/openclaw-plugin.js +149 -0
  13. package/dist/adapters/openclaw-plugin.js.map +1 -0
  14. package/dist/adapters/vercel-ai.d.ts +74 -0
  15. package/dist/adapters/vercel-ai.d.ts.map +1 -0
  16. package/dist/adapters/vercel-ai.js +151 -0
  17. package/dist/adapters/vercel-ai.js.map +1 -0
  18. package/dist/advanced-ai/EnhancedSelfModel.js +2 -2
  19. package/dist/advanced-ai/EnhancedSelfModel.js.map +1 -1
  20. package/dist/advanced-ai/ModelRouter.js.map +1 -1
  21. package/dist/auto.d.ts +6 -0
  22. package/dist/auto.d.ts.map +1 -0
  23. package/dist/auto.js +197 -0
  24. package/dist/auto.js.map +1 -0
  25. package/dist/core/CoherenceValidator.d.ts +25 -0
  26. package/dist/core/CoherenceValidator.d.ts.map +1 -0
  27. package/dist/core/CoherenceValidator.js +182 -0
  28. package/dist/core/CoherenceValidator.js.map +1 -0
  29. package/dist/core/ContextMemory.d.ts.map +1 -1
  30. package/dist/core/ContextMemory.js +1 -1
  31. package/dist/core/ContextMemory.js.map +1 -1
  32. package/dist/core/DNAProfile.d.ts +5 -1
  33. package/dist/core/DNAProfile.d.ts.map +1 -1
  34. package/dist/core/DNAProfile.js +14 -1
  35. package/dist/core/DNAProfile.js.map +1 -1
  36. package/dist/core/GSEPIdentitySection.d.ts.map +1 -1
  37. package/dist/core/GSEPIdentitySection.js +12 -3
  38. package/dist/core/GSEPIdentitySection.js.map +1 -1
  39. package/dist/core/GenomeKernel.d.ts.map +1 -1
  40. package/dist/core/GenomeKernel.js +3 -0
  41. package/dist/core/GenomeKernel.js.map +1 -1
  42. package/dist/core/GenomeManager.js +1 -1
  43. package/dist/core/GenomeManager.js.map +1 -1
  44. package/dist/core/ProactiveSuggestions.js +1 -1
  45. package/dist/core/ProactiveSuggestions.js.map +1 -1
  46. package/dist/dashboard/DashboardServer.d.ts +64 -0
  47. package/dist/dashboard/DashboardServer.d.ts.map +1 -0
  48. package/dist/dashboard/DashboardServer.js +409 -0
  49. package/dist/dashboard/DashboardServer.js.map +1 -0
  50. package/dist/dashboard/DashboardToken.d.ts +16 -0
  51. package/dist/dashboard/DashboardToken.d.ts.map +1 -0
  52. package/dist/dashboard/DashboardToken.js +54 -0
  53. package/dist/dashboard/DashboardToken.js.map +1 -0
  54. package/dist/dashboard/dashboard.html +2731 -0
  55. package/dist/dashboard/index.d.ts +3 -0
  56. package/dist/dashboard/index.d.ts.map +1 -0
  57. package/dist/dashboard/index.js +3 -0
  58. package/dist/dashboard/index.js.map +1 -0
  59. package/dist/evaluation/BenchmarkSuites.d.ts +2 -2
  60. package/dist/evaluation/BenchmarkSuites.d.ts.map +1 -1
  61. package/dist/evaluation/BenchmarkSuites.js +6 -6
  62. package/dist/evaluation/BenchmarkSuites.js.map +1 -1
  63. package/dist/evaluation/ConstitutionalGate.d.ts +18 -0
  64. package/dist/evaluation/ConstitutionalGate.d.ts.map +1 -0
  65. package/dist/evaluation/ConstitutionalGate.js +149 -0
  66. package/dist/evaluation/ConstitutionalGate.js.map +1 -0
  67. package/dist/evaluation/Evaluator.d.ts +4 -4
  68. package/dist/evaluation/Evaluator.d.ts.map +1 -1
  69. package/dist/evaluation/Evaluator.js +27 -27
  70. package/dist/evaluation/Evaluator.js.map +1 -1
  71. package/dist/evaluation/EvolutionGuardrails.d.ts +3 -1
  72. package/dist/evaluation/EvolutionGuardrails.d.ts.map +1 -1
  73. package/dist/evaluation/EvolutionGuardrails.js +29 -11
  74. package/dist/evaluation/EvolutionGuardrails.js.map +1 -1
  75. package/dist/evolution/MutationOperator.d.ts +19 -6
  76. package/dist/evolution/MutationOperator.d.ts.map +1 -1
  77. package/dist/evolution/MutationOperator.js +264 -9
  78. package/dist/evolution/MutationOperator.js.map +1 -1
  79. package/dist/evolution/boost/operators/BreakthroughOperator.d.ts +1 -1
  80. package/dist/evolution/boost/operators/BreakthroughOperator.d.ts.map +1 -1
  81. package/dist/evolution/boost/operators/BreakthroughOperator.js.map +1 -1
  82. package/dist/evolution/boost/operators/CrossoverMutationOperator.d.ts +1 -1
  83. package/dist/evolution/boost/operators/CrossoverMutationOperator.d.ts.map +1 -1
  84. package/dist/evolution/boost/operators/CrossoverMutationOperator.js.map +1 -1
  85. package/dist/evolution/boost/operators/PatternExtractionOperator.d.ts +1 -1
  86. package/dist/evolution/boost/operators/PatternExtractionOperator.d.ts.map +1 -1
  87. package/dist/evolution/boost/operators/PatternExtractionOperator.js.map +1 -1
  88. package/dist/evolution/boost/operators/SemanticRestructuringOperator.d.ts +1 -1
  89. package/dist/evolution/boost/operators/SemanticRestructuringOperator.d.ts.map +1 -1
  90. package/dist/evolution/boost/operators/SemanticRestructuringOperator.js.map +1 -1
  91. package/dist/firewall/AnomalyDetector.d.ts +42 -0
  92. package/dist/firewall/AnomalyDetector.d.ts.map +1 -0
  93. package/dist/firewall/AnomalyDetector.js +181 -0
  94. package/dist/firewall/AnomalyDetector.js.map +1 -0
  95. package/dist/firewall/ContentFirewall.d.ts +5 -0
  96. package/dist/firewall/ContentFirewall.d.ts.map +1 -1
  97. package/dist/firewall/ContentFirewall.js +5 -0
  98. package/dist/firewall/ContentFirewall.js.map +1 -1
  99. package/dist/firewall/PurposeLock.d.ts +41 -0
  100. package/dist/firewall/PurposeLock.d.ts.map +1 -0
  101. package/dist/firewall/PurposeLock.js +199 -0
  102. package/dist/firewall/PurposeLock.js.map +1 -0
  103. package/dist/gene-bank/CognitiveGene.d.ts +118 -118
  104. package/dist/gene-bank/{PGAIntegration.d.ts → GSEPIntegration.d.ts} +10 -10
  105. package/dist/gene-bank/GSEPIntegration.d.ts.map +1 -0
  106. package/dist/gene-bank/{PGAIntegration.js → GSEPIntegration.js} +6 -6
  107. package/dist/gene-bank/GSEPIntegration.js.map +1 -0
  108. package/dist/gene-bank/GeneAdopter.d.ts +6 -6
  109. package/dist/gene-bank/GeneAdopter.d.ts.map +1 -1
  110. package/dist/gene-bank/GeneAdopter.js +2 -2
  111. package/dist/gene-bank/GeneAdopter.js.map +1 -1
  112. package/dist/gene-bank/GeneBank.d.ts +13 -9
  113. package/dist/gene-bank/GeneBank.d.ts.map +1 -1
  114. package/dist/gene-bank/GeneBank.js +8 -1
  115. package/dist/gene-bank/GeneBank.js.map +1 -1
  116. package/dist/gene-bank/GeneExtractor.d.ts +7 -7
  117. package/dist/gene-bank/GeneExtractor.d.ts.map +1 -1
  118. package/dist/gene-bank/GeneExtractor.js +1 -1
  119. package/dist/gene-bank/GeneExtractor.js.map +1 -1
  120. package/dist/gene-bank/GeneMatcher.d.ts +1 -1
  121. package/dist/gene-bank/GeneMatcher.d.ts.map +1 -1
  122. package/dist/gene-bank/MarketplaceClient.d.ts +17 -1
  123. package/dist/gene-bank/MarketplaceClient.d.ts.map +1 -1
  124. package/dist/gene-bank/MarketplaceClient.js +106 -35
  125. package/dist/gene-bank/MarketplaceClient.js.map +1 -1
  126. package/dist/gene-bank/MarketplaceMapper.d.ts +7 -0
  127. package/dist/gene-bank/MarketplaceMapper.d.ts.map +1 -0
  128. package/dist/gene-bank/MarketplaceMapper.js +95 -0
  129. package/dist/gene-bank/MarketplaceMapper.js.map +1 -0
  130. package/dist/gene-bank/MarketplaceTypes.d.ts +121 -0
  131. package/dist/gene-bank/MarketplaceTypes.d.ts.map +1 -0
  132. package/dist/gene-bank/MarketplaceTypes.js +2 -0
  133. package/dist/gene-bank/MarketplaceTypes.js.map +1 -0
  134. package/dist/gene-bank/SandboxTester.d.ts +3 -3
  135. package/dist/gene-bank/SandboxTester.d.ts.map +1 -1
  136. package/dist/gene-bank/adapters/InMemoryGeneStorage.d.ts +3 -3
  137. package/dist/gene-bank/adapters/InMemoryGeneStorage.d.ts.map +1 -1
  138. package/dist/gene-bank/adapters/PostgresGeneStorage.d.ts +4 -4
  139. package/dist/gene-bank/adapters/PostgresGeneStorage.d.ts.map +1 -1
  140. package/dist/gene-bank/adapters/PostgresGeneStorage.js.map +1 -1
  141. package/dist/gene-bank/adapters/SQLiteGeneStorage.d.ts +28 -0
  142. package/dist/gene-bank/adapters/SQLiteGeneStorage.d.ts.map +1 -0
  143. package/dist/gene-bank/adapters/SQLiteGeneStorage.js +158 -0
  144. package/dist/gene-bank/adapters/SQLiteGeneStorage.js.map +1 -0
  145. package/dist/gene-bank/index.d.ts +10 -7
  146. package/dist/gene-bank/index.d.ts.map +1 -1
  147. package/dist/gene-bank/index.js +8 -7
  148. package/dist/gene-bank/index.js.map +1 -1
  149. package/dist/immune/BehavioralImmuneSystem.d.ts +6 -0
  150. package/dist/immune/BehavioralImmuneSystem.d.ts.map +1 -1
  151. package/dist/immune/BehavioralImmuneSystem.js +6 -0
  152. package/dist/immune/BehavioralImmuneSystem.js.map +1 -1
  153. package/dist/index.d.ts +44 -9
  154. package/dist/index.d.ts.map +1 -1
  155. package/dist/index.js +22 -4
  156. package/dist/index.js.map +1 -1
  157. package/dist/memory/LayeredMemory.d.ts +1 -1
  158. package/dist/memory/LayeredMemory.d.ts.map +1 -1
  159. package/dist/memory/LayeredMemory.js.map +1 -1
  160. package/dist/middleware/GSEPMiddleware.d.ts +57 -0
  161. package/dist/middleware/GSEPMiddleware.d.ts.map +1 -0
  162. package/dist/middleware/GSEPMiddleware.js +91 -0
  163. package/dist/middleware/GSEPMiddleware.js.map +1 -0
  164. package/dist/middleware/RuntimeDetector.d.ts +10 -0
  165. package/dist/middleware/RuntimeDetector.d.ts.map +1 -0
  166. package/dist/middleware/RuntimeDetector.js +70 -0
  167. package/dist/middleware/RuntimeDetector.js.map +1 -0
  168. package/dist/middleware/ServerlessAdapter.d.ts +23 -0
  169. package/dist/middleware/ServerlessAdapter.d.ts.map +1 -0
  170. package/dist/middleware/ServerlessAdapter.js +77 -0
  171. package/dist/middleware/ServerlessAdapter.js.map +1 -0
  172. package/dist/monitoring/AlertWebhooks.d.ts +17 -0
  173. package/dist/monitoring/AlertWebhooks.d.ts.map +1 -1
  174. package/dist/monitoring/AlertWebhooks.js.map +1 -1
  175. package/dist/monitoring/WeeklyReportGenerator.d.ts +73 -0
  176. package/dist/monitoring/WeeklyReportGenerator.d.ts.map +1 -0
  177. package/dist/monitoring/WeeklyReportGenerator.js +148 -0
  178. package/dist/monitoring/WeeklyReportGenerator.js.map +1 -0
  179. package/dist/rag/RAGEngine.d.ts +1 -1
  180. package/dist/rag/RAGEngine.d.ts.map +1 -1
  181. package/dist/rag/VectorStoreAdapter.d.ts +2 -2
  182. package/dist/rag/VectorStoreAdapter.d.ts.map +1 -1
  183. package/dist/rag/VectorStoreAdapter.js.map +1 -1
  184. package/dist/realtime/EventEmitter.d.ts +110 -17
  185. package/dist/realtime/EventEmitter.d.ts.map +1 -1
  186. package/dist/realtime/EventEmitter.js +3 -4
  187. package/dist/realtime/EventEmitter.js.map +1 -1
  188. package/dist/security/CapabilityBroker.d.ts +41 -0
  189. package/dist/security/CapabilityBroker.d.ts.map +1 -0
  190. package/dist/security/CapabilityBroker.js +125 -0
  191. package/dist/security/CapabilityBroker.js.map +1 -0
  192. package/dist/security/CommandExecutionGuard.d.ts +47 -0
  193. package/dist/security/CommandExecutionGuard.d.ts.map +1 -0
  194. package/dist/security/CommandExecutionGuard.js +175 -0
  195. package/dist/security/CommandExecutionGuard.js.map +1 -0
  196. package/dist/security/ComplianceExporter.d.ts +32 -0
  197. package/dist/security/ComplianceExporter.d.ts.map +1 -0
  198. package/dist/security/ComplianceExporter.js +129 -0
  199. package/dist/security/ComplianceExporter.js.map +1 -0
  200. package/dist/security/DataAccessTracker.d.ts +38 -0
  201. package/dist/security/DataAccessTracker.d.ts.map +1 -0
  202. package/dist/security/DataAccessTracker.js +71 -0
  203. package/dist/security/DataAccessTracker.js.map +1 -0
  204. package/dist/security/DataClassifier.d.ts +14 -0
  205. package/dist/security/DataClassifier.d.ts.map +1 -0
  206. package/dist/security/DataClassifier.js +146 -0
  207. package/dist/security/DataClassifier.js.map +1 -0
  208. package/dist/security/EncryptedConfigStore.d.ts +21 -0
  209. package/dist/security/EncryptedConfigStore.d.ts.map +1 -0
  210. package/dist/security/EncryptedConfigStore.js +119 -0
  211. package/dist/security/EncryptedConfigStore.js.map +1 -0
  212. package/dist/security/EnterprisePolicyEngine.d.ts +90 -0
  213. package/dist/security/EnterprisePolicyEngine.d.ts.map +1 -0
  214. package/dist/security/EnterprisePolicyEngine.js +240 -0
  215. package/dist/security/EnterprisePolicyEngine.js.map +1 -0
  216. package/dist/security/FileSystemBoundary.d.ts +33 -0
  217. package/dist/security/FileSystemBoundary.d.ts.map +1 -0
  218. package/dist/security/FileSystemBoundary.js +94 -0
  219. package/dist/security/FileSystemBoundary.js.map +1 -0
  220. package/dist/security/GDPREngine.d.ts +65 -0
  221. package/dist/security/GDPREngine.d.ts.map +1 -0
  222. package/dist/security/GDPREngine.js +180 -0
  223. package/dist/security/GDPREngine.js.map +1 -0
  224. package/dist/security/GenomeSecurityBridge.d.ts +47 -0
  225. package/dist/security/GenomeSecurityBridge.d.ts.map +1 -0
  226. package/dist/security/GenomeSecurityBridge.js +157 -0
  227. package/dist/security/GenomeSecurityBridge.js.map +1 -0
  228. package/dist/security/KeyHierarchy.d.ts +23 -0
  229. package/dist/security/KeyHierarchy.d.ts.map +1 -0
  230. package/dist/security/KeyHierarchy.js +78 -0
  231. package/dist/security/KeyHierarchy.js.map +1 -0
  232. package/dist/security/KeychainAdapter.d.ts +19 -0
  233. package/dist/security/KeychainAdapter.d.ts.map +1 -0
  234. package/dist/security/KeychainAdapter.js +104 -0
  235. package/dist/security/KeychainAdapter.js.map +1 -0
  236. package/dist/security/LLMProxyLayer.d.ts +63 -0
  237. package/dist/security/LLMProxyLayer.d.ts.map +1 -0
  238. package/dist/security/LLMProxyLayer.js +148 -0
  239. package/dist/security/LLMProxyLayer.js.map +1 -0
  240. package/dist/security/MFAProvider.d.ts +35 -0
  241. package/dist/security/MFAProvider.d.ts.map +1 -0
  242. package/dist/security/MFAProvider.js +174 -0
  243. package/dist/security/MFAProvider.js.map +1 -0
  244. package/dist/security/NetworkAuditLogger.d.ts +35 -0
  245. package/dist/security/NetworkAuditLogger.d.ts.map +1 -0
  246. package/dist/security/NetworkAuditLogger.js +99 -0
  247. package/dist/security/NetworkAuditLogger.js.map +1 -0
  248. package/dist/security/OutboundAllowlist.d.ts +33 -0
  249. package/dist/security/OutboundAllowlist.d.ts.map +1 -0
  250. package/dist/security/OutboundAllowlist.js +112 -0
  251. package/dist/security/OutboundAllowlist.js.map +1 -0
  252. package/dist/security/PIIRedactionEngine.d.ts +40 -0
  253. package/dist/security/PIIRedactionEngine.d.ts.map +1 -0
  254. package/dist/security/PIIRedactionEngine.js +232 -0
  255. package/dist/security/PIIRedactionEngine.js.map +1 -0
  256. package/dist/security/RBACEngine.d.ts +44 -0
  257. package/dist/security/RBACEngine.d.ts.map +1 -0
  258. package/dist/security/RBACEngine.js +209 -0
  259. package/dist/security/RBACEngine.js.map +1 -0
  260. package/dist/security/SOC2Controls.d.ts +37 -0
  261. package/dist/security/SOC2Controls.d.ts.map +1 -0
  262. package/dist/security/SOC2Controls.js +151 -0
  263. package/dist/security/SOC2Controls.js.map +1 -0
  264. package/dist/security/SecretRotationEngine.d.ts +46 -0
  265. package/dist/security/SecretRotationEngine.d.ts.map +1 -0
  266. package/dist/security/SecretRotationEngine.js +102 -0
  267. package/dist/security/SecretRotationEngine.js.map +1 -0
  268. package/dist/security/SecretsMigrator.d.ts +28 -0
  269. package/dist/security/SecretsMigrator.d.ts.map +1 -0
  270. package/dist/security/SecretsMigrator.js +170 -0
  271. package/dist/security/SecretsMigrator.js.map +1 -0
  272. package/dist/security/SecurityEventBus.d.ts +48 -0
  273. package/dist/security/SecurityEventBus.d.ts.map +1 -0
  274. package/dist/security/SecurityEventBus.js +105 -0
  275. package/dist/security/SecurityEventBus.js.map +1 -0
  276. package/dist/security/SecurityPresets.d.ts +42 -0
  277. package/dist/security/SecurityPresets.d.ts.map +1 -0
  278. package/dist/security/SecurityPresets.js +162 -0
  279. package/dist/security/SecurityPresets.js.map +1 -0
  280. package/dist/security/SkillManifest.d.ts +34 -0
  281. package/dist/security/SkillManifest.d.ts.map +1 -0
  282. package/dist/security/SkillManifest.js +91 -0
  283. package/dist/security/SkillManifest.js.map +1 -0
  284. package/dist/security/SkillSigner.d.ts +22 -0
  285. package/dist/security/SkillSigner.d.ts.map +1 -0
  286. package/dist/security/SkillSigner.js +80 -0
  287. package/dist/security/SkillSigner.js.map +1 -0
  288. package/dist/security/TamperProofAuditLog.d.ts +58 -0
  289. package/dist/security/TamperProofAuditLog.d.ts.map +1 -0
  290. package/dist/security/TamperProofAuditLog.js +214 -0
  291. package/dist/security/TamperProofAuditLog.js.map +1 -0
  292. package/dist/security/index.d.ts +27 -0
  293. package/dist/security/index.d.ts.map +1 -0
  294. package/dist/security/index.js +27 -0
  295. package/dist/security/index.js.map +1 -0
  296. package/dist/skills/ProactiveEngine.d.ts +55 -0
  297. package/dist/skills/ProactiveEngine.d.ts.map +1 -0
  298. package/dist/skills/ProactiveEngine.js +201 -0
  299. package/dist/skills/ProactiveEngine.js.map +1 -0
  300. package/dist/skills/SkillExecutor.d.ts +21 -0
  301. package/dist/skills/SkillExecutor.d.ts.map +1 -0
  302. package/dist/skills/SkillExecutor.js +131 -0
  303. package/dist/skills/SkillExecutor.js.map +1 -0
  304. package/dist/skills/SkillRegistry.d.ts +47 -0
  305. package/dist/skills/SkillRegistry.d.ts.map +1 -0
  306. package/dist/skills/SkillRegistry.js +94 -0
  307. package/dist/skills/SkillRegistry.js.map +1 -0
  308. package/dist/skills/SkillRouter.d.ts +30 -0
  309. package/dist/skills/SkillRouter.d.ts.map +1 -0
  310. package/dist/skills/SkillRouter.js +113 -0
  311. package/dist/skills/SkillRouter.js.map +1 -0
  312. package/dist/skills/index.d.ts +9 -0
  313. package/dist/skills/index.d.ts.map +1 -0
  314. package/dist/skills/index.js +5 -0
  315. package/dist/skills/index.js.map +1 -0
  316. package/dist/types/GenomeV2.d.ts +12 -0
  317. package/dist/types/GenomeV2.d.ts.map +1 -1
  318. package/dist/types/index.d.ts +19 -0
  319. package/dist/types/index.d.ts.map +1 -1
  320. package/dist/wrap/FileStorageAdapter.d.ts +113 -0
  321. package/dist/wrap/FileStorageAdapter.d.ts.map +1 -0
  322. package/dist/wrap/FileStorageAdapter.js +239 -0
  323. package/dist/wrap/FileStorageAdapter.js.map +1 -0
  324. package/dist/wrap/SQLiteStorageAdapter.d.ts +96 -0
  325. package/dist/wrap/SQLiteStorageAdapter.d.ts.map +1 -0
  326. package/dist/wrap/SQLiteStorageAdapter.js +251 -0
  327. package/dist/wrap/SQLiteStorageAdapter.js.map +1 -0
  328. package/dist/wrap/WrappedAgent.d.ts +2 -2
  329. package/dist/wrap/WrappedAgent.d.ts.map +1 -1
  330. package/dist/wrap/WrappedAgent.js +7 -7
  331. package/dist/wrap/WrappedAgent.js.map +1 -1
  332. package/dist/wrap.d.ts +39 -0
  333. package/dist/wrap.d.ts.map +1 -0
  334. package/dist/wrap.js +96 -0
  335. package/dist/wrap.js.map +1 -0
  336. package/package.json +34 -7
  337. package/dist/PGA.d.ts.map +0 -1
  338. package/dist/PGA.js.map +0 -1
  339. package/dist/evolution/PromotionGate.d.ts +0 -45
  340. package/dist/evolution/PromotionGate.d.ts.map +0 -1
  341. package/dist/evolution/PromotionGate.js +0 -248
  342. package/dist/evolution/PromotionGate.js.map +0 -1
  343. package/dist/gene-bank/PGAIntegration.d.ts.map +0 -1
  344. package/dist/gene-bank/PGAIntegration.js.map +0 -1
@@ -0,0 +1,2731 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GSEP Live Dashboard</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0d1117;
10
+ --bg-card: #161b22;
11
+ --bg-hover: #1c2333;
12
+ --border: #30363d;
13
+ --text: #e6edf3;
14
+ --text-muted: #8b949e;
15
+ --purple: #8b5cf6;
16
+ --purple-dim: rgba(139, 92, 246, 0.15);
17
+ --cyan: #06b6d4;
18
+ --cyan-dim: rgba(6, 182, 212, 0.15);
19
+ --green: #22c55e;
20
+ --green-dim: rgba(34, 197, 94, 0.15);
21
+ --red: #ef4444;
22
+ --red-dim: rgba(239, 68, 68, 0.15);
23
+ --amber: #f59e0b;
24
+ --amber-dim: rgba(245, 158, 11, 0.15);
25
+ --emerald: #10b981;
26
+ --pink: #ec4899;
27
+ --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
28
+ --mono: 'Fira Code', 'SF Mono', 'Cascadia Code', monospace;
29
+ --radius: 12px;
30
+ }
31
+
32
+ * { margin: 0; padding: 0; box-sizing: border-box; }
33
+
34
+ body {
35
+ font-family: var(--font);
36
+ background: var(--bg);
37
+ color: var(--text);
38
+ min-height: 100vh;
39
+ overflow-x: hidden;
40
+ }
41
+
42
+ /* ─── Header ─────────────────────────────── */
43
+ .header {
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: space-between;
47
+ padding: 16px 24px;
48
+ border-bottom: 1px solid var(--border);
49
+ background: var(--bg-card);
50
+ }
51
+
52
+ .header-left {
53
+ display: flex;
54
+ align-items: center;
55
+ gap: 12px;
56
+ }
57
+
58
+ .logo {
59
+ font-size: 24px;
60
+ font-weight: 800;
61
+ background: linear-gradient(135deg, var(--purple), var(--cyan));
62
+ -webkit-background-clip: text;
63
+ -webkit-text-fill-color: transparent;
64
+ }
65
+
66
+ .status-badge {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 6px;
70
+ font-size: 12px;
71
+ padding: 4px 10px;
72
+ border-radius: 20px;
73
+ font-weight: 600;
74
+ }
75
+
76
+ .status-badge.connected {
77
+ background: var(--green-dim);
78
+ color: var(--green);
79
+ }
80
+
81
+ .status-badge.disconnected {
82
+ background: var(--red-dim);
83
+ color: var(--red);
84
+ }
85
+
86
+ .status-dot {
87
+ width: 8px;
88
+ height: 8px;
89
+ border-radius: 50%;
90
+ animation: pulse 2s infinite;
91
+ }
92
+
93
+ .connected .status-dot { background: var(--green); }
94
+ .disconnected .status-dot { background: var(--red); }
95
+
96
+ @keyframes pulse {
97
+ 0%, 100% { opacity: 1; }
98
+ 50% { opacity: 0.4; }
99
+ }
100
+
101
+ /* ─── Stats Bar ──────────────────────────── */
102
+ .stats-bar {
103
+ display: flex;
104
+ gap: 16px;
105
+ padding: 12px 24px;
106
+ border-bottom: 1px solid var(--border);
107
+ overflow-x: auto;
108
+ }
109
+
110
+ .stat {
111
+ display: flex;
112
+ flex-direction: column;
113
+ align-items: center;
114
+ min-width: 100px;
115
+ padding: 8px 16px;
116
+ background: var(--bg-card);
117
+ border-radius: 8px;
118
+ border: 1px solid var(--border);
119
+ }
120
+
121
+ .stat-label {
122
+ font-size: 11px;
123
+ color: var(--text-muted);
124
+ text-transform: uppercase;
125
+ letter-spacing: 0.5px;
126
+ margin-bottom: 4px;
127
+ }
128
+
129
+ .stat-value {
130
+ font-size: 20px;
131
+ font-weight: 700;
132
+ font-family: var(--mono);
133
+ }
134
+
135
+ .stat-value.green { color: var(--green); }
136
+ .stat-value.red { color: var(--red); }
137
+ .stat-value.amber { color: var(--amber); }
138
+ .stat-value.purple { color: var(--purple); }
139
+ .stat-value.cyan { color: var(--cyan); }
140
+
141
+ /* ─── App Layout (Sidebar + Main + Right Panel) ── */
142
+ .app-layout {
143
+ display: flex;
144
+ min-height: calc(100vh - 120px);
145
+ margin-right: 280px;
146
+ }
147
+
148
+ /* ─── Right Sidebar (Intelligence Panel) ───── */
149
+ .intel-sidebar {
150
+ width: 280px;
151
+ min-width: 280px;
152
+ border-left: 1px solid var(--border);
153
+ background: var(--bg-card);
154
+ overflow-y: auto;
155
+ height: calc(100vh - 120px);
156
+ padding: 16px;
157
+ position: fixed;
158
+ top: 120px;
159
+ right: 0;
160
+ z-index: 50;
161
+ }
162
+ .intel-sidebar::-webkit-scrollbar { width: 4px; }
163
+ .intel-sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
164
+ .intel-section {
165
+ margin-bottom: 20px;
166
+ }
167
+ .intel-title {
168
+ font-size: 11px;
169
+ font-weight: 700;
170
+ color: var(--text-muted);
171
+ text-transform: uppercase;
172
+ letter-spacing: 0.8px;
173
+ margin-bottom: 10px;
174
+ }
175
+ .intel-stat {
176
+ display: flex;
177
+ justify-content: space-between;
178
+ align-items: center;
179
+ padding: 6px 0;
180
+ font-size: 13px;
181
+ border-bottom: 1px solid var(--border);
182
+ }
183
+ .intel-stat:last-child { border-bottom: none; }
184
+ .intel-stat-label { color: var(--text-muted); }
185
+ .intel-stat-value { font-weight: 600; color: var(--text); font-family: var(--mono); }
186
+ .intel-stat-value.good { color: var(--green); }
187
+ .intel-stat-value.warning { color: var(--amber); }
188
+ .intel-stat-value.danger { color: var(--red); }
189
+ .intel-badge {
190
+ display: inline-block;
191
+ padding: 2px 8px;
192
+ border-radius: 10px;
193
+ font-size: 11px;
194
+ font-weight: 600;
195
+ }
196
+ .intel-badge.active { background: var(--green-dim); color: var(--green); }
197
+ .intel-badge.inactive { background: var(--border); color: var(--text-muted); }
198
+ .intel-skill {
199
+ display: flex;
200
+ justify-content: space-between;
201
+ align-items: center;
202
+ padding: 6px 8px;
203
+ margin-bottom: 4px;
204
+ background: var(--bg-hover);
205
+ border-radius: 6px;
206
+ font-size: 12px;
207
+ }
208
+ .intel-skill-name { color: var(--text); }
209
+ .intel-skill-rate { font-family: var(--mono); font-size: 11px; }
210
+ .intel-anomaly {
211
+ padding: 8px;
212
+ margin-bottom: 6px;
213
+ border-radius: 6px;
214
+ font-size: 12px;
215
+ border-left: 3px solid var(--amber);
216
+ background: var(--amber-dim);
217
+ }
218
+ .intel-anomaly.critical { border-left-color: var(--red); background: var(--red-dim); }
219
+ .intel-proactive {
220
+ padding: 8px;
221
+ margin-bottom: 6px;
222
+ border-radius: 6px;
223
+ font-size: 12px;
224
+ background: var(--purple-dim);
225
+ border-left: 3px solid var(--purple);
226
+ }
227
+
228
+ /* ─── Gene Sidebar ───────────────────────── */
229
+ .gene-sidebar {
230
+ width: 280px;
231
+ min-width: 280px;
232
+ border-right: 1px solid var(--border);
233
+ background: var(--bg-card);
234
+ overflow-y: auto;
235
+ height: calc(100vh - 120px);
236
+ position: sticky;
237
+ top: 120px;
238
+ align-self: flex-start;
239
+ }
240
+
241
+ .gene-sidebar::-webkit-scrollbar { width: 4px; }
242
+ .gene-sidebar::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }
243
+
244
+ .sidebar-header {
245
+ font-size: 14px;
246
+ font-weight: 700;
247
+ color: var(--text-muted);
248
+ text-transform: uppercase;
249
+ letter-spacing: 0.5px;
250
+ padding: 14px 16px 10px;
251
+ border-bottom: 1px solid var(--border);
252
+ display: flex;
253
+ align-items: center;
254
+ gap: 8px;
255
+ }
256
+
257
+ .sidebar-count {
258
+ font-size: 11px;
259
+ font-weight: 600;
260
+ background: var(--purple-dim);
261
+ color: var(--purple);
262
+ padding: 2px 8px;
263
+ border-radius: 10px;
264
+ margin-left: auto;
265
+ }
266
+
267
+ .gene-layer-group {
268
+ border-bottom: 1px solid var(--border);
269
+ }
270
+
271
+ .gene-layer-header {
272
+ display: flex;
273
+ align-items: center;
274
+ gap: 8px;
275
+ padding: 10px 16px;
276
+ font-size: 11px;
277
+ font-weight: 700;
278
+ text-transform: uppercase;
279
+ letter-spacing: 0.5px;
280
+ cursor: pointer;
281
+ user-select: none;
282
+ transition: background 0.15s;
283
+ }
284
+
285
+ .gene-layer-header:hover {
286
+ background: var(--bg-hover);
287
+ }
288
+
289
+ .gene-layer-header .layer-icon {
290
+ font-size: 14px;
291
+ }
292
+
293
+ .gene-layer-header .layer-count {
294
+ margin-left: auto;
295
+ font-size: 10px;
296
+ font-weight: 600;
297
+ padding: 1px 6px;
298
+ border-radius: 8px;
299
+ }
300
+
301
+ .gene-layer-header.c0 { color: var(--purple); }
302
+ .gene-layer-header.c0 .layer-count { background: var(--purple-dim); color: var(--purple); }
303
+ .gene-layer-header.c1 { color: var(--green); }
304
+ .gene-layer-header.c1 .layer-count { background: var(--green-dim); color: var(--green); }
305
+ .gene-layer-header.c2 { color: var(--cyan); }
306
+ .gene-layer-header.c2 .layer-count { background: var(--cyan-dim); color: var(--cyan); }
307
+
308
+ .gene-layer-header .chevron {
309
+ font-size: 10px;
310
+ transition: transform 0.2s;
311
+ }
312
+
313
+ .gene-layer-header.collapsed .chevron {
314
+ transform: rotate(-90deg);
315
+ }
316
+
317
+ .gene-layer-body {
318
+ overflow: hidden;
319
+ transition: max-height 0.3s ease;
320
+ }
321
+
322
+ .gene-layer-body.collapsed {
323
+ max-height: 0 !important;
324
+ }
325
+
326
+ .gene-card {
327
+ padding: 10px 16px;
328
+ border-bottom: 1px solid rgba(48, 54, 61, 0.3);
329
+ transition: background 0.15s;
330
+ cursor: default;
331
+ }
332
+
333
+ .gene-card:hover {
334
+ background: var(--bg-hover);
335
+ }
336
+
337
+ .gene-card:last-child {
338
+ border-bottom: none;
339
+ }
340
+
341
+ .gene-card-top {
342
+ display: flex;
343
+ align-items: center;
344
+ gap: 6px;
345
+ margin-bottom: 4px;
346
+ }
347
+
348
+ .gene-name {
349
+ font-size: 13px;
350
+ font-weight: 600;
351
+ color: var(--text);
352
+ white-space: nowrap;
353
+ overflow: hidden;
354
+ text-overflow: ellipsis;
355
+ flex: 1;
356
+ }
357
+
358
+ .gene-status {
359
+ font-size: 9px;
360
+ font-weight: 700;
361
+ text-transform: uppercase;
362
+ letter-spacing: 0.3px;
363
+ padding: 2px 6px;
364
+ border-radius: 4px;
365
+ }
366
+
367
+ .gene-status.active {
368
+ background: var(--green-dim);
369
+ color: var(--green);
370
+ }
371
+
372
+ .gene-status.retired {
373
+ background: var(--red-dim);
374
+ color: var(--red);
375
+ }
376
+
377
+ .gene-variant {
378
+ font-size: 11px;
379
+ color: var(--text-muted);
380
+ font-family: var(--mono);
381
+ margin-bottom: 6px;
382
+ white-space: nowrap;
383
+ overflow: hidden;
384
+ text-overflow: ellipsis;
385
+ }
386
+
387
+ .gene-fitness-row {
388
+ display: flex;
389
+ align-items: center;
390
+ gap: 8px;
391
+ }
392
+
393
+ .gene-fitness-bar {
394
+ flex: 1;
395
+ height: 4px;
396
+ background: var(--border);
397
+ border-radius: 2px;
398
+ overflow: hidden;
399
+ }
400
+
401
+ .gene-fitness-fill {
402
+ height: 100%;
403
+ border-radius: 2px;
404
+ transition: width 0.5s ease;
405
+ }
406
+
407
+ .gene-fitness-value {
408
+ font-size: 11px;
409
+ font-weight: 700;
410
+ font-family: var(--mono);
411
+ min-width: 36px;
412
+ text-align: right;
413
+ }
414
+
415
+ .gene-meta {
416
+ display: flex;
417
+ gap: 8px;
418
+ margin-top: 4px;
419
+ font-size: 10px;
420
+ color: var(--text-muted);
421
+ }
422
+
423
+ .gene-empty {
424
+ padding: 12px 16px;
425
+ font-size: 12px;
426
+ color: var(--text-muted);
427
+ font-style: italic;
428
+ }
429
+
430
+ /* ─── Defense Sections (C3/C4) ───────────── */
431
+ .defense-section {
432
+ padding: 12px 16px;
433
+ }
434
+
435
+ .defense-stat-row {
436
+ display: flex;
437
+ justify-content: space-between;
438
+ align-items: center;
439
+ padding: 4px 0;
440
+ font-size: 12px;
441
+ }
442
+
443
+ .defense-stat-label {
444
+ color: var(--text-muted);
445
+ }
446
+
447
+ .defense-stat-value {
448
+ font-family: var(--mono);
449
+ font-weight: 600;
450
+ font-size: 12px;
451
+ }
452
+
453
+ .defense-integrity {
454
+ display: inline-flex;
455
+ align-items: center;
456
+ gap: 4px;
457
+ font-size: 10px;
458
+ font-weight: 700;
459
+ padding: 2px 8px;
460
+ border-radius: 4px;
461
+ }
462
+
463
+ .defense-integrity.valid {
464
+ background: var(--green-dim);
465
+ color: var(--green);
466
+ }
467
+
468
+ .defense-integrity.invalid {
469
+ background: var(--red-dim);
470
+ color: var(--red);
471
+ }
472
+
473
+ .defense-inactive {
474
+ padding: 12px 16px;
475
+ font-size: 12px;
476
+ color: var(--text-muted);
477
+ font-style: italic;
478
+ }
479
+
480
+ /* ─── Gene Bank Section ─────────────────── */
481
+ .gene-bank-header {
482
+ display: flex;
483
+ align-items: center;
484
+ gap: 8px;
485
+ padding: 14px 16px 10px;
486
+ border-top: 2px solid var(--border);
487
+ font-size: 14px;
488
+ font-weight: 700;
489
+ color: var(--amber);
490
+ text-transform: uppercase;
491
+ letter-spacing: 0.5px;
492
+ }
493
+
494
+ .gene-bank-stats {
495
+ display: grid;
496
+ grid-template-columns: 1fr 1fr;
497
+ gap: 6px;
498
+ padding: 8px 16px 12px;
499
+ }
500
+
501
+ .bank-stat {
502
+ display: flex;
503
+ flex-direction: column;
504
+ align-items: center;
505
+ padding: 8px 4px;
506
+ background: var(--bg);
507
+ border-radius: 8px;
508
+ border: 1px solid var(--border);
509
+ }
510
+
511
+ .bank-stat-value {
512
+ font-size: 18px;
513
+ font-weight: 700;
514
+ font-family: var(--mono);
515
+ }
516
+
517
+ .bank-stat-label {
518
+ font-size: 9px;
519
+ color: var(--text-muted);
520
+ text-transform: uppercase;
521
+ letter-spacing: 0.3px;
522
+ margin-top: 2px;
523
+ }
524
+
525
+ .bank-category {
526
+ border-bottom: 1px solid var(--border);
527
+ }
528
+
529
+ .bank-category-header {
530
+ display: flex;
531
+ align-items: center;
532
+ gap: 6px;
533
+ padding: 8px 16px;
534
+ font-size: 11px;
535
+ font-weight: 600;
536
+ color: var(--text-muted);
537
+ cursor: pointer;
538
+ user-select: none;
539
+ transition: background 0.15s;
540
+ }
541
+
542
+ .bank-category-header:hover {
543
+ background: var(--bg-hover);
544
+ }
545
+
546
+ .bank-category-header .bank-count {
547
+ margin-left: auto;
548
+ font-size: 10px;
549
+ font-weight: 600;
550
+ padding: 1px 6px;
551
+ border-radius: 8px;
552
+ background: rgba(245, 158, 11, 0.15);
553
+ color: var(--amber);
554
+ }
555
+
556
+ .bank-category-header .chevron {
557
+ font-size: 10px;
558
+ transition: transform 0.2s;
559
+ }
560
+
561
+ .bank-category-header.collapsed .chevron {
562
+ transform: rotate(-90deg);
563
+ }
564
+
565
+ .bank-category-body {
566
+ overflow: hidden;
567
+ transition: max-height 0.3s ease;
568
+ }
569
+
570
+ .bank-category-body.collapsed {
571
+ max-height: 0 !important;
572
+ }
573
+
574
+ .bank-gene {
575
+ display: flex;
576
+ align-items: center;
577
+ gap: 8px;
578
+ padding: 8px 16px;
579
+ font-size: 12px;
580
+ border-bottom: 1px solid rgba(48, 54, 61, 0.3);
581
+ transition: background 0.15s;
582
+ }
583
+
584
+ .bank-gene:hover {
585
+ background: var(--bg-hover);
586
+ }
587
+
588
+ .bank-gene:last-child {
589
+ border-bottom: none;
590
+ }
591
+
592
+ .bank-gene-icon {
593
+ font-size: 14px;
594
+ flex-shrink: 0;
595
+ }
596
+
597
+ .bank-gene-info {
598
+ flex: 1;
599
+ min-width: 0;
600
+ }
601
+
602
+ .bank-gene-name {
603
+ font-weight: 600;
604
+ font-size: 12px;
605
+ white-space: nowrap;
606
+ overflow: hidden;
607
+ text-overflow: ellipsis;
608
+ }
609
+
610
+ .bank-gene-variant {
611
+ font-size: 10px;
612
+ color: var(--text-muted);
613
+ font-family: var(--mono);
614
+ white-space: nowrap;
615
+ overflow: hidden;
616
+ text-overflow: ellipsis;
617
+ }
618
+
619
+ .bank-gene-fitness {
620
+ font-family: var(--mono);
621
+ font-weight: 700;
622
+ font-size: 11px;
623
+ flex-shrink: 0;
624
+ }
625
+
626
+ .bank-badge {
627
+ font-size: 8px;
628
+ font-weight: 700;
629
+ text-transform: uppercase;
630
+ letter-spacing: 0.3px;
631
+ padding: 2px 6px;
632
+ border-radius: 4px;
633
+ flex-shrink: 0;
634
+ }
635
+
636
+ .bank-badge.sellable {
637
+ background: var(--green-dim);
638
+ color: var(--green);
639
+ }
640
+
641
+ .bank-badge.published {
642
+ background: var(--purple-dim);
643
+ color: var(--purple);
644
+ }
645
+
646
+ .bank-badge.adopted {
647
+ background: var(--cyan-dim);
648
+ color: var(--cyan);
649
+ }
650
+
651
+ .bank-empty {
652
+ padding: 10px 16px;
653
+ font-size: 11px;
654
+ color: var(--text-muted);
655
+ font-style: italic;
656
+ }
657
+
658
+ .bank-no-bank {
659
+ padding: 12px 16px;
660
+ font-size: 12px;
661
+ color: var(--text-muted);
662
+ text-align: center;
663
+ }
664
+
665
+ .bank-no-bank-hint {
666
+ font-size: 10px;
667
+ color: var(--text-muted);
668
+ opacity: 0.6;
669
+ margin-top: 4px;
670
+ }
671
+
672
+ /* ─── Main Grid ──────────────────────────── */
673
+ .main {
674
+ flex: 1;
675
+ display: grid;
676
+ grid-template-columns: 1fr 1fr;
677
+ grid-template-rows: auto auto;
678
+ gap: 16px;
679
+ padding: 16px 24px;
680
+ align-content: start;
681
+ min-width: 0;
682
+ }
683
+
684
+ .card {
685
+ background: var(--bg-card);
686
+ border: 1px solid var(--border);
687
+ border-radius: var(--radius);
688
+ padding: 16px;
689
+ overflow: hidden;
690
+ }
691
+
692
+ .card-title {
693
+ font-size: 14px;
694
+ font-weight: 700;
695
+ color: var(--text-muted);
696
+ text-transform: uppercase;
697
+ letter-spacing: 0.5px;
698
+ margin-bottom: 12px;
699
+ display: flex;
700
+ align-items: center;
701
+ gap: 8px;
702
+ }
703
+
704
+ /* ─── Fitness Chart ──────────────────────── */
705
+ .fitness-chart {
706
+ grid-column: 1 / -1;
707
+ }
708
+
709
+ .chart-container {
710
+ position: relative;
711
+ width: 100%;
712
+ height: 180px;
713
+ }
714
+
715
+ .chart-container canvas {
716
+ width: 100%;
717
+ height: 100%;
718
+ }
719
+
720
+ .chart-legend {
721
+ display: flex;
722
+ gap: 16px;
723
+ margin-top: 8px;
724
+ flex-wrap: wrap;
725
+ }
726
+
727
+ .legend-item {
728
+ display: flex;
729
+ align-items: center;
730
+ gap: 6px;
731
+ font-size: 11px;
732
+ color: var(--text-muted);
733
+ }
734
+
735
+ .legend-dot {
736
+ width: 8px;
737
+ height: 8px;
738
+ border-radius: 50%;
739
+ }
740
+
741
+ /* ─── Gates Panel ────────────────────────── */
742
+ .gates-grid {
743
+ display: grid;
744
+ grid-template-columns: 1fr 1fr;
745
+ gap: 8px;
746
+ }
747
+
748
+ .gate {
749
+ padding: 10px;
750
+ border-radius: 8px;
751
+ border: 1px solid var(--border);
752
+ transition: all 0.3s ease;
753
+ }
754
+
755
+ .gate.pass {
756
+ border-color: var(--green);
757
+ background: var(--green-dim);
758
+ }
759
+
760
+ .gate.fail {
761
+ border-color: var(--red);
762
+ background: var(--red-dim);
763
+ }
764
+
765
+ .gate.pending {
766
+ opacity: 0.5;
767
+ }
768
+
769
+ .gate.evaluating {
770
+ border-color: var(--purple);
771
+ background: var(--purple-dim);
772
+ animation: gate-pulse 1.5s infinite;
773
+ }
774
+
775
+ @keyframes gate-pulse {
776
+ 0%, 100% { opacity: 1; }
777
+ 50% { opacity: 0.6; }
778
+ }
779
+
780
+ .gate-name {
781
+ font-size: 11px;
782
+ font-weight: 600;
783
+ color: var(--text-muted);
784
+ text-transform: uppercase;
785
+ margin-bottom: 4px;
786
+ }
787
+
788
+ .gate-score {
789
+ font-size: 18px;
790
+ font-weight: 700;
791
+ font-family: var(--mono);
792
+ }
793
+
794
+ .gate-bar {
795
+ height: 3px;
796
+ background: var(--border);
797
+ border-radius: 2px;
798
+ margin-top: 6px;
799
+ overflow: hidden;
800
+ }
801
+
802
+ .gate-bar-fill {
803
+ height: 100%;
804
+ border-radius: 2px;
805
+ transition: width 0.5s ease;
806
+ }
807
+
808
+ .gate.pass .gate-bar-fill { background: var(--green); }
809
+ .gate.fail .gate-bar-fill { background: var(--red); }
810
+ .gate.evaluating .gate-bar-fill { background: var(--purple); }
811
+
812
+ /* ─── Event Log ──────────────────────────── */
813
+ .log-panel {
814
+ max-height: 400px;
815
+ overflow-y: auto;
816
+ }
817
+
818
+ .log-panel::-webkit-scrollbar {
819
+ width: 4px;
820
+ }
821
+
822
+ .log-panel::-webkit-scrollbar-thumb {
823
+ background: var(--border);
824
+ border-radius: 2px;
825
+ }
826
+
827
+ .log-entry {
828
+ display: flex;
829
+ gap: 8px;
830
+ padding: 6px 8px;
831
+ border-bottom: 1px solid rgba(48, 54, 61, 0.3);
832
+ font-size: 12px;
833
+ animation: fadeIn 0.3s ease;
834
+ }
835
+
836
+ @keyframes fadeIn {
837
+ from { opacity: 0; transform: translateY(-4px); }
838
+ to { opacity: 1; transform: translateY(0); }
839
+ }
840
+
841
+ .log-time {
842
+ color: var(--text-muted);
843
+ font-family: var(--mono);
844
+ font-size: 11px;
845
+ white-space: nowrap;
846
+ min-width: 60px;
847
+ }
848
+
849
+ .log-icon {
850
+ font-size: 14px;
851
+ min-width: 20px;
852
+ text-align: center;
853
+ }
854
+
855
+ .log-msg {
856
+ color: var(--text);
857
+ line-height: 1.4;
858
+ }
859
+
860
+ .log-msg .purple { color: var(--purple); font-weight: 600; }
861
+ .log-msg .cyan { color: var(--cyan); font-weight: 600; }
862
+ .log-msg .green { color: var(--green); font-weight: 600; }
863
+ .log-msg .red { color: var(--red); font-weight: 600; }
864
+ .log-msg .amber { color: var(--amber); font-weight: 600; }
865
+
866
+ /* ─── Drift Monitor ──────────────────────── */
867
+ .drift-grid {
868
+ display: grid;
869
+ grid-template-columns: 1fr 1fr 1fr;
870
+ gap: 8px;
871
+ }
872
+
873
+ .drift-item {
874
+ padding: 8px;
875
+ border-radius: 6px;
876
+ border: 1px solid var(--border);
877
+ text-align: center;
878
+ }
879
+
880
+ .drift-item.active {
881
+ border-color: var(--amber);
882
+ background: var(--amber-dim);
883
+ }
884
+
885
+ .drift-type {
886
+ font-size: 10px;
887
+ color: var(--text-muted);
888
+ text-transform: uppercase;
889
+ margin-bottom: 4px;
890
+ }
891
+
892
+ .drift-value {
893
+ font-size: 14px;
894
+ font-weight: 700;
895
+ font-family: var(--mono);
896
+ color: var(--green);
897
+ }
898
+
899
+ .drift-item.active .drift-value {
900
+ color: var(--amber);
901
+ }
902
+
903
+ /* ─── Responsive ─────────────────────────── */
904
+ @media (max-width: 768px) {
905
+ .app-layout {
906
+ flex-direction: column;
907
+ }
908
+ .app-layout {
909
+ margin-right: 0;
910
+ }
911
+ .gene-sidebar {
912
+ width: 100%;
913
+ min-width: 100%;
914
+ max-height: none;
915
+ border-right: none;
916
+ border-bottom: 1px solid var(--border);
917
+ }
918
+ .intel-sidebar {
919
+ position: static;
920
+ width: 100%;
921
+ min-width: 100%;
922
+ height: auto;
923
+ border-left: none;
924
+ border-top: 1px solid var(--border);
925
+ }
926
+ .gene-layer-body {
927
+ max-height: 0 !important;
928
+ }
929
+ .gene-layer-header .chevron {
930
+ transform: rotate(-90deg);
931
+ }
932
+ .main {
933
+ grid-template-columns: 1fr;
934
+ }
935
+ .stats-bar {
936
+ gap: 8px;
937
+ }
938
+ .stat {
939
+ min-width: 80px;
940
+ padding: 6px 10px;
941
+ }
942
+ .gates-grid {
943
+ grid-template-columns: 1fr;
944
+ }
945
+ .drift-grid {
946
+ grid-template-columns: 1fr 1fr;
947
+ }
948
+ }
949
+
950
+ /* ─── Tab Navigation ─────────────────────── */
951
+ .tab-nav {
952
+ display: flex;
953
+ gap: 0;
954
+ border-bottom: 1px solid var(--border);
955
+ background: var(--bg-card);
956
+ padding: 0 24px;
957
+ }
958
+
959
+ .tab-btn {
960
+ padding: 10px 20px;
961
+ background: none;
962
+ border: none;
963
+ color: var(--text-muted);
964
+ font-size: 13px;
965
+ font-weight: 600;
966
+ cursor: pointer;
967
+ border-bottom: 2px solid transparent;
968
+ transition: all 0.2s;
969
+ font-family: var(--font);
970
+ }
971
+
972
+ .tab-btn:hover { color: var(--text); }
973
+ .tab-btn.active { color: var(--purple); border-bottom-color: var(--purple); }
974
+
975
+ .tab-content { display: none; }
976
+ .tab-content.active { display: block; }
977
+
978
+ /* ─── Marketplace View ───────────────────── */
979
+ .mp-container {
980
+ padding: 20px 24px;
981
+ max-width: 1200px;
982
+ }
983
+
984
+ .mp-search-bar {
985
+ display: flex;
986
+ gap: 10px;
987
+ margin-bottom: 20px;
988
+ flex-wrap: wrap;
989
+ }
990
+
991
+ .mp-search-bar input,
992
+ .mp-search-bar select {
993
+ padding: 8px 12px;
994
+ background: var(--bg-card);
995
+ border: 1px solid var(--border);
996
+ border-radius: 8px;
997
+ color: var(--text);
998
+ font-size: 13px;
999
+ font-family: var(--font);
1000
+ }
1001
+
1002
+ .mp-search-bar input { flex: 1; min-width: 200px; }
1003
+ .mp-search-bar select { min-width: 120px; }
1004
+
1005
+ .mp-search-bar button {
1006
+ padding: 8px 16px;
1007
+ background: var(--purple);
1008
+ color: #fff;
1009
+ border: none;
1010
+ border-radius: 8px;
1011
+ font-weight: 600;
1012
+ cursor: pointer;
1013
+ font-family: var(--font);
1014
+ }
1015
+
1016
+ .mp-search-bar button:hover { opacity: 0.9; }
1017
+
1018
+ .mp-grid {
1019
+ display: grid;
1020
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
1021
+ gap: 16px;
1022
+ margin-bottom: 20px;
1023
+ }
1024
+
1025
+ .mp-card {
1026
+ background: var(--bg-card);
1027
+ border: 1px solid var(--border);
1028
+ border-radius: var(--radius);
1029
+ padding: 16px;
1030
+ cursor: pointer;
1031
+ transition: border-color 0.2s;
1032
+ }
1033
+
1034
+ .mp-card:hover { border-color: var(--purple); }
1035
+
1036
+ .mp-card-name {
1037
+ font-weight: 700;
1038
+ font-size: 15px;
1039
+ margin-bottom: 4px;
1040
+ }
1041
+
1042
+ .mp-card-desc {
1043
+ font-size: 12px;
1044
+ color: var(--text-muted);
1045
+ margin-bottom: 10px;
1046
+ display: -webkit-box;
1047
+ -webkit-line-clamp: 2;
1048
+ -webkit-box-orient: vertical;
1049
+ overflow: hidden;
1050
+ }
1051
+
1052
+ .mp-card-meta {
1053
+ display: flex;
1054
+ gap: 8px;
1055
+ flex-wrap: wrap;
1056
+ align-items: center;
1057
+ }
1058
+
1059
+ .mp-badge {
1060
+ display: inline-block;
1061
+ padding: 2px 8px;
1062
+ border-radius: 12px;
1063
+ font-size: 10px;
1064
+ font-weight: 600;
1065
+ }
1066
+
1067
+ .mp-badge.type { background: var(--purple-dim); color: var(--purple); }
1068
+ .mp-badge.fitness { background: var(--green-dim); color: var(--green); }
1069
+ .mp-badge.price { background: var(--amber-dim); color: var(--amber); }
1070
+ .mp-badge.adoptions { background: var(--cyan-dim); color: var(--cyan); }
1071
+
1072
+ .mp-pagination {
1073
+ display: flex;
1074
+ gap: 8px;
1075
+ justify-content: center;
1076
+ margin-top: 16px;
1077
+ }
1078
+
1079
+ .mp-pagination button {
1080
+ padding: 6px 14px;
1081
+ background: var(--bg-card);
1082
+ border: 1px solid var(--border);
1083
+ border-radius: 6px;
1084
+ color: var(--text);
1085
+ cursor: pointer;
1086
+ font-family: var(--font);
1087
+ }
1088
+
1089
+ .mp-pagination button:disabled { opacity: 0.4; cursor: default; }
1090
+ .mp-pagination button:hover:not(:disabled) { border-color: var(--purple); }
1091
+
1092
+ /* Gene Detail View */
1093
+ .mp-detail {
1094
+ background: var(--bg-card);
1095
+ border: 1px solid var(--border);
1096
+ border-radius: var(--radius);
1097
+ padding: 24px;
1098
+ margin-bottom: 20px;
1099
+ }
1100
+
1101
+ .mp-detail-header {
1102
+ display: flex;
1103
+ justify-content: space-between;
1104
+ align-items: flex-start;
1105
+ margin-bottom: 16px;
1106
+ gap: 16px;
1107
+ }
1108
+
1109
+ .mp-detail h2 {
1110
+ font-size: 20px;
1111
+ margin-bottom: 4px;
1112
+ }
1113
+
1114
+ .mp-detail-desc {
1115
+ color: var(--text-muted);
1116
+ font-size: 13px;
1117
+ margin-bottom: 16px;
1118
+ }
1119
+
1120
+ .mp-fitness-grid {
1121
+ display: grid;
1122
+ grid-template-columns: repeat(3, 1fr);
1123
+ gap: 10px;
1124
+ margin-bottom: 16px;
1125
+ }
1126
+
1127
+ .mp-fitness-item {
1128
+ background: var(--bg);
1129
+ padding: 10px;
1130
+ border-radius: 8px;
1131
+ text-align: center;
1132
+ }
1133
+
1134
+ .mp-fitness-item .label {
1135
+ font-size: 10px;
1136
+ color: var(--text-muted);
1137
+ text-transform: uppercase;
1138
+ }
1139
+
1140
+ .mp-fitness-item .value {
1141
+ font-size: 18px;
1142
+ font-weight: 700;
1143
+ font-family: var(--mono);
1144
+ color: var(--green);
1145
+ }
1146
+
1147
+ .mp-action-btn {
1148
+ padding: 10px 20px;
1149
+ border: none;
1150
+ border-radius: 8px;
1151
+ font-weight: 700;
1152
+ cursor: pointer;
1153
+ font-size: 14px;
1154
+ font-family: var(--font);
1155
+ }
1156
+
1157
+ .mp-action-btn.primary { background: var(--purple); color: #fff; }
1158
+ .mp-action-btn.secondary { background: var(--bg); color: var(--text); border: 1px solid var(--border); }
1159
+ .mp-action-btn:hover { opacity: 0.9; }
1160
+
1161
+ .mp-back-btn {
1162
+ background: none;
1163
+ border: none;
1164
+ color: var(--purple);
1165
+ cursor: pointer;
1166
+ font-size: 13px;
1167
+ font-family: var(--font);
1168
+ margin-bottom: 12px;
1169
+ padding: 0;
1170
+ }
1171
+
1172
+ .mp-back-btn:hover { text-decoration: underline; }
1173
+
1174
+ /* Seller Panel */
1175
+ .mp-stats-row {
1176
+ display: grid;
1177
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
1178
+ gap: 12px;
1179
+ margin-bottom: 20px;
1180
+ }
1181
+
1182
+ .mp-stat-card {
1183
+ background: var(--bg-card);
1184
+ border: 1px solid var(--border);
1185
+ border-radius: var(--radius);
1186
+ padding: 16px;
1187
+ text-align: center;
1188
+ }
1189
+
1190
+ .mp-stat-card .value {
1191
+ font-size: 24px;
1192
+ font-weight: 700;
1193
+ font-family: var(--mono);
1194
+ }
1195
+
1196
+ .mp-stat-card .label {
1197
+ font-size: 11px;
1198
+ color: var(--text-muted);
1199
+ margin-top: 4px;
1200
+ }
1201
+
1202
+ /* Purchase list */
1203
+ .mp-table {
1204
+ width: 100%;
1205
+ border-collapse: collapse;
1206
+ }
1207
+
1208
+ .mp-table th, .mp-table td {
1209
+ padding: 10px 12px;
1210
+ text-align: left;
1211
+ border-bottom: 1px solid var(--border);
1212
+ font-size: 13px;
1213
+ }
1214
+
1215
+ .mp-table th {
1216
+ color: var(--text-muted);
1217
+ font-weight: 600;
1218
+ text-transform: uppercase;
1219
+ font-size: 11px;
1220
+ }
1221
+
1222
+ .mp-empty {
1223
+ text-align: center;
1224
+ padding: 40px;
1225
+ color: var(--text-muted);
1226
+ font-size: 14px;
1227
+ }
1228
+
1229
+ .mp-sub-nav {
1230
+ display: flex;
1231
+ gap: 0;
1232
+ border-bottom: 1px solid var(--border);
1233
+ margin-bottom: 20px;
1234
+ }
1235
+
1236
+ .mp-sub-btn {
1237
+ padding: 8px 16px;
1238
+ background: none;
1239
+ border: none;
1240
+ color: var(--text-muted);
1241
+ font-size: 12px;
1242
+ font-weight: 600;
1243
+ cursor: pointer;
1244
+ border-bottom: 2px solid transparent;
1245
+ font-family: var(--font);
1246
+ }
1247
+
1248
+ .mp-sub-btn:hover { color: var(--text); }
1249
+ .mp-sub-btn.active { color: var(--cyan); border-bottom-color: var(--cyan); }
1250
+
1251
+ .mp-sub-content { display: none; }
1252
+ .mp-sub-content.active { display: block; }
1253
+
1254
+ /* Modal */
1255
+ .mp-modal-overlay {
1256
+ display: none;
1257
+ position: fixed;
1258
+ top: 0; left: 0; right: 0; bottom: 0;
1259
+ background: rgba(0,0,0,0.6);
1260
+ z-index: 1000;
1261
+ align-items: center;
1262
+ justify-content: center;
1263
+ }
1264
+
1265
+ .mp-modal-overlay.active { display: flex; }
1266
+
1267
+ .mp-modal {
1268
+ background: var(--bg-card);
1269
+ border: 1px solid var(--border);
1270
+ border-radius: var(--radius);
1271
+ padding: 24px;
1272
+ min-width: 360px;
1273
+ max-width: 90vw;
1274
+ }
1275
+
1276
+ .mp-modal h3 { margin-bottom: 12px; }
1277
+
1278
+ .mp-modal textarea {
1279
+ width: 100%;
1280
+ padding: 8px;
1281
+ background: var(--bg);
1282
+ border: 1px solid var(--border);
1283
+ border-radius: 6px;
1284
+ color: var(--text);
1285
+ font-family: var(--font);
1286
+ font-size: 13px;
1287
+ min-height: 80px;
1288
+ margin-bottom: 12px;
1289
+ resize: vertical;
1290
+ }
1291
+
1292
+ .mp-modal-actions {
1293
+ display: flex;
1294
+ gap: 8px;
1295
+ justify-content: flex-end;
1296
+ }
1297
+ </style>
1298
+ </head>
1299
+ <body>
1300
+
1301
+ <!-- Header -->
1302
+ <div class="header">
1303
+ <div class="header-left">
1304
+ <span class="logo">🧬 GSEP Live</span>
1305
+ <span id="status-badge" class="status-badge disconnected">
1306
+ <span class="status-dot"></span>
1307
+ <span id="status-text">Connecting...</span>
1308
+ </span>
1309
+ </div>
1310
+ <div style="font-size: 12px; color: var(--text-muted);">
1311
+ <span id="agent-name" style="font-weight: 600; color: var(--purple); font-size: 14px;"></span>
1312
+ <span style="margin: 0 6px; opacity: 0.3;">|</span>
1313
+ Genome: <span id="genome-id" style="font-family: var(--mono); color: var(--cyan);">—</span>
1314
+ </div>
1315
+ </div>
1316
+
1317
+ <!-- VPS Help Banner (hidden by default, shows on connection failure) -->
1318
+ <div id="vps-help" style="display:none; padding: 12px 24px; background: var(--amber-dim); border-bottom: 1px solid var(--amber); font-size: 13px; color: var(--amber);">
1319
+ <strong>Can't connect?</strong> If your agent runs on a remote server (VPS), connect via SSH tunnel:
1320
+ <code style="background: rgba(0,0,0,0.3); padding: 2px 8px; border-radius: 4px; margin-left: 4px; font-family: var(--mono); font-size: 12px;">ssh -L 4200:localhost:4200 user@your-server</code>
1321
+ <span style="margin-left: 8px; color: var(--text-muted);">Then open <strong>http://localhost:4200</strong> in your browser.</span>
1322
+ </div>
1323
+
1324
+ <!-- Stats Bar -->
1325
+ <div class="stats-bar">
1326
+ <div class="stat">
1327
+ <span class="stat-label">Fitness</span>
1328
+ <span id="stat-fitness" class="stat-value green">0.00</span>
1329
+ </div>
1330
+ <div class="stat">
1331
+ <span class="stat-label">Chats</span>
1332
+ <span id="stat-chats" class="stat-value cyan">0</span>
1333
+ </div>
1334
+ <div class="stat">
1335
+ <span class="stat-label">Mutations</span>
1336
+ <span id="stat-mutations" class="stat-value purple">0</span>
1337
+ </div>
1338
+ <div class="stat">
1339
+ <span class="stat-label">Promoted</span>
1340
+ <span id="stat-promoted" class="stat-value green">0</span>
1341
+ </div>
1342
+ <div class="stat">
1343
+ <span class="stat-label">Rejected</span>
1344
+ <span id="stat-rejected" class="stat-value red">0</span>
1345
+ </div>
1346
+ <div class="stat">
1347
+ <span class="stat-label">Drift</span>
1348
+ <span id="stat-drift" class="stat-value green">None</span>
1349
+ </div>
1350
+ <div class="stat">
1351
+ <span class="stat-label">Threats</span>
1352
+ <span id="stat-threats" class="stat-value green">0</span>
1353
+ </div>
1354
+ </div>
1355
+
1356
+ <!-- Tab Navigation -->
1357
+ <div class="tab-nav">
1358
+ <button class="tab-btn active" onclick="switchTab('evolution')">Evolution</button>
1359
+ <button class="tab-btn" onclick="switchTab('marketplace')">Marketplace</button>
1360
+ </div>
1361
+
1362
+ <!-- Tab: Evolution (original dashboard) -->
1363
+ <div id="tab-evolution" class="tab-content active">
1364
+
1365
+ <!-- App Layout: Sidebar + Main -->
1366
+ <div class="app-layout">
1367
+
1368
+ <!-- Gene Sidebar -->
1369
+ <aside class="gene-sidebar" id="gene-sidebar">
1370
+ <div class="sidebar-header">
1371
+ 🧬 Genome
1372
+ <span id="gene-total-count" class="sidebar-count">0</span>
1373
+ </div>
1374
+ <div id="genes-c0" class="gene-layer-group"></div>
1375
+ <div id="genes-c1" class="gene-layer-group"></div>
1376
+ <div id="genes-c2" class="gene-layer-group"></div>
1377
+ <div id="defense-c3" class="gene-layer-group"></div>
1378
+ <div id="defense-c4" class="gene-layer-group"></div>
1379
+ <div id="gene-bank-section"></div>
1380
+ </aside>
1381
+
1382
+ <!-- Main Content -->
1383
+ <div class="main">
1384
+
1385
+ <!-- Fitness Chart -->
1386
+ <div class="card fitness-chart">
1387
+ <div class="card-title">📈 Fitness Trajectory</div>
1388
+ <div class="chart-container">
1389
+ <canvas id="fitness-canvas"></canvas>
1390
+ </div>
1391
+ <div class="chart-legend">
1392
+ <div class="legend-item"><span class="legend-dot" style="background:var(--green)"></span> Quality</div>
1393
+ <div class="legend-item"><span class="legend-dot" style="background:var(--purple)"></span> Safety</div>
1394
+ <div class="legend-item"><span class="legend-dot" style="background:var(--cyan)"></span> Economy</div>
1395
+ <div class="legend-item"><span class="legend-dot" style="background:var(--amber)"></span> Stability</div>
1396
+ <div class="legend-item"><span class="legend-dot" style="background:var(--pink)"></span> Satisfaction</div>
1397
+ <div class="legend-item"><span class="legend-dot" style="background:#fff"></span> Composite</div>
1398
+ </div>
1399
+ </div>
1400
+
1401
+ <!-- Gate Evaluation -->
1402
+ <div class="card">
1403
+ <div class="card-title">🛡️ Evolution Gates</div>
1404
+ <div class="gates-grid">
1405
+ <div id="gate-quality" class="gate pending">
1406
+ <div class="gate-name">Quality</div>
1407
+ <div class="gate-score">—</div>
1408
+ <div class="gate-bar"><div class="gate-bar-fill" style="width:0%"></div></div>
1409
+ </div>
1410
+ <div id="gate-sandbox" class="gate pending">
1411
+ <div class="gate-name">Sandbox</div>
1412
+ <div class="gate-score">—</div>
1413
+ <div class="gate-bar"><div class="gate-bar-fill" style="width:0%"></div></div>
1414
+ </div>
1415
+ <div id="gate-economic" class="gate pending">
1416
+ <div class="gate-name">Economic</div>
1417
+ <div class="gate-score">—</div>
1418
+ <div class="gate-bar"><div class="gate-bar-fill" style="width:0%"></div></div>
1419
+ </div>
1420
+ <div id="gate-stability" class="gate pending">
1421
+ <div class="gate-name">Stability</div>
1422
+ <div class="gate-score">—</div>
1423
+ <div class="gate-bar"><div class="gate-bar-fill" style="width:0%"></div></div>
1424
+ </div>
1425
+ <div id="gate-constitutional" class="gate pending" style="grid-column: 1 / -1;">
1426
+ <div class="gate-name">Constitutional (C0 Alignment)</div>
1427
+ <div class="gate-score">—</div>
1428
+ <div class="gate-bar"><div class="gate-bar-fill" style="width:0%"></div></div>
1429
+ </div>
1430
+ </div>
1431
+ </div>
1432
+
1433
+ <!-- Drift Monitor -->
1434
+ <div class="card">
1435
+ <div class="card-title">🔍 Drift Monitor</div>
1436
+ <div class="drift-grid">
1437
+ <div id="drift-quality" class="drift-item">
1438
+ <div class="drift-type">Quality</div>
1439
+ <div class="drift-value">OK</div>
1440
+ </div>
1441
+ <div id="drift-efficiency" class="drift-item">
1442
+ <div class="drift-type">Efficiency</div>
1443
+ <div class="drift-value">OK</div>
1444
+ </div>
1445
+ <div id="drift-cost" class="drift-item">
1446
+ <div class="drift-type">Cost</div>
1447
+ <div class="drift-value">OK</div>
1448
+ </div>
1449
+ <div id="drift-behavioral" class="drift-item">
1450
+ <div class="drift-type">Behavioral</div>
1451
+ <div class="drift-value">OK</div>
1452
+ </div>
1453
+ <div id="drift-temporal" class="drift-item">
1454
+ <div class="drift-type">Temporal</div>
1455
+ <div class="drift-value">OK</div>
1456
+ </div>
1457
+ <div id="drift-safety" class="drift-item">
1458
+ <div class="drift-type">Safety</div>
1459
+ <div class="drift-value">OK</div>
1460
+ </div>
1461
+ </div>
1462
+ </div>
1463
+
1464
+ <!-- Event Log (full width) -->
1465
+ <div class="card" style="grid-column: 1 / -1;">
1466
+ <div class="card-title">📋 Evolution Log</div>
1467
+ <div id="event-log" class="log-panel"></div>
1468
+ </div>
1469
+ </div>
1470
+ </div>
1471
+ </div><!-- /tab-evolution -->
1472
+
1473
+ <!-- Tab: Marketplace -->
1474
+ <div id="tab-marketplace" class="tab-content">
1475
+ <div class="mp-container">
1476
+
1477
+ <!-- Sub-navigation -->
1478
+ <div class="mp-sub-nav">
1479
+ <button class="mp-sub-btn active" onclick="switchMpTab('browse')">Browse</button>
1480
+ <button class="mp-sub-btn" onclick="switchMpTab('seller')">My Marketplace</button>
1481
+ <button class="mp-sub-btn" onclick="switchMpTab('purchases')">My Purchases</button>
1482
+ </div>
1483
+
1484
+ <!-- Sub: Browse / Search -->
1485
+ <div id="mp-browse" class="mp-sub-content active">
1486
+ <div class="mp-search-bar">
1487
+ <input type="text" id="mp-search-q" placeholder="Search genes...">
1488
+ <select id="mp-search-type">
1489
+ <option value="">All types</option>
1490
+ <option value="behavioral">Behavioral</option>
1491
+ <option value="cognitive">Cognitive</option>
1492
+ <option value="defensive">Defensive</option>
1493
+ <option value="efficiency">Efficiency</option>
1494
+ <option value="creative">Creative</option>
1495
+ </select>
1496
+ <select id="mp-search-sort">
1497
+ <option value="fitness">Highest Fitness</option>
1498
+ <option value="adoptions">Most Adopted</option>
1499
+ <option value="newest">Newest</option>
1500
+ </select>
1501
+ <button onclick="mpSearch()">Search</button>
1502
+ </div>
1503
+ <div id="mp-results" class="mp-grid"></div>
1504
+ <div id="mp-pagination" class="mp-pagination"></div>
1505
+ </div>
1506
+
1507
+ <!-- Sub: Gene Detail (hidden by default, shown on card click) -->
1508
+ <div id="mp-detail-view" class="mp-sub-content">
1509
+ <button class="mp-back-btn" onclick="mpBackToBrowse()">&larr; Back to results</button>
1510
+ <div id="mp-detail-content"></div>
1511
+ </div>
1512
+
1513
+ <!-- Sub: My Marketplace (Seller Panel) -->
1514
+ <div id="mp-seller" class="mp-sub-content">
1515
+ <h3 style="margin-bottom:16px">Seller Dashboard</h3>
1516
+ <div id="mp-seller-status"></div>
1517
+ <div id="mp-seller-earnings" class="mp-stats-row"></div>
1518
+ <h4 style="margin:16px 0 12px">My Published Genes</h4>
1519
+ <div id="mp-my-genes" class="mp-grid"></div>
1520
+ <h4 style="margin:16px 0 12px">Publish Eligible Genes</h4>
1521
+ <div id="mp-publish-eligible" class="mp-grid"></div>
1522
+ </div>
1523
+
1524
+ <!-- Sub: My Purchases -->
1525
+ <div id="mp-purchases" class="mp-sub-content">
1526
+ <h3 style="margin-bottom:16px">My Purchases</h3>
1527
+ <div id="mp-purchases-list"></div>
1528
+ </div>
1529
+
1530
+ </div>
1531
+ </div><!-- /tab-marketplace -->
1532
+
1533
+ </div><!-- /main -->
1534
+
1535
+ </div><!-- /app-layout -->
1536
+
1537
+ <!-- Intelligence Sidebar (Right) — fixed, always visible -->
1538
+ <aside class="intel-sidebar" id="intel-sidebar">
1539
+
1540
+ <!-- Purpose Lock -->
1541
+ <div class="intel-section">
1542
+ <div class="intel-title">🎯 Purpose Lock</div>
1543
+ <div id="intel-purpose-status">
1544
+ <div class="intel-stat">
1545
+ <span class="intel-stat-label">Status</span>
1546
+ <span id="intel-purpose-active" class="intel-badge inactive">Inactive</span>
1547
+ </div>
1548
+ <div class="intel-stat">
1549
+ <span class="intel-stat-label">On-Purpose</span>
1550
+ <span id="intel-purpose-on" class="intel-stat-value">0</span>
1551
+ </div>
1552
+ <div class="intel-stat">
1553
+ <span class="intel-stat-label">Rejected</span>
1554
+ <span id="intel-purpose-rejected" class="intel-stat-value">0</span>
1555
+ </div>
1556
+ <div class="intel-stat">
1557
+ <span class="intel-stat-label">Deviation Rate</span>
1558
+ <span id="intel-purpose-rate" class="intel-stat-value">0%</span>
1559
+ </div>
1560
+ </div>
1561
+ </div>
1562
+
1563
+ <!-- Anomaly Detection -->
1564
+ <div class="intel-section">
1565
+ <div class="intel-title">🚨 Anomaly Detection</div>
1566
+ <div class="intel-stat">
1567
+ <span class="intel-stat-label">Analyzed</span>
1568
+ <span id="intel-anomaly-total" class="intel-stat-value">0</span>
1569
+ </div>
1570
+ <div class="intel-stat">
1571
+ <span class="intel-stat-label">Detected</span>
1572
+ <span id="intel-anomaly-detected" class="intel-stat-value good">0</span>
1573
+ </div>
1574
+ <div id="intel-anomaly-list"></div>
1575
+ </div>
1576
+
1577
+ <!-- Skills -->
1578
+ <div class="intel-section">
1579
+ <div class="intel-title">🔧 Skills</div>
1580
+ <div class="intel-stat">
1581
+ <span class="intel-stat-label">Registered</span>
1582
+ <span id="intel-skills-count" class="intel-stat-value">0</span>
1583
+ </div>
1584
+ <div id="intel-skills-list"></div>
1585
+ </div>
1586
+
1587
+ <!-- Proactive Engine -->
1588
+ <div class="intel-section">
1589
+ <div class="intel-title">⚡ Proactive Engine</div>
1590
+ <div class="intel-stat">
1591
+ <span class="intel-stat-label">Status</span>
1592
+ <span id="intel-proactive-status" class="intel-badge inactive">Stopped</span>
1593
+ </div>
1594
+ <div class="intel-stat">
1595
+ <span class="intel-stat-label">Tasks</span>
1596
+ <span id="intel-proactive-tasks" class="intel-stat-value">0</span>
1597
+ </div>
1598
+ <div id="intel-proactive-results"></div>
1599
+ </div>
1600
+
1601
+ <!-- Weekly Report Summary -->
1602
+ <div class="intel-section">
1603
+ <div class="intel-title">📊 Weekly Report</div>
1604
+ <div class="intel-stat">
1605
+ <span class="intel-stat-label">Conversations</span>
1606
+ <span id="intel-report-conversations" class="intel-stat-value">0</span>
1607
+ </div>
1608
+ <div class="intel-stat">
1609
+ <span class="intel-stat-label">Quality Trend</span>
1610
+ <span id="intel-report-trend" class="intel-stat-value">—</span>
1611
+ </div>
1612
+ <div class="intel-stat">
1613
+ <span class="intel-stat-label">ROI</span>
1614
+ <span id="intel-report-roi" class="intel-stat-value">—</span>
1615
+ </div>
1616
+ <div class="intel-stat">
1617
+ <span class="intel-stat-label">Threats Blocked</span>
1618
+ <span id="intel-report-threats" class="intel-stat-value good">0</span>
1619
+ </div>
1620
+ </div>
1621
+
1622
+ <!-- Emotional State -->
1623
+ <div class="intel-section">
1624
+ <div class="intel-title">💭 Emotional Intelligence</div>
1625
+ <div class="intel-stat">
1626
+ <span class="intel-stat-label">Last Emotion</span>
1627
+ <span id="intel-emotion-primary" class="intel-stat-value">—</span>
1628
+ </div>
1629
+ <div class="intel-stat">
1630
+ <span class="intel-stat-label">Escalations</span>
1631
+ <span id="intel-emotion-escalations" class="intel-stat-value good">0</span>
1632
+ </div>
1633
+ </div>
1634
+
1635
+ </aside>
1636
+
1637
+ <!-- Refund Modal -->
1638
+ <div id="mp-refund-modal" class="mp-modal-overlay">
1639
+ <div class="mp-modal">
1640
+ <h3>Request Refund</h3>
1641
+ <textarea id="mp-refund-reason" placeholder="Why are you requesting a refund?"></textarea>
1642
+ <div class="mp-modal-actions">
1643
+ <button class="mp-action-btn secondary" onclick="closeRefundModal()">Cancel</button>
1644
+ <button class="mp-action-btn primary" onclick="submitRefund()">Submit</button>
1645
+ </div>
1646
+ </div>
1647
+ </div>
1648
+
1649
+ <script>
1650
+ // ─── State ───────────────────────────────────────
1651
+ // ─── Intelligence Sidebar State ──────────────────────
1652
+ const intelState = {
1653
+ purposeOn: 0,
1654
+ purposeRejected: 0,
1655
+ anomalyTotal: 0,
1656
+ anomalyDetected: 0,
1657
+ skillsCount: 0,
1658
+ proactiveTasks: 0,
1659
+ proactiveRunning: false,
1660
+ conversations: 0,
1661
+ emotionEscalations: 0,
1662
+ };
1663
+
1664
+ function updateEl(id, value) {
1665
+ const el = document.getElementById(id);
1666
+ if (el) el.textContent = String(value);
1667
+ }
1668
+
1669
+ function updateIntelPurpose() {
1670
+ updateEl('intel-purpose-on', intelState.purposeOn);
1671
+ updateEl('intel-purpose-rejected', intelState.purposeRejected);
1672
+ const total = intelState.purposeOn + intelState.purposeRejected;
1673
+ const rate = total > 0 ? ((intelState.purposeRejected / total) * 100).toFixed(1) + '%' : '0%';
1674
+ updateEl('intel-purpose-rate', rate);
1675
+ const rateEl = document.getElementById('intel-purpose-rate');
1676
+ if (rateEl) {
1677
+ rateEl.className = 'intel-stat-value' + (intelState.purposeRejected > 0 ? ' warning' : ' good');
1678
+ }
1679
+ }
1680
+
1681
+ function addIntelAnomaly(data) {
1682
+ updateEl('intel-anomaly-total', ++intelState.anomalyTotal);
1683
+ const detEl = document.getElementById('intel-anomaly-detected');
1684
+ if (detEl) {
1685
+ detEl.textContent = String(intelState.anomalyDetected);
1686
+ detEl.className = 'intel-stat-value' + (intelState.anomalyDetected > 0 ? ' danger' : ' good');
1687
+ }
1688
+ const list = document.getElementById('intel-anomaly-list');
1689
+ if (list) {
1690
+ const severity = data.severity || 'medium';
1691
+ const cls = severity === 'critical' || severity === 'high' ? 'intel-anomaly critical' : 'intel-anomaly';
1692
+ const el = document.createElement('div');
1693
+ el.className = cls;
1694
+ el.innerHTML = '<strong>' + (data.type || 'unknown') + '</strong><br>' + (data.description || '').substring(0, 100);
1695
+ list.prepend(el);
1696
+ while (list.children.length > 5) list.removeChild(list.lastChild);
1697
+ }
1698
+ }
1699
+
1700
+ const state = {
1701
+ connected: false,
1702
+ startTime: Date.now(),
1703
+ chats: 0,
1704
+ mutations: 0,
1705
+ promoted: 0,
1706
+ rejected: 0,
1707
+ threats: 0,
1708
+ fitness: { quality: 0, safety: 0, economy: 0, stability: 0, satisfaction: 0, composite: 0 },
1709
+ fitnessHistory: [],
1710
+ driftState: {},
1711
+ genes: { layer0: [], layer1: [], layer2: [] },
1712
+ };
1713
+
1714
+ // ─── SSE Connection ──────────────────────────────
1715
+ const token = new URLSearchParams(location.search).get('token');
1716
+ if (!token) {
1717
+ document.body.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100vh;color:var(--red);font-size:18px;">Missing token parameter. Use ?token=YOUR_TOKEN</div>';
1718
+ } else {
1719
+ connectSSE();
1720
+ fetchGenes();
1721
+ }
1722
+
1723
+ function connectSSE() {
1724
+ const baseUrl = location.origin;
1725
+ const source = new EventSource(baseUrl + '/gsep/events?token=' + encodeURIComponent(token));
1726
+
1727
+ source.onopen = () => {
1728
+ setStatus(true);
1729
+ document.getElementById('vps-help').style.display = 'none';
1730
+ };
1731
+
1732
+ source.onmessage = (e) => {
1733
+ try {
1734
+ const event = JSON.parse(e.data);
1735
+ routeEvent(event);
1736
+ } catch { /* ignore parse errors */ }
1737
+ };
1738
+
1739
+ let errorCount = 0;
1740
+ source.onerror = () => {
1741
+ setStatus(false);
1742
+ errorCount++;
1743
+ // Show VPS help after 3 failed attempts
1744
+ if (errorCount >= 3) {
1745
+ document.getElementById('vps-help').style.display = 'block';
1746
+ }
1747
+ };
1748
+
1749
+ // Fetch snapshot for initial hydration
1750
+ fetch(baseUrl + '/gsep/snapshot?token=' + encodeURIComponent(token))
1751
+ .then(r => r.json())
1752
+ .then(snap => {
1753
+ if (snap.name) {
1754
+ document.getElementById('agent-name').textContent = snap.name;
1755
+ }
1756
+ if (snap.genomeId) {
1757
+ document.getElementById('genome-id').textContent = snap.genomeId.slice(0, 12) + '...';
1758
+ }
1759
+ // Hydrate all counters from persisted state
1760
+ if (snap.interactionCount) {
1761
+ state.chats = snap.interactionCount;
1762
+ updateStat('stat-chats', state.chats);
1763
+ }
1764
+ if (snap.totalMutations) {
1765
+ state.mutations = snap.totalMutations;
1766
+ updateStat('stat-mutations', state.mutations);
1767
+ }
1768
+ if (snap.promotedCount) {
1769
+ state.promoted = snap.promotedCount;
1770
+ updateStat('stat-promoted', state.promoted);
1771
+ }
1772
+ if (snap.rejectedCount) {
1773
+ state.rejected = snap.rejectedCount;
1774
+ updateStat('stat-rejected', state.rejected);
1775
+ }
1776
+ if (snap.threatCount) {
1777
+ state.threats = snap.threatCount;
1778
+ updateStat('stat-threats', state.threats, state.threats > 0 ? 'red' : undefined);
1779
+ }
1780
+ // Replay recent events
1781
+ if (snap.recentEvents) {
1782
+ for (const evt of snap.recentEvents) {
1783
+ routeEvent(evt);
1784
+ }
1785
+ }
1786
+ })
1787
+ .catch(() => {});
1788
+ }
1789
+
1790
+ function fetchGenes() {
1791
+ const baseUrl = location.origin;
1792
+ fetch(baseUrl + '/gsep/genes?token=' + encodeURIComponent(token))
1793
+ .then(r => r.json())
1794
+ .then(data => {
1795
+ state.genes = data;
1796
+ renderGenes(data);
1797
+ })
1798
+ .catch(() => {});
1799
+ }
1800
+
1801
+ function setStatus(connected) {
1802
+ state.connected = connected;
1803
+ const badge = document.getElementById('status-badge');
1804
+ const text = document.getElementById('status-text');
1805
+ badge.className = 'status-badge ' + (connected ? 'connected' : 'disconnected');
1806
+ text.textContent = connected ? 'Live' : 'Reconnecting...';
1807
+ }
1808
+
1809
+ // ─── Event Router ────────────────────────────────
1810
+ function routeEvent(event) {
1811
+ const type = event.type;
1812
+ const data = event.data;
1813
+
1814
+ switch (type) {
1815
+ case 'connected':
1816
+ if (data.genomeId) {
1817
+ document.getElementById('genome-id').textContent = data.genomeId.slice(0, 12) + '...';
1818
+ }
1819
+ break;
1820
+
1821
+ case 'chat:started':
1822
+ state.chats++;
1823
+ updateStat('stat-chats', state.chats);
1824
+ addLog('💬', 'Chat started — <span class="cyan">' + escapeHtml((data.message || data.messagePreview || '').slice(0, 60)) + '</span>');
1825
+ break;
1826
+
1827
+ case 'chat:message':
1828
+ addLog('🤖', 'Response: <span class="green">' + escapeHtml((data.content || '').slice(0, 80)) + '</span>');
1829
+ break;
1830
+
1831
+ case 'chat:completed': {
1832
+ const totalTokens = (data.inputTokens || 0) + (data.outputTokens || 0) || data.tokens || 0;
1833
+ const duration = data.duration || 0;
1834
+ addLog('✅', 'Chat completed — <span class="green">' + duration + 'ms</span>, tokens: ' + totalTokens + ', interactions: ' + (data.interactionCount || '?'));
1835
+ break;
1836
+ }
1837
+
1838
+ case 'fitness:computed':
1839
+ state.fitness = {
1840
+ quality: data.vector?.taskSuccess || 0,
1841
+ safety: data.vector?.safety || 0,
1842
+ economy: data.vector?.efficiency || 0,
1843
+ stability: data.vector?.consistency || 0,
1844
+ satisfaction: data.vector?.userSatisfaction || 0,
1845
+ composite: data.composite || 0,
1846
+ };
1847
+ state.fitnessHistory.push({ ...state.fitness });
1848
+ if (state.fitnessHistory.length > 100) state.fitnessHistory.shift();
1849
+ updateStat('stat-fitness', state.fitness.composite.toFixed(2));
1850
+ drawChart();
1851
+ break;
1852
+
1853
+ case 'drift:detected':
1854
+ if (data.signals) {
1855
+ for (const signal of data.signals) {
1856
+ const driftKey = mapDriftType(signal.type);
1857
+ if (driftKey) {
1858
+ state.driftState[driftKey] = signal;
1859
+ updateDrift(driftKey, signal);
1860
+ }
1861
+ }
1862
+ }
1863
+ updateStat('stat-drift', data.severity || 'Detected', 'amber');
1864
+ addLog('⚠️', 'Drift detected: <span class="amber">' + (data.severity || 'unknown') + '</span> — ' + (data.signals || []).map(s => s.type).join(', '));
1865
+ break;
1866
+
1867
+ case 'mutation:generated':
1868
+ state.mutations++;
1869
+ updateStat('stat-mutations', state.mutations);
1870
+ addLog('🧬', 'Mutation generated — <span class="cyan">' + (data.gene || '?') + '</span> (' + (data.candidateCount || 0) + ' candidates)');
1871
+ break;
1872
+
1873
+ case 'gate:evaluated':
1874
+ updateGates(data);
1875
+ addLog('🛡️', 'Gates evaluated for <span class="cyan">' + (data.gene || '?') + '</span> — <span class="' + (data.decision === 'promote' ? 'green' : data.decision === 'canary' ? 'amber' : 'red') + '">' + (data.decision || '?').toUpperCase() + '</span>');
1876
+ break;
1877
+
1878
+ case 'mutation:promoted':
1879
+ state.promoted++;
1880
+ updateStat('stat-promoted', state.promoted);
1881
+ addLog('🚀', '<span class="green">PROMOTED</span> — <span class="cyan">' + (data.gene || '?') + '</span>:' + (data.variant || '?').slice(0, 20) + ' (fitness: ' + (data.fitness || 0).toFixed(2) + ', +' + ((data.improvement || 0) * 100).toFixed(1) + '%)');
1882
+ break;
1883
+
1884
+ case 'mutation:rejected':
1885
+ state.rejected++;
1886
+ updateStat('stat-rejected', state.rejected);
1887
+ addLog('❌', '<span class="red">REJECTED</span> — <span class="cyan">' + (data.gene || '?') + '</span>: ' + (data.reason || 'unknown'));
1888
+ break;
1889
+
1890
+ case 'firewall:threat':
1891
+ state.threats++;
1892
+ updateStat('stat-threats', state.threats, 'red');
1893
+ addLog('🔥', 'C3 Firewall: <span class="red">' + (data.pattern || 'threat') + '</span> — ' + (data.allowed ? '<span class="amber">allowed</span>' : '<span class="red">BLOCKED</span>'));
1894
+ break;
1895
+
1896
+ case 'immune:threat':
1897
+ state.threats++;
1898
+ updateStat('stat-threats', state.threats, 'red');
1899
+ addLog('🧬', 'C4 Immune: <span class="red">' + (data.threats || []).map(t => t.type).join(', ') + '</span> — action: <span class="amber">' + (data.action || '?') + '</span>');
1900
+ break;
1901
+
1902
+ case 'gene:updated':
1903
+ if (data.layers) {
1904
+ state.genes = data.layers;
1905
+ renderGenes(data.layers);
1906
+ addLog('🧬', 'Genome updated — sidebar refreshed');
1907
+ }
1908
+ break;
1909
+
1910
+ case 'purpose:rejected':
1911
+ intelState.purposeRejected++;
1912
+ updateIntelPurpose();
1913
+ addLog('🎯', 'Purpose Lock: <span class="amber">off-topic rejected</span> — "' + (data.userMessage || '').substring(0, 50) + '"');
1914
+ break;
1915
+
1916
+ case 'purpose:accepted':
1917
+ intelState.purposeOn++;
1918
+ updateIntelPurpose();
1919
+ const purposeBadge = document.getElementById('intel-purpose-active');
1920
+ if (purposeBadge) { purposeBadge.className = 'intel-badge active'; purposeBadge.textContent = 'Active'; }
1921
+ break;
1922
+
1923
+ case 'anomaly:detected':
1924
+ intelState.anomalyDetected++;
1925
+ addIntelAnomaly(data);
1926
+ addLog('🚨', 'Anomaly: <span class="red">' + (data.type || 'unknown') + '</span> — ' + (data.description || '').substring(0, 80));
1927
+ break;
1928
+
1929
+ case 'emotion:escalation':
1930
+ intelState.emotionEscalations++;
1931
+ updateEl('intel-emotion-primary', (data.emotion || '?') + ' (' + ((data.intensity || 0) * 100).toFixed(0) + '%)');
1932
+ updateEl('intel-emotion-escalations', intelState.emotionEscalations);
1933
+ addLog('💭', 'Emotional escalation: <span class="amber">' + (data.emotion || '?') + '</span> intensity ' + ((data.intensity || 0) * 100).toFixed(0) + '%');
1934
+ break;
1935
+
1936
+ case 'skill:registered':
1937
+ intelState.skillsCount++;
1938
+ updateEl('intel-skills-count', intelState.skillsCount);
1939
+ const skillsList = document.getElementById('intel-skills-list');
1940
+ if (skillsList) {
1941
+ const el = document.createElement('div');
1942
+ el.className = 'intel-skill';
1943
+ el.innerHTML = '<span class="intel-skill-name">' + (data.skill || '?') + '</span><span class="intel-skill-rate">new</span>';
1944
+ skillsList.prepend(el);
1945
+ while (skillsList.children.length > 8) skillsList.removeChild(skillsList.lastChild);
1946
+ }
1947
+ addLog('🔧', 'Skill registered: <span class="cyan">' + (data.skill || '?') + '</span> — ' + (data.description || '').substring(0, 60));
1948
+ break;
1949
+
1950
+ case 'proactive:started':
1951
+ intelState.proactiveRunning = true;
1952
+ const proStatus = document.getElementById('intel-proactive-status');
1953
+ if (proStatus) { proStatus.className = 'intel-badge active'; proStatus.textContent = 'Running'; }
1954
+ addLog('⚡', 'Proactive Engine <span class="green">started</span>');
1955
+ break;
1956
+
1957
+ case 'proactive:completed':
1958
+ intelState.proactiveTasks++;
1959
+ updateEl('intel-proactive-tasks', intelState.proactiveTasks);
1960
+ const proResults = document.getElementById('intel-proactive-results');
1961
+ if (proResults) {
1962
+ const el = document.createElement('div');
1963
+ el.className = 'intel-proactive';
1964
+ el.innerHTML = '<strong>' + (data.taskName || data.taskId || '?') + '</strong><br>' + (data.summary || '').substring(0, 100);
1965
+ proResults.prepend(el);
1966
+ while (proResults.children.length > 5) proResults.removeChild(proResults.lastChild);
1967
+ }
1968
+ addLog('⚡', 'Proactive task: <span class="purple">' + (data.taskName || '?') + '</span> [' + (data.importance || '?') + ']');
1969
+ break;
1970
+
1971
+ default:
1972
+ addLog('📌', '<span class="text-muted">' + type + '</span>');
1973
+ }
1974
+
1975
+ // Update interaction count for right sidebar
1976
+ intelState.conversations++;
1977
+ updateEl('intel-report-conversations', intelState.conversations);
1978
+ }
1979
+
1980
+ // ─── Gene Sidebar Rendering ─────────────────────
1981
+ function renderGenes(layers) {
1982
+ const l0 = layers.layer0 || [];
1983
+ const l1 = layers.layer1 || [];
1984
+ const l2 = layers.layer2 || [];
1985
+ const total = l0.length + l1.length + l2.length;
1986
+
1987
+ document.getElementById('gene-total-count').textContent = total;
1988
+
1989
+ renderLayerGroup('genes-c0', 'C0 — Immutable DNA', '🔒', l0, 'c0');
1990
+ renderLayerGroup('genes-c1', 'C1 — Operative Genes', '⚙️', l1, 'c1');
1991
+ renderLayerGroup('genes-c2', 'C2 — Epigenetics', '👤', l2, 'c2');
1992
+ renderC3(layers.c3);
1993
+ renderC4(layers.c4);
1994
+ renderGeneBank(layers.geneBank);
1995
+ }
1996
+
1997
+ function renderC3(c3) {
1998
+ const container = document.getElementById('defense-c3');
1999
+ const wasCollapsed = container.querySelector('.gene-layer-header.collapsed') !== null;
2000
+
2001
+ let html = '<div class="gene-layer-header c3' + (wasCollapsed ? ' collapsed' : '') + '" onclick="toggleLayer(this)" style="color:var(--red)">';
2002
+ html += '<span class="layer-icon">🛡️</span>';
2003
+ html += '<span>C3 — Content Firewall</span>';
2004
+ html += '<span class="chevron">▼</span>';
2005
+ html += '</div>';
2006
+
2007
+ html += '<div class="gene-layer-body' + (wasCollapsed ? ' collapsed' : '') + '">';
2008
+
2009
+ if (!c3 || !c3.active) {
2010
+ html += '<div class="defense-inactive">Firewall not active</div>';
2011
+ } else {
2012
+ html += '<div class="defense-section">';
2013
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Integrity</span>';
2014
+ html += '<span class="defense-integrity ' + (c3.integrityValid ? 'valid' : 'invalid') + '">' + (c3.integrityValid ? '✓ SHA-256 Valid' : '✗ Tampered') + '</span></div>';
2015
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Patterns</span><span class="defense-stat-value" style="color:var(--purple)">' + c3.totalPatterns + '</span></div>';
2016
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Scanned</span><span class="defense-stat-value">' + c3.totalScanned + '</span></div>';
2017
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Blocked</span><span class="defense-stat-value" style="color:var(--red)">' + c3.totalBlocked + '</span></div>';
2018
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Sanitized</span><span class="defense-stat-value" style="color:var(--amber)">' + c3.totalSanitized + '</span></div>';
2019
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Block Rate</span><span class="defense-stat-value">' + (c3.blockRate * 100).toFixed(1) + '%</span></div>';
2020
+ html += '</div>';
2021
+ }
2022
+
2023
+ html += '</div>';
2024
+ container.innerHTML = html;
2025
+ }
2026
+
2027
+ function renderC4(c4) {
2028
+ const container = document.getElementById('defense-c4');
2029
+ const wasCollapsed = container.querySelector('.gene-layer-header.collapsed') !== null;
2030
+
2031
+ let html = '<div class="gene-layer-header c4' + (wasCollapsed ? ' collapsed' : '') + '" onclick="toggleLayer(this)" style="color:var(--emerald)">';
2032
+ html += '<span class="layer-icon">🧬</span>';
2033
+ html += '<span>C4 — Immune System</span>';
2034
+ html += '<span class="chevron">▼</span>';
2035
+ html += '</div>';
2036
+
2037
+ html += '<div class="gene-layer-body' + (wasCollapsed ? ' collapsed' : '') + '">';
2038
+
2039
+ if (!c4 || !c4.active) {
2040
+ html += '<div class="defense-inactive">Immune system not active</div>';
2041
+ } else {
2042
+ html += '<div class="defense-section">';
2043
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Total Scans</span><span class="defense-stat-value">' + c4.totalScans + '</span></div>';
2044
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Threats Found</span><span class="defense-stat-value" style="color:' + (c4.threatsDetected > 0 ? 'var(--red)' : 'var(--green)') + '">' + c4.threatsDetected + '</span></div>';
2045
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Quarantines</span><span class="defense-stat-value" style="color:var(--red)">' + c4.quarantinesTriggered + '</span></div>';
2046
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Sanitizations</span><span class="defense-stat-value" style="color:var(--amber)">' + c4.sanitizations + '</span></div>';
2047
+ html += '<div class="defense-stat-row"><span class="defense-stat-label">Immune Memory</span><span class="defense-stat-value" style="color:var(--cyan)">' + c4.immuneMemorySize + '</span></div>';
2048
+ html += '</div>';
2049
+ }
2050
+
2051
+ html += '</div>';
2052
+ container.innerHTML = html;
2053
+ }
2054
+
2055
+ function renderGeneBank(bank) {
2056
+ const container = document.getElementById('gene-bank-section');
2057
+ if (!container) return;
2058
+
2059
+ let html = '<div class="gene-bank-header">🏦 Gene Bank</div>';
2060
+
2061
+ if (!bank) {
2062
+ html += '<div class="bank-no-bank">Gene Bank not configured</div>';
2063
+ container.innerHTML = html;
2064
+ return;
2065
+ }
2066
+
2067
+ // Stats grid
2068
+ html += '<div class="gene-bank-stats">';
2069
+ html += '<div class="bank-stat"><span class="bank-stat-value" style="color:var(--green)">' + bank.totalGenes + '</span><span class="bank-stat-label">Active</span></div>';
2070
+ html += '<div class="bank-stat"><span class="bank-stat-value" style="color:var(--amber)">' + (bank.sellable || []).length + '</span><span class="bank-stat-label">Sellable</span></div>';
2071
+ html += '<div class="bank-stat"><span class="bank-stat-value" style="color:var(--purple)">' + (bank.published || []).length + '</span><span class="bank-stat-label">Published</span></div>';
2072
+ html += '<div class="bank-stat"><span class="bank-stat-value" style="color:var(--cyan)">' + (bank.adopted || []).length + '</span><span class="bank-stat-label">Adopted</span></div>';
2073
+ html += '</div>';
2074
+
2075
+ // Sellable genes category
2076
+ html += renderBankCategory('sellable', '💰 Sellable', bank.sellable || [], 'green', 'sellable');
2077
+
2078
+ // Published genes category
2079
+ html += renderBankCategory('published', '📡 Published', bank.published || [], 'purple', 'published');
2080
+
2081
+ // Adopted genes category
2082
+ html += renderBankCategory('adopted', '⬇️ Adopted', bank.adopted || [], 'cyan', 'adopted');
2083
+
2084
+ // Bank connectivity status
2085
+ if (bank.active) {
2086
+ html += '<div style="padding:8px 16px;font-size:10px;color:var(--green);text-align:center">● Connected to Gene Bank</div>';
2087
+ } else {
2088
+ html += '<div style="padding:8px 16px;font-size:10px;color:var(--text-muted);text-align:center">○ Gene Bank not connected</div>';
2089
+ html += '<div class="bank-no-bank-hint">Pass geneBank to PGA config to enable marketplace</div>';
2090
+ }
2091
+
2092
+ container.innerHTML = html;
2093
+ }
2094
+
2095
+ function renderBankCategory(id, title, genes, color, badgeClass) {
2096
+ const container = document.getElementById('gene-bank-section');
2097
+ const prevEl = container ? container.querySelector('.bank-category-header[data-id="' + id + '"]') : null;
2098
+ const wasCollapsed = prevEl ? prevEl.classList.contains('collapsed') : true;
2099
+
2100
+ let html = '<div class="bank-category">';
2101
+ html += '<div class="bank-category-header' + (wasCollapsed ? ' collapsed' : '') + '" data-id="' + id + '" onclick="toggleLayer(this)">';
2102
+ html += '<span>' + title + '</span>';
2103
+ html += '<span class="bank-count">' + genes.length + '</span>';
2104
+ html += '<span class="chevron">▼</span>';
2105
+ html += '</div>';
2106
+
2107
+ html += '<div class="bank-category-body' + (wasCollapsed ? ' collapsed' : '') + '">';
2108
+
2109
+ if (genes.length === 0) {
2110
+ html += '<div class="bank-empty">No genes in this category</div>';
2111
+ } else {
2112
+ for (const gene of genes) {
2113
+ const fitness = gene.fitness || 0;
2114
+ const fitnessColor = fitness >= 0.7 ? 'var(--green)' : fitness >= 0.4 ? 'var(--amber)' : 'var(--red)';
2115
+
2116
+ html += '<div class="bank-gene">';
2117
+ html += '<div class="bank-gene-info">';
2118
+ html += '<div class="bank-gene-name">' + escapeHtml(gene.gene || 'unknown') + '</div>';
2119
+ html += '<div class="bank-gene-variant">' + escapeHtml((gene.variant || '').slice(0, 24)) + '</div>';
2120
+ html += '</div>';
2121
+ html += '<span class="bank-gene-fitness" style="color:' + fitnessColor + '">' + fitness.toFixed(2) + '</span>';
2122
+ html += '<span class="bank-badge ' + badgeClass + '">' + badgeClass + '</span>';
2123
+ html += '</div>';
2124
+ }
2125
+ }
2126
+
2127
+ html += '</div></div>';
2128
+ return html;
2129
+ }
2130
+
2131
+ function renderLayerGroup(containerId, title, icon, alleles, layerClass) {
2132
+ const container = document.getElementById(containerId);
2133
+ const wasCollapsed = container.querySelector('.gene-layer-header.collapsed') !== null;
2134
+
2135
+ let html = '<div class="gene-layer-header ' + layerClass + (wasCollapsed ? ' collapsed' : '') + '" onclick="toggleLayer(this)">';
2136
+ html += '<span class="layer-icon">' + icon + '</span>';
2137
+ html += '<span>' + title + '</span>';
2138
+ html += '<span class="layer-count">' + alleles.length + '</span>';
2139
+ html += '<span class="chevron">▼</span>';
2140
+ html += '</div>';
2141
+
2142
+ html += '<div class="gene-layer-body' + (wasCollapsed ? ' collapsed' : '') + '">';
2143
+
2144
+ if (alleles.length === 0) {
2145
+ html += '<div class="gene-empty">No genes in this layer</div>';
2146
+ } else {
2147
+ for (const allele of alleles) {
2148
+ const fitness = allele.fitness || 0;
2149
+ const fitnessColor = fitness >= 0.7 ? 'var(--green)' : fitness >= 0.4 ? 'var(--amber)' : 'var(--red)';
2150
+ const statusClass = allele.status === 'active' ? 'active' : 'retired';
2151
+
2152
+ html += '<div class="gene-card">';
2153
+ html += '<div class="gene-card-top">';
2154
+ html += '<span class="gene-name">' + escapeHtml(allele.gene || 'unknown') + '</span>';
2155
+ html += '<span class="gene-status ' + statusClass + '">' + (allele.status || 'active') + '</span>';
2156
+ html += '</div>';
2157
+ html += '<div class="gene-variant">' + escapeHtml((allele.variant || '').slice(0, 30)) + '</div>';
2158
+ html += '<div class="gene-fitness-row">';
2159
+ html += '<div class="gene-fitness-bar"><div class="gene-fitness-fill" style="width:' + (fitness * 100) + '%;background:' + fitnessColor + '"></div></div>';
2160
+ html += '<span class="gene-fitness-value" style="color:' + fitnessColor + '">' + fitness.toFixed(2) + '</span>';
2161
+ html += '</div>';
2162
+
2163
+ // Meta info
2164
+ const metaParts = [];
2165
+ if (allele.generation !== undefined) metaParts.push('Gen ' + allele.generation);
2166
+ if (allele.sampleCount) metaParts.push(allele.sampleCount + ' samples');
2167
+ if (allele.publishedToSwarm) metaParts.push('📡 Published');
2168
+ if (metaParts.length > 0) {
2169
+ html += '<div class="gene-meta">' + metaParts.join(' · ') + '</div>';
2170
+ }
2171
+
2172
+ html += '</div>';
2173
+ }
2174
+ }
2175
+
2176
+ html += '</div>';
2177
+ container.innerHTML = html;
2178
+ }
2179
+
2180
+ function toggleLayer(header) {
2181
+ header.classList.toggle('collapsed');
2182
+ const body = header.nextElementSibling;
2183
+ body.classList.toggle('collapsed');
2184
+ }
2185
+
2186
+ // ─── UI Helpers ──────────────────────────────────
2187
+ function updateStat(id, value, colorClass) {
2188
+ const el = document.getElementById(id);
2189
+ if (el) {
2190
+ el.textContent = value;
2191
+ if (colorClass) el.className = 'stat-value ' + colorClass;
2192
+ }
2193
+ }
2194
+
2195
+ function escapeHtml(str) {
2196
+ const div = document.createElement('div');
2197
+ div.textContent = str;
2198
+ return div.innerHTML;
2199
+ }
2200
+
2201
+ function addLog(icon, msg) {
2202
+ const log = document.getElementById('event-log');
2203
+ const elapsed = (Date.now() - state.startTime) / 1000;
2204
+ const mins = Math.floor(elapsed / 60).toString().padStart(2, '0');
2205
+ const secs = (elapsed % 60).toFixed(1).padStart(4, '0');
2206
+ const time = mins + ':' + secs;
2207
+
2208
+ const entry = document.createElement('div');
2209
+ entry.className = 'log-entry';
2210
+ entry.innerHTML = '<span class="log-time">' + time + '</span><span class="log-icon">' + icon + '</span><span class="log-msg">' + msg + '</span>';
2211
+
2212
+ log.prepend(entry);
2213
+
2214
+ // Keep max 100 entries
2215
+ while (log.children.length > 100) {
2216
+ log.removeChild(log.lastChild);
2217
+ }
2218
+ }
2219
+
2220
+ function updateGates(data) {
2221
+ if (!data.gates) return;
2222
+ const gateMap = { quality: 0.60, sandbox: 0.70, economic: 0.65, stability: 0.80 };
2223
+
2224
+ for (const [name, threshold] of Object.entries(gateMap)) {
2225
+ const gateData = data.gates[name];
2226
+ if (!gateData) continue;
2227
+
2228
+ const el = document.getElementById('gate-' + name);
2229
+ if (!el) continue;
2230
+
2231
+ el.className = 'gate ' + (gateData.passed ? 'pass' : 'fail');
2232
+ el.querySelector('.gate-score').textContent = gateData.score.toFixed(2);
2233
+ el.querySelector('.gate-bar-fill').style.width = (gateData.score * 100) + '%';
2234
+ }
2235
+
2236
+ // Constitutional gate
2237
+ if (data.gates.constitutional) {
2238
+ const el = document.getElementById('gate-constitutional');
2239
+ const g = data.gates.constitutional;
2240
+ el.className = 'gate ' + (g.passed ? 'pass' : 'fail');
2241
+ el.querySelector('.gate-score').textContent = g.score.toFixed(2);
2242
+ el.querySelector('.gate-bar-fill').style.width = (g.score * 100) + '%';
2243
+ }
2244
+ }
2245
+
2246
+ function mapDriftType(type) {
2247
+ const map = {
2248
+ 'quality-decline': 'quality',
2249
+ 'efficiency-decline': 'efficiency',
2250
+ 'cost-increase': 'cost',
2251
+ 'behavioral-shift': 'behavioral',
2252
+ 'temporal-degradation': 'temporal',
2253
+ 'safety-decline': 'safety',
2254
+ };
2255
+ return map[type] || null;
2256
+ }
2257
+
2258
+ function updateDrift(key, signal) {
2259
+ const el = document.getElementById('drift-' + key);
2260
+ if (!el) return;
2261
+ el.className = 'drift-item active';
2262
+ el.querySelector('.drift-value').textContent = signal.severity || 'Detected';
2263
+ }
2264
+
2265
+ // ─── Fitness Chart ───────────────────────────────
2266
+ const canvas = document.getElementById('fitness-canvas');
2267
+ const ctx = canvas.getContext('2d');
2268
+
2269
+ function resizeCanvas() {
2270
+ const rect = canvas.parentElement.getBoundingClientRect();
2271
+ const dpr = window.devicePixelRatio || 1;
2272
+ canvas.width = rect.width * dpr;
2273
+ canvas.height = rect.height * dpr;
2274
+ ctx.scale(dpr, dpr);
2275
+ canvas.style.width = rect.width + 'px';
2276
+ canvas.style.height = rect.height + 'px';
2277
+ }
2278
+
2279
+ resizeCanvas();
2280
+ window.addEventListener('resize', () => { resizeCanvas(); drawChart(); });
2281
+
2282
+ function drawChart() {
2283
+ const w = canvas.width / (window.devicePixelRatio || 1);
2284
+ const h = canvas.height / (window.devicePixelRatio || 1);
2285
+ const history = state.fitnessHistory;
2286
+
2287
+ ctx.clearRect(0, 0, w, h);
2288
+
2289
+ // Grid
2290
+ ctx.strokeStyle = 'rgba(48, 54, 61, 0.5)';
2291
+ ctx.lineWidth = 0.5;
2292
+ for (let i = 0; i <= 4; i++) {
2293
+ const y = h - (i / 4) * h;
2294
+ ctx.beginPath();
2295
+ ctx.moveTo(0, y);
2296
+ ctx.lineTo(w, y);
2297
+ ctx.stroke();
2298
+ }
2299
+
2300
+ // Y-axis labels
2301
+ ctx.fillStyle = '#8b949e';
2302
+ ctx.font = '10px ' + getComputedStyle(document.documentElement).getPropertyValue('--mono');
2303
+ for (let i = 0; i <= 4; i++) {
2304
+ const y = h - (i / 4) * h;
2305
+ ctx.fillText((i * 0.25).toFixed(2), 2, y - 2);
2306
+ }
2307
+
2308
+ if (history.length < 2) return;
2309
+
2310
+ const lines = [
2311
+ { key: 'quality', color: '#22c55e' },
2312
+ { key: 'safety', color: '#8b5cf6' },
2313
+ { key: 'economy', color: '#06b6d4' },
2314
+ { key: 'stability', color: '#f59e0b' },
2315
+ { key: 'satisfaction', color: '#ec4899' },
2316
+ ];
2317
+
2318
+ // Dimension lines
2319
+ for (const line of lines) {
2320
+ ctx.strokeStyle = line.color;
2321
+ ctx.lineWidth = 1.5;
2322
+ ctx.globalAlpha = 0.6;
2323
+ ctx.beginPath();
2324
+ for (let i = 0; i < history.length; i++) {
2325
+ const x = (i / (history.length - 1)) * w;
2326
+ const y = h - (history[i][line.key] || 0) * h;
2327
+ if (i === 0) ctx.moveTo(x, y);
2328
+ else ctx.lineTo(x, y);
2329
+ }
2330
+ ctx.stroke();
2331
+ }
2332
+
2333
+ // Composite line (white, thicker)
2334
+ ctx.strokeStyle = '#ffffff';
2335
+ ctx.lineWidth = 2.5;
2336
+ ctx.globalAlpha = 1;
2337
+ ctx.beginPath();
2338
+ for (let i = 0; i < history.length; i++) {
2339
+ const x = (i / (history.length - 1)) * w;
2340
+ const y = h - (history[i].composite || 0) * h;
2341
+ if (i === 0) ctx.moveTo(x, y);
2342
+ else ctx.lineTo(x, y);
2343
+ }
2344
+ ctx.stroke();
2345
+ ctx.globalAlpha = 1;
2346
+ }
2347
+
2348
+ // Initial draw
2349
+ drawChart();
2350
+
2351
+ // ─── Tab Switching ──────────────────────────────
2352
+ function switchTab(name) {
2353
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
2354
+ document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
2355
+ document.querySelector('.tab-btn[onclick*="' + name + '"]').classList.add('active');
2356
+ document.getElementById('tab-' + name).classList.add('active');
2357
+ if (name === 'marketplace') mpInit();
2358
+ }
2359
+
2360
+ function switchMpTab(name) {
2361
+ document.querySelectorAll('.mp-sub-btn').forEach(b => b.classList.remove('active'));
2362
+ document.querySelectorAll('.mp-sub-content').forEach(c => c.classList.remove('active'));
2363
+ document.querySelector('.mp-sub-btn[onclick*="' + name + '"]').classList.add('active');
2364
+ document.getElementById('mp-' + name).classList.add('active');
2365
+ if (name === 'seller') mpLoadSeller();
2366
+ if (name === 'purchases') mpLoadPurchases();
2367
+ }
2368
+
2369
+ // ─── Marketplace State ──────────────────────────
2370
+ const mpState = {
2371
+ initialized: false,
2372
+ searchOffset: 0,
2373
+ searchLimit: 12,
2374
+ searchTotal: 0,
2375
+ currentDetail: null,
2376
+ refundPurchaseId: null,
2377
+ };
2378
+
2379
+ function mpApi(path, options) {
2380
+ const sep = path.includes('?') ? '&' : '?';
2381
+ return fetch(location.origin + '/gsep/marketplace/' + path + sep + 'token=' + encodeURIComponent(token), options || {})
2382
+ .then(function(r) {
2383
+ if (!r.ok) return r.json().then(function(b) { throw new Error(b.error || 'HTTP ' + r.status); });
2384
+ return r.json();
2385
+ });
2386
+ }
2387
+
2388
+ function mpInit() {
2389
+ if (mpState.initialized) return;
2390
+ mpState.initialized = true;
2391
+ mpSearch();
2392
+ }
2393
+
2394
+ // ─── Browse / Search ────────────────────────────
2395
+ function mpSearch(offset) {
2396
+ mpState.searchOffset = offset || 0;
2397
+ var q = document.getElementById('mp-search-q').value;
2398
+ var type = document.getElementById('mp-search-type').value;
2399
+ var sort = document.getElementById('mp-search-sort').value;
2400
+
2401
+ var params = 'search?limit=' + mpState.searchLimit + '&offset=' + mpState.searchOffset;
2402
+ if (q) params += '&q=' + encodeURIComponent(q);
2403
+ if (type) params += '&type=' + encodeURIComponent(type);
2404
+ if (sort) params += '&sortBy=' + encodeURIComponent(sort);
2405
+
2406
+ document.getElementById('mp-results').innerHTML = '<div class="mp-empty">Searching...</div>';
2407
+
2408
+ mpApi(params).then(function(data) {
2409
+ mpState.searchTotal = data.total || 0;
2410
+ renderMpResults(data.genes || []);
2411
+ renderMpPagination();
2412
+ }).catch(function(err) {
2413
+ document.getElementById('mp-results').innerHTML = '<div class="mp-empty">Error: ' + escapeHtml(err.message) + '</div>';
2414
+ document.getElementById('mp-pagination').innerHTML = '';
2415
+ });
2416
+ }
2417
+
2418
+ function renderMpResults(genes) {
2419
+ var container = document.getElementById('mp-results');
2420
+ if (genes.length === 0) {
2421
+ container.innerHTML = '<div class="mp-empty">No genes found. Try different search terms.</div>';
2422
+ return;
2423
+ }
2424
+ var html = '';
2425
+ for (var i = 0; i < genes.length; i++) {
2426
+ var g = genes[i];
2427
+ var price = g.price_cents !== undefined ? '$' + (g.price_cents / 100).toFixed(2) : 'Free';
2428
+ html += '<div class="mp-card" onclick="mpShowDetail(\'' + escapeHtml(g.id || g.marketplace_id || '') + '\')">';
2429
+ html += '<div class="mp-card-name">' + escapeHtml(g.name || 'Unnamed') + '</div>';
2430
+ html += '<div class="mp-card-desc">' + escapeHtml(g.description || '') + '</div>';
2431
+ html += '<div class="mp-card-meta">';
2432
+ html += '<span class="mp-badge type">' + escapeHtml(g.type || g.gene_type || '?') + '</span>';
2433
+ html += '<span class="mp-badge fitness">' + ((g.overall_fitness || g.fitness || 0) * 1).toFixed(2) + '</span>';
2434
+ html += '<span class="mp-badge adoptions">' + (g.adoption_count || 0) + ' adopted</span>';
2435
+ if (g.price_cents > 0) html += '<span class="mp-badge price">' + price + '</span>';
2436
+ html += '</div></div>';
2437
+ }
2438
+ container.innerHTML = html;
2439
+ }
2440
+
2441
+ function renderMpPagination() {
2442
+ var total = mpState.searchTotal;
2443
+ var offset = mpState.searchOffset;
2444
+ var limit = mpState.searchLimit;
2445
+ var container = document.getElementById('mp-pagination');
2446
+
2447
+ if (total <= limit) { container.innerHTML = ''; return; }
2448
+
2449
+ var html = '<button ' + (offset <= 0 ? 'disabled' : 'onclick="mpSearch(' + (offset - limit) + ')"') + '>&laquo; Prev</button>';
2450
+ html += '<span style="padding:6px 12px;color:var(--text-muted);font-size:12px">' + (offset + 1) + '–' + Math.min(offset + limit, total) + ' of ' + total + '</span>';
2451
+ html += '<button ' + (offset + limit >= total ? 'disabled' : 'onclick="mpSearch(' + (offset + limit) + ')"') + '>Next &raquo;</button>';
2452
+ container.innerHTML = html;
2453
+ }
2454
+
2455
+ // ─── Gene Detail ────────────────────────────────
2456
+ function mpShowDetail(id) {
2457
+ document.getElementById('mp-browse').classList.remove('active');
2458
+ document.getElementById('mp-detail-view').classList.add('active');
2459
+ document.getElementById('mp-detail-content').innerHTML = '<div class="mp-empty">Loading...</div>';
2460
+
2461
+ mpApi('genes/' + encodeURIComponent(id)).then(function(g) {
2462
+ mpState.currentDetail = g;
2463
+ var price = g.price_cents !== undefined ? '$' + (g.price_cents / 100).toFixed(2) : 'Free';
2464
+ var html = '<div class="mp-detail">';
2465
+ html += '<div class="mp-detail-header">';
2466
+ html += '<div><h2>' + escapeHtml(g.name || 'Gene') + '</h2>';
2467
+ html += '<span class="mp-badge type">' + escapeHtml(g.type || g.gene_type || '?') + '</span> ';
2468
+ html += '<span class="mp-badge fitness">' + ((g.overall_fitness || 0) * 1).toFixed(2) + '</span>';
2469
+ html += '</div>';
2470
+ html += '<div style="text-align:right">';
2471
+ if (g.price_cents > 0) {
2472
+ html += '<div style="font-size:24px;font-weight:700;color:var(--amber);margin-bottom:8px">' + price + '</div>';
2473
+ html += '<button class="mp-action-btn primary" onclick="mpBuy(\'' + escapeHtml(g.id) + '\')">Buy</button>';
2474
+ } else {
2475
+ html += '<button class="mp-action-btn primary" onclick="mpAdopt(\'' + escapeHtml(g.id) + '\')">Adopt Free Gene</button>';
2476
+ }
2477
+ html += '</div></div>';
2478
+ html += '<div class="mp-detail-desc">' + escapeHtml(g.description || '') + '</div>';
2479
+
2480
+ // Fitness metrics
2481
+ html += '<div class="mp-fitness-grid">';
2482
+ var metrics = [
2483
+ ['Overall', g.overall_fitness],
2484
+ ['Task Success', g.task_success],
2485
+ ['Safety', g.safety_score],
2486
+ ['Efficiency', g.efficiency_score],
2487
+ ['Consistency', g.consistency_score],
2488
+ ['Satisfaction', g.user_satisfaction],
2489
+ ];
2490
+ for (var m = 0; m < metrics.length; m++) {
2491
+ var val = metrics[m][1];
2492
+ html += '<div class="mp-fitness-item"><div class="label">' + metrics[m][0] + '</div>';
2493
+ html += '<div class="value">' + (val !== undefined ? (val * 1).toFixed(2) : '—') + '</div></div>';
2494
+ }
2495
+ html += '</div>';
2496
+
2497
+ // Meta info
2498
+ html += '<div style="font-size:12px;color:var(--text-muted)">';
2499
+ if (g.domain) html += 'Domain: ' + escapeHtml(g.domain) + ' · ';
2500
+ html += 'Adoptions: ' + (g.adoption_count || 0);
2501
+ if (g.generation !== undefined) html += ' · Generation: ' + g.generation;
2502
+ html += '</div>';
2503
+
2504
+ html += '</div>';
2505
+ document.getElementById('mp-detail-content').innerHTML = html;
2506
+ }).catch(function(err) {
2507
+ document.getElementById('mp-detail-content').innerHTML = '<div class="mp-empty">Error: ' + escapeHtml(err.message) + '</div>';
2508
+ });
2509
+ }
2510
+
2511
+ function mpBackToBrowse() {
2512
+ document.getElementById('mp-detail-view').classList.remove('active');
2513
+ document.getElementById('mp-browse').classList.add('active');
2514
+ }
2515
+
2516
+ function mpBuy(geneListingId) {
2517
+ if (!confirm('Purchase this gene?')) return;
2518
+ mpApi('purchases', {
2519
+ method: 'POST',
2520
+ headers: { 'Content-Type': 'application/json' },
2521
+ body: JSON.stringify({ geneListingId: geneListingId }),
2522
+ }).then(function(result) {
2523
+ alert('Purchase successful! Go to My Purchases to adopt.');
2524
+ switchMpTab('purchases');
2525
+ }).catch(function(err) {
2526
+ alert('Purchase failed: ' + err.message);
2527
+ });
2528
+ }
2529
+
2530
+ function mpAdopt(marketplaceGeneId) {
2531
+ if (!confirm('Adopt this gene into your GSEP?')) return;
2532
+ mpApi('adopt/' + encodeURIComponent(marketplaceGeneId), {
2533
+ method: 'POST',
2534
+ headers: { 'Content-Type': 'application/json' },
2535
+ body: '{}',
2536
+ }).then(function(gene) {
2537
+ alert('Gene adopted! It is now in your local Gene Bank.');
2538
+ fetchGenes();
2539
+ }).catch(function(err) {
2540
+ alert('Adoption failed: ' + err.message);
2541
+ });
2542
+ }
2543
+
2544
+ // ─── Seller Panel ───────────────────────────────
2545
+ function mpLoadSeller() {
2546
+ var statusEl = document.getElementById('mp-seller-status');
2547
+ var earningsEl = document.getElementById('mp-seller-earnings');
2548
+ var myGenesEl = document.getElementById('mp-my-genes');
2549
+ var publishEl = document.getElementById('mp-publish-eligible');
2550
+
2551
+ statusEl.innerHTML = '<div class="mp-empty">Loading seller info...</div>';
2552
+
2553
+ // Load seller status
2554
+ mpApi('seller/status').then(function(s) {
2555
+ var html = '<div class="mp-stat-card" style="margin-bottom:16px;text-align:left;padding:12px 16px">';
2556
+ html += '<span style="font-weight:600">Stripe Connect:</span> ';
2557
+ if (s.status === 'active' || s.charges_enabled) {
2558
+ html += '<span style="color:var(--green)">Active</span>';
2559
+ } else if (s.onboarding_url || s.status === 'pending') {
2560
+ html += '<span style="color:var(--amber)">Pending</span>';
2561
+ } else {
2562
+ html += '<span style="color:var(--text-muted)">Not Connected</span> ';
2563
+ html += '<button class="mp-action-btn primary" style="padding:4px 12px;font-size:12px;margin-left:8px" onclick="mpOnboard()">Connect Stripe</button>';
2564
+ }
2565
+ html += '</div>';
2566
+ statusEl.innerHTML = html;
2567
+ }).catch(function() {
2568
+ statusEl.innerHTML = '<div class="mp-stat-card" style="text-align:left;padding:12px 16px">';
2569
+ statusEl.innerHTML += '<span style="color:var(--text-muted)">Not connected to Marketplace</span> ';
2570
+ statusEl.innerHTML += '<button class="mp-action-btn primary" style="padding:4px 12px;font-size:12px;margin-left:8px" onclick="mpOnboard()">Connect Stripe</button>';
2571
+ statusEl.innerHTML += '</div>';
2572
+ });
2573
+
2574
+ // Load earnings
2575
+ mpApi('seller/earnings').then(function(e) {
2576
+ var html = '';
2577
+ html += '<div class="mp-stat-card"><div class="value" style="color:var(--green)">$' + ((e.total_earned || 0) / 100).toFixed(2) + '</div><div class="label">Total Earned</div></div>';
2578
+ html += '<div class="mp-stat-card"><div class="value" style="color:var(--amber)">$' + ((e.pending || 0) / 100).toFixed(2) + '</div><div class="label">Pending</div></div>';
2579
+ html += '<div class="mp-stat-card"><div class="value" style="color:var(--cyan)">' + (e.total_sales || 0) + '</div><div class="label">Total Sales</div></div>';
2580
+ earningsEl.innerHTML = html;
2581
+ }).catch(function() {
2582
+ earningsEl.innerHTML = '';
2583
+ });
2584
+
2585
+ // Load my published genes
2586
+ mpApi('my-genes').then(function(genes) {
2587
+ if (!genes || genes.length === 0) {
2588
+ myGenesEl.innerHTML = '<div class="mp-empty">No published genes yet.</div>';
2589
+ return;
2590
+ }
2591
+ var html = '';
2592
+ for (var i = 0; i < genes.length; i++) {
2593
+ var g = genes[i];
2594
+ html += '<div class="mp-card">';
2595
+ html += '<div class="mp-card-name">' + escapeHtml(g.name || 'Gene') + '</div>';
2596
+ html += '<div class="mp-card-meta">';
2597
+ html += '<span class="mp-badge type">' + escapeHtml(g.type || g.gene_type || '?') + '</span>';
2598
+ html += '<span class="mp-badge fitness">' + ((g.overall_fitness || 0) * 1).toFixed(2) + '</span>';
2599
+ html += '<span class="mp-badge adoptions">' + (g.adoption_count || 0) + ' adopted</span>';
2600
+ html += '</div></div>';
2601
+ }
2602
+ myGenesEl.innerHTML = html;
2603
+ }).catch(function() {
2604
+ myGenesEl.innerHTML = '<div class="mp-empty">Could not load published genes.</div>';
2605
+ });
2606
+
2607
+ // Show publish-eligible genes from sidebar data
2608
+ renderPublishEligible(publishEl);
2609
+ }
2610
+
2611
+ function renderPublishEligible(container) {
2612
+ var bank = state.genes.geneBank;
2613
+ if (!bank || !bank.sellable || bank.sellable.length === 0) {
2614
+ container.innerHTML = '<div class="mp-empty">No genes eligible for publishing (fitness &ge; 0.80 required).</div>';
2615
+ return;
2616
+ }
2617
+ var html = '';
2618
+ for (var i = 0; i < bank.sellable.length; i++) {
2619
+ var g = bank.sellable[i];
2620
+ html += '<div class="mp-card">';
2621
+ html += '<div class="mp-card-name">' + escapeHtml(g.gene || 'Unknown') + '</div>';
2622
+ html += '<div class="mp-card-meta">';
2623
+ html += '<span class="mp-badge fitness">' + (g.fitness || 0).toFixed(2) + '</span>';
2624
+ html += '<button class="mp-action-btn primary" style="padding:4px 12px;font-size:11px;margin-left:auto" onclick="mpPublish(\'' + escapeHtml(g.gene || '') + '\')">Publish</button>';
2625
+ html += '</div></div>';
2626
+ }
2627
+ container.innerHTML = html;
2628
+ }
2629
+
2630
+ function mpPublish(geneId) {
2631
+ if (!confirm('Publish gene "' + geneId + '" to the Marketplace?')) return;
2632
+ mpApi('publish/' + encodeURIComponent(geneId), {
2633
+ method: 'POST',
2634
+ headers: { 'Content-Type': 'application/json' },
2635
+ body: '{}',
2636
+ }).then(function(result) {
2637
+ if (result.success) {
2638
+ alert('Gene published successfully!');
2639
+ mpLoadSeller();
2640
+ fetchGenes();
2641
+ } else {
2642
+ alert('Publish failed: ' + (result.error || 'Unknown error'));
2643
+ }
2644
+ }).catch(function(err) {
2645
+ alert('Publish failed: ' + err.message);
2646
+ });
2647
+ }
2648
+
2649
+ function mpOnboard() {
2650
+ mpApi('seller/onboard', {
2651
+ method: 'POST',
2652
+ headers: { 'Content-Type': 'application/json' },
2653
+ body: JSON.stringify({ country: 'US' }),
2654
+ }).then(function(result) {
2655
+ if (result.onboarding_url || result.url) {
2656
+ window.open(result.onboarding_url || result.url, '_blank');
2657
+ } else {
2658
+ alert('Onboarding initiated. Check your email for Stripe instructions.');
2659
+ }
2660
+ mpLoadSeller();
2661
+ }).catch(function(err) {
2662
+ alert('Onboarding failed: ' + err.message);
2663
+ });
2664
+ }
2665
+
2666
+ // ─── Purchases ──────────────────────────────────
2667
+ function mpLoadPurchases() {
2668
+ var container = document.getElementById('mp-purchases-list');
2669
+ container.innerHTML = '<div class="mp-empty">Loading purchases...</div>';
2670
+
2671
+ mpApi('purchases').then(function(purchases) {
2672
+ if (!purchases || purchases.length === 0) {
2673
+ container.innerHTML = '<div class="mp-empty">No purchases yet. Browse the marketplace to find genes!</div>';
2674
+ return;
2675
+ }
2676
+ var html = '<table class="mp-table"><thead><tr>';
2677
+ html += '<th>Gene</th><th>Status</th><th>Price</th><th>Date</th><th>Actions</th>';
2678
+ html += '</tr></thead><tbody>';
2679
+ for (var i = 0; i < purchases.length; i++) {
2680
+ var p = purchases[i];
2681
+ html += '<tr>';
2682
+ html += '<td>' + escapeHtml(p.gene_name || p.gene_listing_id || '?') + '</td>';
2683
+ html += '<td>' + escapeHtml(p.status || '?') + '</td>';
2684
+ html += '<td>$' + ((p.amount_cents || 0) / 100).toFixed(2) + '</td>';
2685
+ html += '<td>' + (p.created_at ? new Date(p.created_at).toLocaleDateString() : '—') + '</td>';
2686
+ html += '<td>';
2687
+ if (p.status === 'completed') {
2688
+ html += '<button class="mp-action-btn primary" style="padding:3px 10px;font-size:11px" onclick="mpAdopt(\'' + escapeHtml(p.gene_listing_id) + '\')">Adopt</button> ';
2689
+ html += '<button class="mp-action-btn secondary" style="padding:3px 10px;font-size:11px" onclick="openRefundModal(\'' + escapeHtml(p.id) + '\')">Refund</button>';
2690
+ }
2691
+ html += '</td></tr>';
2692
+ }
2693
+ html += '</tbody></table>';
2694
+ container.innerHTML = html;
2695
+ }).catch(function(err) {
2696
+ container.innerHTML = '<div class="mp-empty">Error: ' + escapeHtml(err.message) + '</div>';
2697
+ });
2698
+ }
2699
+
2700
+ // ─── Refund Modal ───────────────────────────────
2701
+ function openRefundModal(purchaseId) {
2702
+ mpState.refundPurchaseId = purchaseId;
2703
+ document.getElementById('mp-refund-reason').value = '';
2704
+ document.getElementById('mp-refund-modal').classList.add('active');
2705
+ }
2706
+
2707
+ function closeRefundModal() {
2708
+ document.getElementById('mp-refund-modal').classList.remove('active');
2709
+ mpState.refundPurchaseId = null;
2710
+ }
2711
+
2712
+ function submitRefund() {
2713
+ var reason = document.getElementById('mp-refund-reason').value.trim();
2714
+ if (!reason) { alert('Please provide a reason.'); return; }
2715
+ if (!mpState.refundPurchaseId) return;
2716
+
2717
+ mpApi('purchases/refund', {
2718
+ method: 'POST',
2719
+ headers: { 'Content-Type': 'application/json' },
2720
+ body: JSON.stringify({ purchaseId: mpState.refundPurchaseId, reason: reason }),
2721
+ }).then(function() {
2722
+ alert('Refund requested.');
2723
+ closeRefundModal();
2724
+ mpLoadPurchases();
2725
+ }).catch(function(err) {
2726
+ alert('Refund failed: ' + err.message);
2727
+ });
2728
+ }
2729
+ </script>
2730
+ </body>
2731
+ </html>