{"context":{"domain":"cict-ds"},"layers":{"ApplicationLayer":{"reference":"ApplicationLayer","availableInstances":[{"reference":"ComponentsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"SystemsDataSet","params":{"filter":{"domain":"data2"},"pagination":{"size":1},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSource"}}}},{"reference":"ExternalComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"SystemsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5e3b796d6228227ca637d0f4","name":"data2","domain":"data2","organization":"62292184611f480b3191c37c","visibility":"public","updatedAt":"2022-03-09T02:01:26.213Z"}]}},{"reference":"OrganizationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"DataComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@DataSet","reference":"OneFileComponentInstancesDataSet","params":{"pagination":{"size":1},"master":{"dataset":{"component":"data2-core-types@ProcessReference","params":{"reference":"SystemsDataSet"}},"localField":"system","masterField":"_id"},"datasource":{"component":"data2-core-types@ProcessReference","params":{"reference":"FileComponentInstancesDataSource"}}}},{"reference":"ApplicationsDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ServiceComponentInstancesDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@Data2TableDataSource","reference":"FileComponentInstancesDataSource","params":{"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}}]}},{"component":"data2-core@MemoryDataSource","reference":"EmptyDataSource","params":{"initialData":[]}},{"reference":"ComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"UserComponentsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"DataComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ServiceComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ExternalComponentInstancesDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}},{"reference":"ApplicationsDataSet","component":"data2-core@DataSet","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"EmptyDataSource"}}}}],"processesParams":{}},"HostApplicationLayer":{"reference":"HostApplicationLayer","availableInstances":[{"_id":"5f15ff190528b92601890f71","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"SystemsTableDataSource"}},"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"domain"}}},"pk":"_id","fields":[{"component":"data2-core-types@FieldReference","params":{"key":"_id"}},{"component":"data2-core-types@FieldReference","params":{"key":"name"}},{"component":"data2-core-types@FieldReference","params":{"key":"domain"}},{"component":"data2-core-types@FieldReference","params":{"key":"visibility"}},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization._id"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.name"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.billingStatus"}}},"break":true},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"organization.publicTrial.active"}}},"break":true}],"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"staticPagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}},"policies":[{"component":"data2-core-auth@Policy","params":{"allow":{"params":{"actions":[{"params":{"components":[{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"_id"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"domain"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"},{"params":{"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$params.query"}}}},"field":{"component":"data2-core-primitives@String","params":{"content":"customDomains"}},"fieldType":"string"},"component":"data2@7KkQq0vK4a7D2gg6IVJT7"}]},"component":"data2-core-logical-operators@Or"}]},"component":"data2-core-flow@Series"}}}]},"reference":"SystemsDataSet","updatedAt":"2025-11-05T19:23:47.135Z","createdAt":"2020-07-20T20:31:21.838Z"},{"_id":"5f1601ce0528b92601890f7c","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":1000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"sort":{"params.package":1,"params.component":1},"pk":"_id","autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"staticFilter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}},"filter":{"system":{"exists":{"params":{"content":false},"component":"data2-core-primitives@Boolean"}}}},"reference":"ComponentsDataSet","updatedAt":"2025-11-05T19:37:01.935Z","createdAt":"2020-07-20T20:42:54.898Z"},{"_id":"5f9041dbc8da12be8b94aea4","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":2000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentsTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"UserComponentsDataSet","updatedAt":"2025-11-06T21:06:58.095Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"65d8c4fabc9dbb8f94de8d53","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ExternalComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ExternalComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:23.742Z","createdAt":"2024-02-23T16:17:01.816Z"},{"reference":"SystemsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a601c3c3a42b34509a601cf","name":"app","domain":"cict-ds","visibility":"private","updatedAt":"2026-07-22T01:26:25.160Z","createdAt":"2026-07-22T01:26:20.475Z"}]}},{"reference":"ComponentsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"5f52a278a575399a45d239b4","component":"data2-core-types@Component","params":{"package":"data2-core-primitives","component":"Router","interactions":{"component":"data2-core-types@FieldSet","params":{"fields":{"goBack":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.goBack","args":[]}}}},"icon":{"component":"data2-core-primitives@String","params":{"content":"arrow-left"}},"visibility":"public"}},"goTo":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"component":"data2-core-types@Reference","params":{"reference":"url"}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}}}]}},"parameters":[{"component":"data2-core-primitives@String","params":{"content":"url"}},{"component":"data2-core-primitives@String","params":{"content":"preserveQuery"}}]},"break":true},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Path to route"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Path to redirect user"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Boolean"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Preserve Query"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Preserve query string parameters  preserve query parameters on navigating."}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"}]}},"open":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@This","params":{}},"prop":"history.push","args":[{"pathname":{"component":"data2-core-types@Reference","params":{"reference":"value"}}}]}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"share"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}]}},"close":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-types@Function"}},"definition":{"component":"data2-core-functions@Function","params":{"body":{"preventEvaluation":true,"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":"close"}},"parameters":["value"]}},"icon":{"component":"data2-core-primitives@String","params":{"content":"times"}},"visibility":"public","arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Link to open"}},"description":{"component":"data2-core-primitives@String","params":{"content":""}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2-core-types@ComponentParameter"},{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Target"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Where to open the link"}},"visibility":"public","required":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"oneOf":[{"params":{"content":"_blank"},"component":"data2-core-primitives@String"},{"params":{"content":"_self"},"component":"data2-core-primitives@String"},{"params":{"content":"_parent"},"component":"data2-core-primitives@String"},{"params":{"content":"_top"},"component":"data2-core-primitives@String"}]},"component":"data2-core-types@ComponentParameter"}],"description":{"component":"data2-core-primitives@String","params":{"content":"Closes a window that was opened via router.open."}}}}}}}}},{"_id":"620d28658271aa4129919d89","component":"data2-core-types@Component","params":{"package":"data2-interface-services","component":"DeviceController","interactions":{"params":{"fields":{"postMessage":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@String","title":{"component":"data2-core-primitives@String","params":{"content":"data"}}}}]},"component":"data2-core-types@ComponentParameter"},"print":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"zoom":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Number"}},"title":{"component":"data2-core-primitives@String","params":{"content":"zoom"}},"visibility":"public"}}},"visibility":"public"},"component":"data2-core-types@ComponentParameter"}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"share":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}}},"component":"data2-core-types@ComponentParameter"},"clipboard.copy":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"clipboard.paste":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}}}}]},"component":"data2-core-types@ComponentParameter"},"download":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}]},"component":"data2-core-types@ComponentParameter"},"downloadUrl":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Url"}}}}]},"component":"data2-core-types@ComponentParameter"},"promptFiles":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"parameters":{"params":{"fields":{"multiple":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Multiple"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Whether or not to allow selecting multiple files"}},"component":"data2-core-primitives@Boolean"}},"acceptedFormats":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Accepted Formats"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Accepted file formats"}},"component":"data2-core-primitives@String","list":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}},"timeout":{"component":"data2-core-types@ComponentParameter","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Timeout"}},"description":{"component":"data2-core-primitives@String","params":{"content":"Max milliseconds to wait for file upload"}},"component":"data2-core-primitives@Number"}}}},"component":"data2-core-types@FieldSet"}}}],"visibility":"public"},"component":"data2-core-types@ComponentParameter"},"meetingRecord":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","definition":{"params":{"actions":[{"params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"meeting"}},"script":{"component":"data2-core-primitives@String","params":{"content":"(function() {\n  let mediaRecorder;\n  let audioChunks = [];\n  let combinedStream;\n  let audioContext;\n  \n  window.meetingCapture = async function() {\n    try {\n      // Solicitar acesso ao áudio da guia (e vídeo, que será ignorado depois)\n      const tabStream = await navigator.mediaDevices.getDisplayMedia({ \n        video: true, // Necessário para alguns navegadores\n        audio: true\n      });\n      \n      // Remover a trilha de vídeo\n      const tabAudioTrack = tabStream.getAudioTracks()[0];\n      \n      // Solicitar acesso ao microfone\n      const micStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n      const micAudioTrack = micStream.getAudioTracks()[0];\n      \n      // Criar um novo MediaStream com ambas as trilhas de áudio\n      combinedStream = new MediaStream([tabAudioTrack, micAudioTrack]);\n      \n      // Criar um contexto de áudio para misturar os streams\n      audioContext = new AudioContext();\n      const destination = audioContext.createMediaStreamDestination();\n      \n      // Conectar a trilha de áudio da guia\n      const tabSource = audioContext.createMediaStreamSource(new MediaStream([tabAudioTrack]));\n      tabSource.connect(destination);\n      \n      // Conectar a trilha de áudio do microfone\n      const micSource = audioContext.createMediaStreamSource(new MediaStream([micAudioTrack]));\n      micSource.connect(destination);\n      \n      // Iniciar gravação com o stream misturado\n      mediaRecorder = new MediaRecorder(destination.stream);\n      audioChunks = [];\n      mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);\n      mediaRecorder.start();\n      \n      console.log('Gravação iniciada. Chame meetingCaptureStop() para finalizar.');\n    } catch (error) {\n      console.error('Erro ao iniciar a captura:', error);\n    }\n  };\n  \n  window.meetingCaptureStop = function() {\n    return new Promise((resolve, reject) => {\n      if (!mediaRecorder) {\n        reject('Nenhuma gravação em andamento.');\n        return;\n      }\n      \n      mediaRecorder.onstop = () => {\n        const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });\n        combinedStream.getTracks().forEach(track => track.stop());\n        audioContext.close();\n        \n        resolve(audioBlob);\n        console.log('Gravação finalizada. O arquivo de áudio está pronto para upload.');\n      };\n      \n      mediaRecorder.stop();\n    });\n  };\n})();"}}},"component":"data2-core-web@Script"},{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCapture"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"meetingRecordStop":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"public","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Filename"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Content"}}}},{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Object"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Options"}},"parameters":{"params":{"fields":{"type":{"component":"data2-core-types@ComponentParameter","params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@String"}},"title":{"component":"data2-core-primitives@String","params":{"content":"Mimetype"}}}}}},"component":"data2-core-types@FieldSet"}}}],"definition":{"params":{"actions":[{"component":"data2-core-types@Getter","params":{"module":{"params":{},"component":"data2-core-web@Window"},"prop":{"component":"data2-core-primitives@String","params":{"content":"meetingCaptureStop"}}}}]},"component":"data2-core-flow@Series"}},"component":"data2-core-types@ComponentParameter"},"notify":{"params":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core-primitives@Function"}},"visibility":"private","arguments":[{"component":"data2-core-types@ComponentParameter","params":{"component":"data2-core-primitives@Object","parameters":{"title":{"params":{"component":"data2-core-primitives@String","index":{"component":"data2-core-primitives@Number","params":{"content":0}},"title":{"component":"data2-core-primitives@String","params":{"content":"title"}}},"component":"data2-core-types@ComponentParameter"},"sound":{"params":{"component":"data2-core-types@FileComponentUrlReference","index":{"component":"data2-core-primitives@Number","params":{"content":1}},"title":{"component":"data2-core-primitives@String","params":{"content":"sound"}}},"component":"data2-core-types@ComponentParameter"}},"flat":{"component":"data2-core-primitives@Boolean","params":{"content":true}}}}]},"component":"data2-core-types@ComponentParameter"}}},"component":"data2-core-types@FieldSet"}}},{"_id":"6a604520d774752eb7db51e3","component":"data2-core-types@Component","system":"6a601c3c3a42b34509a601cf","params":{"extends":"data2-core-primitives@Block","component":"CommandCenter","package":"cict-ds","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const T_DISP=4000,T_ENROUTE=49000,T_SCENE=89000;\nconst SC={Intake:\"#30b0c7\",Reported:\"#ff3b30\",Dispatched:\"#ff9500\",EnRoute:\"#0a84ff\",OnScene:\"#5e5ce6\",Resolved:\"#34c759\"};\nconst SL={Intake:[\"Intake\",\"接线中\"],Reported:[\"Reported\",\"已报告\"],Dispatched:[\"Dispatched\",\"已派遣\"],EnRoute:[\"En route\",\"前往中\"],OnScene:[\"On scene\",\"已到场\"],Resolved:[\"Resolved\",\"已解决\"]};\nconst PORD={Intake:0,Reported:0,Dispatched:1,EnRoute:2,OnScene:3,Resolved:4};\nconst EC={panic:\"#ff3b30\",fear:\"#ff9500\",distress:\"#5e5ce6\",anger:\"#c9302c\",confusion:\"#ffcc00\",calm:\"#34c759\"};\nconst LEAF={name:\"leaflet@1.9.4\",css:\"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css\"};\nconst ICE=[{urls:[\"stun:stun.l.google.com:19302\",\"stun:stun1.l.google.com:19302\"]},{urls:[\"turn:openrelay.metered.ca:80\",\"turn:openrelay.metered.ca:443\",\"turn:openrelay.metered.ca:443?transport=tcp\"],username:\"openrelayproject\",credential:\"openrelayproject\"},{urls:[\"turn:openrelay.metered.ca:80?transport=tcp\"],username:\"openrelayproject\",credential:\"openrelayproject\"}];\nconst CAMS=[{id:\"CAM-01\",name:\"Yangtze Bridge\",zh:\"长江大桥\",lat:30.5530,lng:114.2930,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-02\",name:\"Jiefang Ave\",zh:\"解放大道\",lat:30.5960,lng:114.2870,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"},{id:\"CAM-03\",name:\"Optics Valley Sq\",zh:\"光谷广场\",lat:30.5070,lng:114.4260,yt:\"Ksrleaxxxhw\",feed:\"New Orleans, Bourbon St\"},{id:\"CAM-04\",name:\"Wuhan Railway Stn\",zh:\"武汉站\",lat:30.6080,lng:114.4270,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-05\",name:\"Chu River Han St\",zh:\"楚河汉街\",lat:30.5540,lng:114.3480,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"},{id:\"CAM-06\",name:\"Yellow Crane Tower\",zh:\"黄鹤楼\",lat:30.5449,lng:114.3025,yt:\"Ksrleaxxxhw\",feed:\"New Orleans, Bourbon St\"},{id:\"CAM-07\",name:\"Hankou Bund\",zh:\"汉口江滩\",lat:30.5850,lng:114.3020,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-08\",name:\"East Lake\",zh:\"东湖\",lat:30.5560,lng:114.4180,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"}];\nfunction phaseOf(el){if(el<T_DISP)return \"Dispatched\";if(el<T_ENROUTE)return \"EnRoute\";if(el<T_SCENE)return \"OnScene\";return \"Resolved\";}\nfunction dstat(inc){if(inc.dispatchedAt&&inc.status!==\"Resolved\"){return phaseOf(Date.now()-inc.dispatchedAt);}return inc.status;}\nfunction hasXY(i){return i&&Number.isFinite(i.lat)&&Number.isFinite(i.lng)&&i.lat>10;}\nfunction ease(p){return p*p*(3-2*p);}\nfunction esc(s){return String(s==null?\"\":s).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\");}\nfunction un(r){return (r&&typeof r===\"object\"&&r.data!==undefined&&r.items===undefined&&r.ok===undefined&&r.cams===undefined)?r.data:r;}\nfunction dist(a,b){const dy=a[0]-b[0],dx=a[1]-b[1];return Math.sqrt(dy*dy+dx*dx);}\nfunction pointAlong(pts,p){if(!pts||pts.length===0)return null;if(pts.length===1)return pts[0];let total=0;const segs=[];for(let i=1;i<pts.length;i++){const d=dist(pts[i-1],pts[i]);segs.push(d);total+=d;}if(total<=0)return pts[0];const target=Math.max(0,Math.min(1,p))*total;let acc=0;for(let i=1;i<pts.length;i++){const d=segs[i-1];if(acc+d>=target){const f=d>0?(target-acc)/d:0;return [pts[i-1][0]+(pts[i][0]-pts[i-1][0])*f,pts[i-1][1]+(pts[i][1]-pts[i-1][1])*f];}acc+=d;}return pts[pts.length-1];}\nfunction unitP(el){if(el<=T_DISP)return 0;if(el>=T_ENROUTE)return 1;return (el-T_DISP)/(T_ENROUTE-T_DISP);}\nfunction maxUnits(inc){const sev=(inc.severity||\"\");const trapped=(inc.peopleTrapped||\"\").indexOf(\"Yes\")>=0;if(sev.indexOf(\"Large\")>=0||sev.indexOf(\"大\")>=0||trapped)return 3;return 2;}\nfunction gauge(e){const c=EC[e.emotion]||\"#8a8a8e\";const iv=Number(e.intensity)||0;const pct=Math.round(iv*100);const r=26,circ=2*Math.PI*r,off=circ*(1-iv);return `<svg width=\"62\" height=\"62\" viewBox=\"0 0 64 64\"><circle cx=\"32\" cy=\"32\" r=\"${r}\" fill=\"none\" stroke=\"rgba(120,120,128,.16)\" stroke-width=\"6\"/><circle cx=\"32\" cy=\"32\" r=\"${r}\" fill=\"none\" stroke=\"${c}\" stroke-width=\"6\" stroke-linecap=\"round\" stroke-dasharray=\"${circ.toFixed(1)}\" stroke-dashoffset=\"${off.toFixed(1)}\" transform=\"rotate(-90 32 32)\"/><text x=\"32\" y=\"31\" text-anchor=\"middle\" font-size=\"15\" font-weight=\"800\" fill=\"${c}\">${pct}</text><text x=\"32\" y=\"43\" text-anchor=\"middle\" font-size=\"7\" fill=\"#8a8a8e\">STRESS</text></svg>`;}\nfunction emoChart(ems){const W=260,H=54;if(!ems||!ems.length)return \"\";const n=ems.length;const pts=ems.map((e,i)=>{const x=n===1?W/2:(i/(n-1))*W;const y=H-4-(Number(e.intensity)||0)*(H-10);return [x,y];});const line=pts.map((p,i)=>(i?\"L\":\"M\")+p[0].toFixed(1)+\" \"+p[1].toFixed(1)).join(\" \");const area=`M0 ${H} `+pts.map(p=>`L${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(\" \")+` L${W} ${H} Z`;const dots=ems.map((e,i)=>{const c=EC[e.emotion]||\"#8a8a8e\";return `<circle cx=\"${pts[i][0].toFixed(1)}\" cy=\"${pts[i][1].toFixed(1)}\" r=\"3\" fill=\"${c}\" stroke=\"#fff\" stroke-width=\"1.5\"/>`;}).join(\"\");const last=EC[ems[n-1].emotion]||\"#8a8a8e\";return `<svg width=\"100%\" viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"none\" style=\"display:block\"><defs><linearGradient id=\"eg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"${last}\" stop-opacity=\"0.35\"/><stop offset=\"1\" stop-color=\"${last}\" stop-opacity=\"0.02\"/></linearGradient></defs><path d=\"${area}\" fill=\"url(#eg)\"/><path d=\"${line}\" fill=\"none\" stroke=\"${last}\" stroke-width=\"2\"/>${dots}</svg>`;}\nfunction fmtTime(t){if(!t)return \"—\";try{return new Date(t).toLocaleTimeString(\"en-GB\");}catch(e){return \"—\";}}\nfunction renderDetail(d2,sel){\n    const st=d2.state;const dl=st.dispLang||\"en\";const xl=st.xl||{};const pick=(m)=>{if(m.role===\"dispatcher\")return m.raw||m.translated||m.en||\"\";if(dl===\"zh\")return m.zh||m.en||m.raw||\"\";if(dl===\"en\")return m.en||m.raw||\"\";const k=(m.t||\"\")+\"|\"+dl;return xl[k]||m.en||m.raw||\"\";};const ds=dstat(sel);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";\n    const ems=sel.emotions||[];const le=ems.length?ems[ems.length-1]:null;\n    const units=sel.units||[];const mx=maxUnits(sel);\n    const now=Date.now();\n    let emoBlock=\"\";\n    if(le){const elc=EC[le.emotion]||\"#8a8a8e\";emoBlock=`<div class=\"sec\"><div class=\"sechd\">Caller stress · 来电者情绪</div><div style=\"display:flex;align-items:center;gap:12px\"><div style=\"text-align:center\">${gauge(le)}<div style=\"font-size:11px;font-weight:700;color:${elc}\">${esc(le.label_en)} ${esc(le.label_zh)}</div></div><div style=\"flex:1;min-width:0\">${emoChart(ems)}</div></div></div>`;}\n    let unitBlock=\"\";\n    if(units.length){unitBlock=`<div class=\"sec\"><div class=\"sechd\">Assigned units · 出动队伍 (${units.length})</div>${units.map(u=>{const el=now-(u.dispatchedAt||now);const ph=phaseOf(el);const usl=SL[ph]||[\"\",\"\"];const uc=SC[ph]||\"#0a84ff\";const eta=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"Dispatched\"?\"leaving 出发\":(ph===\"OnScene\"?\"on scene 到场\":\"resolved 完成\"));return `<div class=\"unit\"><div class=\"unitico\">🚒</div><div class=\"unitnm\">${esc(u.name||\"Unit\")}<br><span style=\"font-size:10.5px;color:#8a8a8e;font-weight:500\">${esc(u.role===\"backup\"?\"Reinforcement 增援\":\"Primary 主力\")}</span></div><div style=\"text-align:right\"><span class=\"unitst\" style=\"background:${uc}22;color:${uc}\">${usl[0]}</span><div style=\"font-size:10px;color:#8a8a8e;margin-top:2px\">${eta}</div></div></div>`;}).join(\"\")}</div>`;}\n    let vidBlock=\"\";\n    if(sel.liveVideo){vidBlock=`<div class=\"sec\"><div class=\"sechd\">📹 Live video · 现场直播 <span class=\"vtag\">● LIVE</span></div>${st.watching===sel._id?`<video class=\"livevid\" autoplay playsinline muted></video><div style=\"font-size:10.5px;color:#8a8a8e;margin-top:5px\">${esc(st.vidStatus||\"Connecting… 连接中\")}</div><button class=\"wdbgbtn\">🐞 ${st.wdbgOn?\"hide debug\":\"debug\"}</button>${st.wdbgOn?`<pre class=\"vdbg\">status: ${esc(st.vidStatus||\"-\")}\\ntracks: ${esc(\"\"+((st.wstats||{}).tracks!=null?(st.wstats||{}).tracks:\"-\"))}  sig: ${esc((st.wstats||{}).sig||\"-\")}\\nice: ${esc((st.wstats||{}).ice||\"-\")}  conn: ${esc((st.wstats||{}).conn||\"-\")}\\nvideo: ${esc((st.wstats||{}).video||\"0x0\")}\\ncandidates: ${esc((st.wstats||{}).cand||\"(none yet - TURN down?)\")}\\n---\\n${esc((st.wlog||[]).join(\"\\n\"))}</pre>`:\"\"}`:`<button class=\"btn red watch\" data-id=\"${sel._id}\">▶ Watch live · 观看直播</button>`}</div>`;}\n    const cams=CAMS.map(cm=>({cm,d:dist([sel.lat,sel.lng],[cm.lat,cm.lng])})).sort((a,b)=>a.d-b.d).slice(0,3);\n    const camBlock=hasXY(sel)?`<div class=\"sec\"><div class=\"sechd\">Nearby cameras · 附近监控</div>${cams.map(x=>`<div class=\"camrow opencam\" data-cam=\"${x.cm.id}\">📹 ${esc(x.cm.name)} ${esc(x.cm.zh)}<span class=\"camdist\">${(x.d*111).toFixed(1)} km</span></div>`).join(\"\")}</div>`:\"\";\n    const tr=sel.transcript||[];\n    const thread=tr.length?tr.map(m=>{const role=m.role||\"agent\";const rl=role===\"reporter\"?\"Caller 报案人\":(role===\"dispatcher\"?\"Dispatcher 调度员\":\"AI Agent 智能助理\");const main=esc(pick(m));const zh=(role===\"agent\"&&m.zh)?`<div class=\"zh\">${esc(m.zh)}</div>`:\"\";const orig=(m.raw&&m.srcLang&&m.srcLang!==\"en\")?`<div class=\"torig\"><span class=\"rfrom\">译自 translated from ${esc(m.srcLang)}:</span> ${esc(m.raw)}</div>`:\"\";return `<div class=\"tmsg ${role}\"><span class=\"rolelbl\">${rl}</span>${main}${zh}${orig}</div>`;}).join(\"\"):`<div style=\"font-size:12px;color:#8a8a8e\">No messages yet · 暂无对话</div>`;\n    const human=sel.humanControl===true;\n    const dispBar=human?`<div class=\"dispbar\"><input class=\"dispinput\" placeholder=\"Message the caller… 给报案人发消息\"/><button class=\"dispsend\" data-id=\"${sel._id}\">↑</button></div>`:\"\";\n    const threadBlock=`<div class=\"sec\"><div class=\"sechd\">Live transcript · 实时对话 ${human?'<span class=\"vtag\" style=\"background:#ff9500\">HUMAN 人工</span>':\"\"}</div><div class=\"thread\">${thread}</div>${dispBar}<button class=\"btn ${human?\"gray\":\"orange\"} takeover\" data-id=\"${sel._id}\" style=\"margin-top:8px\">${human?\"↩ Resume AI · 恢复AI\":\"✋ Take over · 人工接管\"}</button></div>`;\n    const disp=sel.dispatchedAt;\n    const tlrows=[[\"Reported 已报告\",sel.createdAt,\"#ff3b30\",true],[\"Dispatched 已派遣\",disp,\"#ff9500\",!!disp],[\"En route 前往中\",disp?disp+T_DISP:null,\"#0a84ff\",disp&&(now-disp)>=T_DISP],[\"On scene 已到场\",disp?disp+T_ENROUTE:null,\"#5e5ce6\",disp&&(now-disp)>=T_ENROUTE],[\"Resolved 已解决\",disp?disp+T_SCENE:null,\"#34c759\",(sel.status===\"Resolved\")||(disp&&(now-disp)>=T_SCENE)]];\n    const timeline=`<div class=\"sec\"><div class=\"sechd\">Timeline · 时间线</div><div class=\"tl\">${tlrows.map(r=>`<div class=\"tlrow ${r[3]?\"\":\"pending\"}\"><div class=\"tldot\" style=\"background:${r[3]?r[2]:\"#c7c7cc\"}\"></div><div class=\"tltxt\">${r[0]}<div class=\"tlt\">${r[3]?fmtTime(r[1]):\"pending 待定\"}</div></div></div>`).join(\"\")}</div></div>`;\n    const canReinforce=(disp&&sel.status!==\"Resolved\"&&dstat(sel)!==\"Resolved\"&&units.length<mx);\n    let actions=\"\";\n    if(ds===\"Reported\"){actions=`<button class=\"btn red disp\" data-id=\"${sel._id}\">🚒 Dispatch nearest unit · 派遣最近队伍</button>`;}\n    else if(canReinforce){actions=`<button class=\"btn orange reinforce\" data-id=\"${sel._id}\">➕ Send reinforcements · 派遣增援 (${units.length}/${mx})</button>`;}\n    const sm=((d2.state.summaries||{})[sel._id])||null;\n    const summaryBlock=`<div class=\"sec\"><div class=\"sechd\">Case summary · 案情摘要</div>${sm?`<div class=\"summarybox\"><div>${esc(sm.summary_en||\"\")}</div><div class=\"zh2\">${esc(sm.summary_zh||\"\")}</div></div>`:`<div class=\"summarybox gen\">Generating… · 生成中…</div>`}</div>`;\n    const aa=sel.afterAction;\n    const aaBlock=aa?`<div class=\"sec\"><div class=\"sechd\">After-action report · 事后报告</div><div class=\"aabox\"><div>${esc(aa.report_en||\"\")}</div><div class=\"zh2\">${esc(aa.report_zh||\"\")}</div></div></div>`:\"\";\n    return `<div class=\"detail\" data-key=\"detail\"><button class=\"close\">✕</button><div class=\"dttl\">${esc(sel.title||\"Incoming call · 接线中\")}</div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span>${summaryBlock}\n      <div class=\"sec\"><div class=\"dkv\"><span class=\"k\">Location 位置</span><span class=\"v\">${esc(sel.locationText||\"—\")}</span><span class=\"k\">District 辖区</span><span class=\"v\">${esc(sel.district||\"—\")}</span><span class=\"k\">Type 类型</span><span class=\"v\">🔥 ${esc(sel.fireType||\"—\")}</span><span class=\"k\">Severity 级别</span><span class=\"v\">${esc(sel.severity||\"—\")}</span><span class=\"k\">Trapped 被困</span><span class=\"v\">${esc(sel.peopleTrapped||\"—\")}</span><span class=\"k\">Hazards 危险</span><span class=\"v\">${esc(sel.hazards||\"—\")}</span><span class=\"k\">Coords 坐标</span><span class=\"v\">${hasXY(sel)?sel.lat.toFixed(4)+\", \"+sel.lng.toFixed(4):\"pending\"}</span></div>${sel.photoAnalysis?`<div class=\"dvis\"><b>📸 AI vision · 影像分析</b><br>${esc(sel.photoAnalysis)}</div>`:\"\"}${sel.photoAddress?`<div class=\"dvis\" style=\"background:rgba(255,149,0,.12);border-color:rgba(255,149,0,.3)\"><b style=\"color:#b35a00\">📍 Address from photo</b> <span class=\"fromphoto\">来自照片 from photo</span><br>${esc(sel.photoAddress)}</div>`:\"\"}${actions}</div>\n      ${vidBlock}${unitBlock}${emoBlock}${threadBlock}${camBlock}${timeline}${aaBlock}</div>`;\n}\nfunction renderCam(d2,cam){\n    return `<div class=\"camov\"><div class=\"camwin\"><div class=\"camfeed\">${cam.yt?`<iframe class=\"camiframe\" src=\"https://www.youtube-nocookie.com/embed/${cam.yt}?autoplay=1&mute=1&playsinline=1&rel=0\" allow=\"autoplay; encrypted-media; picture-in-picture\" allowfullscreen></iframe>`:`<div class=\"vidph\">No live feed available · 无可用信号</div>`}<div class=\"camhud\"><div class=\"tl2\"><span class=\"rec\"></span>LIVE</div><div class=\"br\" style=\"background:#34c759;color:#000\">PARTNER FEED</div></div></div><div class=\"camttl\"><span>📹 ${esc(cam.name)} · ${esc(cam.zh)} <span style=\"opacity:.65;font-weight:500;font-size:11px\">— LIVE · ${esc(cam.feed||\"partner feed\")} · 合作信号</span></span><button class=\"camclose\">✕</button></div></div></div>`;\n}\nexport default {\n  css:`\n  .shell{height:100%;display:grid;grid-template-columns:360px 1fr;font-family:-apple-system,\"SF Pro Display\",\"SF Pro Text\",\"PingFang SC\",\"Helvetica Neue\",system-ui,sans-serif;background:#f5f5f7;color:#1d1d1f;-webkit-font-smoothing:antialiased;overflow:hidden;}\n  .side{background:rgba(255,255,255,.7);backdrop-filter:saturate(180%) blur(30px);-webkit-backdrop-filter:saturate(180%) blur(30px);border-right:1px solid rgba(0,0,0,.07);display:flex;flex-direction:column;overflow:hidden;}\n  .shead{padding:18px 20px;border-bottom:1px solid rgba(0,0,0,.06);display:flex;justify-content:space-between;align-items:flex-start;}\n  .brand{display:flex;gap:11px;align-items:center;}\n  .badge{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;background:linear-gradient(180deg,#ff5a4d,#ff3b30);box-shadow:0 5px 14px rgba(255,59,48,.35);font-size:19px;}\n  .stitle{font-size:16px;font-weight:700;letter-spacing:-.02em;}\n  .ssub{font-size:11.5px;color:#6e6e73;}\n  .stat{text-align:right;}.statn{font-size:26px;font-weight:800;color:#ff3b30;line-height:1;}.statl{font-size:10px;color:#8a8a8e;margin-top:2px;}\n  .qhead{padding:12px 20px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#8a8a8e;}\n  .queue{flex:1;overflow:auto;padding:6px 12px 16px;display:flex;flex-direction:column;gap:9px;}\n  .card{background:#fff;border:1px solid rgba(0,0,0,.06);border-radius:16px;padding:13px 14px;cursor:pointer;transition:transform .15s,box-shadow .2s,border-color .2s;box-shadow:0 1px 3px rgba(0,0,0,.04);animation:cardin .35s ease;}\n  .card:hover{transform:translateY(-1px);box-shadow:0 8px 22px rgba(0,0,0,.08);}\n  .card.sel{border-color:#0a84ff;box-shadow:0 8px 22px rgba(10,132,255,.18);}\n  .crow{display:flex;align-items:flex-start;gap:9px;}\n  .dot{width:9px;height:9px;border-radius:50%;flex:0 0 9px;margin-top:5px;}\n  .ctitle{flex:1;min-width:0;}.cen{font-size:14px;font-weight:650;line-height:1.2;}.czh{font-size:12px;color:#6e6e73;margin-top:1px;}\n  .pill{font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:8px;white-space:nowrap;}\n  .cmeta{font-size:11.5px;color:#6e6e73;margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;align-items:center;}\n  .etag{font-size:10px;font-weight:700;color:#fff;padding:1px 7px;border-radius:6px;}\n  .vtag{font-size:10px;font-weight:700;color:#fff;background:#ff2d55;padding:1px 7px;border-radius:6px;}\n  .btn{margin-top:9px;width:100%;border:0;border-radius:11px;padding:9px;font:inherit;font-size:12px;font-weight:650;color:#fff;cursor:pointer;transition:transform .15s,opacity .2s;}\n  .btn:active{transform:scale(.97);}.btn[disabled]{opacity:.45;cursor:default;}\n  .btn.red{background:linear-gradient(180deg,#ff5a4d,#ff3b30);box-shadow:0 4px 12px rgba(255,59,48,.3);}\n  .btn.orange{background:linear-gradient(180deg,#ffab2e,#ff9500);box-shadow:0 4px 12px rgba(255,149,0,.3);}\n  .btn.blue{background:linear-gradient(180deg,#3a95ff,#0a84ff);box-shadow:0 4px 12px rgba(10,132,255,.3);}\n  .btn.gray{background:rgba(120,120,128,.16);color:#1d1d1f;}\n  .empty{color:#8a8a8e;text-align:center;padding:40px 12px;font-size:13px;line-height:1.6;}\n  .sk{height:74px;border-radius:16px;background:linear-gradient(90deg,#ececf0 25%,#f5f5f8 37%,#ececf0 63%);background-size:400% 100%;animation:sh 1.4s ease infinite;}\n  .main{position:relative;display:flex;flex-direction:column;overflow:hidden;}\n  .mapbox{position:absolute;inset:0;background:#eef0f4;}\n  .maploading{position:absolute;inset:0;z-index:500;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;background:linear-gradient(90deg,#e9ebef 25%,#f3f4f7 37%,#e9ebef 63%);background-size:400% 100%;animation:sh 1.4s ease infinite;color:#6e6e73;font-size:13px;font-weight:600;}\n  .maploading .ring{width:26px;height:26px;border:3px solid rgba(0,0,0,.12);border-top-color:#ff3b30;border-radius:50%;animation:spin .8s linear infinite;}\n  .maptop{position:absolute;top:0;left:0;right:0;z-index:600;display:flex;justify-content:space-between;align-items:center;padding:14px 20px;background:linear-gradient(180deg,rgba(245,245,247,.92),rgba(245,245,247,0));backdrop-filter:blur(10px);pointer-events:none;}\n  .mt-l{font-size:17px;font-weight:700;letter-spacing:-.02em;}.mt-sub{font-size:12px;color:#8a8a8e;font-weight:500;margin-left:8px;}\n  .mt-r{display:flex;gap:8px;align-items:center;pointer-events:auto;}\n  .camtoggle{border:0;font:inherit;font-size:12px;font-weight:600;padding:6px 11px;border-radius:10px;cursor:pointer;background:rgba(255,255,255,.8);color:#3c3c43;box-shadow:0 1px 3px rgba(0,0,0,.08);}\n  .camtoggle[data-on]{background:#0a84ff;color:#fff;}\n  .displang{border:0;font:inherit;font-size:12px;font-weight:600;padding:6px 8px;border-radius:10px;background:rgba(255,255,255,.8);color:#3c3c43;box-shadow:0 1px 3px rgba(0,0,0,.08);cursor:pointer;}\n  .card.live{border-color:#ff2d55!important;box-shadow:0 0 0 1px #ff2d55,0 8px 22px rgba(255,45,85,.2)!important;}\n  .clock{font-size:13px;font-weight:600;color:#6e6e73;font-variant-numeric:tabular-nums;background:rgba(255,255,255,.72);padding:6px 12px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.06);display:flex;align-items:center;gap:7px;}\n  .livedot{width:7px;height:7px;border-radius:50%;background:#34c759;animation:livep 1.8s infinite;}\n  .mapwrap{flex:1;position:relative;overflow:hidden;}\n  .legend{position:absolute;bottom:16px;left:14px;z-index:550;display:flex;flex-wrap:wrap;gap:8px;background:rgba(255,255,255,.8);backdrop-filter:blur(20px);border:1px solid rgba(0,0,0,.06);border-radius:12px;padding:8px 11px;box-shadow:0 4px 16px rgba(0,0,0,.1);max-width:58%;}\n  .lg{font-size:11px;color:#3c3c43;display:flex;align-items:center;gap:5px;font-weight:600;}\n  .detail{position:absolute;right:16px;top:70px;bottom:16px;width:360px;overflow:auto;background:rgba(255,255,255,.9);backdrop-filter:saturate(180%) blur(30px);-webkit-backdrop-filter:saturate(180%) blur(30px);border:1px solid rgba(0,0,0,.08);border-radius:20px;padding:18px;box-shadow:0 24px 70px rgba(0,0,0,.22);z-index:650;animation:slidein .3s cubic-bezier(.2,.9,.3,1.1);}\n  .close{position:absolute;top:12px;right:12px;width:26px;height:26px;border:0;border-radius:50%;background:rgba(120,120,128,.14);cursor:pointer;font-size:12px;color:#6e6e73;z-index:2;}\n  .dttl{font-size:16px;font-weight:700;padding-right:30px;margin-bottom:6px;letter-spacing:-.01em;}\n  .sec{margin-top:14px;padding-top:12px;border-top:1px solid rgba(0,0,0,.07);}\n  .sechd{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:#8a8a8e;margin-bottom:8px;display:flex;align-items:center;gap:6px;}\n  .dkv{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;font-size:12.5px;}\n  .dkv .k{color:#8a8a8e;}.dkv .v{font-weight:600;text-align:right;}\n  .dvis{margin-top:8px;font-size:11.5px;line-height:1.45;background:rgba(52,199,89,.12);border:1px solid rgba(52,199,89,.25);border-radius:12px;padding:9px 11px;}.dvis b{color:#248a3d;}\n  .fromphoto{font-size:9px;font-weight:700;background:#ff9500;color:#fff;padding:1px 6px;border-radius:6px;}\n  .wdbgbtn{margin-top:6px;font-size:10px;border:0;background:rgba(120,120,128,.16);border-radius:8px;padding:4px 8px;cursor:pointer;color:#3c3c43;}\n  .vdbg{margin-top:6px;font-size:9.5px;font-family:ui-monospace,monospace;background:#0b0b0f;color:#8fef8f;padding:8px;border-radius:8px;white-space:pre-wrap;max-height:150px;overflow:auto;line-height:1.4;}\n  .summarybox{font-size:12.5px;line-height:1.5;background:rgba(10,132,255,.08);border:1px solid rgba(10,132,255,.2);border-radius:12px;padding:10px 12px;}\n  .summarybox.gen{color:#8a8a8e;font-style:italic;}\n  .zh2{color:#6e6e73;margin-top:4px;font-size:11.5px;}\n  .aabox{font-size:12.5px;line-height:1.5;background:rgba(52,199,89,.1);border:1px solid rgba(52,199,89,.25);border-radius:12px;padding:10px 12px;}\n  .torig{font-size:10px;opacity:.75;margin-top:3px;font-style:italic;}\n  .rfrom{font-size:9px;font-weight:700;opacity:.7;}\n  .unit{display:flex;align-items:center;gap:9px;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.05);}\n  .unit:last-child{border-bottom:0;}\n  .unitico{width:26px;height:26px;border-radius:8px;background:#0a84ff;display:flex;align-items:center;justify-content:center;font-size:13px;flex:0 0 26px;}\n  .unitnm{flex:1;min-width:0;font-size:12.5px;font-weight:600;}\n  .unitst{font-size:10.5px;font-weight:700;padding:2px 7px;border-radius:7px;}\n  .tl{display:flex;flex-direction:column;gap:0;}\n  .tlrow{display:flex;gap:10px;align-items:flex-start;position:relative;padding-bottom:12px;}\n  .tlrow:before{content:\"\";position:absolute;left:6px;top:16px;bottom:0;width:2px;background:rgba(0,0,0,.08);}\n  .tlrow:last-child:before{display:none;}\n  .tldot{width:14px;height:14px;border-radius:50%;flex:0 0 14px;margin-top:1px;border:2px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.08);z-index:1;}\n  .tltxt{flex:1;font-size:12px;}.tlt{color:#8a8a8e;font-size:10.5px;font-variant-numeric:tabular-nums;}\n  .tlrow.pending .tltxt,.tlrow.pending .tlt{opacity:.4;}\n  .thread{display:flex;flex-direction:column;gap:7px;max-height:230px;overflow:auto;padding-right:4px;}\n  .tmsg{max-width:88%;padding:8px 11px;border-radius:14px;font-size:12.5px;line-height:1.35;}\n  .tmsg.reporter{align-self:flex-end;background:linear-gradient(180deg,#0a84ff,#0071e3);color:#fff;border-bottom-right-radius:5px;}\n  .tmsg.agent{align-self:flex-start;background:#f0f0f3;color:#1d1d1f;border-bottom-left-radius:5px;}\n  .tmsg.dispatcher{align-self:flex-start;background:linear-gradient(180deg,#ffab2e,#ff9500);color:#fff;border-bottom-left-radius:5px;}\n  .tmsg .rolelbl{font-size:9px;font-weight:700;opacity:.7;text-transform:uppercase;letter-spacing:.04em;display:block;margin-bottom:2px;}\n  .tmsg .zh{opacity:.85;font-size:11.5px;margin-top:2px;}\n  .dispbar{display:flex;gap:6px;margin-top:9px;}\n  .dispbar input{flex:1;border:1px solid rgba(0,0,0,.12);border-radius:10px;padding:8px 11px;font:inherit;font-size:13px;outline:none;}\n  .dispbar input:focus{border-color:#ff9500;box-shadow:0 0 0 3px rgba(255,149,0,.14);}\n  .dispbar button{border:0;border-radius:10px;padding:0 13px;background:#ff9500;color:#fff;font-weight:700;cursor:pointer;}\n  .livevid{width:100%;border-radius:12px;background:#000;display:block;aspect-ratio:16/9;object-fit:cover;box-shadow:0 6px 18px rgba(0,0,0,.25);}\n  .vidph{width:100%;aspect-ratio:16/9;border-radius:12px;background:#0b0b0f;display:flex;align-items:center;justify-content:center;color:#8a8a8e;font-size:12px;text-align:center;padding:10px;box-sizing:border-box;}\n  .camrow{display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;font-size:12px;}\n  .camrow:hover{color:#0a84ff;}\n  .camdist{margin-left:auto;color:#8a8a8e;font-size:11px;}\n  .camov{position:absolute;inset:0;z-index:900;background:rgba(0,0,0,.55);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px;animation:fade .25s ease;}\n  .camwin{width:min(720px,92%);background:#0b0b0f;border-radius:18px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.5);border:1px solid rgba(255,255,255,.08);}\n  .camfeed{position:relative;aspect-ratio:16/9;overflow:hidden;background:#0b0b0f;}\n  .camiframe{width:100%;height:100%;border:0;display:block;}\n  .camscene{position:absolute;inset:0;background:linear-gradient(180deg,#2a3550 0%,#3a4a6a 42%,#4a4438 42%,#2c2822 100%);}\n  .camscene:before{content:\"\";position:absolute;left:0;right:0;top:42%;bottom:0;background:repeating-linear-gradient(90deg,transparent 0,transparent 46px,rgba(255,220,120,.5) 46px,rgba(255,220,120,.5) 54px,transparent 54px,transparent 100px);animation:road 2.2s linear infinite;opacity:.5;}\n  .carA,.carB{position:absolute;width:34px;height:16px;border-radius:4px;top:62%;}\n  .carA{background:#e8e8ee;animation:drivea 3.4s linear infinite;}\n  .carB{background:#ff6b5c;top:74%;animation:driveb 4.6s linear infinite;}\n  .scan{position:absolute;inset:0;background:repeating-linear-gradient(0deg,rgba(255,255,255,.05) 0,rgba(255,255,255,.05) 1px,transparent 1px,transparent 3px);pointer-events:none;}\n  .noise{position:absolute;inset:0;opacity:.06;background-image:radial-gradient(#fff 0.5px,transparent 0.5px);background-size:3px 3px;animation:noise .2s steps(2) infinite;pointer-events:none;}\n  .camhud{position:absolute;inset:0;color:#e6ffe6;font-family:ui-monospace,monospace;font-size:11px;pointer-events:none;}\n  .camhud .tl2{position:absolute;top:10px;left:12px;display:flex;align-items:center;gap:6px;}\n  .rec{width:9px;height:9px;border-radius:50%;background:#ff3b30;animation:blink 1s infinite;}\n  .camhud .tr{position:absolute;top:10px;right:12px;}\n  .camhud .bl{position:absolute;bottom:10px;left:12px;}\n  .camhud .br{position:absolute;bottom:10px;right:12px;background:#ff9500;color:#000;font-weight:800;padding:1px 6px;border-radius:4px;}\n  .camttl{padding:11px 14px;color:#fff;font-size:13px;font-weight:600;display:flex;justify-content:space-between;align-items:center;background:#141419;}\n  .camttl button{border:0;background:rgba(255,255,255,.14);color:#fff;width:26px;height:26px;border-radius:50%;cursor:pointer;}\n  .leaflet-div-icon{background:transparent!important;border:none!important;}\n  .leaflet-container{background:#eef0f4;font-family:inherit;}\n  .leaflet-top.leaflet-left{top:60px;}\n  .leaflet-control-zoom{border:none!important;box-shadow:0 4px 14px rgba(0,0,0,.14)!important;border-radius:12px!important;overflow:hidden;}\n  .leaflet-control-zoom a{background:rgba(255,255,255,.9)!important;color:#1d1d1f!important;border:none!important;font-weight:600;}\n  .leaflet-control-attribution{background:rgba(255,255,255,.7)!important;font-size:9px!important;color:#8a8a8e!important;}\n  .lstn{display:flex;flex-direction:column;align-items:center;gap:1px;}\n  .lstn .box{width:24px;height:24px;border-radius:8px;background:#fff;border:2px solid #0071e3;display:flex;align-items:center;justify-content:center;font-size:13px;box-shadow:0 2px 6px rgba(0,0,0,.2);}\n  .lstn .nm{font-size:9px;font-weight:700;color:#0071e3;background:rgba(255,255,255,.85);padding:0 4px;border-radius:4px;white-space:nowrap;}\n  .lcam{width:26px;height:26px;border-radius:7px;background:#1d1d1f;border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:12px;box-shadow:0 2px 6px rgba(0,0,0,.3);cursor:pointer;}\n  .lpin{position:relative;width:28px;height:28px;}\n  .lpin .ring{position:absolute;inset:2px;border-radius:50%;background:var(--c);opacity:0;}\n  .lpin.act .ring{opacity:.28;animation:pulse 1.8s infinite;}\n  .lpin .core{position:absolute;inset:6px;border-radius:50%;background:var(--c);border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;font-size:8px;}\n  .lpin .vbadge{position:absolute;top:-6px;right:-6px;width:15px;height:15px;border-radius:50%;background:#ff2d55;border:1.5px solid #fff;font-size:8px;display:flex;align-items:center;justify-content:center;}\n  .lsquad{display:flex;flex-direction:column;align-items:center;}\n  .lsquad .eta{font-size:10px;font-weight:800;color:#0071e3;background:rgba(255,255,255,.92);padding:0 5px;border-radius:5px;white-space:nowrap;min-height:12px;box-shadow:0 1px 3px rgba(0,0,0,.15);}\n  .lsquad .veh{width:22px;height:22px;border-radius:50%;background:#0a84ff;border:2.5px solid #fff;display:flex;align-items:center;justify-content:center;font-size:11px;box-shadow:0 3px 8px rgba(10,132,255,.5);margin-top:1px;}\n  @keyframes slidein{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}\n  @keyframes fade{from{opacity:0;}to{opacity:1;}}\n  @keyframes livep{0%{box-shadow:0 0 0 0 rgba(52,199,89,.5);}70%{box-shadow:0 0 0 7px rgba(52,199,89,0);}100%{box-shadow:0 0 0 0 rgba(52,199,89,0);}}\n  @keyframes cardin{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}\n  @keyframes sh{0%{background-position:100% 0;}100%{background-position:0 0;}}\n  @keyframes spin{to{transform:rotate(360deg);}}\n  @keyframes pulse{0%{transform:scale(1);opacity:.3;}70%{transform:scale(2.1);opacity:0;}100%{transform:scale(1);opacity:0;}}\n  @keyframes road{to{background-position:-100px 0;}}\n  @keyframes drivea{from{left:-40px;}to{left:100%;}}\n  @keyframes driveb{from{left:100%;}to{left:-40px;}}\n  @keyframes blink{50%{opacity:.3;}}\n  @keyframes noise{to{transform:translate(2px,1px);}}\n  `,\n  render(d2){\n    const st=d2.state;const incs=st.incidents||[];\n    let _ls=\"idle\",_le=\"\";try{d2.lib(LEAF);if(d2.lib.state)_ls=d2.lib.state(LEAF);if(d2.lib.error)_le=d2.lib.error(LEAF)||\"\";}catch(e){_le=String((e&&e.message)||e);}\n    let queue;\n    if(!st.loaded){queue=`<div class=\"sk\"></div><div class=\"sk\"></div><div class=\"sk\"></div>`;}\n    else{queue=incs.map(inc=>{const ds=dstat(inc);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";const sel=st.selectedId===inc._id;const parts=(inc.title||\" / \").split(\" / \");const trapped=(inc.peopleTrapped||\"\").indexOf(\"Yes\")>=0;const ems=inc.emotions||[];const le=ems.length?ems[ems.length-1]:null;const ec=le?(EC[le.emotion]||\"#8a8a8e\"):null;const nu=(inc.units||[]).length;\n      return `<div class=\"card ${sel?\"sel\":\"\"} ${inc.liveVideo?\"live\":\"\"}\" data-id=\"${inc._id}\" data-key=\"q${inc._id}\"><div class=\"crow\"><span class=\"dot\" style=\"background:${c}\"></span><div class=\"ctitle\"><div class=\"cen\">${esc(parts[0]||inc.title||\"Incoming call…\")}</div><div class=\"czh\">${esc(parts[1]||\"\")}</div></div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span></div><div class=\"cmeta\">${inc.district?esc(inc.district)+\" · \":\"\"}${inc.severity?esc(inc.severity)+\" · \":\"\"}${inc.fireType?\"🔥 \"+esc(inc.fireType):\"\"}${trapped?' · <b style=\"color:#ff3b30\">TRAPPED 被困</b>':\"\"}${nu?` · 🚒×${nu}`:\"\"}${inc.liveVideo?' · <span class=\"vtag\">● LIVE 直播</span>':\"\"}${le?`<span class=\"etag\" style=\"background:${ec}\">${esc(le.label_en)} ${Math.round((Number(le.intensity)||0)*100)}%</span>`:\"\"}</div>${ds===\"Reported\"?`<button class=\"btn red disp\" data-id=\"${inc._id}\">🚒 Dispatch nearest · 派遣最近队伍</button>`:\"\"}</div>`;}).join(\"\")||`<div class=\"empty\">Waiting for incidents…<br>等待警情接入…</div>`;}\n    const sel=incs.find(i=>i._id===st.selectedId);\n    let detail=\"\";\n    if(sel){detail=renderDetail(d2,sel);}\n    const activeCount=incs.filter(i=>dstat(i)!==\"Resolved\"&&i.status!==\"Resolved\").length;\n    const legend=Object.keys(SL).map(k=>`<span class=\"lg\"><span class=\"dot\" style=\"background:${SC[k]}\"></span>${SL[k][0]} ${SL[k][1]}</span>`).join(\"\")+`<span class=\"lg\">🚒 Unit · 队伍</span><span class=\"lg\">📹 Camera · 监控</span>`;\n    const _d=st.mapDbg||{};const loading=st.mapReady?\"\":`<div class=\"maploading\"><div class=\"ring\"></div><div>${(_le||_d.err)?(\"Map failed: \"+esc(_le||_d.err)):\"Loading Wuhan map… · 加载武汉地图中…\"}</div><div style=\"font-size:10px;opacity:.7;margin-top:8px;font-family:ui-monospace,monospace;text-align:center\">lib: ${esc(_ls)} · attempts: ${_d.n||0} · L: ${_d.L?\"YES\":\"no\"} · box: ${_d.w==null?\"?\":_d.w}×${_d.h==null?\"?\":_d.h}</div></div>`;\n    const camov=st.camView?renderCam(d2,st.camView):\"\";\n    return `<div class=\"shell\"><aside class=\"side\"><div class=\"shead\"><div class=\"brand\"><div class=\"badge\">🚒</div><div><div class=\"stitle\">Command Center</div><div class=\"ssub\">武汉消防指挥中心</div></div></div><div class=\"stat\"><div class=\"statn\">${activeCount}</div><div class=\"statl\">Active · 进行中</div></div></div><div class=\"qhead\">Incident queue · 警情队列</div><div class=\"queue\">${queue}</div></aside><main class=\"main\"><div class=\"maptop\"><div class=\"mt-l\">Wuhan · 武汉<span class=\"mt-sub\">Live incident map · 实时警情地图</span></div><div class=\"mt-r\"><select class=\"displang\" title=\"Dispatcher language\"><option value=\"en\" ${(st.dispLang||\"en\")===\"en\"?\"selected\":\"\"}>🇦 EN</option><option value=\"zh\" ${st.dispLang===\"zh\"?\"selected\":\"\"}>🇨🇳 中文</option><option value=\"pt\" ${st.dispLang===\"pt\"?\"selected\":\"\"}>🇧🇷 PT</option><option value=\"es\" ${st.dispLang===\"es\"?\"selected\":\"\"}>🇪🇸 ES</option></select><button class=\"camtoggle\" ${st.showCams?\"data-on\":\"\"}>📹 Cameras 监控</button><div class=\"clock\"><span class=\"livedot\"></span><span id=\"clk\"></span></div></div></div><div class=\"mapwrap\"><div class=\"mapbox\" data-d2-static></div>${loading}<div class=\"legend\">${legend}</div>${camov}</div>${detail}</main></div>`;\n  },\n  renderDetail(d2,sel){\n    const st=d2.state;const ds=dstat(sel);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";\n    const ems=sel.emotions||[];const le=ems.length?ems[ems.length-1]:null;\n    const units=sel.units||[];const mx=maxUnits(sel);\n    const now=Date.now();\n    let emoBlock=\"\";\n    if(le){const elc=EC[le.emotion]||\"#8a8a8e\";emoBlock=`<div class=\"sec\"><div class=\"sechd\">Caller stress · 来电者情绪</div><div style=\"display:flex;align-items:center;gap:12px\"><div style=\"text-align:center\">${gauge(le)}<div style=\"font-size:11px;font-weight:700;color:${elc}\">${esc(le.label_en)} ${esc(le.label_zh)}</div></div><div style=\"flex:1;min-width:0\">${emoChart(ems)}</div></div></div>`;}\n    let unitBlock=\"\";\n    if(units.length){unitBlock=`<div class=\"sec\"><div class=\"sechd\">Assigned units · 出动队伍 (${units.length})</div>${units.map(u=>{const el=now-(u.dispatchedAt||now);const ph=phaseOf(el);const usl=SL[ph]||[\"\",\"\"];const uc=SC[ph]||\"#0a84ff\";const eta=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"Dispatched\"?\"leaving 出发\":(ph===\"OnScene\"?\"on scene 到场\":\"resolved 完成\"));return `<div class=\"unit\"><div class=\"unitico\">🚒</div><div class=\"unitnm\">${esc(u.name||\"Unit\")}<br><span style=\"font-size:10.5px;color:#8a8a8e;font-weight:500\">${esc(u.role===\"backup\"?\"Reinforcement 增援\":\"Primary 主力\")}</span></div><div style=\"text-align:right\"><span class=\"unitst\" style=\"background:${uc}22;color:${uc}\">${usl[0]}</span><div style=\"font-size:10px;color:#8a8a8e;margin-top:2px\">${eta}</div></div></div>`;}).join(\"\")}</div>`;}\n    let vidBlock=\"\";\n    if(sel.liveVideo){vidBlock=`<div class=\"sec\"><div class=\"sechd\">📹 Live video · 现场直播 <span class=\"vtag\">● LIVE</span></div>${st.watching===sel._id?`<video class=\"livevid\" autoplay playsinline muted></video><div style=\"font-size:10.5px;color:#8a8a8e;margin-top:5px\">${esc(st.vidStatus||\"Connecting… 连接中\")}</div><button class=\"wdbgbtn\">🐞 ${st.wdbgOn?\"hide debug\":\"debug\"}</button>${st.wdbgOn?`<pre class=\"vdbg\">status: ${esc(st.vidStatus||\"-\")}\\ntracks: ${esc(\"\"+((st.wstats||{}).tracks!=null?(st.wstats||{}).tracks:\"-\"))}  sig: ${esc((st.wstats||{}).sig||\"-\")}\\nice: ${esc((st.wstats||{}).ice||\"-\")}  conn: ${esc((st.wstats||{}).conn||\"-\")}\\nvideo: ${esc((st.wstats||{}).video||\"0x0\")}\\ncandidates: ${esc((st.wstats||{}).cand||\"(none yet - TURN down?)\")}\\n---\\n${esc((st.wlog||[]).join(\"\\n\"))}</pre>`:\"\"}`:`<button class=\"btn red watch\" data-id=\"${sel._id}\">▶ Watch live · 观看直播</button>`}</div>`;}\n    const cams=CAMS.map(cm=>({cm,d:dist([sel.lat,sel.lng],[cm.lat,cm.lng])})).sort((a,b)=>a.d-b.d).slice(0,3);\n    const camBlock=hasXY(sel)?`<div class=\"sec\"><div class=\"sechd\">Nearby cameras · 附近监控</div>${cams.map(x=>`<div class=\"camrow opencam\" data-cam=\"${x.cm.id}\">📹 ${esc(x.cm.name)} ${esc(x.cm.zh)}<span class=\"camdist\">${(x.d*111).toFixed(1)} km</span></div>`).join(\"\")}</div>`:\"\";\n    const tr=sel.transcript||[];\n    const thread=tr.length?tr.map(m=>{const role=m.role||\"agent\";const rl=role===\"reporter\"?\"Caller 报案人\":(role===\"dispatcher\"?\"Dispatcher 调度员\":\"AI Agent 智能助理\");const main=esc(pick(m));const zh=(role===\"agent\"&&m.zh)?`<div class=\"zh\">${esc(m.zh)}</div>`:\"\";const orig=(m.raw&&m.srcLang&&m.srcLang!==\"en\")?`<div class=\"torig\"><span class=\"rfrom\">译自 translated from ${esc(m.srcLang)}:</span> ${esc(m.raw)}</div>`:\"\";return `<div class=\"tmsg ${role}\"><span class=\"rolelbl\">${rl}</span>${main}${zh}${orig}</div>`;}).join(\"\"):`<div style=\"font-size:12px;color:#8a8a8e\">No messages yet · 暂无对话</div>`;\n    const human=sel.humanControl===true;\n    const dispBar=human?`<div class=\"dispbar\"><input class=\"dispinput\" placeholder=\"Message the caller… 给报案人发消息\"/><button class=\"dispsend\" data-id=\"${sel._id}\">↑</button></div>`:\"\";\n    const threadBlock=`<div class=\"sec\"><div class=\"sechd\">Live transcript · 实时对话 ${human?'<span class=\"vtag\" style=\"background:#ff9500\">HUMAN 人工</span>':\"\"}</div><div class=\"thread\">${thread}</div>${dispBar}<button class=\"btn ${human?\"gray\":\"orange\"} takeover\" data-id=\"${sel._id}\" style=\"margin-top:8px\">${human?\"↩ Resume AI · 恢复AI\":\"✋ Take over · 人工接管\"}</button></div>`;\n    const disp=sel.dispatchedAt;\n    const tlrows=[[\"Reported 已报告\",sel.createdAt,\"#ff3b30\",true],[\"Dispatched 已派遣\",disp,\"#ff9500\",!!disp],[\"En route 前往中\",disp?disp+T_DISP:null,\"#0a84ff\",disp&&(now-disp)>=T_DISP],[\"On scene 已到场\",disp?disp+T_ENROUTE:null,\"#5e5ce6\",disp&&(now-disp)>=T_ENROUTE],[\"Resolved 已解决\",disp?disp+T_SCENE:null,\"#34c759\",(sel.status===\"Resolved\")||(disp&&(now-disp)>=T_SCENE)]];\n    const timeline=`<div class=\"sec\"><div class=\"sechd\">Timeline · 时间线</div><div class=\"tl\">${tlrows.map(r=>`<div class=\"tlrow ${r[3]?\"\":\"pending\"}\"><div class=\"tldot\" style=\"background:${r[3]?r[2]:\"#c7c7cc\"}\"></div><div class=\"tltxt\">${r[0]}<div class=\"tlt\">${r[3]?fmtTime(r[1]):\"pending 待定\"}</div></div></div>`).join(\"\")}</div></div>`;\n    const canReinforce=(disp&&sel.status!==\"Resolved\"&&dstat(sel)!==\"Resolved\"&&units.length<mx);\n    let actions=\"\";\n    if(ds===\"Reported\"){actions=`<button class=\"btn red disp\" data-id=\"${sel._id}\">🚒 Dispatch nearest unit · 派遣最近队伍</button>`;}\n    else if(canReinforce){actions=`<button class=\"btn orange reinforce\" data-id=\"${sel._id}\">➕ Send reinforcements · 派遣增援 (${units.length}/${mx})</button>`;}\n    const sm=((d2.state.summaries||{})[sel._id])||null;\n    const summaryBlock=`<div class=\"sec\"><div class=\"sechd\">Case summary · 案情摘要</div>${sm?`<div class=\"summarybox\"><div>${esc(sm.summary_en||\"\")}</div><div class=\"zh2\">${esc(sm.summary_zh||\"\")}</div></div>`:`<div class=\"summarybox gen\">Generating… · 生成中…</div>`}</div>`;\n    const aa=sel.afterAction;\n    const aaBlock=aa?`<div class=\"sec\"><div class=\"sechd\">After-action report · 事后报告</div><div class=\"aabox\"><div>${esc(aa.report_en||\"\")}</div><div class=\"zh2\">${esc(aa.report_zh||\"\")}</div></div></div>`:\"\";\n    return `<div class=\"detail\" data-key=\"detail\"><button class=\"close\">✕</button><div class=\"dttl\">${esc(sel.title||\"Incoming call · 接线中\")}</div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span>${summaryBlock}\n      <div class=\"sec\"><div class=\"dkv\"><span class=\"k\">Location 位置</span><span class=\"v\">${esc(sel.locationText||\"—\")}</span><span class=\"k\">District 辖区</span><span class=\"v\">${esc(sel.district||\"—\")}</span><span class=\"k\">Type 类型</span><span class=\"v\">🔥 ${esc(sel.fireType||\"—\")}</span><span class=\"k\">Severity 级别</span><span class=\"v\">${esc(sel.severity||\"—\")}</span><span class=\"k\">Trapped 被困</span><span class=\"v\">${esc(sel.peopleTrapped||\"—\")}</span><span class=\"k\">Hazards 危险</span><span class=\"v\">${esc(sel.hazards||\"—\")}</span><span class=\"k\">Coords 坐标</span><span class=\"v\">${hasXY(sel)?sel.lat.toFixed(4)+\", \"+sel.lng.toFixed(4):\"pending\"}</span></div>${sel.photoAnalysis?`<div class=\"dvis\"><b>📸 AI vision · 影像分析</b><br>${esc(sel.photoAnalysis)}</div>`:\"\"}${sel.photoAddress?`<div class=\"dvis\" style=\"background:rgba(255,149,0,.12);border-color:rgba(255,149,0,.3)\"><b style=\"color:#b35a00\">📍 Address from photo</b> <span class=\"fromphoto\">来自照片 from photo</span><br>${esc(sel.photoAddress)}</div>`:\"\"}${actions}</div>\n      ${vidBlock}${unitBlock}${emoBlock}${threadBlock}${camBlock}${timeline}${aaBlock}</div>`;\n  },\n  renderCam(d2,cam){\n    return `<div class=\"camov\"><div class=\"camwin\"><div class=\"camfeed\">${cam.yt?`<iframe class=\"camiframe\" src=\"https://www.youtube-nocookie.com/embed/${cam.yt}?autoplay=1&mute=1&playsinline=1&rel=0\" allow=\"autoplay; encrypted-media; picture-in-picture\" allowfullscreen></iframe>`:`<div class=\"vidph\">No live feed available · 无可用信号</div>`}<div class=\"camhud\"><div class=\"tl2\"><span class=\"rec\"></span>LIVE</div><div class=\"br\" style=\"background:#34c759;color:#000\">PARTNER FEED</div></div></div><div class=\"camttl\"><span>📹 ${esc(cam.name)} · ${esc(cam.zh)} <span style=\"opacity:.65;font-weight:500;font-size:11px\">— LIVE · ${esc(cam.feed||\"partner feed\")} · 合作信号</span></span><button class=\"camclose\">✕</button></div></div></div>`;\n  },\n  mounted(d2){\n    if(!d2.state.incidents)d2.state.incidents=[];\n    if(!d2.state.stations)d2.state.stations=[];\n    if(d2.state.loaded===undefined)d2.state.loaded=false;\n    if(d2.state.showCams===undefined)d2.state.showCams=false;\n    let map=null,L=null,built=false;\n    const stnMk={},incMk={},camMk={},squadMk={},routeLn={},routeGeo={},routeFetching={},lastPhase={};\n    let refetching=false,lastFetch=0;\n    const mergeLocal=(server)=>{const cur=d2.state.incidents||[];const by={};cur.forEach(i=>by[i._id]=i);d2.state.incidents=server.map(s=>{const l=by[s._id];if(l&&l.dispatchedAt&&!s.dispatchedAt){return {...s,dispatchedAt:l.dispatchedAt,status:l.status,assignedStationId:l.assignedStationId,units:l.units};}return s;});d2.state.loaded=true;};\n    const refetch=async()=>{const _n=Date.now();if(refetching||_n-lastFetch<800)return;lastFetch=_n;refetching=true;try{const r=un(await d2.rush(\"fire_list\").invoke({}));mergeLocal((r&&r.items)||[]);updateMarkers();triggerAI();const _w=d2.state.watching;if(_w){const _wi=(d2.state.incidents||[]).find(i=>i._id===_w);if(!_wi||!_wi.liveVideo){try{stopWatch();}catch(e){}d2.state.watching=null;}}}catch(e){}refetching=false;};\n    const loadStations=async()=>{try{const r=un(await d2.rush(\"fire_stations\").invoke({}));d2.state.stations=(r&&r.items)||[];updateMarkers();}catch(e){}};\n    const loadCams=async()=>{try{const r=un(await d2.rush(\"fire_cams\").invoke({}));d2.state.realCams=(r&&r.cams)||[];}catch(e){}};\n    const sumState={};const aaState={};\n    const ensureSummary=async(inc)=>{if(!inc)return;const key=inc._id;if(sumState[key]===\"pending\")return;const cur=dstat(inc);const cached=(d2.state.summaries||{})[key];if(cached&&cached._st===cur)return;sumState[key]=\"pending\";try{const r=un(await d2.rush(\"fire_summary\").invoke({mode:\"live\",record:inc}));const res=(r&&r.result)||{};const s=d2.state.summaries||{};s[key]={summary_en:res.summary_en,summary_zh:res.summary_zh,_st:cur};d2.state.summaries=s;}catch(e){}sumState[key]=cur;};\n    const ensureAfterAction=async(inc)=>{if(!inc)return;const key=inc._id;if(inc.afterAction||aaState[key])return;if(dstat(inc)!==\"Resolved\"&&inc.status!==\"Resolved\")return;aaState[key]=1;try{const r=un(await d2.rush(\"fire_summary\").invoke({mode:\"closing\",record:inc}));const res=(r&&r.result)||{};const rec={...inc,afterAction:{report_en:res.report_en,report_zh:res.report_zh,at:Date.now()}};d2.state.incidents=(d2.state.incidents||[]).map(i=>i._id===key?rec:i);d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});}catch(e){}};\n    const triggerAI=()=>{const incs=d2.state.incidents||[];const sel=incs.find(i=>i._id===d2.state.selectedId);if(sel)ensureSummary(sel);incs.forEach(i=>{if((dstat(i)===\"Resolved\"||i.status===\"Resolved\")&&!i.afterAction)ensureAfterAction(i);});};\n    const ensureXlate=async(sel)=>{const dl=d2.state.dispLang||\"en\";if(!sel||dl===\"en\"||dl===\"zh\")return;const turns=sel.transcript||[];for(let i=0;i<turns.length;i++){const m=turns[i];if(m.role===\"dispatcher\")continue;const src=m.raw||m.en;if(!src)continue;const k=(m.t||\"\")+\"|\"+dl;const xl=d2.state.xl||{};if(xl[k])continue;try{const r=un(await d2.rush(\"fire_xlate\").invoke({text:src,target:dl}));if(r&&r.translation){const nx=d2.state.xl||{};nx[k]=r.translation;d2.state.xl={...nx};}}catch(e){}}};\n    const stationById=(id)=>(d2.state.stations||[]).find(s=>s._id===id);\n    const ensureRoute=async(inc,u)=>{const key=inc._id+\":\"+u.stationId;if(routeGeo[key]||routeFetching[key])return;const s=stationById(u.stationId);if(!s||!hasXY(inc)||!hasXY(s)){return;}routeFetching[key]=true;try{const url=`https://router.project-osrm.org/route/v1/driving/${s.lng},${s.lat};${inc.lng},${inc.lat}?overview=full&geometries=geojson`;const resp=await fetch(url);const j=await resp.json();const co=j&&j.routes&&j.routes[0]&&j.routes[0].geometry&&j.routes[0].geometry.coordinates;if(co&&co.length>1){routeGeo[key]=co.map(c=>[c[1],c[0]]);}else{routeGeo[key]=[[s.lat,s.lng],[inc.lat,inc.lng]];}}catch(e){const s2=stationById(u.stationId);if(s2)routeGeo[key]=[[s2.lat,s2.lng],[inc.lat,inc.lng]];}routeFetching[key]=false;};\n    const updateMarkers=()=>{\n      if(!built||!L||!map)return;\n      const incs=d2.state.incidents||[];const stations=d2.state.stations||[];\n      stations.forEach(s=>{if(!hasXY(s))return;if(!stnMk[s._id]){const ic=L.divIcon({className:\"\",iconSize:[54,44],iconAnchor:[27,22],html:`<div class=\"lstn\"><span class=\"box\">🚒</span><span class=\"nm\">${esc((s.name||\"\").split(\" \")[0])}</span></div>`});stnMk[s._id]=L.marker([s.lat,s.lng],{icon:ic,interactive:false}).addTo(map);}});\n      CAMS.forEach(cm=>{const on=d2.state.showCams;if(on&&!camMk[cm.id]){const ic=L.divIcon({className:\"\",iconSize:[26,26],iconAnchor:[13,13],html:`<div class=\"lcam\">📹</div>`});const m=L.marker([cm.lat,cm.lng],{icon:ic}).addTo(map);m.on(\"click\",()=>{d2.state.camView=cm;});camMk[cm.id]=m;}else if(!on&&camMk[cm.id]){map.removeLayer(camMk[cm.id]);delete camMk[cm.id];}});\n      const seen={};\n      incs.forEach(inc=>{if(!hasXY(inc))return;seen[inc._id]=1;const ds=dstat(inc);const c=SC[ds]||\"#ff3b30\";const act=ds!==\"Resolved\";const vb=inc.liveVideo?`<span class=\"vbadge\">●</span>`:\"\";const ic=L.divIcon({className:\"\",iconSize:[28,28],iconAnchor:[14,14],html:`<div class=\"lpin ${act?\"act\":\"\"}\" style=\"--c:${c}\"><span class=\"ring\"></span><span class=\"core\">🔥</span>${vb}</div>`});\n        if(!incMk[inc._id]){const m=L.marker([inc.lat,inc.lng],{icon:ic}).addTo(map);m.on(\"click\",()=>{d2.state.selectedId=inc._id;ensureSummary(inc);ensureXlate(inc);});incMk[inc._id]=m;}else{incMk[inc._id].setIcon(ic);incMk[inc._id].setLatLng([inc.lat,inc.lng]);}\n        const units=inc.units||[];\n        units.forEach(u=>{const key=inc._id+\":\"+u.stationId;ensureRoute(inc,u);const geo=routeGeo[key];if(geo&&!routeLn[key]){routeLn[key]=L.polyline(geo,{color:\"#0a84ff\",weight:3,opacity:.55,dashArray:\"1 8\",lineCap:\"round\"}).addTo(map);}else if(geo&&routeLn[key]){routeLn[key].setLatLngs(geo);}\n          if(!squadMk[key]){const s=stationById(u.stationId);const start=s?[s.lat,s.lng]:[inc.lat,inc.lng];const sic=L.divIcon({className:\"\",iconSize:[40,42],iconAnchor:[20,32],html:`<div class=\"lsquad\"><span class=\"eta\"></span><span class=\"veh\">🚒</span></div>`});squadMk[key]=L.marker(start,{icon:sic,interactive:false,zIndexOffset:1000}).addTo(map);}\n        });\n      });\n      Object.keys(incMk).forEach(id=>{if(!seen[id]){map.removeLayer(incMk[id]);delete incMk[id];Object.keys(squadMk).forEach(k=>{if(k.indexOf(id+\":\")===0){map.removeLayer(squadMk[k]);delete squadMk[k];}});Object.keys(routeLn).forEach(k=>{if(k.indexOf(id+\":\")===0){map.removeLayer(routeLn[k]);delete routeLn[k];}});}});\n    };\n    const buildMap=(LL,el)=>{if(built)return;L=(LL&&LL.default)||LL;try{map=L.map(el,{zoomControl:true,attributionControl:true}).setView([30.5928,114.3055],12);L.tileLayer(\"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",{subdomains:\"abcd\",maxZoom:19,attribution:\"© OpenStreetMap © CARTO\"}).addTo(map);try{map.attributionControl.setPosition(\"bottomright\");}catch(e){}built=true;d2.state.mapReady=true;setTimeout(()=>{try{map.invalidateSize();}catch(e){}},250);try{const ro=new ResizeObserver(()=>{try{map.invalidateSize();}catch(e){}});ro.observe(el);d2.onUnmount(()=>ro.disconnect());}catch(e){}updateMarkers();}catch(e){d2.state.mapErr=String((e&&e.message)||e);}};\n    const tryBuild=()=>{if(built)return;let LL=null;try{LL=d2.lib(LEAF);}catch(e){}let er=\"idle\";try{if(d2.lib.state)er=d2.lib.state(LEAF);}catch(e){}const el=d2.$(\".mapbox\");const dbg={n:(((d2.state.mapDbg||{}).n)||0)+1,L:!!LL,w:el?el.clientWidth:null,h:el?el.clientHeight:null};if(er===\"error\"){try{dbg.err=(d2.lib.error&&d2.lib.error(LEAF))||\"load error\";}catch(e){}d2.state.mapErr=dbg.err;}d2.state.mapDbg=dbg;if(LL&&el){buildMap(LL,el);}else if(er!==\"error\"){setTimeout(tryBuild,300);}};\n    loadStations();refetch();loadCams();tryBuild();\n    // ---- signaling + webrtc (answer side) ----\n    let sock=null;let pc=null,remoteStream=null,attIv=null,sigSince=\"\";const myId=\"cc-\"+Math.random().toString(36).slice(2,8);\n    const wsetStat=(k,v)=>{d2.state.wstats={...(d2.state.wstats||{}),[k]:v};};wsetStat(\"me\",myId);\n    const wlog=(s)=>{const ln=new Date().toISOString().slice(11,19)+\" \"+s;try{console.log(\"[CC-RTC]\",ln);}catch(e){}d2.state.wlog=[...((d2.state.wlog||[]).slice(-11)),ln];};\n    const emitSig=(payload)=>{try{payload.from=myId;d2.rush(\"fire_signal\").invoke(payload);wlog(\"→ \"+payload.t+\" (via server)\");}catch(e){wlog(\"emit ERR \"+(e&&e.message));}};\n    const attachVideo=()=>{try{const v=d2.$(\".livevid\");if(v&&remoteStream){if(v.srcObject!==remoteStream){v.srcObject=remoteStream;v.play&&v.play().catch(()=>{});}wsetStat(\"video\",`${v.videoWidth||0}x${v.videoHeight||0} rs:${v.readyState}`);}}catch(e){}};\n    const stopWatch=()=>{try{if(pc){pc.close();}}catch(e){}pc=null;remoteStream=null;if(attIv){clearInterval(attIv);attIv=null;}d2.state.wstats={};};\n    const startWatch=(incId)=>{const sel=(d2.state.incidents||[]).find(i=>i._id===incId);if(!sel)return;d2.state.watching=incId;d2.state.vidStatus=\"Connecting… 连接中\";d2.state.wlog=[];d2.state.wstats={me:myId,inc:String(incId).slice(-6)};wlog(\"WATCH me=\"+myId+\" inc=\"+incId);sigSince=new Date(Date.now()-20000).toISOString();try{pc=new RTCPeerConnection({iceServers:ICE});let host=0,srflx=0,relay=0;pc.ontrack=(e)=>{remoteStream=e.streams&&e.streams[0];wsetStat(\"tracks\",(remoteStream&&remoteStream.getTracks().length)||0);wlog(\"ontrack \"+((remoteStream&&remoteStream.getTracks().map(t=>t.kind).join(\"+\"))||\"\"));d2.state.vidStatus=\"Live 直播中\";attachVideo();};pc.onicecandidate=(e)=>{if(e.candidate){const cc=e.candidate.candidate||\"\";const ty=cc.indexOf(\"typ relay\")>=0?\"relay\":cc.indexOf(\"typ srflx\")>=0?\"srflx\":\"host\";if(ty===\"relay\")relay++;else if(ty===\"srflx\")srflx++;else host++;wsetStat(\"cand\",`host:${host} srflx:${srflx} relay:${relay}`);emitSig({sig:true,incidentId:incId,t:\"ice\",role:\"watcher\",c:e.candidate});}};pc.oniceconnectionstatechange=()=>{wsetStat(\"ice\",pc.iceConnectionState);wlog(\"ice=\"+pc.iceConnectionState);};pc.onconnectionstatechange=()=>{wsetStat(\"conn\",pc.connectionState);wlog(\"conn=\"+pc.connectionState);};emitSig({sig:true,incidentId:incId,t:\"watch-request\",role:\"watcher\"});if(attIv)clearInterval(attIv);attIv=setInterval(attachVideo,700);d2.onUnmount(()=>{if(attIv)clearInterval(attIv);});}catch(e){d2.state.vidStatus=\"WebRTC unavailable 不支持\";wlog(\"pc ERR \"+(e&&e.message));}};\n    const handleSig=async(p)=>{if(!p||!p.sig||p.role===\"watcher\")return;if(d2.state.watching!==p.incidentId)return;wlog(\"← \"+p.t);try{if(p.t===\"offer\"&&pc){await pc.setRemoteDescription(p.sdp);wsetStat(\"sig\",pc.signalingState);const ans=await pc.createAnswer();await pc.setLocalDescription(ans);emitSig({sig:true,incidentId:p.incidentId,t:\"answer\",role:\"watcher\",sdp:ans});wlog(\"answer sent\");}else if(p.t===\"ice\"&&pc&&p.c){try{await pc.addIceCandidate(p.c);}catch(e){}}else if(p.t===\"announce\"){emitSig({sig:true,incidentId:p.incidentId,t:\"watch-request\",role:\"watcher\"});}else if(p.t===\"end\"){stopWatch();d2.state.watching=null;d2.state.vidStatus=\"Broadcast ended 直播已结束\";}}catch(e){wlog(\"handleSig ERR \"+(e&&e.message));}};\n    try{sock=d2.socket(\"feed\");sock.subscribe(\"wuhan\");sock.onMessage((m)=>{const p=(m&&m.data!==undefined)?m.data:m;const room=(m&&(m.room||m.channel))||\"wuhan\";if(d2.state.watching){wlog(\"recv[\"+room+\"] \"+((p&&p.sig)?(\"sig/\"+(p.t||\"?\")):\"incident\")+((p&&p.incidentId)?(\" inc=\"+String(p.incidentId).slice(-5)):\"\")+((p&&p.from)?(\" from=\"+p.from):\"\"));}if(p&&p.sig){if(p.from===myId)return;handleSig(p);}else{refetch();}});}catch(e){}\n    const hb=setInterval(async()=>{const incs=d2.state.incidents||[];const anyActive=incs.some(i=>i.dispatchedAt&&i.status!==\"Resolved\"&&PORD[dstat(i)]<4);if(anyActive){try{await d2.rush(\"fire_tick\").invoke({});}catch(e){}refetch();}},2500);\n    const poll=setInterval(()=>{refetch();},6000);const ccSigPoll=async()=>{if(!d2.state.watching)return;try{const r=un(await d2.rush(\"fire_signal_poll\").invoke({incidentId:d2.state.watching,since:sigSince||\"\"}));const items=(r&&r.items)||[];if(r&&r.now)sigSince=r.now;items.forEach(row=>{const p=(row&&row.body)?row.body:row;if(!p||!p.sig||p.from===myId)return;wlog(\"poll← \"+p.t+(p.from?(\" from=\"+p.from):\"\"));handleSig(p);});}catch(e){}};const ccSigIv=setInterval(ccSigPoll,1000);d2.onUnmount(()=>clearInterval(ccSigIv));\n    d2.onUnmount(()=>{clearInterval(hb);clearInterval(poll);});\n    const ck=setInterval(()=>{const el=d2.$(\"#clk\");if(el)el.textContent=new Date().toLocaleTimeString(\"en-GB\");const ts=d2.$(\"#camts\");if(ts)ts.textContent=new Date().toLocaleString(\"en-GB\");attachVideo();},1000);\n    d2.onUnmount(()=>clearInterval(ck));\n    const nearestStations=(inc,exclude)=>{return (d2.state.stations||[]).filter(s=>hasXY(s)&&exclude.indexOf(s._id)<0).map(s=>({s,d:dist([inc.lat,inc.lng],[s.lat,s.lng])})).sort((a,b)=>a.d-b.d).map(x=>x.s);};\n    const dispatch=(id)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc||dstat(inc)!==\"Reported\")return;const ns=nearestStations(inc,[]);if(!ns.length)return;const best=ns[0];const now=Date.now();const unit={stationId:best._id,name:`${best.name} ${best.nameZh}`,dispatchedAt:now,role:\"primary\"};const rec={...inc,status:\"Dispatched\",assignedStationId:best._id,assignedStationName:unit.name,dispatchedAt:now,units:[unit]};d2.state.incidents=incs.map(i=>i._id===id?rec:i);d2.state.selectedId=id;updateMarkers();d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const reinforce=(id)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc)return;const units=inc.units||[];const mx=maxUnits(inc);if(units.length>=mx)return;const used=units.map(u=>u.stationId);const ns=nearestStations(inc,used);if(!ns.length)return;const best=ns[0];const now=Date.now();const unit={stationId:best._id,name:`${best.name} ${best.nameZh}`,dispatchedAt:now,role:\"backup\"};const rec={...inc,units:[...units,unit]};d2.state.incidents=incs.map(i=>i._id===id?rec:i);updateMarkers();d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const setHuman=(id,val)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc)return;const rec={...inc,humanControl:val};d2.state.incidents=incs.map(i=>i._id===id?rec:i);d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const dispatcherSay=(id,text)=>{const t=(text||\"\").trim();if(!t)return;const inc=(d2.state.incidents||[]).find(i=>i._id===id);if(!inc)return;const tgt=inc.citizenLang||\"en\";d2.rush(\"fire_say\").invoke({incidentId:id,text:t,target:tgt,transcript:inc.transcript||[]}).then(()=>refetch()).catch(()=>{});};\n    d2.on(\".disp\",\"click\",(e,el)=>{dispatch(el.dataset.id);});\n    d2.on(\".reinforce\",\"click\",(e,el)=>{reinforce(el.dataset.id);});\n    d2.on(\".card\",\"click\",(e,el)=>{d2.state.selectedId=el.dataset.id;const inc=(d2.state.incidents||[]).find(i=>i._id===el.dataset.id);if(inc){ensureSummary(inc);ensureXlate(inc);}});\n    d2.on(\".close\",\"click\",()=>{d2.state.selectedId=null;stopWatch();d2.state.watching=null;});\n    d2.on(\".wdbgbtn\",\"click\",()=>{d2.state.wdbgOn=!d2.state.wdbgOn;});\n    d2.on(\".camtoggle\",\"click\",()=>{d2.state.showCams=!d2.state.showCams;updateMarkers();});\n    d2.on(\".displang\",\"change\",(e,el)=>{d2.state.dispLang=el.value;const s=(d2.state.incidents||[]).find(i=>i._id===d2.state.selectedId);if(s)ensureXlate(s);});\n    d2.on(\".opencam\",\"click\",(e,el)=>{const cm=CAMS.find(c=>c.id===el.dataset.cam);if(cm)d2.state.camView=cm;});\n    d2.on(\".camclose\",\"click\",()=>{d2.state.camView=null;});\n    d2.on(\".takeover\",\"click\",(e,el)=>{const id=el.dataset.id;const inc=(d2.state.incidents||[]).find(i=>i._id===id);setHuman(id,!(inc&&inc.humanControl===true));});\n    d2.on(\".dispsend\",\"click\",(e,el)=>{const inp=d2.$(\".dispinput\");if(inp){dispatcherSay(el.dataset.id,inp.value);inp.value=\"\";}});\n    d2.on(\".dispinput\",\"keydown\",(e,el)=>{if(e.key===\"Enter\"){e.preventDefault();const id=(d2.$(\".dispsend\")||{}).dataset?d2.$(\".dispsend\").dataset.id:d2.state.selectedId;dispatcherSay(d2.state.selectedId,el.value);el.value=\"\";}});\n    d2.on(\".watch\",\"click\",(e,el)=>{startWatch(el.dataset.id);});\n    d2.raf(()=>{\n      if(!built)return;const now=Date.now();const incs=d2.state.incidents||[];let need=false;\n      incs.forEach(inc=>{if(!inc.dispatchedAt||inc.status===\"Resolved\")return;const ph=phaseOf(now-inc.dispatchedAt);if(lastPhase[inc._id]!==ph){lastPhase[inc._id]=ph;need=true;}});\n      Object.keys(squadMk).forEach(key=>{const idx=key.indexOf(\":\");const incId=key.slice(0,idx);const stId=key.slice(idx+1);const inc=incs.find(i=>i._id===incId);if(!inc){return;}const u=(inc.units||[]).find(x=>x.stationId===stId);if(!u){return;}const geo=routeGeo[key];const s=stationById(stId);let pos=null;const el=now-(u.dispatchedAt||now);const p=unitP(el);if(geo&&geo.length){pos=pointAlong(geo,p);}else if(s&&hasXY(inc)){pos=[s.lat+(inc.lat-s.lat)*ease(p),s.lng+(inc.lng-s.lng)*ease(p)];}if(pos&&Number.isFinite(pos[0])&&Number.isFinite(pos[1])){try{squadMk[key].setLatLng(pos);}catch(e){}const elm=squadMk[key].getElement&&squadMk[key].getElement();if(elm){const et=elm.querySelector(\".eta\");if(et){const ph=phaseOf(el);et.textContent=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"OnScene\"?\"到场\":\"\");}}}});\n      if(need){updateMarkers();d2.refresh();}\n    });\n  },\n};"}}}}}},"__d2PrevCode":{"code":"const T_DISP=4000,T_ENROUTE=49000,T_SCENE=89000;\nconst SC={Intake:\"#30b0c7\",Reported:\"#ff3b30\",Dispatched:\"#ff9500\",EnRoute:\"#0a84ff\",OnScene:\"#5e5ce6\",Resolved:\"#34c759\"};\nconst SL={Intake:[\"Intake\",\"接线中\"],Reported:[\"Reported\",\"已报告\"],Dispatched:[\"Dispatched\",\"已派遣\"],EnRoute:[\"En route\",\"前往中\"],OnScene:[\"On scene\",\"已到场\"],Resolved:[\"Resolved\",\"已解决\"]};\nconst PORD={Intake:0,Reported:0,Dispatched:1,EnRoute:2,OnScene:3,Resolved:4};\nconst EC={panic:\"#ff3b30\",fear:\"#ff9500\",distress:\"#5e5ce6\",anger:\"#c9302c\",confusion:\"#ffcc00\",calm:\"#34c759\"};\nconst LEAF={name:\"leaflet@1.9.4\",css:\"https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.css\"};\nconst ICE=[{urls:[\"stun:stun.l.google.com:19302\",\"stun:stun1.l.google.com:19302\"]},{urls:[\"turn:openrelay.metered.ca:80\",\"turn:openrelay.metered.ca:443\",\"turn:openrelay.metered.ca:443?transport=tcp\"],username:\"openrelayproject\",credential:\"openrelayproject\"},{urls:[\"turn:openrelay.metered.ca:80?transport=tcp\"],username:\"openrelayproject\",credential:\"openrelayproject\"}];\nconst CAMS=[{id:\"CAM-01\",name:\"Yangtze Bridge\",zh:\"长江大桥\",lat:30.5530,lng:114.2930,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-02\",name:\"Jiefang Ave\",zh:\"解放大道\",lat:30.5960,lng:114.2870,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"},{id:\"CAM-03\",name:\"Optics Valley Sq\",zh:\"光谷广场\",lat:30.5070,lng:114.4260,yt:\"Ksrleaxxxhw\",feed:\"New Orleans, Bourbon St\"},{id:\"CAM-04\",name:\"Wuhan Railway Stn\",zh:\"武汉站\",lat:30.6080,lng:114.4270,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-05\",name:\"Chu River Han St\",zh:\"楚河汉街\",lat:30.5540,lng:114.3480,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"},{id:\"CAM-06\",name:\"Yellow Crane Tower\",zh:\"黄鹤楼\",lat:30.5449,lng:114.3025,yt:\"Ksrleaxxxhw\",feed:\"New Orleans, Bourbon St\"},{id:\"CAM-07\",name:\"Hankou Bund\",zh:\"汉口江滩\",lat:30.5850,lng:114.3020,yt:\"VGnFLdQW39A\",feed:\"Times Square, New York\"},{id:\"CAM-08\",name:\"East Lake\",zh:\"东湖\",lat:30.5560,lng:114.4180,yt:\"JQ_jwk_7OVE\",feed:\"New York City\"}];\nfunction phaseOf(el){if(el<T_DISP)return \"Dispatched\";if(el<T_ENROUTE)return \"EnRoute\";if(el<T_SCENE)return \"OnScene\";return \"Resolved\";}\nfunction dstat(inc){if(inc.dispatchedAt&&inc.status!==\"Resolved\"){return phaseOf(Date.now()-inc.dispatchedAt);}return inc.status;}\nfunction hasXY(i){return i&&Number.isFinite(i.lat)&&Number.isFinite(i.lng)&&i.lat>10;}\nfunction ease(p){return p*p*(3-2*p);}\nfunction esc(s){return String(s==null?\"\":s).replace(/&/g,\"&amp;\").replace(/</g,\"&lt;\").replace(/>/g,\"&gt;\");}\nfunction un(r){return (r&&typeof r===\"object\"&&r.data!==undefined&&r.items===undefined&&r.ok===undefined&&r.cams===undefined)?r.data:r;}\nfunction dist(a,b){const dy=a[0]-b[0],dx=a[1]-b[1];return Math.sqrt(dy*dy+dx*dx);}\nfunction pointAlong(pts,p){if(!pts||pts.length===0)return null;if(pts.length===1)return pts[0];let total=0;const segs=[];for(let i=1;i<pts.length;i++){const d=dist(pts[i-1],pts[i]);segs.push(d);total+=d;}if(total<=0)return pts[0];const target=Math.max(0,Math.min(1,p))*total;let acc=0;for(let i=1;i<pts.length;i++){const d=segs[i-1];if(acc+d>=target){const f=d>0?(target-acc)/d:0;return [pts[i-1][0]+(pts[i][0]-pts[i-1][0])*f,pts[i-1][1]+(pts[i][1]-pts[i-1][1])*f];}acc+=d;}return pts[pts.length-1];}\nfunction unitP(el){if(el<=T_DISP)return 0;if(el>=T_ENROUTE)return 1;return (el-T_DISP)/(T_ENROUTE-T_DISP);}\nfunction maxUnits(inc){const sev=(inc.severity||\"\");const trapped=(inc.peopleTrapped||\"\").indexOf(\"Yes\")>=0;if(sev.indexOf(\"Large\")>=0||sev.indexOf(\"大\")>=0||trapped)return 3;if(sev.indexOf(\"Medium\")>=0||sev.indexOf(\"中\")>=0)return 2;return 1;}\nfunction gauge(e){const c=EC[e.emotion]||\"#8a8a8e\";const iv=Number(e.intensity)||0;const pct=Math.round(iv*100);const r=26,circ=2*Math.PI*r,off=circ*(1-iv);return `<svg width=\"62\" height=\"62\" viewBox=\"0 0 64 64\"><circle cx=\"32\" cy=\"32\" r=\"${r}\" fill=\"none\" stroke=\"rgba(120,120,128,.16)\" stroke-width=\"6\"/><circle cx=\"32\" cy=\"32\" r=\"${r}\" fill=\"none\" stroke=\"${c}\" stroke-width=\"6\" stroke-linecap=\"round\" stroke-dasharray=\"${circ.toFixed(1)}\" stroke-dashoffset=\"${off.toFixed(1)}\" transform=\"rotate(-90 32 32)\"/><text x=\"32\" y=\"31\" text-anchor=\"middle\" font-size=\"15\" font-weight=\"800\" fill=\"${c}\">${pct}</text><text x=\"32\" y=\"43\" text-anchor=\"middle\" font-size=\"7\" fill=\"#8a8a8e\">STRESS</text></svg>`;}\nfunction emoChart(ems){const W=260,H=54;if(!ems||!ems.length)return \"\";const n=ems.length;const pts=ems.map((e,i)=>{const x=n===1?W/2:(i/(n-1))*W;const y=H-4-(Number(e.intensity)||0)*(H-10);return [x,y];});const line=pts.map((p,i)=>(i?\"L\":\"M\")+p[0].toFixed(1)+\" \"+p[1].toFixed(1)).join(\" \");const area=`M0 ${H} `+pts.map(p=>`L${p[0].toFixed(1)} ${p[1].toFixed(1)}`).join(\" \")+` L${W} ${H} Z`;const dots=ems.map((e,i)=>{const c=EC[e.emotion]||\"#8a8a8e\";return `<circle cx=\"${pts[i][0].toFixed(1)}\" cy=\"${pts[i][1].toFixed(1)}\" r=\"3\" fill=\"${c}\" stroke=\"#fff\" stroke-width=\"1.5\"/>`;}).join(\"\");const last=EC[ems[n-1].emotion]||\"#8a8a8e\";return `<svg width=\"100%\" viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"none\" style=\"display:block\"><defs><linearGradient id=\"eg\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"${last}\" stop-opacity=\"0.35\"/><stop offset=\"1\" stop-color=\"${last}\" stop-opacity=\"0.02\"/></linearGradient></defs><path d=\"${area}\" fill=\"url(#eg)\"/><path d=\"${line}\" fill=\"none\" stroke=\"${last}\" stroke-width=\"2\"/>${dots}</svg>`;}\nfunction fmtTime(t){if(!t)return \"—\";try{return new Date(t).toLocaleTimeString(\"en-GB\");}catch(e){return \"—\";}}\nfunction renderDetail(d2,sel){\n    const st=d2.state;const dl=st.dispLang||\"en\";const xl=st.xl||{};const pick=(m)=>{if(m.role===\"dispatcher\")return m.raw||m.translated||m.en||\"\";if(dl===\"zh\")return m.zh||m.en||m.raw||\"\";if(dl===\"en\")return m.en||m.raw||\"\";const k=(m.t||\"\")+\"|\"+dl;return xl[k]||m.en||m.raw||\"\";};const ds=dstat(sel);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";\n    const ems=sel.emotions||[];const le=ems.length?ems[ems.length-1]:null;\n    const units=sel.units||[];const mx=maxUnits(sel);\n    const now=Date.now();\n    let emoBlock=\"\";\n    if(le){const elc=EC[le.emotion]||\"#8a8a8e\";emoBlock=`<div class=\"sec\"><div class=\"sechd\">Caller stress · 来电者情绪</div><div style=\"display:flex;align-items:center;gap:12px\"><div style=\"text-align:center\">${gauge(le)}<div style=\"font-size:11px;font-weight:700;color:${elc}\">${esc(le.label_en)} ${esc(le.label_zh)}</div></div><div style=\"flex:1;min-width:0\">${emoChart(ems)}</div></div></div>`;}\n    let unitBlock=\"\";\n    if(units.length){unitBlock=`<div class=\"sec\"><div class=\"sechd\">Assigned units · 出动队伍 (${units.length})</div>${units.map(u=>{const el=now-(u.dispatchedAt||now);const ph=phaseOf(el);const usl=SL[ph]||[\"\",\"\"];const uc=SC[ph]||\"#0a84ff\";const eta=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"Dispatched\"?\"leaving 出发\":(ph===\"OnScene\"?\"on scene 到场\":\"resolved 完成\"));return `<div class=\"unit\"><div class=\"unitico\">🚒</div><div class=\"unitnm\">${esc(u.name||\"Unit\")}<br><span style=\"font-size:10.5px;color:#8a8a8e;font-weight:500\">${esc(u.role===\"backup\"?\"Reinforcement 增援\":\"Primary 主力\")}</span></div><div style=\"text-align:right\"><span class=\"unitst\" style=\"background:${uc}22;color:${uc}\">${usl[0]}</span><div style=\"font-size:10px;color:#8a8a8e;margin-top:2px\">${eta}</div></div></div>`;}).join(\"\")}</div>`;}\n    let vidBlock=\"\";\n    if(sel.liveVideo){vidBlock=`<div class=\"sec\"><div class=\"sechd\">📹 Live video · 现场直播 <span class=\"vtag\">● LIVE</span></div>${st.watching===sel._id?`<video class=\"livevid\" autoplay playsinline muted></video><div style=\"font-size:10.5px;color:#8a8a8e;margin-top:5px\">${esc(st.vidStatus||\"Connecting… 连接中\")}</div><button class=\"wdbgbtn\">🐞 ${st.wdbgOn?\"hide debug\":\"debug\"}</button>${st.wdbgOn?`<pre class=\"vdbg\">status: ${esc(st.vidStatus||\"-\")}\\ntracks: ${esc(\"\"+((st.wstats||{}).tracks!=null?(st.wstats||{}).tracks:\"-\"))}  sig: ${esc((st.wstats||{}).sig||\"-\")}\\nice: ${esc((st.wstats||{}).ice||\"-\")}  conn: ${esc((st.wstats||{}).conn||\"-\")}\\nvideo: ${esc((st.wstats||{}).video||\"0x0\")}\\ncandidates: ${esc((st.wstats||{}).cand||\"(none yet - TURN down?)\")}\\n---\\n${esc((st.wlog||[]).join(\"\\n\"))}</pre>`:\"\"}`:`<button class=\"btn red watch\" data-id=\"${sel._id}\">▶ Watch live · 观看直播</button>`}</div>`;}\n    const cams=CAMS.map(cm=>({cm,d:dist([sel.lat,sel.lng],[cm.lat,cm.lng])})).sort((a,b)=>a.d-b.d).slice(0,3);\n    const camBlock=hasXY(sel)?`<div class=\"sec\"><div class=\"sechd\">Nearby cameras · 附近监控</div>${cams.map(x=>`<div class=\"camrow opencam\" data-cam=\"${x.cm.id}\">📹 ${esc(x.cm.name)} ${esc(x.cm.zh)}<span class=\"camdist\">${(x.d*111).toFixed(1)} km</span></div>`).join(\"\")}</div>`:\"\";\n    const tr=sel.transcript||[];\n    const thread=tr.length?tr.map(m=>{const role=m.role||\"agent\";const rl=role===\"reporter\"?\"Caller 报案人\":(role===\"dispatcher\"?\"Dispatcher 调度员\":\"AI Agent 智能助理\");const main=esc(pick(m));const zh=(role===\"agent\"&&m.zh)?`<div class=\"zh\">${esc(m.zh)}</div>`:\"\";const orig=(m.raw&&m.srcLang&&m.srcLang!==\"en\")?`<div class=\"torig\"><span class=\"rfrom\">译自 translated from ${esc(m.srcLang)}:</span> ${esc(m.raw)}</div>`:\"\";return `<div class=\"tmsg ${role}\"><span class=\"rolelbl\">${rl}</span>${main}${zh}${orig}</div>`;}).join(\"\"):`<div style=\"font-size:12px;color:#8a8a8e\">No messages yet · 暂无对话</div>`;\n    const human=sel.humanControl===true;\n    const dispBar=human?`<div class=\"dispbar\"><input class=\"dispinput\" placeholder=\"Message the caller… 给报案人发消息\"/><button class=\"dispsend\" data-id=\"${sel._id}\">↑</button></div>`:\"\";\n    const threadBlock=`<div class=\"sec\"><div class=\"sechd\">Live transcript · 实时对话 ${human?'<span class=\"vtag\" style=\"background:#ff9500\">HUMAN 人工</span>':\"\"}</div><div class=\"thread\">${thread}</div>${dispBar}<button class=\"btn ${human?\"gray\":\"orange\"} takeover\" data-id=\"${sel._id}\" style=\"margin-top:8px\">${human?\"↩ Resume AI · 恢复AI\":\"✋ Take over · 人工接管\"}</button></div>`;\n    const disp=sel.dispatchedAt;\n    const tlrows=[[\"Reported 已报告\",sel.createdAt,\"#ff3b30\",true],[\"Dispatched 已派遣\",disp,\"#ff9500\",!!disp],[\"En route 前往中\",disp?disp+T_DISP:null,\"#0a84ff\",disp&&(now-disp)>=T_DISP],[\"On scene 已到场\",disp?disp+T_ENROUTE:null,\"#5e5ce6\",disp&&(now-disp)>=T_ENROUTE],[\"Resolved 已解决\",disp?disp+T_SCENE:null,\"#34c759\",(sel.status===\"Resolved\")||(disp&&(now-disp)>=T_SCENE)]];\n    const timeline=`<div class=\"sec\"><div class=\"sechd\">Timeline · 时间线</div><div class=\"tl\">${tlrows.map(r=>`<div class=\"tlrow ${r[3]?\"\":\"pending\"}\"><div class=\"tldot\" style=\"background:${r[3]?r[2]:\"#c7c7cc\"}\"></div><div class=\"tltxt\">${r[0]}<div class=\"tlt\">${r[3]?fmtTime(r[1]):\"pending 待定\"}</div></div></div>`).join(\"\")}</div></div>`;\n    const canReinforce=(disp&&sel.status!==\"Resolved\"&&dstat(sel)!==\"Resolved\"&&units.length<mx);\n    let actions=\"\";\n    if(ds===\"Reported\"){actions=`<button class=\"btn red disp\" data-id=\"${sel._id}\">🚒 Dispatch nearest unit · 派遣最近队伍</button>`;}\n    else if(canReinforce){actions=`<button class=\"btn orange reinforce\" data-id=\"${sel._id}\">➕ Send reinforcements · 派遣增援 (${units.length}/${mx})</button>`;}\n    const sm=((d2.state.summaries||{})[sel._id])||null;\n    const summaryBlock=`<div class=\"sec\"><div class=\"sechd\">Case summary · 案情摘要</div>${sm?`<div class=\"summarybox\"><div>${esc(sm.summary_en||\"\")}</div><div class=\"zh2\">${esc(sm.summary_zh||\"\")}</div></div>`:`<div class=\"summarybox gen\">Generating… · 生成中…</div>`}</div>`;\n    const aa=sel.afterAction;\n    const aaBlock=aa?`<div class=\"sec\"><div class=\"sechd\">After-action report · 事后报告</div><div class=\"aabox\"><div>${esc(aa.report_en||\"\")}</div><div class=\"zh2\">${esc(aa.report_zh||\"\")}</div></div></div>`:\"\";\n    return `<div class=\"detail\" data-key=\"detail\"><button class=\"close\">✕</button><div class=\"dttl\">${esc(sel.title||\"Incoming call · 接线中\")}</div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span>${summaryBlock}\n      <div class=\"sec\"><div class=\"dkv\"><span class=\"k\">Location 位置</span><span class=\"v\">${esc(sel.locationText||\"—\")}</span><span class=\"k\">District 辖区</span><span class=\"v\">${esc(sel.district||\"—\")}</span><span class=\"k\">Type 类型</span><span class=\"v\">🔥 ${esc(sel.fireType||\"—\")}</span><span class=\"k\">Severity 级别</span><span class=\"v\">${esc(sel.severity||\"—\")}</span><span class=\"k\">Trapped 被困</span><span class=\"v\">${esc(sel.peopleTrapped||\"—\")}</span><span class=\"k\">Hazards 危险</span><span class=\"v\">${esc(sel.hazards||\"—\")}</span><span class=\"k\">Coords 坐标</span><span class=\"v\">${hasXY(sel)?sel.lat.toFixed(4)+\", \"+sel.lng.toFixed(4):\"pending\"}</span></div>${sel.photoAnalysis?`<div class=\"dvis\"><b>📸 AI vision · 影像分析</b><br>${esc(sel.photoAnalysis)}</div>`:\"\"}${sel.photoAddress?`<div class=\"dvis\" style=\"background:rgba(255,149,0,.12);border-color:rgba(255,149,0,.3)\"><b style=\"color:#b35a00\">📍 Address from photo</b> <span class=\"fromphoto\">来自照片 from photo</span><br>${esc(sel.photoAddress)}</div>`:\"\"}${actions}</div>\n      ${vidBlock}${unitBlock}${emoBlock}${threadBlock}${camBlock}${timeline}${aaBlock}</div>`;\n}\nfunction renderCam(d2,cam){\n    return `<div class=\"camov\"><div class=\"camwin\"><div class=\"camfeed\">${cam.yt?`<iframe class=\"camiframe\" src=\"https://www.youtube-nocookie.com/embed/${cam.yt}?autoplay=1&mute=1&playsinline=1&rel=0\" allow=\"autoplay; encrypted-media; picture-in-picture\" allowfullscreen></iframe>`:`<div class=\"vidph\">No live feed available · 无可用信号</div>`}<div class=\"camhud\"><div class=\"tl2\"><span class=\"rec\"></span>LIVE</div><div class=\"br\" style=\"background:#34c759;color:#000\">PARTNER FEED</div></div></div><div class=\"camttl\"><span>📹 ${esc(cam.name)} · ${esc(cam.zh)} <span style=\"opacity:.65;font-weight:500;font-size:11px\">— LIVE · ${esc(cam.feed||\"partner feed\")} · 合作信号</span></span><button class=\"camclose\">✕</button></div></div></div>`;\n}\nexport default {\n  css:`\n  .shell{height:100%;display:grid;grid-template-columns:360px 1fr;font-family:-apple-system,\"SF Pro Display\",\"SF Pro Text\",\"PingFang SC\",\"Helvetica Neue\",system-ui,sans-serif;background:#f5f5f7;color:#1d1d1f;-webkit-font-smoothing:antialiased;overflow:hidden;}\n  .side{background:rgba(255,255,255,.7);backdrop-filter:saturate(180%) blur(30px);-webkit-backdrop-filter:saturate(180%) blur(30px);border-right:1px solid rgba(0,0,0,.07);display:flex;flex-direction:column;overflow:hidden;}\n  .shead{padding:18px 20px;border-bottom:1px solid rgba(0,0,0,.06);display:flex;justify-content:space-between;align-items:flex-start;}\n  .brand{display:flex;gap:11px;align-items:center;}\n  .badge{width:38px;height:38px;border-radius:11px;display:flex;align-items:center;justify-content:center;background:linear-gradient(180deg,#ff5a4d,#ff3b30);box-shadow:0 5px 14px rgba(255,59,48,.35);font-size:19px;}\n  .stitle{font-size:16px;font-weight:700;letter-spacing:-.02em;}\n  .ssub{font-size:11.5px;color:#6e6e73;}\n  .stat{text-align:right;}.statn{font-size:26px;font-weight:800;color:#ff3b30;line-height:1;}.statl{font-size:10px;color:#8a8a8e;margin-top:2px;}\n  .qhead{padding:12px 20px 6px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#8a8a8e;}\n  .queue{flex:1;overflow:auto;padding:6px 12px 16px;display:flex;flex-direction:column;gap:9px;}\n  .card{background:#fff;border:1px solid rgba(0,0,0,.06);border-radius:16px;padding:13px 14px;cursor:pointer;transition:transform .15s,box-shadow .2s,border-color .2s;box-shadow:0 1px 3px rgba(0,0,0,.04);animation:cardin .35s ease;}\n  .card:hover{transform:translateY(-1px);box-shadow:0 8px 22px rgba(0,0,0,.08);}\n  .card.sel{border-color:#0a84ff;box-shadow:0 8px 22px rgba(10,132,255,.18);}\n  .crow{display:flex;align-items:flex-start;gap:9px;}\n  .dot{width:9px;height:9px;border-radius:50%;flex:0 0 9px;margin-top:5px;}\n  .ctitle{flex:1;min-width:0;}.cen{font-size:14px;font-weight:650;line-height:1.2;}.czh{font-size:12px;color:#6e6e73;margin-top:1px;}\n  .pill{font-size:10.5px;font-weight:700;padding:3px 8px;border-radius:8px;white-space:nowrap;}\n  .cmeta{font-size:11.5px;color:#6e6e73;margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;align-items:center;}\n  .etag{font-size:10px;font-weight:700;color:#fff;padding:1px 7px;border-radius:6px;}\n  .vtag{font-size:10px;font-weight:700;color:#fff;background:#ff2d55;padding:1px 7px;border-radius:6px;}\n  .btn{margin-top:9px;width:100%;border:0;border-radius:11px;padding:9px;font:inherit;font-size:12px;font-weight:650;color:#fff;cursor:pointer;transition:transform .15s,opacity .2s;}\n  .btn:active{transform:scale(.97);}.btn[disabled]{opacity:.45;cursor:default;}\n  .btn.red{background:linear-gradient(180deg,#ff5a4d,#ff3b30);box-shadow:0 4px 12px rgba(255,59,48,.3);}\n  .btn.orange{background:linear-gradient(180deg,#ffab2e,#ff9500);box-shadow:0 4px 12px rgba(255,149,0,.3);}\n  .btn.blue{background:linear-gradient(180deg,#3a95ff,#0a84ff);box-shadow:0 4px 12px rgba(10,132,255,.3);}\n  .btn.gray{background:rgba(120,120,128,.16);color:#1d1d1f;}\n  .empty{color:#8a8a8e;text-align:center;padding:40px 12px;font-size:13px;line-height:1.6;}\n  .sk{height:74px;border-radius:16px;background:linear-gradient(90deg,#ececf0 25%,#f5f5f8 37%,#ececf0 63%);background-size:400% 100%;animation:sh 1.4s ease infinite;}\n  .main{position:relative;display:flex;flex-direction:column;overflow:hidden;}\n  .mapbox{position:absolute;inset:0;background:#eef0f4;}\n  .maploading{position:absolute;inset:0;z-index:500;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;background:linear-gradient(90deg,#e9ebef 25%,#f3f4f7 37%,#e9ebef 63%);background-size:400% 100%;animation:sh 1.4s ease infinite;color:#6e6e73;font-size:13px;font-weight:600;}\n  .maploading .ring{width:26px;height:26px;border:3px solid rgba(0,0,0,.12);border-top-color:#ff3b30;border-radius:50%;animation:spin .8s linear infinite;}\n  .maptop{position:absolute;top:0;left:0;right:0;z-index:600;display:flex;justify-content:space-between;align-items:center;padding:14px 20px;background:linear-gradient(180deg,rgba(245,245,247,.92),rgba(245,245,247,0));backdrop-filter:blur(10px);pointer-events:none;}\n  .mt-l{font-size:17px;font-weight:700;letter-spacing:-.02em;}.mt-sub{font-size:12px;color:#8a8a8e;font-weight:500;margin-left:8px;}\n  .mt-r{display:flex;gap:8px;align-items:center;pointer-events:auto;}\n  .camtoggle{border:0;font:inherit;font-size:12px;font-weight:600;padding:6px 11px;border-radius:10px;cursor:pointer;background:rgba(255,255,255,.8);color:#3c3c43;box-shadow:0 1px 3px rgba(0,0,0,.08);}\n  .camtoggle[data-on]{background:#0a84ff;color:#fff;}\n  .displang{border:0;font:inherit;font-size:12px;font-weight:600;padding:6px 8px;border-radius:10px;background:rgba(255,255,255,.8);color:#3c3c43;box-shadow:0 1px 3px rgba(0,0,0,.08);cursor:pointer;}\n  .card.live{border-color:#ff2d55!important;box-shadow:0 0 0 1px #ff2d55,0 8px 22px rgba(255,45,85,.2)!important;}\n  .clock{font-size:13px;font-weight:600;color:#6e6e73;font-variant-numeric:tabular-nums;background:rgba(255,255,255,.72);padding:6px 12px;border-radius:10px;box-shadow:0 1px 3px rgba(0,0,0,.06);display:flex;align-items:center;gap:7px;}\n  .livedot{width:7px;height:7px;border-radius:50%;background:#34c759;animation:livep 1.8s infinite;}\n  .mapwrap{flex:1;position:relative;overflow:hidden;}\n  .legend{position:absolute;bottom:16px;left:14px;z-index:550;display:flex;flex-wrap:wrap;gap:8px;background:rgba(255,255,255,.8);backdrop-filter:blur(20px);border:1px solid rgba(0,0,0,.06);border-radius:12px;padding:8px 11px;box-shadow:0 4px 16px rgba(0,0,0,.1);max-width:58%;}\n  .lg{font-size:11px;color:#3c3c43;display:flex;align-items:center;gap:5px;font-weight:600;}\n  .detail{position:absolute;right:16px;top:70px;bottom:16px;width:360px;overflow:auto;background:rgba(255,255,255,.9);backdrop-filter:saturate(180%) blur(30px);-webkit-backdrop-filter:saturate(180%) blur(30px);border:1px solid rgba(0,0,0,.08);border-radius:20px;padding:18px;box-shadow:0 24px 70px rgba(0,0,0,.22);z-index:650;animation:slidein .3s cubic-bezier(.2,.9,.3,1.1);}\n  .close{position:absolute;top:12px;right:12px;width:26px;height:26px;border:0;border-radius:50%;background:rgba(120,120,128,.14);cursor:pointer;font-size:12px;color:#6e6e73;z-index:2;}\n  .dttl{font-size:16px;font-weight:700;padding-right:30px;margin-bottom:6px;letter-spacing:-.01em;}\n  .sec{margin-top:14px;padding-top:12px;border-top:1px solid rgba(0,0,0,.07);}\n  .sechd{font-size:10.5px;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:#8a8a8e;margin-bottom:8px;display:flex;align-items:center;gap:6px;}\n  .dkv{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;font-size:12.5px;}\n  .dkv .k{color:#8a8a8e;}.dkv .v{font-weight:600;text-align:right;}\n  .dvis{margin-top:8px;font-size:11.5px;line-height:1.45;background:rgba(52,199,89,.12);border:1px solid rgba(52,199,89,.25);border-radius:12px;padding:9px 11px;}.dvis b{color:#248a3d;}\n  .fromphoto{font-size:9px;font-weight:700;background:#ff9500;color:#fff;padding:1px 6px;border-radius:6px;}\n  .wdbgbtn{margin-top:6px;font-size:10px;border:0;background:rgba(120,120,128,.16);border-radius:8px;padding:4px 8px;cursor:pointer;color:#3c3c43;}\n  .vdbg{margin-top:6px;font-size:9.5px;font-family:ui-monospace,monospace;background:#0b0b0f;color:#8fef8f;padding:8px;border-radius:8px;white-space:pre-wrap;max-height:150px;overflow:auto;line-height:1.4;}\n  .summarybox{font-size:12.5px;line-height:1.5;background:rgba(10,132,255,.08);border:1px solid rgba(10,132,255,.2);border-radius:12px;padding:10px 12px;}\n  .summarybox.gen{color:#8a8a8e;font-style:italic;}\n  .zh2{color:#6e6e73;margin-top:4px;font-size:11.5px;}\n  .aabox{font-size:12.5px;line-height:1.5;background:rgba(52,199,89,.1);border:1px solid rgba(52,199,89,.25);border-radius:12px;padding:10px 12px;}\n  .torig{font-size:10px;opacity:.75;margin-top:3px;font-style:italic;}\n  .rfrom{font-size:9px;font-weight:700;opacity:.7;}\n  .unit{display:flex;align-items:center;gap:9px;padding:8px 0;border-bottom:1px solid rgba(0,0,0,.05);}\n  .unit:last-child{border-bottom:0;}\n  .unitico{width:26px;height:26px;border-radius:8px;background:#0a84ff;display:flex;align-items:center;justify-content:center;font-size:13px;flex:0 0 26px;}\n  .unitnm{flex:1;min-width:0;font-size:12.5px;font-weight:600;}\n  .unitst{font-size:10.5px;font-weight:700;padding:2px 7px;border-radius:7px;}\n  .tl{display:flex;flex-direction:column;gap:0;}\n  .tlrow{display:flex;gap:10px;align-items:flex-start;position:relative;padding-bottom:12px;}\n  .tlrow:before{content:\"\";position:absolute;left:6px;top:16px;bottom:0;width:2px;background:rgba(0,0,0,.08);}\n  .tlrow:last-child:before{display:none;}\n  .tldot{width:14px;height:14px;border-radius:50%;flex:0 0 14px;margin-top:1px;border:2px solid #fff;box-shadow:0 0 0 1px rgba(0,0,0,.08);z-index:1;}\n  .tltxt{flex:1;font-size:12px;}.tlt{color:#8a8a8e;font-size:10.5px;font-variant-numeric:tabular-nums;}\n  .tlrow.pending .tltxt,.tlrow.pending .tlt{opacity:.4;}\n  .thread{display:flex;flex-direction:column;gap:7px;max-height:230px;overflow:auto;padding-right:4px;}\n  .tmsg{max-width:88%;padding:8px 11px;border-radius:14px;font-size:12.5px;line-height:1.35;}\n  .tmsg.reporter{align-self:flex-end;background:linear-gradient(180deg,#0a84ff,#0071e3);color:#fff;border-bottom-right-radius:5px;}\n  .tmsg.agent{align-self:flex-start;background:#f0f0f3;color:#1d1d1f;border-bottom-left-radius:5px;}\n  .tmsg.dispatcher{align-self:flex-start;background:linear-gradient(180deg,#ffab2e,#ff9500);color:#fff;border-bottom-left-radius:5px;}\n  .tmsg .rolelbl{font-size:9px;font-weight:700;opacity:.7;text-transform:uppercase;letter-spacing:.04em;display:block;margin-bottom:2px;}\n  .tmsg .zh{opacity:.85;font-size:11.5px;margin-top:2px;}\n  .dispbar{display:flex;gap:6px;margin-top:9px;}\n  .dispbar input{flex:1;border:1px solid rgba(0,0,0,.12);border-radius:10px;padding:8px 11px;font:inherit;font-size:13px;outline:none;}\n  .dispbar input:focus{border-color:#ff9500;box-shadow:0 0 0 3px rgba(255,149,0,.14);}\n  .dispbar button{border:0;border-radius:10px;padding:0 13px;background:#ff9500;color:#fff;font-weight:700;cursor:pointer;}\n  .livevid{width:100%;border-radius:12px;background:#000;display:block;aspect-ratio:16/9;object-fit:cover;box-shadow:0 6px 18px rgba(0,0,0,.25);}\n  .vidph{width:100%;aspect-ratio:16/9;border-radius:12px;background:#0b0b0f;display:flex;align-items:center;justify-content:center;color:#8a8a8e;font-size:12px;text-align:center;padding:10px;box-sizing:border-box;}\n  .camrow{display:flex;align-items:center;gap:8px;padding:6px 0;cursor:pointer;font-size:12px;}\n  .camrow:hover{color:#0a84ff;}\n  .camdist{margin-left:auto;color:#8a8a8e;font-size:11px;}\n  .camov{position:absolute;inset:0;z-index:900;background:rgba(0,0,0,.55);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:24px;animation:fade .25s ease;}\n  .camwin{width:min(720px,92%);background:#0b0b0f;border-radius:18px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.5);border:1px solid rgba(255,255,255,.08);}\n  .camfeed{position:relative;aspect-ratio:16/9;overflow:hidden;background:#0b0b0f;}\n  .camiframe{width:100%;height:100%;border:0;display:block;}\n  .camscene{position:absolute;inset:0;background:linear-gradient(180deg,#2a3550 0%,#3a4a6a 42%,#4a4438 42%,#2c2822 100%);}\n  .camscene:before{content:\"\";position:absolute;left:0;right:0;top:42%;bottom:0;background:repeating-linear-gradient(90deg,transparent 0,transparent 46px,rgba(255,220,120,.5) 46px,rgba(255,220,120,.5) 54px,transparent 54px,transparent 100px);animation:road 2.2s linear infinite;opacity:.5;}\n  .carA,.carB{position:absolute;width:34px;height:16px;border-radius:4px;top:62%;}\n  .carA{background:#e8e8ee;animation:drivea 3.4s linear infinite;}\n  .carB{background:#ff6b5c;top:74%;animation:driveb 4.6s linear infinite;}\n  .scan{position:absolute;inset:0;background:repeating-linear-gradient(0deg,rgba(255,255,255,.05) 0,rgba(255,255,255,.05) 1px,transparent 1px,transparent 3px);pointer-events:none;}\n  .noise{position:absolute;inset:0;opacity:.06;background-image:radial-gradient(#fff 0.5px,transparent 0.5px);background-size:3px 3px;animation:noise .2s steps(2) infinite;pointer-events:none;}\n  .camhud{position:absolute;inset:0;color:#e6ffe6;font-family:ui-monospace,monospace;font-size:11px;pointer-events:none;}\n  .camhud .tl2{position:absolute;top:10px;left:12px;display:flex;align-items:center;gap:6px;}\n  .rec{width:9px;height:9px;border-radius:50%;background:#ff3b30;animation:blink 1s infinite;}\n  .camhud .tr{position:absolute;top:10px;right:12px;}\n  .camhud .bl{position:absolute;bottom:10px;left:12px;}\n  .camhud .br{position:absolute;bottom:10px;right:12px;background:#ff9500;color:#000;font-weight:800;padding:1px 6px;border-radius:4px;}\n  .camttl{padding:11px 14px;color:#fff;font-size:13px;font-weight:600;display:flex;justify-content:space-between;align-items:center;background:#141419;}\n  .camttl button{border:0;background:rgba(255,255,255,.14);color:#fff;width:26px;height:26px;border-radius:50%;cursor:pointer;}\n  .leaflet-div-icon{background:transparent!important;border:none!important;}\n  .leaflet-container{background:#eef0f4;font-family:inherit;}\n  .leaflet-top.leaflet-left{top:60px;}\n  .leaflet-control-zoom{border:none!important;box-shadow:0 4px 14px rgba(0,0,0,.14)!important;border-radius:12px!important;overflow:hidden;}\n  .leaflet-control-zoom a{background:rgba(255,255,255,.9)!important;color:#1d1d1f!important;border:none!important;font-weight:600;}\n  .leaflet-control-attribution{background:rgba(255,255,255,.7)!important;font-size:9px!important;color:#8a8a8e!important;}\n  .lstn{display:flex;flex-direction:column;align-items:center;gap:1px;}\n  .lstn .box{width:24px;height:24px;border-radius:8px;background:#fff;border:2px solid #0071e3;display:flex;align-items:center;justify-content:center;font-size:13px;box-shadow:0 2px 6px rgba(0,0,0,.2);}\n  .lstn .nm{font-size:9px;font-weight:700;color:#0071e3;background:rgba(255,255,255,.85);padding:0 4px;border-radius:4px;white-space:nowrap;}\n  .lcam{width:26px;height:26px;border-radius:7px;background:#1d1d1f;border:2px solid #fff;display:flex;align-items:center;justify-content:center;font-size:12px;box-shadow:0 2px 6px rgba(0,0,0,.3);cursor:pointer;}\n  .lpin{position:relative;width:28px;height:28px;}\n  .lpin .ring{position:absolute;inset:2px;border-radius:50%;background:var(--c);opacity:0;}\n  .lpin.act .ring{opacity:.28;animation:pulse 1.8s infinite;}\n  .lpin .core{position:absolute;inset:6px;border-radius:50%;background:var(--c);border:2px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,.3);display:flex;align-items:center;justify-content:center;font-size:8px;}\n  .lpin .vbadge{position:absolute;top:-6px;right:-6px;width:15px;height:15px;border-radius:50%;background:#ff2d55;border:1.5px solid #fff;font-size:8px;display:flex;align-items:center;justify-content:center;}\n  .lsquad{display:flex;flex-direction:column;align-items:center;}\n  .lsquad .eta{font-size:10px;font-weight:800;color:#0071e3;background:rgba(255,255,255,.92);padding:0 5px;border-radius:5px;white-space:nowrap;min-height:12px;box-shadow:0 1px 3px rgba(0,0,0,.15);}\n  .lsquad .veh{width:22px;height:22px;border-radius:50%;background:#0a84ff;border:2.5px solid #fff;display:flex;align-items:center;justify-content:center;font-size:11px;box-shadow:0 3px 8px rgba(10,132,255,.5);margin-top:1px;}\n  @keyframes slidein{from{opacity:0;transform:translateY(12px);}to{opacity:1;transform:none;}}\n  @keyframes fade{from{opacity:0;}to{opacity:1;}}\n  @keyframes livep{0%{box-shadow:0 0 0 0 rgba(52,199,89,.5);}70%{box-shadow:0 0 0 7px rgba(52,199,89,0);}100%{box-shadow:0 0 0 0 rgba(52,199,89,0);}}\n  @keyframes cardin{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:none;}}\n  @keyframes sh{0%{background-position:100% 0;}100%{background-position:0 0;}}\n  @keyframes spin{to{transform:rotate(360deg);}}\n  @keyframes pulse{0%{transform:scale(1);opacity:.3;}70%{transform:scale(2.1);opacity:0;}100%{transform:scale(1);opacity:0;}}\n  @keyframes road{to{background-position:-100px 0;}}\n  @keyframes drivea{from{left:-40px;}to{left:100%;}}\n  @keyframes driveb{from{left:100%;}to{left:-40px;}}\n  @keyframes blink{50%{opacity:.3;}}\n  @keyframes noise{to{transform:translate(2px,1px);}}\n  `,\n  render(d2){\n    const st=d2.state;const incs=st.incidents||[];\n    let _ls=\"idle\",_le=\"\";try{d2.lib(LEAF);if(d2.lib.state)_ls=d2.lib.state(LEAF);if(d2.lib.error)_le=d2.lib.error(LEAF)||\"\";}catch(e){_le=String((e&&e.message)||e);}\n    let queue;\n    if(!st.loaded){queue=`<div class=\"sk\"></div><div class=\"sk\"></div><div class=\"sk\"></div>`;}\n    else{queue=incs.map(inc=>{const ds=dstat(inc);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";const sel=st.selectedId===inc._id;const parts=(inc.title||\" / \").split(\" / \");const trapped=(inc.peopleTrapped||\"\").indexOf(\"Yes\")>=0;const ems=inc.emotions||[];const le=ems.length?ems[ems.length-1]:null;const ec=le?(EC[le.emotion]||\"#8a8a8e\"):null;const nu=(inc.units||[]).length;\n      return `<div class=\"card ${sel?\"sel\":\"\"} ${inc.liveVideo?\"live\":\"\"}\" data-id=\"${inc._id}\" data-key=\"q${inc._id}\"><div class=\"crow\"><span class=\"dot\" style=\"background:${c}\"></span><div class=\"ctitle\"><div class=\"cen\">${esc(parts[0]||inc.title||\"Incoming call…\")}</div><div class=\"czh\">${esc(parts[1]||\"\")}</div></div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span></div><div class=\"cmeta\">${inc.district?esc(inc.district)+\" · \":\"\"}${inc.severity?esc(inc.severity)+\" · \":\"\"}${inc.fireType?\"🔥 \"+esc(inc.fireType):\"\"}${trapped?' · <b style=\"color:#ff3b30\">TRAPPED 被困</b>':\"\"}${nu?` · 🚒×${nu}`:\"\"}${inc.liveVideo?' · <span class=\"vtag\">● LIVE 直播</span>':\"\"}${le?`<span class=\"etag\" style=\"background:${ec}\">${esc(le.label_en)} ${Math.round((Number(le.intensity)||0)*100)}%</span>`:\"\"}</div>${ds===\"Reported\"?`<button class=\"btn red disp\" data-id=\"${inc._id}\">🚒 Dispatch nearest · 派遣最近队伍</button>`:\"\"}</div>`;}).join(\"\")||`<div class=\"empty\">Waiting for incidents…<br>等待警情接入…</div>`;}\n    const sel=incs.find(i=>i._id===st.selectedId);\n    let detail=\"\";\n    if(sel){detail=renderDetail(d2,sel);}\n    const activeCount=incs.filter(i=>dstat(i)!==\"Resolved\"&&i.status!==\"Resolved\").length;\n    const legend=Object.keys(SL).map(k=>`<span class=\"lg\"><span class=\"dot\" style=\"background:${SC[k]}\"></span>${SL[k][0]} ${SL[k][1]}</span>`).join(\"\")+`<span class=\"lg\">🚒 Unit · 队伍</span><span class=\"lg\">📹 Camera · 监控</span>`;\n    const _d=st.mapDbg||{};const loading=st.mapReady?\"\":`<div class=\"maploading\"><div class=\"ring\"></div><div>${(_le||_d.err)?(\"Map failed: \"+esc(_le||_d.err)):\"Loading Wuhan map… · 加载武汉地图中…\"}</div><div style=\"font-size:10px;opacity:.7;margin-top:8px;font-family:ui-monospace,monospace;text-align:center\">lib: ${esc(_ls)} · attempts: ${_d.n||0} · L: ${_d.L?\"YES\":\"no\"} · box: ${_d.w==null?\"?\":_d.w}×${_d.h==null?\"?\":_d.h}</div></div>`;\n    const camov=st.camView?renderCam(d2,st.camView):\"\";\n    return `<div class=\"shell\"><aside class=\"side\"><div class=\"shead\"><div class=\"brand\"><div class=\"badge\">🚒</div><div><div class=\"stitle\">Command Center</div><div class=\"ssub\">武汉消防指挥中心</div></div></div><div class=\"stat\"><div class=\"statn\">${activeCount}</div><div class=\"statl\">Active · 进行中</div></div></div><div class=\"qhead\">Incident queue · 警情队列</div><div class=\"queue\">${queue}</div></aside><main class=\"main\"><div class=\"maptop\"><div class=\"mt-l\">Wuhan · 武汉<span class=\"mt-sub\">Live incident map · 实时警情地图</span></div><div class=\"mt-r\"><select class=\"displang\" title=\"Dispatcher language\"><option value=\"en\" ${(st.dispLang||\"en\")===\"en\"?\"selected\":\"\"}>🇦 EN</option><option value=\"zh\" ${st.dispLang===\"zh\"?\"selected\":\"\"}>🇨🇳 中文</option><option value=\"pt\" ${st.dispLang===\"pt\"?\"selected\":\"\"}>🇧🇷 PT</option><option value=\"es\" ${st.dispLang===\"es\"?\"selected\":\"\"}>🇪🇸 ES</option></select><button class=\"camtoggle\" ${st.showCams?\"data-on\":\"\"}>📹 Cameras 监控</button><div class=\"clock\"><span class=\"livedot\"></span><span id=\"clk\"></span></div></div></div><div class=\"mapwrap\"><div class=\"mapbox\" data-d2-static></div>${loading}<div class=\"legend\">${legend}</div>${camov}</div>${detail}</main></div>`;\n  },\n  renderDetail(d2,sel){\n    const st=d2.state;const ds=dstat(sel);const sl=SL[ds]||[\"\",\"\"];const c=SC[ds]||\"#ff3b30\";\n    const ems=sel.emotions||[];const le=ems.length?ems[ems.length-1]:null;\n    const units=sel.units||[];const mx=maxUnits(sel);\n    const now=Date.now();\n    let emoBlock=\"\";\n    if(le){const elc=EC[le.emotion]||\"#8a8a8e\";emoBlock=`<div class=\"sec\"><div class=\"sechd\">Caller stress · 来电者情绪</div><div style=\"display:flex;align-items:center;gap:12px\"><div style=\"text-align:center\">${gauge(le)}<div style=\"font-size:11px;font-weight:700;color:${elc}\">${esc(le.label_en)} ${esc(le.label_zh)}</div></div><div style=\"flex:1;min-width:0\">${emoChart(ems)}</div></div></div>`;}\n    let unitBlock=\"\";\n    if(units.length){unitBlock=`<div class=\"sec\"><div class=\"sechd\">Assigned units · 出动队伍 (${units.length})</div>${units.map(u=>{const el=now-(u.dispatchedAt||now);const ph=phaseOf(el);const usl=SL[ph]||[\"\",\"\"];const uc=SC[ph]||\"#0a84ff\";const eta=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"Dispatched\"?\"leaving 出发\":(ph===\"OnScene\"?\"on scene 到场\":\"resolved 完成\"));return `<div class=\"unit\"><div class=\"unitico\">🚒</div><div class=\"unitnm\">${esc(u.name||\"Unit\")}<br><span style=\"font-size:10.5px;color:#8a8a8e;font-weight:500\">${esc(u.role===\"backup\"?\"Reinforcement 增援\":\"Primary 主力\")}</span></div><div style=\"text-align:right\"><span class=\"unitst\" style=\"background:${uc}22;color:${uc}\">${usl[0]}</span><div style=\"font-size:10px;color:#8a8a8e;margin-top:2px\">${eta}</div></div></div>`;}).join(\"\")}</div>`;}\n    let vidBlock=\"\";\n    if(sel.liveVideo){vidBlock=`<div class=\"sec\"><div class=\"sechd\">📹 Live video · 现场直播 <span class=\"vtag\">● LIVE</span></div>${st.watching===sel._id?`<video class=\"livevid\" autoplay playsinline muted></video><div style=\"font-size:10.5px;color:#8a8a8e;margin-top:5px\">${esc(st.vidStatus||\"Connecting… 连接中\")}</div><button class=\"wdbgbtn\">🐞 ${st.wdbgOn?\"hide debug\":\"debug\"}</button>${st.wdbgOn?`<pre class=\"vdbg\">status: ${esc(st.vidStatus||\"-\")}\\ntracks: ${esc(\"\"+((st.wstats||{}).tracks!=null?(st.wstats||{}).tracks:\"-\"))}  sig: ${esc((st.wstats||{}).sig||\"-\")}\\nice: ${esc((st.wstats||{}).ice||\"-\")}  conn: ${esc((st.wstats||{}).conn||\"-\")}\\nvideo: ${esc((st.wstats||{}).video||\"0x0\")}\\ncandidates: ${esc((st.wstats||{}).cand||\"(none yet - TURN down?)\")}\\n---\\n${esc((st.wlog||[]).join(\"\\n\"))}</pre>`:\"\"}`:`<button class=\"btn red watch\" data-id=\"${sel._id}\">▶ Watch live · 观看直播</button>`}</div>`;}\n    const cams=CAMS.map(cm=>({cm,d:dist([sel.lat,sel.lng],[cm.lat,cm.lng])})).sort((a,b)=>a.d-b.d).slice(0,3);\n    const camBlock=hasXY(sel)?`<div class=\"sec\"><div class=\"sechd\">Nearby cameras · 附近监控</div>${cams.map(x=>`<div class=\"camrow opencam\" data-cam=\"${x.cm.id}\">📹 ${esc(x.cm.name)} ${esc(x.cm.zh)}<span class=\"camdist\">${(x.d*111).toFixed(1)} km</span></div>`).join(\"\")}</div>`:\"\";\n    const tr=sel.transcript||[];\n    const thread=tr.length?tr.map(m=>{const role=m.role||\"agent\";const rl=role===\"reporter\"?\"Caller 报案人\":(role===\"dispatcher\"?\"Dispatcher 调度员\":\"AI Agent 智能助理\");const main=esc(pick(m));const zh=(role===\"agent\"&&m.zh)?`<div class=\"zh\">${esc(m.zh)}</div>`:\"\";const orig=(m.raw&&m.srcLang&&m.srcLang!==\"en\")?`<div class=\"torig\"><span class=\"rfrom\">译自 translated from ${esc(m.srcLang)}:</span> ${esc(m.raw)}</div>`:\"\";return `<div class=\"tmsg ${role}\"><span class=\"rolelbl\">${rl}</span>${main}${zh}${orig}</div>`;}).join(\"\"):`<div style=\"font-size:12px;color:#8a8a8e\">No messages yet · 暂无对话</div>`;\n    const human=sel.humanControl===true;\n    const dispBar=human?`<div class=\"dispbar\"><input class=\"dispinput\" placeholder=\"Message the caller… 给报案人发消息\"/><button class=\"dispsend\" data-id=\"${sel._id}\">↑</button></div>`:\"\";\n    const threadBlock=`<div class=\"sec\"><div class=\"sechd\">Live transcript · 实时对话 ${human?'<span class=\"vtag\" style=\"background:#ff9500\">HUMAN 人工</span>':\"\"}</div><div class=\"thread\">${thread}</div>${dispBar}<button class=\"btn ${human?\"gray\":\"orange\"} takeover\" data-id=\"${sel._id}\" style=\"margin-top:8px\">${human?\"↩ Resume AI · 恢复AI\":\"✋ Take over · 人工接管\"}</button></div>`;\n    const disp=sel.dispatchedAt;\n    const tlrows=[[\"Reported 已报告\",sel.createdAt,\"#ff3b30\",true],[\"Dispatched 已派遣\",disp,\"#ff9500\",!!disp],[\"En route 前往中\",disp?disp+T_DISP:null,\"#0a84ff\",disp&&(now-disp)>=T_DISP],[\"On scene 已到场\",disp?disp+T_ENROUTE:null,\"#5e5ce6\",disp&&(now-disp)>=T_ENROUTE],[\"Resolved 已解决\",disp?disp+T_SCENE:null,\"#34c759\",(sel.status===\"Resolved\")||(disp&&(now-disp)>=T_SCENE)]];\n    const timeline=`<div class=\"sec\"><div class=\"sechd\">Timeline · 时间线</div><div class=\"tl\">${tlrows.map(r=>`<div class=\"tlrow ${r[3]?\"\":\"pending\"}\"><div class=\"tldot\" style=\"background:${r[3]?r[2]:\"#c7c7cc\"}\"></div><div class=\"tltxt\">${r[0]}<div class=\"tlt\">${r[3]?fmtTime(r[1]):\"pending 待定\"}</div></div></div>`).join(\"\")}</div></div>`;\n    const canReinforce=(disp&&sel.status!==\"Resolved\"&&dstat(sel)!==\"Resolved\"&&units.length<mx);\n    let actions=\"\";\n    if(ds===\"Reported\"){actions=`<button class=\"btn red disp\" data-id=\"${sel._id}\">🚒 Dispatch nearest unit · 派遣最近队伍</button>`;}\n    else if(canReinforce){actions=`<button class=\"btn orange reinforce\" data-id=\"${sel._id}\">➕ Send reinforcements · 派遣增援 (${units.length}/${mx})</button>`;}\n    const sm=((d2.state.summaries||{})[sel._id])||null;\n    const summaryBlock=`<div class=\"sec\"><div class=\"sechd\">Case summary · 案情摘要</div>${sm?`<div class=\"summarybox\"><div>${esc(sm.summary_en||\"\")}</div><div class=\"zh2\">${esc(sm.summary_zh||\"\")}</div></div>`:`<div class=\"summarybox gen\">Generating… · 生成中…</div>`}</div>`;\n    const aa=sel.afterAction;\n    const aaBlock=aa?`<div class=\"sec\"><div class=\"sechd\">After-action report · 事后报告</div><div class=\"aabox\"><div>${esc(aa.report_en||\"\")}</div><div class=\"zh2\">${esc(aa.report_zh||\"\")}</div></div></div>`:\"\";\n    return `<div class=\"detail\" data-key=\"detail\"><button class=\"close\">✕</button><div class=\"dttl\">${esc(sel.title||\"Incoming call · 接线中\")}</div><span class=\"pill\" style=\"background:${c}22;color:${c}\">${sl[0]} · ${sl[1]}</span>${summaryBlock}\n      <div class=\"sec\"><div class=\"dkv\"><span class=\"k\">Location 位置</span><span class=\"v\">${esc(sel.locationText||\"—\")}</span><span class=\"k\">District 辖区</span><span class=\"v\">${esc(sel.district||\"—\")}</span><span class=\"k\">Type 类型</span><span class=\"v\">🔥 ${esc(sel.fireType||\"—\")}</span><span class=\"k\">Severity 级别</span><span class=\"v\">${esc(sel.severity||\"—\")}</span><span class=\"k\">Trapped 被困</span><span class=\"v\">${esc(sel.peopleTrapped||\"—\")}</span><span class=\"k\">Hazards 危险</span><span class=\"v\">${esc(sel.hazards||\"—\")}</span><span class=\"k\">Coords 坐标</span><span class=\"v\">${hasXY(sel)?sel.lat.toFixed(4)+\", \"+sel.lng.toFixed(4):\"pending\"}</span></div>${sel.photoAnalysis?`<div class=\"dvis\"><b>📸 AI vision · 影像分析</b><br>${esc(sel.photoAnalysis)}</div>`:\"\"}${sel.photoAddress?`<div class=\"dvis\" style=\"background:rgba(255,149,0,.12);border-color:rgba(255,149,0,.3)\"><b style=\"color:#b35a00\">📍 Address from photo</b> <span class=\"fromphoto\">来自照片 from photo</span><br>${esc(sel.photoAddress)}</div>`:\"\"}${actions}</div>\n      ${vidBlock}${unitBlock}${emoBlock}${threadBlock}${camBlock}${timeline}${aaBlock}</div>`;\n  },\n  renderCam(d2,cam){\n    return `<div class=\"camov\"><div class=\"camwin\"><div class=\"camfeed\">${cam.yt?`<iframe class=\"camiframe\" src=\"https://www.youtube-nocookie.com/embed/${cam.yt}?autoplay=1&mute=1&playsinline=1&rel=0\" allow=\"autoplay; encrypted-media; picture-in-picture\" allowfullscreen></iframe>`:`<div class=\"vidph\">No live feed available · 无可用信号</div>`}<div class=\"camhud\"><div class=\"tl2\"><span class=\"rec\"></span>LIVE</div><div class=\"br\" style=\"background:#34c759;color:#000\">PARTNER FEED</div></div></div><div class=\"camttl\"><span>📹 ${esc(cam.name)} · ${esc(cam.zh)} <span style=\"opacity:.65;font-weight:500;font-size:11px\">— LIVE · ${esc(cam.feed||\"partner feed\")} · 合作信号</span></span><button class=\"camclose\">✕</button></div></div></div>`;\n  },\n  mounted(d2){\n    if(!d2.state.incidents)d2.state.incidents=[];\n    if(!d2.state.stations)d2.state.stations=[];\n    if(d2.state.loaded===undefined)d2.state.loaded=false;\n    if(d2.state.showCams===undefined)d2.state.showCams=false;\n    let map=null,L=null,built=false;\n    const stnMk={},incMk={},camMk={},squadMk={},routeLn={},routeGeo={},routeFetching={},lastPhase={};\n    let refetching=false,lastFetch=0;\n    const mergeLocal=(server)=>{const cur=d2.state.incidents||[];const by={};cur.forEach(i=>by[i._id]=i);d2.state.incidents=server.map(s=>{const l=by[s._id];if(l&&l.dispatchedAt&&!s.dispatchedAt){return {...s,dispatchedAt:l.dispatchedAt,status:l.status,assignedStationId:l.assignedStationId,units:l.units};}return s;});d2.state.loaded=true;};\n    const refetch=async()=>{const _n=Date.now();if(refetching||_n-lastFetch<800)return;lastFetch=_n;refetching=true;try{const r=un(await d2.rush(\"fire_list\").invoke({}));mergeLocal((r&&r.items)||[]);updateMarkers();triggerAI();const _w=d2.state.watching;if(_w){const _wi=(d2.state.incidents||[]).find(i=>i._id===_w);if(!_wi||!_wi.liveVideo){try{stopWatch();}catch(e){}d2.state.watching=null;}}}catch(e){}refetching=false;};\n    const loadStations=async()=>{try{const r=un(await d2.rush(\"fire_stations\").invoke({}));d2.state.stations=(r&&r.items)||[];updateMarkers();}catch(e){}};\n    const loadCams=async()=>{try{const r=un(await d2.rush(\"fire_cams\").invoke({}));d2.state.realCams=(r&&r.cams)||[];}catch(e){}};\n    const sumState={};const aaState={};\n    const ensureSummary=async(inc)=>{if(!inc)return;const key=inc._id;if(sumState[key]===\"pending\")return;const cur=dstat(inc);const cached=(d2.state.summaries||{})[key];if(cached&&cached._st===cur)return;sumState[key]=\"pending\";try{const r=un(await d2.rush(\"fire_summary\").invoke({mode:\"live\",record:inc}));const res=(r&&r.result)||{};const s=d2.state.summaries||{};s[key]={summary_en:res.summary_en,summary_zh:res.summary_zh,_st:cur};d2.state.summaries=s;}catch(e){}sumState[key]=cur;};\n    const ensureAfterAction=async(inc)=>{if(!inc)return;const key=inc._id;if(inc.afterAction||aaState[key])return;if(dstat(inc)!==\"Resolved\"&&inc.status!==\"Resolved\")return;aaState[key]=1;try{const r=un(await d2.rush(\"fire_summary\").invoke({mode:\"closing\",record:inc}));const res=(r&&r.result)||{};const rec={...inc,afterAction:{report_en:res.report_en,report_zh:res.report_zh,at:Date.now()}};d2.state.incidents=(d2.state.incidents||[]).map(i=>i._id===key?rec:i);d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});}catch(e){}};\n    const triggerAI=()=>{const incs=d2.state.incidents||[];const sel=incs.find(i=>i._id===d2.state.selectedId);if(sel)ensureSummary(sel);incs.forEach(i=>{if((dstat(i)===\"Resolved\"||i.status===\"Resolved\")&&!i.afterAction)ensureAfterAction(i);});};\n    const ensureXlate=async(sel)=>{const dl=d2.state.dispLang||\"en\";if(!sel||dl===\"en\"||dl===\"zh\")return;const turns=sel.transcript||[];for(let i=0;i<turns.length;i++){const m=turns[i];if(m.role===\"dispatcher\")continue;const src=m.raw||m.en;if(!src)continue;const k=(m.t||\"\")+\"|\"+dl;const xl=d2.state.xl||{};if(xl[k])continue;try{const r=un(await d2.rush(\"fire_xlate\").invoke({text:src,target:dl}));if(r&&r.translation){const nx=d2.state.xl||{};nx[k]=r.translation;d2.state.xl={...nx};}}catch(e){}}};\n    const stationById=(id)=>(d2.state.stations||[]).find(s=>s._id===id);\n    const ensureRoute=async(inc,u)=>{const key=inc._id+\":\"+u.stationId;if(routeGeo[key]||routeFetching[key])return;const s=stationById(u.stationId);if(!s||!hasXY(inc)||!hasXY(s)){return;}routeFetching[key]=true;try{const url=`https://router.project-osrm.org/route/v1/driving/${s.lng},${s.lat};${inc.lng},${inc.lat}?overview=full&geometries=geojson`;const resp=await fetch(url);const j=await resp.json();const co=j&&j.routes&&j.routes[0]&&j.routes[0].geometry&&j.routes[0].geometry.coordinates;if(co&&co.length>1){routeGeo[key]=co.map(c=>[c[1],c[0]]);}else{routeGeo[key]=[[s.lat,s.lng],[inc.lat,inc.lng]];}}catch(e){const s2=stationById(u.stationId);if(s2)routeGeo[key]=[[s2.lat,s2.lng],[inc.lat,inc.lng]];}routeFetching[key]=false;};\n    const updateMarkers=()=>{\n      if(!built||!L||!map)return;\n      const incs=d2.state.incidents||[];const stations=d2.state.stations||[];\n      stations.forEach(s=>{if(!hasXY(s))return;if(!stnMk[s._id]){const ic=L.divIcon({className:\"\",iconSize:[54,44],iconAnchor:[27,22],html:`<div class=\"lstn\"><span class=\"box\">🚒</span><span class=\"nm\">${esc((s.name||\"\").split(\" \")[0])}</span></div>`});stnMk[s._id]=L.marker([s.lat,s.lng],{icon:ic,interactive:false}).addTo(map);}});\n      CAMS.forEach(cm=>{const on=d2.state.showCams;if(on&&!camMk[cm.id]){const ic=L.divIcon({className:\"\",iconSize:[26,26],iconAnchor:[13,13],html:`<div class=\"lcam\">📹</div>`});const m=L.marker([cm.lat,cm.lng],{icon:ic}).addTo(map);m.on(\"click\",()=>{d2.state.camView=cm;});camMk[cm.id]=m;}else if(!on&&camMk[cm.id]){map.removeLayer(camMk[cm.id]);delete camMk[cm.id];}});\n      const seen={};\n      incs.forEach(inc=>{if(!hasXY(inc))return;seen[inc._id]=1;const ds=dstat(inc);const c=SC[ds]||\"#ff3b30\";const act=ds!==\"Resolved\";const vb=inc.liveVideo?`<span class=\"vbadge\">●</span>`:\"\";const ic=L.divIcon({className:\"\",iconSize:[28,28],iconAnchor:[14,14],html:`<div class=\"lpin ${act?\"act\":\"\"}\" style=\"--c:${c}\"><span class=\"ring\"></span><span class=\"core\">🔥</span>${vb}</div>`});\n        if(!incMk[inc._id]){const m=L.marker([inc.lat,inc.lng],{icon:ic}).addTo(map);m.on(\"click\",()=>{d2.state.selectedId=inc._id;ensureSummary(inc);ensureXlate(inc);});incMk[inc._id]=m;}else{incMk[inc._id].setIcon(ic);incMk[inc._id].setLatLng([inc.lat,inc.lng]);}\n        const units=inc.units||[];\n        units.forEach(u=>{const key=inc._id+\":\"+u.stationId;ensureRoute(inc,u);const geo=routeGeo[key];if(geo&&!routeLn[key]){routeLn[key]=L.polyline(geo,{color:\"#0a84ff\",weight:3,opacity:.55,dashArray:\"1 8\",lineCap:\"round\"}).addTo(map);}else if(geo&&routeLn[key]){routeLn[key].setLatLngs(geo);}\n          if(!squadMk[key]){const s=stationById(u.stationId);const start=s?[s.lat,s.lng]:[inc.lat,inc.lng];const sic=L.divIcon({className:\"\",iconSize:[40,42],iconAnchor:[20,32],html:`<div class=\"lsquad\"><span class=\"eta\"></span><span class=\"veh\">🚒</span></div>`});squadMk[key]=L.marker(start,{icon:sic,interactive:false,zIndexOffset:1000}).addTo(map);}\n        });\n      });\n      Object.keys(incMk).forEach(id=>{if(!seen[id]){map.removeLayer(incMk[id]);delete incMk[id];Object.keys(squadMk).forEach(k=>{if(k.indexOf(id+\":\")===0){map.removeLayer(squadMk[k]);delete squadMk[k];}});Object.keys(routeLn).forEach(k=>{if(k.indexOf(id+\":\")===0){map.removeLayer(routeLn[k]);delete routeLn[k];}});}});\n    };\n    const buildMap=(LL,el)=>{if(built)return;L=(LL&&LL.default)||LL;try{map=L.map(el,{zoomControl:true,attributionControl:true}).setView([30.5928,114.3055],12);L.tileLayer(\"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",{subdomains:\"abcd\",maxZoom:19,attribution:\"© OpenStreetMap © CARTO\"}).addTo(map);try{map.attributionControl.setPosition(\"bottomright\");}catch(e){}built=true;d2.state.mapReady=true;setTimeout(()=>{try{map.invalidateSize();}catch(e){}},250);try{const ro=new ResizeObserver(()=>{try{map.invalidateSize();}catch(e){}});ro.observe(el);d2.onUnmount(()=>ro.disconnect());}catch(e){}updateMarkers();}catch(e){d2.state.mapErr=String((e&&e.message)||e);}};\n    const tryBuild=()=>{if(built)return;let LL=null;try{LL=d2.lib(LEAF);}catch(e){}let er=\"idle\";try{if(d2.lib.state)er=d2.lib.state(LEAF);}catch(e){}const el=d2.$(\".mapbox\");const dbg={n:(((d2.state.mapDbg||{}).n)||0)+1,L:!!LL,w:el?el.clientWidth:null,h:el?el.clientHeight:null};if(er===\"error\"){try{dbg.err=(d2.lib.error&&d2.lib.error(LEAF))||\"load error\";}catch(e){}d2.state.mapErr=dbg.err;}d2.state.mapDbg=dbg;if(LL&&el){buildMap(LL,el);}else if(er!==\"error\"){setTimeout(tryBuild,300);}};\n    loadStations();refetch();loadCams();tryBuild();\n    // ---- signaling + webrtc (answer side) ----\n    let sock=null;let pc=null,remoteStream=null,attIv=null;const myId=\"cc-\"+Math.random().toString(36).slice(2,8);\n    const wsetStat=(k,v)=>{d2.state.wstats={...(d2.state.wstats||{}),[k]:v};};wsetStat(\"me\",myId);\n    const wlog=(s)=>{const ln=new Date().toISOString().slice(11,19)+\" \"+s;try{console.log(\"[CC-RTC]\",ln);}catch(e){}d2.state.wlog=[...((d2.state.wlog||[]).slice(-11)),ln];};\n    const emitSig=(payload)=>{try{payload.from=myId;d2.rush(\"fire_signal\").invoke(payload);wlog(\"→ \"+payload.t+\" (via server)\");}catch(e){wlog(\"emit ERR \"+(e&&e.message));}};\n    const attachVideo=()=>{try{const v=d2.$(\".livevid\");if(v&&remoteStream){if(v.srcObject!==remoteStream){v.srcObject=remoteStream;v.play&&v.play().catch(()=>{});}wsetStat(\"video\",`${v.videoWidth||0}x${v.videoHeight||0} rs:${v.readyState}`);}}catch(e){}};\n    const stopWatch=()=>{try{if(pc){pc.close();}}catch(e){}pc=null;remoteStream=null;if(attIv){clearInterval(attIv);attIv=null;}d2.state.wstats={};};\n    const startWatch=(incId)=>{const sel=(d2.state.incidents||[]).find(i=>i._id===incId);if(!sel)return;d2.state.watching=incId;d2.state.vidStatus=\"Connecting… 连接中\";d2.state.wlog=[];d2.state.wstats={me:myId,inc:String(incId).slice(-6)};wlog(\"WATCH me=\"+myId+\" inc=\"+incId);try{pc=new RTCPeerConnection({iceServers:ICE});let host=0,srflx=0,relay=0;pc.ontrack=(e)=>{remoteStream=e.streams&&e.streams[0];wsetStat(\"tracks\",(remoteStream&&remoteStream.getTracks().length)||0);wlog(\"ontrack \"+((remoteStream&&remoteStream.getTracks().map(t=>t.kind).join(\"+\"))||\"\"));d2.state.vidStatus=\"Live 直播中\";attachVideo();};pc.onicecandidate=(e)=>{if(e.candidate){const cc=e.candidate.candidate||\"\";const ty=cc.indexOf(\"typ relay\")>=0?\"relay\":cc.indexOf(\"typ srflx\")>=0?\"srflx\":\"host\";if(ty===\"relay\")relay++;else if(ty===\"srflx\")srflx++;else host++;wsetStat(\"cand\",`host:${host} srflx:${srflx} relay:${relay}`);emitSig({sig:true,incidentId:incId,t:\"ice\",role:\"watcher\",c:e.candidate});}};pc.oniceconnectionstatechange=()=>{wsetStat(\"ice\",pc.iceConnectionState);wlog(\"ice=\"+pc.iceConnectionState);};pc.onconnectionstatechange=()=>{wsetStat(\"conn\",pc.connectionState);wlog(\"conn=\"+pc.connectionState);};emitSig({sig:true,incidentId:incId,t:\"watch-request\",role:\"watcher\"});if(attIv)clearInterval(attIv);attIv=setInterval(attachVideo,700);d2.onUnmount(()=>{if(attIv)clearInterval(attIv);});}catch(e){d2.state.vidStatus=\"WebRTC unavailable 不支持\";wlog(\"pc ERR \"+(e&&e.message));}};\n    const handleSig=async(p)=>{if(!p||!p.sig||p.role===\"watcher\")return;if(d2.state.watching!==p.incidentId)return;wlog(\"← \"+p.t);try{if(p.t===\"offer\"&&pc){await pc.setRemoteDescription(p.sdp);wsetStat(\"sig\",pc.signalingState);const ans=await pc.createAnswer();await pc.setLocalDescription(ans);emitSig({sig:true,incidentId:p.incidentId,t:\"answer\",role:\"watcher\",sdp:ans});wlog(\"answer sent\");}else if(p.t===\"ice\"&&pc&&p.c){try{await pc.addIceCandidate(p.c);}catch(e){}}else if(p.t===\"announce\"){emitSig({sig:true,incidentId:p.incidentId,t:\"watch-request\",role:\"watcher\"});}else if(p.t===\"end\"){stopWatch();d2.state.watching=null;d2.state.vidStatus=\"Broadcast ended 直播已结束\";}}catch(e){wlog(\"handleSig ERR \"+(e&&e.message));}};\n    try{sock=d2.socket(\"feed\");sock.subscribe(\"wuhan\");sock.onMessage((m)=>{const p=(m&&m.data!==undefined)?m.data:m;const room=(m&&(m.room||m.channel))||\"wuhan\";if(d2.state.watching){wlog(\"recv[\"+room+\"] \"+((p&&p.sig)?(\"sig/\"+(p.t||\"?\")):\"incident\")+((p&&p.incidentId)?(\" inc=\"+String(p.incidentId).slice(-5)):\"\")+((p&&p.from)?(\" from=\"+p.from):\"\"));}if(p&&p.sig){if(p.from===myId)return;handleSig(p);}else{refetch();}});}catch(e){}\n    const hb=setInterval(async()=>{const incs=d2.state.incidents||[];const anyActive=incs.some(i=>i.dispatchedAt&&i.status!==\"Resolved\"&&PORD[dstat(i)]<4);if(anyActive){try{await d2.rush(\"fire_tick\").invoke({});}catch(e){}refetch();}},2500);\n    const poll=setInterval(()=>{refetch();},6000);\n    d2.onUnmount(()=>{clearInterval(hb);clearInterval(poll);});\n    const ck=setInterval(()=>{const el=d2.$(\"#clk\");if(el)el.textContent=new Date().toLocaleTimeString(\"en-GB\");const ts=d2.$(\"#camts\");if(ts)ts.textContent=new Date().toLocaleString(\"en-GB\");attachVideo();},1000);\n    d2.onUnmount(()=>clearInterval(ck));\n    const nearestStations=(inc,exclude)=>{return (d2.state.stations||[]).filter(s=>hasXY(s)&&exclude.indexOf(s._id)<0).map(s=>({s,d:dist([inc.lat,inc.lng],[s.lat,s.lng])})).sort((a,b)=>a.d-b.d).map(x=>x.s);};\n    const dispatch=(id)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc||dstat(inc)!==\"Reported\")return;const ns=nearestStations(inc,[]);if(!ns.length)return;const best=ns[0];const now=Date.now();const unit={stationId:best._id,name:`${best.name} ${best.nameZh}`,dispatchedAt:now,role:\"primary\"};const rec={...inc,status:\"Dispatched\",assignedStationId:best._id,assignedStationName:unit.name,dispatchedAt:now,units:[unit]};d2.state.incidents=incs.map(i=>i._id===id?rec:i);d2.state.selectedId=id;updateMarkers();d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const reinforce=(id)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc)return;const units=inc.units||[];const mx=maxUnits(inc);if(units.length>=mx)return;const used=units.map(u=>u.stationId);const ns=nearestStations(inc,used);if(!ns.length)return;const best=ns[0];const now=Date.now();const unit={stationId:best._id,name:`${best.name} ${best.nameZh}`,dispatchedAt:now,role:\"backup\"};const rec={...inc,units:[...units,unit]};d2.state.incidents=incs.map(i=>i._id===id?rec:i);updateMarkers();d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const setHuman=(id,val)=>{const incs=d2.state.incidents||[];const inc=incs.find(i=>i._id===id);if(!inc)return;const rec={...inc,humanControl:val};d2.state.incidents=incs.map(i=>i._id===id?rec:i);d2.rush(\"fire_save\").invoke({record:rec}).catch(()=>{});};\n    const dispatcherSay=(id,text)=>{const t=(text||\"\").trim();if(!t)return;const inc=(d2.state.incidents||[]).find(i=>i._id===id);if(!inc)return;const tgt=inc.citizenLang||\"en\";d2.rush(\"fire_say\").invoke({incidentId:id,text:t,target:tgt,transcript:inc.transcript||[]}).then(()=>refetch()).catch(()=>{});};\n    d2.on(\".disp\",\"click\",(e,el)=>{dispatch(el.dataset.id);});\n    d2.on(\".reinforce\",\"click\",(e,el)=>{reinforce(el.dataset.id);});\n    d2.on(\".card\",\"click\",(e,el)=>{d2.state.selectedId=el.dataset.id;const inc=(d2.state.incidents||[]).find(i=>i._id===el.dataset.id);if(inc){ensureSummary(inc);ensureXlate(inc);}});\n    d2.on(\".close\",\"click\",()=>{d2.state.selectedId=null;stopWatch();d2.state.watching=null;});\n    d2.on(\".wdbgbtn\",\"click\",()=>{d2.state.wdbgOn=!d2.state.wdbgOn;});\n    d2.on(\".camtoggle\",\"click\",()=>{d2.state.showCams=!d2.state.showCams;updateMarkers();});\n    d2.on(\".displang\",\"change\",(e,el)=>{d2.state.dispLang=el.value;const s=(d2.state.incidents||[]).find(i=>i._id===d2.state.selectedId);if(s)ensureXlate(s);});\n    d2.on(\".opencam\",\"click\",(e,el)=>{const cm=CAMS.find(c=>c.id===el.dataset.cam);if(cm)d2.state.camView=cm;});\n    d2.on(\".camclose\",\"click\",()=>{d2.state.camView=null;});\n    d2.on(\".takeover\",\"click\",(e,el)=>{const id=el.dataset.id;const inc=(d2.state.incidents||[]).find(i=>i._id===id);setHuman(id,!(inc&&inc.humanControl===true));});\n    d2.on(\".dispsend\",\"click\",(e,el)=>{const inp=d2.$(\".dispinput\");if(inp){dispatcherSay(el.dataset.id,inp.value);inp.value=\"\";}});\n    d2.on(\".dispinput\",\"keydown\",(e,el)=>{if(e.key===\"Enter\"){e.preventDefault();const id=(d2.$(\".dispsend\")||{}).dataset?d2.$(\".dispsend\").dataset.id:d2.state.selectedId;dispatcherSay(d2.state.selectedId,el.value);el.value=\"\";}});\n    d2.on(\".watch\",\"click\",(e,el)=>{startWatch(el.dataset.id);});\n    d2.raf(()=>{\n      if(!built)return;const now=Date.now();const incs=d2.state.incidents||[];let need=false;\n      incs.forEach(inc=>{if(!inc.dispatchedAt||inc.status===\"Resolved\")return;const ph=phaseOf(now-inc.dispatchedAt);if(lastPhase[inc._id]!==ph){lastPhase[inc._id]=ph;need=true;}});\n      Object.keys(squadMk).forEach(key=>{const idx=key.indexOf(\":\");const incId=key.slice(0,idx);const stId=key.slice(idx+1);const inc=incs.find(i=>i._id===incId);if(!inc){return;}const u=(inc.units||[]).find(x=>x.stationId===stId);if(!u){return;}const geo=routeGeo[key];const s=stationById(stId);let pos=null;const el=now-(u.dispatchedAt||now);const p=unitP(el);if(geo&&geo.length){pos=pointAlong(geo,p);}else if(s&&hasXY(inc)){pos=[s.lat+(inc.lat-s.lat)*ease(p),s.lng+(inc.lng-s.lng)*ease(p)];}if(pos&&Number.isFinite(pos[0])&&Number.isFinite(pos[1])){try{squadMk[key].setLatLng(pos);}catch(e){}const elm=squadMk[key].getElement&&squadMk[key].getElement();if(elm){const et=elm.querySelector(\".eta\");if(et){const ph=phaseOf(el);et.textContent=ph===\"EnRoute\"?`ETA ${Math.max(0,Math.ceil((T_ENROUTE-el)/1000))}s`:(ph===\"OnScene\"?\"到场\":\"\");}}}});\n      if(need){updateMarkers();d2.refresh();}\n    });\n  },\n};","at":"2026-07-22T08:49:22.927Z"}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"reference":"Data2BuilderAuthentication","updatedAt":"2025-10-28T02:48:49.233Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a6048d2b541fb9bbddd49fe","component":"data2-core-primitives@Artboard","system":"6a601c3c3a42b34509a601cf","displayName":"Command Center","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Command Center"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":2521,"y":962,"exactX":2521.46,"exactY":962.11}}},"reference":"opI4k_n82-J2i-NK_hX5V"},{"_id":"b33e584fc24f6441785f9fa5","parent":"6a6048d2b541fb9bbddd49fe","component":"cict-ds@CommandCenter","system":"6a601c3c3a42b34509a601cf","displayName":"CommandCenter","params":{"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true,"exactX":false,"exactY":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900,"exactWidth":false,"exactHeight":false}}}},"reference":"WHoT6k4897Alqw4c7-l2z"}]}},{"_id":"5fc4faeec8da12be8bf3d08a","component":"data2-core@Data2TableDataSource","system":"5e3b796d6228227ca637d0f4","params":{"paginate":{"default":1000},"super":{},"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"fileId"}},"title":"File ID","component":"data2-core-primitives@ObjectId"},"break":true}],"hooks":{"before":{"all":[{"hook":"softDelete"}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"timestamp","options":{"fields":["createdAt","updatedAt"]}}],"update":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}],"patch":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}]},"after":{"all":[{"hook":"fileUrl","options":{"downloadField":"params.fileUrl","uploadField":"params.uploadFileUrl","enhanceSchema":false}}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"update":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"patch":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"remove":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}]}},"multi":["create"],"useEstimatedDocumentCount":true},"reference":"FileComponentInstancesTableDataSource","updatedAt":"2022-12-19T20:42:51.837Z","createdAt":"2020-11-30T13:59:52.294Z"},{"reference":"ApplicationsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a6049140968c1e1d6f6b91a","displayName":"Wuhan Command Center","reference":"7pJZAhzpKruouYmKlyVBQ","params":{"name":{"component":"data2-core-primitives@String","params":{"content":"Wuhan Command Center"}},"shortName":{"component":"data2-core-primitives@String","params":{"content":"Wuhan Command Center"}},"domain":{"component":"data2-core-primitives@String","params":{"content":"command"}},"serverSideRendering":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"opI4k_n82-J2i-NK_hX5V"}},"responseHeaders":{"X-Frame-Options":{"component":"data2-core-primitives@String","params":{"content":""}},"Content-Security-Policy":{"component":"data2-core-primitives@String","params":{"content":""}},"X-XSS-Protection":{"component":"data2-core-primitives@String","params":{"content":""}}}},"system":"6a601c3c3a42b34509a601cf","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}