{"context":{"domain":"natura-geomarketing"},"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":"2026-07-27T14:19:36.810Z"}]}},{"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},{"component":"data2-core-types@FieldReference","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"settings.defaultAccessPolicy"}}},"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":"2026-08-07T21:51:24.037Z","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":"6a57cfe9673eb7e3c5e57784","name":"Geomarketing","domain":"natura-geomarketing","visibility":"public","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T20:13:15.077Z","createdAt":"2026-07-15T18:22:33.158Z"}]}},{"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":"6a74ae9d8bf67595bba6de82","component":"data2-core-types@Component","system":"6a57cfe9673eb7e3c5e57784","params":{"extends":"data2-core-primitives@Block","component":"timeline-sim-login","package":"natura-geomarketing","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 AUTH='timelineSimAuthFinal';\n// SSP origin is derived at runtime from a known listener's resolved URL; this is only the fallback.\nconst SSP_FALLBACK='https://natura-geomarketing.data2apis.com';\nconst RUSH_REF='AwW_11Nkg-dKksP7ic8V8';\nconst ROUTER='timelineSimRouter';\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .lroot{--bg:#f5f5f7;--panel:#ffffff;--panel2:#f2f2f7;--line:#e5e5ea;--ink:#1c1c1e;--mut:#6e6e73;--acc:#007aff;--acc2:#0060df;--closing:#ff3b30;--ok:#34c759;\n    min-height:100%;display:flex;align-items:center;justify-content:center;padding:36px 20px;background:var(--bg);\n    font:13px/1.45 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Segoe UI\",Roboto,sans-serif;color:var(--ink);-webkit-font-smoothing:antialiased}\n  .card{width:100%;max-width:400px;background:var(--panel);border:1px solid var(--line);border-radius:14px;\n    box-shadow:0 12px 34px rgba(0,0,0,.07),0 1px 2px rgba(0,0,0,.04);padding:26px 26px 22px}\n  .pill{display:inline-block;font-size:10.5px;padding:2.5px 9px;border-radius:99px;border:1px solid var(--line);color:var(--mut);background:var(--panel2)}\n  h1{font-size:17px;font-weight:600;letter-spacing:-.25px;margin:13px 0 5px}\n  .sub{font-size:12px;color:var(--mut);margin:0 0 20px;line-height:1.5}\n  .fl{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--mut);font-weight:600;margin:0 0 5px}\n  .inp{width:100%;background:var(--panel);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:9px 11px;font:inherit;font-size:13px}\n  .inp:focus{outline:2px solid rgba(0,122,255,.32);outline-offset:0;border-color:var(--acc)}\n  .inp:disabled{opacity:.55}\n  .row{margin-bottom:14px}\n  .act{width:100%;background:var(--acc);color:#fff;border:1px solid transparent;border-radius:8px;padding:10px 14px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 1px 1px rgba(0,0,0,.08);margin-top:4px}\n  .act:hover{background:var(--acc2)}\n  .act:disabled{opacity:.5;cursor:not-allowed}\n  .ghost{width:100%;background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:9px 12px;font:inherit;font-size:12.5px;cursor:pointer;margin-top:8px}\n  .ghost:hover{background:var(--panel2)}\n  .okbox{margin:12px 0 0;padding:8px 10px;border-radius:8px;background:#eefaf0;border:1px solid #bdeecb;color:#1c7c33;font-size:11.5px;font-weight:600}\n  .err{margin:12px 0 0;padding:8px 10px;border-radius:8px;background:#fff1f0;border:1px solid #ffd1cc;color:var(--closing);font-size:11.5px;font-weight:600}\n  .who{font-size:12px;color:var(--mut);margin:0 0 16px}\n  .who b{color:var(--ink)}\n  .foot{margin-top:18px;padding-top:14px;border-top:1px solid var(--line);font-size:10.5px;color:var(--mut);line-height:1.55}\n`,\n  render(d2){\n    const esc=s=>(s==null?'':String(s)).replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\n    const busy=!!d2.state.busy;\n    const err=d2.state.error||'';\n    const a=d2.auth(AUTH);\n    if(!a){\n      return `<div class=\"lroot\"><div class=\"card\">\n        <span class=\"pill\">configuração</span>\n        <h1>Estratégia de autenticação não encontrada</h1>\n        <p class=\"sub\">Não foi possível resolver <b>${esc(AUTH)}</b>. Confirme a referência da AuthenticationStrategy do sistema.</p>\n      </div></div>`;\n    }\n    // Deliberately NOT branching on a.isAuthed. The Router renders this artboard ONLY from its\n    // `login` slot, i.e. only when the route policy rejected the caller — so anyone looking at it\n    // is unauthenticated by construction. isAuthed also reads TRUE under a builder token, which\n    // would show an \"already signed in\" card that bounces to / , gets rejected, and loops.\n    return `<div class=\"lroot\"><div class=\"card\">\n      <span class=\"pill\">acesso restrito</span>\n      <h1>Simulador de Timeline de Fechamentos</h1>\n      <p class=\"sub\">Entre com suas credenciais pra abrir a timeline de fechamentos de setores.</p>\n      <div class=\"row\">\n        <label class=\"fl\" for=\"em\">E-mail</label>\n        <input class=\"inp\" id=\"em\" type=\"email\" autocomplete=\"username\" autocapitalize=\"off\" autocorrect=\"off\" spellcheck=\"false\" placeholder=\"voce@natura.com.br\"${busy?' disabled':''}>\n      </div>\n      <div class=\"row\">\n        <label class=\"fl\" for=\"pw\">Senha</label>\n        <input class=\"inp\" id=\"pw\" type=\"password\" autocomplete=\"current-password\" placeholder=\"••••••••\"${busy?' disabled':''}>\n      </div>\n      <button class=\"act\" id=\"go\"${busy?' disabled':''}>${busy?'Entrando…':'Entrar'}</button>\n      ${err?`<div class=\"err\">⚠ ${esc(err)}</div>`:''}\n      ${d2.state.ok?`<div class=\"okbox\">✓ Autenticado. No app publicado o redirecionamento é automático.</div>`:''}\n      <div class=\"foot\">Geomarketing · Natura. O simulador é read-only sobre o Databricks — cada sessão salva fica presa ao snapshot congelado da simulação.</div>\n    </div></div>`;\n  },\n  mounted(d2, root){\n    // The router re-runs its policy hook when the session changes, so a successful login normally\n    // swaps the gated content on its own. If it does not (same path), reload as a last resort —\n    // cancelled on unmount, so the reload only ever fires when the swap did NOT happen.\n    // Login now runs through the strategy, so the runtime DOES hold the session in memory and the\n    // route policy would re-evaluate on its own. The hard navigation is kept deliberately: it is\n    // the one path that cannot land on stale client state. In the builder there is no app root to\n    // go to, so we just report success instead of navigating out of /preview/<ref>. [agent:ad78d19d]\n    const enter=()=>{\n      if(d2.context.isBuilder){ d2.setState({busy:false,error:'',ok:true}); return; }\n      try{ window.location.replace('/'); }\n      catch(e){ try{ window.location.reload(); }catch(e2){} }\n    };\n    // pt-BR (ABNT2) keyboards put ^ ~ ´ ¨ on DEAD keys: pressing one before a character that cannot\n    // take the accent (like @) emits the bare mark, or leaves a combining codepoint behind. Either\n    // way it must never reach /id/ln, so strip it plus zero-width paste junk and any whitespace.\n    // The PASSWORD is deliberately left untouched — these characters are legitimate in a password.\n    const cleanEmail=s=>String(s==null?'':s)\n      .normalize('NFKC')\n      .replace(/[̀-ͯ]/g,'')\n      .replace(/[​-‍﻿]/g,'')\n      .replace(/[\\^~´¨`]/g,'')\n      .replace(/\\s+/g,'');\n    // A failed login must NEVER navigate or reload: keep the card, keep what was typed (the morph\n    // patches in place so input values survive), show the error and put the cursor back in the\n    // password so it can be corrected immediately.\n    const failWith=msg=>{\n      d2.setState({busy:false,ok:false,error:msg});\n      setTimeout(()=>{ const p=d2.$('#pw'); if(p){ try{ p.focus(); p.select(); }catch(e){} } },0);\n    };\n    const submit=async()=>{\n      if(d2.state.busy) return;\n      const rawEm=(d2.$('#em')||{}).value||'';\n      const em=cleanEmail(rawEm);\n      if(em!==rawEm && d2.$('#em')) d2.$('#em').value=em;   // show the cleaned address back to the user\n      const pw=((d2.$('#pw')||{}).value||'');\n      if(!em||!pw){ failWith('Preencha e-mail e senha.'); return; }\n      d2.setState({busy:true,error:'',ok:false});\n      try{\n        // Direct interaction with the AuthenticationStrategy — no hand-rolled fetch. The runtime\n        // owns the session envelope, so IT attaches `authorization: Bearer <jwt>` (and the session\n        // cookie) to the login POST. The previous hand-rolled fetch could not: it derived the jwt\n        // from document.cookie on the APP origin while the cookie lives on the SSP origin, so the\n        // header was silently dropped on every attempt — the 401 in devtools. [agent:ad78d19d]\n        // ⚠️ `credentials` MUST stay NESTED — a flat {email,password} authenticates nobody and\n        // fails as an UNCATCHABLE 400/403 (no exception, no rejected promise).\n        // ⚠️ A rejected login is NOT an exception either — after the promise settles, read\n        // `isAuthed`. Do not gate the error UI on catch().\n        const a=d2.auth(AUTH);\n        if(!a){ failWith('Estratégia de autenticação não encontrada ('+AUTH+').'); return; }\n        await a.login({credentials:{email:em,password:pw}});\n        if(a.isAuthed){ enter(); return; }\n        failWith('E-mail ou senha inválidos.');\n      }catch(e){\n        failWith('Falha ao autenticar: '+((e&&e.message)||'erro desconhecido'));\n      }\n    };\n    d2.on('#go','click',submit);\n    d2.on('.inp','keydown',e=>{ if(e.key==='Enter'){ e.preventDefault(); submit(); } });\n  }\n}"}}}}}},"__d2PrevCode":{"code":"const AUTH='timelineSimAuthFinal';\n// SSP origin is derived at runtime from a known listener's resolved URL; this is only the fallback.\nconst SSP_FALLBACK='https://natura-geomarketing.data2apis.com';\nconst RUSH_REF='AwW_11Nkg-dKksP7ic8V8';\nconst ROUTER='timelineSimRouter';\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .lroot{--bg:#f5f5f7;--panel:#ffffff;--panel2:#f2f2f7;--line:#e5e5ea;--ink:#1c1c1e;--mut:#6e6e73;--acc:#007aff;--acc2:#0060df;--closing:#ff3b30;--ok:#34c759;\n    min-height:100%;display:flex;align-items:center;justify-content:center;padding:36px 20px;background:var(--bg);\n    font:13px/1.45 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Segoe UI\",Roboto,sans-serif;color:var(--ink);-webkit-font-smoothing:antialiased}\n  .card{width:100%;max-width:400px;background:var(--panel);border:1px solid var(--line);border-radius:14px;\n    box-shadow:0 12px 34px rgba(0,0,0,.07),0 1px 2px rgba(0,0,0,.04);padding:26px 26px 22px}\n  .pill{display:inline-block;font-size:10.5px;padding:2.5px 9px;border-radius:99px;border:1px solid var(--line);color:var(--mut);background:var(--panel2)}\n  h1{font-size:17px;font-weight:600;letter-spacing:-.25px;margin:13px 0 5px}\n  .sub{font-size:12px;color:var(--mut);margin:0 0 20px;line-height:1.5}\n  .fl{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.4px;color:var(--mut);font-weight:600;margin:0 0 5px}\n  .inp{width:100%;background:var(--panel);border:1px solid var(--line);color:var(--ink);border-radius:8px;padding:9px 11px;font:inherit;font-size:13px}\n  .inp:focus{outline:2px solid rgba(0,122,255,.32);outline-offset:0;border-color:var(--acc)}\n  .inp:disabled{opacity:.55}\n  .row{margin-bottom:14px}\n  .act{width:100%;background:var(--acc);color:#fff;border:1px solid transparent;border-radius:8px;padding:10px 14px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;box-shadow:0 1px 1px rgba(0,0,0,.08);margin-top:4px}\n  .act:hover{background:var(--acc2)}\n  .act:disabled{opacity:.5;cursor:not-allowed}\n  .ghost{width:100%;background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:9px 12px;font:inherit;font-size:12.5px;cursor:pointer;margin-top:8px}\n  .ghost:hover{background:var(--panel2)}\n  .okbox{margin:12px 0 0;padding:8px 10px;border-radius:8px;background:#eefaf0;border:1px solid #bdeecb;color:#1c7c33;font-size:11.5px;font-weight:600}\n  .err{margin:12px 0 0;padding:8px 10px;border-radius:8px;background:#fff1f0;border:1px solid #ffd1cc;color:var(--closing);font-size:11.5px;font-weight:600}\n  .who{font-size:12px;color:var(--mut);margin:0 0 16px}\n  .who b{color:var(--ink)}\n  .foot{margin-top:18px;padding-top:14px;border-top:1px solid var(--line);font-size:10.5px;color:var(--mut);line-height:1.55}\n`,\n  render(d2){\n    const esc=s=>(s==null?'':String(s)).replace(/[&<>\"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\n    const busy=!!d2.state.busy;\n    const err=d2.state.error||'';\n    const a=d2.auth(AUTH);\n    if(!a){\n      return `<div class=\"lroot\"><div class=\"card\">\n        <span class=\"pill\">configuração</span>\n        <h1>Estratégia de autenticação não encontrada</h1>\n        <p class=\"sub\">Não foi possível resolver <b>${esc(AUTH)}</b>. Confirme a referência da AuthenticationStrategy do sistema.</p>\n      </div></div>`;\n    }\n    // Deliberately NOT branching on a.isAuthed. The Router renders this artboard ONLY from its\n    // `login` slot, i.e. only when the route policy rejected the caller — so anyone looking at it\n    // is unauthenticated by construction. isAuthed also reads TRUE under a builder token, which\n    // would show an \"already signed in\" card that bounces to / , gets rejected, and loops.\n    return `<div class=\"lroot\"><div class=\"card\">\n      <span class=\"pill\">acesso restrito</span>\n      <h1>Simulador de Timeline de Fechamentos</h1>\n      <p class=\"sub\">Entre com suas credenciais pra abrir a timeline de fechamentos de setores.</p>\n      <div class=\"row\">\n        <label class=\"fl\" for=\"em\">E-mail</label>\n        <input class=\"inp\" id=\"em\" type=\"email\" autocomplete=\"username\" autocapitalize=\"off\" autocorrect=\"off\" spellcheck=\"false\" placeholder=\"voce@natura.com.br\"${busy?' disabled':''}>\n      </div>\n      <div class=\"row\">\n        <label class=\"fl\" for=\"pw\">Senha</label>\n        <input class=\"inp\" id=\"pw\" type=\"password\" autocomplete=\"current-password\" placeholder=\"••••••••\"${busy?' disabled':''}>\n      </div>\n      <button class=\"act\" id=\"go\"${busy?' disabled':''}>${busy?'Entrando…':'Entrar'}</button>\n      ${err?`<div class=\"err\">⚠ ${esc(err)}</div>`:''}\n      ${d2.state.ok?`<div class=\"okbox\">✓ Autenticado. No app publicado o redirecionamento é automático.</div>`:''}\n      <div class=\"foot\">Geomarketing · Natura. O simulador é read-only sobre o Databricks — cada sessão salva fica presa ao snapshot congelado da simulação.</div>\n    </div></div>`;\n  },\n  mounted(d2, root){\n    // The router re-runs its policy hook when the session changes, so a successful login normally\n    // swaps the gated content on its own. If it does not (same path), reload as a last resort —\n    // cancelled on unmount, so the reload only ever fires when the swap did NOT happen.\n    // The direct POST sets the session COOKIE; the in-memory runtime never saw it, so a HARD\n    // navigation is required here — d2.router().goTo() would keep the stale client state and the\n    // route policy would reject again. In the builder there is no app root to go to, so we just\n    // report success instead of navigating out of /preview/<ref>.\n    const enter=()=>{\n      if(d2.context.isBuilder){ d2.setState({busy:false,error:'',ok:true}); return; }\n      try{ window.location.replace('/'); }\n      catch(e){ try{ window.location.reload(); }catch(e2){} }\n    };\n    // pt-BR (ABNT2) keyboards put ^ ~ ´ ¨ on DEAD keys: pressing one before a character that cannot\n    // take the accent (like @) emits the bare mark, or leaves a combining codepoint behind. Either\n    // way it must never reach /id/ln, so strip it plus zero-width paste junk and any whitespace.\n    // The PASSWORD is deliberately left untouched — these characters are legitimate in a password.\n    const cleanEmail=s=>String(s==null?'':s)\n      .normalize('NFKC')\n      .replace(/[̀-ͯ]/g,'')\n      .replace(/[​-‍﻿]/g,'')\n      .replace(/[\\^~´¨`]/g,'')\n      .replace(/\\s+/g,'');\n    // A failed login must NEVER navigate or reload: keep the card, keep what was typed (the morph\n    // patches in place so input values survive), show the error and put the cursor back in the\n    // password so it can be corrected immediately.\n    const failWith=msg=>{\n      d2.setState({busy:false,ok:false,error:msg});\n      setTimeout(()=>{ const p=d2.$('#pw'); if(p){ try{ p.focus(); p.select(); }catch(e){} } },0);\n    };\n    const submit=async()=>{\n      if(d2.state.busy) return;\n      const rawEm=(d2.$('#em')||{}).value||'';\n      const em=cleanEmail(rawEm);\n      if(em!==rawEm && d2.$('#em')) d2.$('#em').value=em;   // show the cleaned address back to the user\n      const pw=((d2.$('#pw')||{}).value||'');\n      if(!em||!pw){ failWith('Preencha e-mail e senha.'); return; }\n      d2.setState({busy:true,error:'',ok:false});\n      try{\n        // Direct interaction with the AuthenticationStrategy — no hand-rolled fetch. The runtime\n        // owns the session envelope, so IT attaches `authorization: Bearer <jwt>` (and the session\n        // cookie) to the login POST. The previous hand-rolled fetch could not: it derived the jwt\n        // from document.cookie on the APP origin while the cookie lives on the SSP origin, so the\n        // header was silently dropped on every attempt — the 401 in devtools. [agent:ad78d19d]\n        // ⚠️ `credentials` MUST stay NESTED — a flat {email,password} authenticates nobody and\n        // fails as an UNCATCHABLE 400/403 (no exception, no rejected promise).\n        // ⚠️ A rejected login is NOT an exception either — after the promise settles, read\n        // `isAuthed`. Do not gate the error UI on catch().\n        const a=d2.auth(AUTH);\n        if(!a){ failWith('Estratégia de autenticação não encontrada ('+AUTH+').'); return; }\n        await a.login({credentials:{email:em,password:pw}});\n        if(a.isAuthed){ enter(); return; }\n        failWith('E-mail ou senha inválidos.');\n      }catch(e){\n        failWith('Falha ao autenticar: '+((e&&e.message)||'erro desconhecido'));\n      }\n    };\n    d2.on('#go','click',submit);\n    d2.on('.inp','keydown',e=>{ if(e.key==='Enter'){ e.preventDefault(); submit(); } });\n  }\n}","at":"2026-08-06T19:07:41.976Z"}}},{"_id":"6a75620d9f1ece2343b4d4da","component":"data2-core-types@Component","system":"6a57cfe9673eb7e3c5e57784","params":{"extends":"data2-core-primitives@Block","component":"cb-senso-de-rede","package":"natura-geomarketing","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ---------- Senso de rede — afinidades entre consultoras de beleza ----------\n// Cada nó é uma CB; cada laço é uma AFINIDADE DERIVADA do snapshot mais recente, nunca um\n// cadastro de relacionamento — não existe tabela de \"quem conhece quem\". Por isso o peso de\n// cada dimensão é o INVERSO DA SUA FREQUÊNCIA na amostra: dividir a mesma líder diz muito mais\n// sobre duas CBs do que ambas estarem ativas no ciclo, e é o peso que decide a cor, a espessura\n// e quais laços sobrevivem ao corte por nó. Sem isso o grafo vira uma malha uniforme onde tudo\n// se liga a tudo e nada significa nada.\nconst T = 'product.perfil_cb.crm_perfil_cb_diaria_historico';\nconst RUSH_FALLBACK = 'AwW_11Nkg-dKksP7ic8V8';\nconst SNAP = `(SELECT MAX(DT_HISTORICO) FROM ${T} WHERE CD_PAIS=1)`;\n// Paleta = cores de sistema do macOS, as mesmas do simulador de timeline, pra que os dois módulos\n// leiam como um produto só e não como duas ferramentas diferentes coladas.\nconst BG = '#f5f5f7';\n\nconst DIMS = [\n  { k:'origem',     label:'Origem',     color:'#ff9500', hint:'mesmo município' },\n  { k:'territorio', label:'Território', color:'#34c759', hint:'mesmo setor' },\n  { k:'rede',       label:'Rede',       color:'#007aff', hint:'mesma líder' },\n  { k:'trajetoria', label:'Trajetória', color:'#af52de', hint:'mesmo nível do plano' },\n  { k:'ritmo',      label:'Ritmo',      color:'#ff2d55', hint:'pedidos por ciclo parecidos' },\n  { k:'alcance',    label:'Alcance',    color:'#ffcc00', hint:'pontos parecidos' },\n  { k:'porte',      label:'Porte',      color:'#30b0c7', hint:'receita parecida' },\n  { k:'marca',      label:'Marca',      color:'#a2845e', hint:'mix Natura/Avon parecido' },\n  { k:'constancia', label:'Constância', color:'#8e8e93', hint:'ativas no ciclo' },\n];\nconst DIM_BY = {}; DIMS.forEach(d => DIM_BY[d.k] = d);\nconst TINTS = ['#e6f0ff','#e6f7ea','#fff2e0','#f6ecfd','#ffe9ee','#e6f5f9','#f2f2f7'];\n\nconst esc = s => (s == null ? '' : String(s)).replace(/[&<>\"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\nconst digits = s => String(s == null ? '' : s).replace(/[^0-9]/g, '');\nconst letters = s => String(s == null ? '' : s).replace(/[^A-Za-zÀ-ÿ]/g, '');\nconst hash = s => { let h = 0; const t = String(s || ''); for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return Math.abs(h); };\nconst titleCase = s => String(s || '').toLowerCase().replace(/(^|[\\s'-])([a-zà-ÿ])/g, (m, a, b) => a + b.toUpperCase());\n\nfunction rowsOf(resp) {\n  const c = (resp.columns || []).map(x => x.name);\n  return (resp.rows || []).map(a => Object.fromEntries(c.map((k, i) => [k, a[i]])));\n}\nasync function q(sql) {\n  const ref = d2.params.consultaRush || RUSH_FALLBACK;\n  let dbx;\n  try {\n    const res = await d2.rush(ref).invoke({ sql });\n    dbx = res.data.result.data;              // envelope do listener: res.data, depois o payload do Databricks\n  } catch (e) {\n    throw new Error('consulta indisponível (rush ' + ref + '): ' + (e && e.message ? e.message : e));\n  }\n  if (dbx.state && dbx.state !== 'SUCCEEDED') {\n    throw new Error('Databricks ' + dbx.state + ': ' + ((dbx.error && dbx.error.message) || 'erro desconhecido'));\n  }\n  return dbx;\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%;--bg:#f5f5f7;--panel:#ffffff;--panel2:#f2f2f7;--line:#e5e5ea;--ink:#1c1c1e;--mut:#6e6e73;--acc:#007aff;--closing:#ff3b30}\n  *{box-sizing:border-box}\n  .root{display:flex;flex-direction:column;height:100%;background:var(--bg);color:var(--ink);\n    font:13px/1.45 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Segoe UI\",Roboto,sans-serif;-webkit-font-smoothing:antialiased}\n  header{padding:10px 16px;background:rgba(246,246,248,.85);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);\n    border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;flex-wrap:wrap;flex:0 0 auto}\n  header h1{font-size:14.5px;margin:0;font-weight:600;letter-spacing:-.1px;color:var(--ink)}\n  .spacer{flex:1}\n  .pill{font-size:10.5px;padding:2.5px 9px;border-radius:99px;border:1px solid var(--line);color:var(--mut);background:var(--panel2);white-space:nowrap}\n  .pill b{color:var(--ink);font-weight:600}\n  .status{font-size:11px;color:var(--mut)}\n  .status.err{color:var(--closing)}\n  .btn{font:inherit;font-size:12px;padding:4.5px 11px;border-radius:7px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}\n  .btn:hover{border-color:#d5d5da;background:#fbfbfd}\n  .btn:active{background:#f0f0f4}\n  .btn:disabled{opacity:.5;cursor:progress}\n  .howto{padding:8px 16px;background:var(--panel);border-bottom:1px solid var(--line);display:flex;gap:16px;flex-wrap:wrap;\n    font-size:11.5px;color:var(--mut);flex:0 0 auto}\n  .howto b{color:var(--ink)} .howto span{max-width:360px}\n  .body{flex:1;display:flex;min-height:0}\n  .stage{flex:1;position:relative;min-width:0}\n  canvas{width:100%;height:100%;display:block}\n  .side{width:334px;flex:0 0 334px;border-left:1px solid var(--line);padding:12px;display:flex;flex-direction:column;gap:12px;overflow:auto}\n  .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 13px}\n  .card h2{margin:0 0 9px;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);font-weight:600}\n  .card h3{margin:0 0 2px;font-size:15px;font-weight:600;letter-spacing:-.2px}\n  .card .sub{font-size:11.5px;color:var(--mut);margin-bottom:10px}\n  .facts{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px}\n  .fact{background:var(--panel2);border-radius:8px;padding:6px 8px}\n  .fact b{display:block;font-size:13px;font-weight:600}\n  .fact span{font-size:9.5px;color:var(--mut);letter-spacing:.05em;text-transform:uppercase}\n  .conn{border-top:1px solid var(--line);padding-top:8px}\n  .conn h4{margin:0 0 4px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);font-weight:600}\n  .crow{display:flex;align-items:center;gap:8px;padding:5px 6px;margin:0 -6px;cursor:pointer;border-radius:7px}\n  .crow:hover{background:var(--panel2)}\n  .cdot{width:22px;height:22px;border-radius:50%;flex:0 0 22px;display:flex;align-items:center;justify-content:center;\n    font-size:9px;font-weight:600;color:#3a3a3c;border:1px solid rgba(60,60,67,.12)}\n  .cname{flex:1;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .tags{display:flex;gap:3px;flex-wrap:wrap;justify-content:flex-end;max-width:110px}\n  .tag{width:7px;height:7px;border-radius:50%}\n  .hintline{font-size:11.5px;color:var(--mut);line-height:1.5}\n  .hintline b{color:var(--ink)}\n  .legend{display:flex;flex-wrap:wrap;gap:6px}\n  .chip{display:inline-flex;align-items:center;gap:6px;background:var(--panel2);border:1px solid var(--line);border-radius:99px;\n    padding:4px 10px 4px 7px;font:inherit;font-size:11.5px;color:var(--ink);cursor:pointer;user-select:none;\n    transition:opacity .12s,border-color .12s,color .12s}\n  .chip i{width:8px;height:8px;border-radius:50%;flex:0 0 8px}\n  .chip b{font-weight:500;color:var(--mut);font-size:10px}\n  .chip:hover{border-color:var(--acc);color:var(--acc)}\n  .chip:focus-visible{outline:2px solid rgba(0,122,255,.35);outline-offset:1px}\n  .chip.off{opacity:.4}\n  .tip{position:absolute;pointer-events:none;background:rgba(28,28,30,.94);color:#fff;border-radius:8px;padding:6px 9px;\n    font-size:11.5px;line-height:1.35;opacity:0;transition:opacity .1s;max-width:230px;z-index:5;box-shadow:0 8px 24px rgba(0,0,0,.18)}\n  .tip.on{opacity:1}\n  .tip em{font-style:normal;color:#aeaeb2;display:block;font-size:10.5px;margin-top:2px}\n  `,\n\n  // Shell estático de propósito: há um <canvas> aqui, e qualquer leitura reativa dentro de\n  // render() re-executa o render inteiro e devolve o canvas ao default 300x150 (checklist §15).\n  // Tudo que muda — contagem, cards, chips — é escrito por DOM dentro de mounted().\n  render() {\n    const chips = DIMS.map(d =>\n      `<button class=\"chip\" data-k=\"${d.k}\" title=\"${esc(d.hint)}\"><i style=\"background:${d.color}\"></i>${d.label} <b data-n=\"${d.k}\"></b></button>`\n    ).join('');\n    return `<div class=\"root\">\n      <header>\n        <h1>Senso de rede — consultoras</h1>\n        <span class=\"pill\"><b id=\"count\">—</b> pessoas</span>\n        <span class=\"status\" id=\"status\">consultando…</span>\n        <div class=\"spacer\"></div>\n        <button class=\"btn\" id=\"reload\">Recarregar</button>\n      </header>\n      <div class=\"howto\">\n        <span><b>1.</b> Cada nó é uma CB — o tamanho segue os pontos do ciclo, o anel marca quem está ativa.</span>\n        <span><b>2.</b> Cada laço é uma afinidade calculada; as contas no meio contam quantas dimensões as duas dividem.</span>\n        <span><b>3.</b> Clique num nó pra ver quem ele alcança — arraste pra reorganizar.</span>\n        <span><b>4.</b> Ligue e desligue dimensões nas marcações à direita.</span>\n      </div>\n      <div class=\"body\">\n        <div class=\"stage\" data-d2-static>\n          <canvas id=\"net\"></canvas>\n          <div class=\"tip\" id=\"tip\"></div>\n        </div>\n        <aside class=\"side\">\n          <section class=\"card\">\n            <h2>Afinidades</h2>\n            <div class=\"legend\" id=\"legend\">${chips}</div>\n          </section>\n          <section class=\"card\" id=\"detail\"></section>\n        </aside>\n      </div>\n    </div>`;\n  },\n\n  mounted(d2, root) {\n    const $ = s => root.querySelector(s);\n    const cv = $('#net'), ctx = cv.getContext('2d'), tip = $('#tip');\n    const S = {\n      nodes: [], edges: [], adj: new Map(), W: {}, on: {},\n      hover: null, sel: null, drag: null, alpha: 0, w: 0, h: 0\n    };\n    DIMS.forEach(d => S.on[d.k] = true);\n\n    const say = (msg, err) => { const el = $('#status'); el.textContent = msg || ''; el.classList.toggle('err', !!err); };\n\n    // ---------- canvas ----------\n    function resize() {\n      const r = cv.getBoundingClientRect();\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      S.w = r.width; S.h = r.height;\n      cv.width = Math.max(1, Math.round(r.width * dpr));\n      cv.height = Math.max(1, Math.round(r.height * dpr));\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);   // o resize zera a matriz — reaplicar sempre\n    }\n    const ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(() => ro.disconnect());\n    resize();\n\n    // ---------- amostra ----------\n    async function nameColumn() {\n      // O nome da CB não está no contrato conhecido da tabela, então em vez de chutar um\n      // DS_NOME que pode não existir (e derrubar a query inteira), descobrimos a coluna.\n      try {\n        const d = await q(`DESCRIBE ${T}`);\n        const cols = rowsOf(d).map(r => String(r.col_name || r.name || '')).filter(Boolean);\n        const cand = cols.filter(c => /NOME|NAME/i.test(c) &&\n          !/LEADER|LIDER|MUNIC|ESTAD|SETOR|GRUPO|CANAL|MARCA|PAIS|REGIA|GV|NVL/i.test(c));\n        cand.sort((a, b) => (/CONSULT|CB|PESSOA/i.test(b) ? 1 : 0) - (/CONSULT|CB|PESSOA/i.test(a) ? 1 : 0));\n        return cand[0] || null;\n      } catch (e) { return null; }\n    }\n    function sqlFor(nomeCol, where, order, lim) {\n      const nome = nomeCol ? `MAX(${nomeCol}) nome,` : '';\n      return `SELECT CD_CONSULTORA cb, ${nome}\n        MAX(DS_MUNICIPIO) municipio, MAX(DS_ESTADO) uf, MAX(CD_SETOR) setor,\n        MAX(CD_GRUPO) grupo, MAX(DS_LEADER_NAME) lider, MAX(DS_NVL_PLAN_CRESC) nivel,\n        SUM(QT_PEDIDOS_CICLO) pedidos, SUM(QT_PONTOS_CICLO) pontos,\n        SUM(VL_PEDIDOS_NATURA_CICLO) rec_nat, SUM(VL_PEDIDOS_AVON_CICLO) rec_avon,\n        MAX(CASE WHEN ST_FEZ_PED_CICLO_ATUAL THEN 1 ELSE 0 END) ativa\n       FROM ${T} WHERE ${where} GROUP BY CD_CONSULTORA ${order} LIMIT ${lim}`;\n    }\n\n    // Faixas relativas à AMOSTRA, não absolutas: \"ritmo parecido\" só quer dizer alguma coisa\n    // comparado com as outras CBs desta consulta.\n    function bucketer(vals) {\n      const v = vals.filter(x => isFinite(x)).sort((a, b) => a - b);\n      if (!v.length) return () => 0;\n      const at = p => v[Math.min(v.length - 1, Math.floor(p * v.length))];\n      const q1 = at(.25), q2 = at(.5), q3 = at(.75);\n      return x => (x <= q1 ? 0 : x <= q2 ? 1 : x <= q3 ? 2 : 3);\n    }\n\n    // Amostra sintética — existe para revisar layout/física sem sessão do simulador (o listener do\n    // Databricks é gated). Nunca é o default e se anuncia na tela: um grafo bonito com gente que não\n    // existe é pior do que um erro honesto.\n    function demoRows(n) {\n      const NOMES = ['Ana Ribeiro','Marta Souza','Cláudia Lima','Rita Alves','Vera Nunes','Sônia Braga','Ivone Castro','Lúcia Prado','Neide Rocha','Tânia Melo','Dora Pires','Elis Faria','Bruna Antunes','Carla Moraes','Selma Dutra','Joana Bastos','Regina Paiva','Marli Cardoso','Fátima Goulart','Simone Teles'];\n      const MUN = ['Brasília','Formosa','Buritis','Unaí','Paracatu','Arinos','Riachinho','Bonfinópolis','Chapada Gaúcha','Urucuia'];\n      const NIV = ['Bronze','Cristal','Prata','Ouro','Diamante'];\n      const out = [];\n      for (let i = 0; i < n; i++) {\n        const h = hash('cb' + i);\n        const mun = MUN[h % MUN.length];\n        const setor = 3200 + ((h >> 3) % 6) * 7;\n        const grupo = 100 + ((h >> 5) % 14);\n        const escala = .25 + ((h >> 7) % 100) / 100 * 1.8;\n        out.push({\n          cb: 900000 + i,\n          nome: NOMES[h % NOMES.length] + ' ' + String.fromCharCode(65 + (h >> 11) % 26) + '.',\n          municipio: mun, uf: mun === 'Chapada Gaúcha' || mun === 'Urucuia' ? 'MG' : 'GO',\n          setor: setor, grupo: grupo, lider: 'Líder ' + grupo,\n          nivel: NIV[(h >> 13) % NIV.length],\n          pedidos: Math.round(4 + escala * 22), pontos: Math.round(120 + escala * 900),\n          rec_nat: Math.round(escala * 3200), rec_avon: Math.round(escala * 3200 * (((h >> 17) % 100) / 100)),\n          ativa: (h >> 19) % 10 > 2 ? 1 : 0\n        });\n      }\n      return out;\n    }\n\n    async function load() {\n      say('consultando o snapshot mais recente…');\n      const lim = Math.max(12, Math.min(400, +d2.param('limite', 82) || 82));\n      const setor = digits(d2.param('setor', ''));\n      const uf = letters(d2.param('uf', '')).toUpperCase();\n      const aleat = String(d2.param('amostra', 'top') || 'top').toLowerCase().indexOf('aleat') === 0;\n      let where = `CD_PAIS=1 AND CD_GRUPO>0 AND DT_HISTORICO=${SNAP}`;\n      if (setor) where += ` AND CD_SETOR=${setor}`;\n      if (uf) where += ` AND UPPER(DS_ESTADO)='${uf}'`;\n      const order = aleat ? 'ORDER BY RAND()' : 'ORDER BY pontos DESC';\n\n      // `amostra: \"demo\"` em vez de um param novo — a declaração já existe e o modo fica visível\n      // no mesmo lugar onde se escolhe top/aleatória, não escondido num booleano à parte.\n      const demo = String(d2.param('amostra', 'top') || '').toLowerCase().indexOf('demo') === 0;\n      let raw;\n      if (demo) {\n        raw = demoRows(lim);\n      } else {\n        const nc = await nameColumn();\n        const attempts = nc ? [nc, null] : [null];\n        let data = null, lastErr = null;\n        for (const col of attempts) {\n          try { data = await q(sqlFor(col, where, order, lim)); break; }\n          catch (e) { lastErr = e; }\n        }\n        if (!data) throw lastErr || new Error('sem resposta da consulta');\n        raw = rowsOf(data);\n      }\n      if (!raw.length) throw new Error('nenhuma CB encontrada com esses filtros' + (setor ? ' (setor ' + setor + ')' : '') + (uf ? ' (UF ' + uf + ')' : ''));\n\n      const bPed = bucketer(raw.map(r => +r.pedidos || 0));\n      const bPon = bucketer(raw.map(r => +r.pontos || 0));\n      const bRec = bucketer(raw.map(r => (+r.rec_nat || 0) + (+r.rec_avon || 0)));\n\n      S.nodes = raw.map((r, i) => {\n        const nat = +r.rec_nat || 0, avon = +r.rec_avon || 0, rec = nat + avon;\n        const nome = titleCase(r.nome || '');\n        const cb = String(r.cb == null ? '' : r.cb);\n        const ini = nome ? d2.fmt.initials(nome, 2) : cb.slice(-2);\n        return {\n          i, cb, nome, ini,\n          municipio: titleCase(r.municipio || ''), uf: String(r.uf || ''),\n          setor: String(r.setor == null ? '' : r.setor), grupo: String(r.grupo == null ? '' : r.grupo),\n          lider: titleCase(r.lider || ''), nivel: String(r.nivel || ''),\n          pedidos: +r.pedidos || 0, pontos: +r.pontos || 0, receita: rec,\n          ativa: +r.ativa === 1 ? 1 : 0,\n          bRitmo: bPed(+r.pedidos || 0), bAlcance: bPon(+r.pontos || 0), bPorte: bRec(rec),\n          bMarca: rec > 0 ? Math.min(3, Math.floor((nat / rec) * 4)) : null,\n          tint: TINTS[hash(String(r.setor) + '|' + String(r.grupo)) % TINTS.length],\n          r: 0, x: 0, y: 0, vx: 0, vy: 0\n        };\n      });\n\n      const maxP = Math.max(...S.nodes.map(n => n.pontos), 1);\n      S.nodes.forEach(n => {\n        n.r = 7 + 17 * Math.sqrt(Math.max(0, n.pontos) / maxP);\n        const a = (n.i / S.nodes.length) * Math.PI * 2, rad = 140 + (hash(n.cb) % 220);\n        n.x = S.w / 2 + Math.cos(a) * rad; n.y = S.h / 2 + Math.sin(a) * rad;\n      });\n\n      DIMS.forEach(d => S.W[d.k] = weightOf(d.k));\n      rebuild();\n      $('#count').textContent = d2.fmt.number(S.nodes.length);\n      say((demo ? '⚠ AMOSTRA SINTÉTICA (dadosDemo) — ' : '') + `${S.nodes.length} CBs · ${S.edges.length} laços · ${S.nodes.filter(n => n.ativa).length} ativas no ciclo`, demo);\n      renderDetail(null);\n    }\n\n    // ---------- afinidades ----------\n    function keyOf(k, n) {\n      if (k === 'origem') return n.municipio ? n.uf + '/' + n.municipio : null;\n      if (k === 'territorio') return n.setor || null;\n      if (k === 'rede') return n.grupo || null;\n      if (k === 'trajetoria') return n.nivel || null;\n      if (k === 'ritmo') return 'r' + n.bRitmo;\n      if (k === 'alcance') return 'a' + n.bAlcance;\n      if (k === 'porte') return 'p' + n.bPorte;\n      if (k === 'marca') return n.bMarca == null ? null : 'm' + n.bMarca;\n      if (k === 'constancia') return n.ativa ? 'ativa' : null;\n      return null;\n    }\n    // Peso = raridade. p = chance de dois nós sorteados caírem no mesmo grupo desta dimensão;\n    // log2(1/p) transforma isso em bits de informação, então \"mesma líder\" pesa muito mais que\n    // \"ambas ativas\" sem que ninguém precise arbitrar uma tabela de pesos à mão.\n    function weightOf(k) {\n      const g = {}, n = S.nodes.length;\n      S.nodes.forEach(x => { const key = keyOf(k, x); if (key != null) g[key] = (g[key] || 0) + 1; });\n      let p = 0; Object.values(g).forEach(c => { p += (c / n) * (c / n); });\n      return p > 0 ? Math.max(.35, Math.min(6, Math.log2(1 / p))) : 0;\n    }\n    function sharedDims(a, b) {\n      const out = [];\n      for (const d of DIMS) {\n        if (!S.on[d.k]) continue;\n        const ka = keyOf(d.k, a); if (ka == null) continue;\n        if (ka === keyOf(d.k, b)) out.push(d.k);\n      }\n      return out;\n    }\n    function rebuild() {\n      const ns = S.nodes, K = Math.max(1, Math.min(8, +d2.param('lacos', 4) || 4));\n      const cand = ns.map(() => []);\n      for (let i = 0; i < ns.length; i++) {\n        for (let j = i + 1; j < ns.length; j++) {\n          const dims = sharedDims(ns[i], ns[j]);\n          if (!dims.length) continue;\n          let sc = 0; for (const k of dims) sc += S.W[k] || 0;\n          if (sc <= 0) continue;\n          cand[i].push({ o: j, sc, dims }); cand[j].push({ o: i, sc, dims });\n        }\n      }\n      // Corte por nó, não global: um corte global deixaria as CBs de município grande com\n      // dezenas de laços e as demais órfãs — cada uma guarda seus K vínculos mais fortes.\n      const seen = new Set(), out = [];\n      cand.forEach((list, i) => {\n        list.sort((a, b) => b.sc - a.sc).slice(0, K).forEach(e => {\n          const a = Math.min(i, e.o), b = Math.max(i, e.o), key = a + ':' + b;\n          if (seen.has(key)) return; seen.add(key);\n          const top = e.dims.slice().sort((x, y) => (S.W[y] || 0) - (S.W[x] || 0))[0];\n          out.push({ a, b, sc: e.sc, dims: e.dims, top });\n        });\n      });\n      S.edges = out;\n      S.adj = new Map();\n      out.forEach((e, idx) => {\n        if (!S.adj.has(e.a)) S.adj.set(e.a, []);\n        if (!S.adj.has(e.b)) S.adj.set(e.b, []);\n        S.adj.get(e.a).push(idx); S.adj.get(e.b).push(idx);\n      });\n      const maxSc = Math.max(...out.map(e => e.sc), 1);\n      out.forEach(e => e.n = e.sc / maxSc);\n      const counts = {}; DIMS.forEach(d => counts[d.k] = 0);\n      out.forEach(e => e.dims.forEach(k => counts[k]++));\n      DIMS.forEach(d => { const el = root.querySelector(`[data-n=\"${d.k}\"]`); if (el) el.textContent = counts[d.k] ? counts[d.k] : ''; });\n      S.alpha = 1;\n    }\n\n    // ---------- física ----------\n    function tick() {\n      const ns = S.nodes, cx = S.w / 2, cy = S.h / 2;\n      if (S.alpha < .02 || !ns.length) return;\n      const k = S.alpha;\n      for (let i = 0; i < ns.length; i++) {\n        const a = ns[i];\n        for (let j = i + 1; j < ns.length; j++) {\n          const b = ns[j];\n          let dx = b.x - a.x, dy = b.y - a.y, d = Math.hypot(dx, dy);\n          if (d < .01) {   // coincidentes: desempata com um deslocamento determinístico\n            dx = ((hash(a.cb + b.cb) % 21) - 10) / 10;\n            dy = ((hash(b.cb + a.cb) % 21) - 10) / 10;\n            d = Math.hypot(dx, dy) || 1;\n          }\n          const ux = dx / d, uy = dy / d;\n          const rep = Math.min(4, 9000 / (d * d)) * k;\n          a.vx -= ux * rep; a.vy -= uy * rep;\n          b.vx += ux * rep; b.vy += uy * rep;\n          // Colisão dura, resolvida na POSIÇÃO e não na velocidade: só repulsão deixa os círculos\n          // se sobreporem enquanto o sistema esfria, e as iniciais viram sopa.\n          const min = a.r + b.r + 6;\n          if (d < min) {\n            const push = (min - d) * .5;\n            a.x -= ux * push; a.y -= uy * push;\n            b.x += ux * push; b.y += uy * push;\n          }\n        }\n      }\n      for (const e of S.edges) {\n        const a = ns[e.a], b = ns[e.b];\n        const dx = b.x - a.x, dy = b.y - a.y, d = Math.max(.01, Math.hypot(dx, dy));\n        const target = 170 - 95 * e.n;          // laço forte = mais curto\n        const f = (d - target) * .022 * (.4 + .6 * e.n) * k;\n        const ux = dx / d, uy = dy / d;\n        a.vx += ux * f; a.vy += uy * f;\n        b.vx -= ux * f; b.vy -= uy * f;\n      }\n      for (const n of ns) {\n        if (S.drag === n) { n.vx = 0; n.vy = 0; continue; }\n        n.vx += (cx - n.x) * .002 * k; n.vy += (cy - n.y) * .002 * k;\n        n.vx *= .84; n.vy *= .84;\n        n.x += Math.max(-9, Math.min(9, n.vx));\n        n.y += Math.max(-9, Math.min(9, n.vy));\n      }\n      S.alpha *= .994;\n    }\n\n    // Enquadramento roda TODO frame, fora do laço de física: se ficasse dentro, congelaria junto\n    // com o alpha e o grafo terminaria transbordando o palco (ou torto) exatamente onde parou.\n    // Também é o que reenquadra depois de um resize, sem precisar reaquecer a simulação.\n    function fit() {\n      const ns = S.nodes;\n      if (!ns.length || S.drag) return;\n      let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n      for (const n of ns) {\n        x0 = Math.min(x0, n.x - n.r); y0 = Math.min(y0, n.y - n.r);\n        x1 = Math.max(x1, n.x + n.r); y1 = Math.max(y1, n.y + n.r);\n      }\n      const cx = S.w / 2, cy = S.h / 2;\n      const ox = cx - (x0 + x1) / 2, oy = cy - (y0 + y1) / 2;\n      if (Math.abs(ox) > .4 || Math.abs(oy) > .4) for (const n of ns) { n.x += ox * .12; n.y += oy * .12; }\n      const sc = Math.min(1, (S.w - 56) / Math.max(1, x1 - x0), (S.h - 56) / Math.max(1, y1 - y0));\n      if (sc < .999) for (const n of ns) {\n        n.x = cx + (n.x - cx) * (1 - (1 - sc) * .12);\n        n.y = cy + (n.y - cy) * (1 - (1 - sc) * .12);\n      }\n    }\n\n    // ---------- desenho ----------\n    function draw() {\n      ctx.clearRect(0, 0, S.w, S.h);\n      ctx.fillStyle = BG; ctx.fillRect(0, 0, S.w, S.h);\n      if (!S.nodes.length) return;\n      const focus = S.sel != null ? S.sel : S.hover;\n      const near = new Set();\n      if (focus != null) { near.add(focus); (S.adj.get(focus) || []).forEach(i => { near.add(S.edges[i].a); near.add(S.edges[i].b); }); }\n\n      S.edges.forEach((e, idx) => {\n        const a = S.nodes[e.a], b = S.nodes[e.b];\n        const lit = focus == null || e.a === focus || e.b === focus;\n        const col = (DIM_BY[e.top] || DIMS[0]).color;\n        ctx.globalAlpha = focus == null ? .16 + .34 * e.n : (lit ? .5 + .4 * e.n : .05);\n        ctx.strokeStyle = col;\n        ctx.lineWidth = .5 + 1.7 * e.n;\n        ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();\n        // uma conta por dimensão compartilhada, no meio do laço — é a leitura rápida de\n        // \"quantas coisas estas duas têm em comum\" sem precisar do hover\n        const dims = e.dims;\n        for (let i = 0; i < dims.length; i++) {\n          const t = (i + 1) / (dims.length + 1);\n          ctx.globalAlpha = focus == null ? .55 : (lit ? .95 : .08);\n          ctx.fillStyle = (DIM_BY[dims[i]] || DIMS[0]).color;\n          ctx.beginPath(); ctx.arc(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, 1.9 + 1.3 * e.n, 0, 6.284); ctx.fill();\n        }\n      });\n\n      S.nodes.forEach(n => {\n        const dim = focus != null && !near.has(n.i);\n        ctx.globalAlpha = dim ? .18 : 1;\n        ctx.beginPath(); ctx.arc(n.x, n.y, n.r, 0, 6.284);\n        ctx.fillStyle = n.tint; ctx.fill();\n        ctx.lineWidth = n.i === focus ? 2.5 : 1;\n        ctx.strokeStyle = n.i === focus ? '#007aff' : (n.ativa ? 'rgba(60,60,67,.38)' : 'rgba(60,60,67,.14)');\n        ctx.stroke();\n        if (n.r >= 12) {   // nós pequenos ficam como pontos, sem inicial ilegível dentro\n          ctx.globalAlpha = dim ? .2 : .85;\n          ctx.fillStyle = '#3a3a3c';\n          ctx.font = `600 ${Math.round(n.r * .78)}px -apple-system,\"Segoe UI\",Roboto,sans-serif`;\n          ctx.textAlign = 'center'; ctx.textBaseline = 'middle';\n          ctx.fillText(n.ini, n.x, n.y + .5);\n        }\n      });\n      ctx.globalAlpha = 1;\n    }\n\n    d2.raf(() => { tick(); fit(); draw(); });\n\n    // ---------- interação ----------\n    function hit(px, py) {\n      let best = null, bd = Infinity;\n      for (const n of S.nodes) {\n        const d = Math.hypot(n.x - px, n.y - py);\n        if (d < n.r + 5 && d < bd) { bd = d; best = n; }\n      }\n      return best;\n    }\n    const rel = ev => { const r = cv.getBoundingClientRect(); return [ev.clientX - r.left, ev.clientY - r.top]; };\n\n    d2.on(cv, 'pointermove', ev => {\n      const [x, y] = rel(ev);\n      if (S.drag) { S.drag.x = x; S.drag.y = y; S.alpha = Math.max(S.alpha, .35); return; }\n      const n = hit(x, y);\n      S.hover = n ? n.i : null;\n      cv.style.cursor = n ? 'pointer' : 'default';\n      if (n) {\n        tip.classList.add('on');\n        tip.innerHTML = `<b>${esc(n.nome || 'CB ' + n.cb)}</b><em>${esc(n.municipio)}${n.uf ? '/' + esc(n.uf) : ''} · setor ${esc(n.setor)} · ${d2.fmt.number(n.pontos)} pontos</em>`;\n        const tw = tip.offsetWidth || 200;\n        tip.style.left = Math.max(6, Math.min(S.w - tw - 6, x + 14)) + 'px';\n        tip.style.top = Math.max(6, y - 42) + 'px';\n      } else tip.classList.remove('on');\n    });\n    d2.on(cv, 'pointerdown', ev => {\n      const [x, y] = rel(ev); const n = hit(x, y);\n      if (n) { S.drag = n; cv.setPointerCapture(ev.pointerId); }\n    });\n    d2.on(cv, 'pointerup', ev => {\n      const [x, y] = rel(ev); const n = hit(x, y);\n      S.drag = null;\n      if (n) { S.sel = (S.sel === n.i ? null : n.i); renderDetail(S.sel); }\n      else { S.sel = null; renderDetail(null); }\n    });\n    d2.on(cv, 'pointerleave', () => { S.hover = null; S.drag = null; tip.classList.remove('on'); });\n\n    d2.on('.chip', 'click', (e, el) => {\n      const k = el.dataset.k;\n      S.on[k] = !S.on[k];\n      el.classList.toggle('off', !S.on[k]);\n      if (S.nodes.length) { rebuild(); renderDetail(S.sel); }\n    });\n    d2.on('.crow', 'click', (e, el) => { S.sel = +el.dataset.i; renderDetail(S.sel); });\n    d2.on('#reload', 'click', (e, el) => {\n      el.disabled = true;\n      S.sel = null; S.hover = null;\n      load().catch(err => say(err && err.message ? err.message : String(err), true))\n        .then(() => { el.disabled = false; });\n    });\n\n    // ---------- painel ----------\n    function renderDetail(i) {\n      const el = $('#detail');\n      if (i == null || !S.nodes[i]) {\n        const on = DIMS.filter(d => S.on[d.k]);\n        el.innerHTML = `<div class=\"hintline\">Cada laço é uma afinidade calculada, não um cadastro: duas CBs se ligam quando compartilham origem, território, líder, trajetória ou um padrão de trabalho parecido.<br><br>O peso de cada dimensão é o inverso da sua frequência — por isso <b>${esc((on.slice().sort((a, b) => (S.W[b.k] || 0) - (S.W[a.k] || 0))[0] || {}).label || '—')}</b> puxa mais que as demais nesta amostra.<br><br>Clique num nó para ver quem ele alcança. Arraste para reorganizar.</div>`;\n        return;\n      }\n      const n = S.nodes[i];\n      const mine = (S.adj.get(i) || []).map(x => S.edges[x]).sort((a, b) => b.sc - a.sc).slice(0, 8);\n      const fact = (v, l) => `<div class=\"fact\"><b>${v}</b><span>${l}</span></div>`;        el.innerHTML = `<div>\n        <h3>${esc(n.nome || 'CB ' + n.cb)}</h3>\n        <div class=\"sub\">${esc(n.municipio)}${n.uf ? ' · ' + esc(n.uf) : ''} · setor ${esc(n.setor)}${n.lider ? ' · líder ' + esc(n.lider) : ''}</div>\n        <div class=\"facts\">\n          ${fact(d2.fmt.number(n.pontos), 'pontos')}\n          ${fact(d2.fmt.number(n.pedidos), 'pedidos')}\n          ${fact(d2.fmt.currency(n.receita, 'BRL'), 'receita')}\n          ${fact(esc(n.nivel || '—'), 'nível')}\n        </div>\n        <div class=\"conn\"><h4>${mine.length} afinidades mais fortes</h4>\n          ${mine.map(e => {\n            const o = S.nodes[e.a === i ? e.b : e.a];\n            return `<div class=\"crow\" data-i=\"${o.i}\" title=\"${esc(e.dims.map(k => (DIM_BY[k] || {}).hint).join(' · '))}\">\n              <span class=\"cdot\" style=\"background:${o.tint}\">${esc(o.ini)}</span>\n              <span class=\"cname\">${esc(o.nome || 'CB ' + o.cb)}</span>\n              <span class=\"tags\">${e.dims.map(k => `<i class=\"tag\" style=\"background:${(DIM_BY[k] || {}).color}\"></i>`).join('')}</span>\n            </div>`;\n          }).join('')}\n        </div>\n      </div>`;\n    }\n\n    load().catch(err => {\n      say(err && err.message ? err.message : String(err), true);\n      $('#detail').innerHTML = `<div class=\"hintline\">A consulta roda no listener do Databricks, que exige sessão do simulador. Se a mensagem acima fala em 401/permissão, é preciso estar logado no app.</div>`;\n    });\n  }\n};"}},"limite":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"limite"}}}},"setor":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"setor"}}}},"uf":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"uf"}}}},"amostra":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"amostra"}}}},"lacos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"lacos"}}}},"consultaRush":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"consultaRush"}}}}}}}},"__d2PrevCode":{"code":"// ---------- Senso de rede — afinidades entre consultoras de beleza ----------\n// Cada nó é uma CB; cada laço é uma AFINIDADE DERIVADA do snapshot mais recente, nunca um\n// cadastro de relacionamento — não existe tabela de \"quem conhece quem\". Por isso o peso de\n// cada dimensão é o INVERSO DA SUA FREQUÊNCIA na amostra: dividir a mesma líder diz muito mais\n// sobre duas CBs do que ambas estarem ativas no ciclo, e é o peso que decide a cor, a espessura\n// e quais laços sobrevivem ao corte por nó. Sem isso o grafo vira uma malha uniforme onde tudo\n// se liga a tudo e nada significa nada.\nconst T = 'product.perfil_cb.crm_perfil_cb_diaria_historico';\nconst RUSH_FALLBACK = 'AwW_11Nkg-dKksP7ic8V8';\nconst SNAP = `(SELECT MAX(DT_HISTORICO) FROM ${T} WHERE CD_PAIS=1)`;\n// Paleta = cores de sistema do macOS, as mesmas do simulador de timeline, pra que os dois módulos\n// leiam como um produto só e não como duas ferramentas diferentes coladas.\nconst BG = '#f5f5f7';\n\nconst DIMS = [\n  { k:'origem',     label:'Origem',     color:'#ff9500', hint:'mesmo município' },\n  { k:'territorio', label:'Território', color:'#34c759', hint:'mesmo setor' },\n  { k:'rede',       label:'Rede',       color:'#007aff', hint:'mesma líder' },\n  { k:'trajetoria', label:'Trajetória', color:'#af52de', hint:'mesmo nível do plano' },\n  { k:'ritmo',      label:'Ritmo',      color:'#ff2d55', hint:'pedidos por ciclo parecidos' },\n  { k:'alcance',    label:'Alcance',    color:'#ffcc00', hint:'pontos parecidos' },\n  { k:'porte',      label:'Porte',      color:'#30b0c7', hint:'receita parecida' },\n  { k:'marca',      label:'Marca',      color:'#a2845e', hint:'mix Natura/Avon parecido' },\n  { k:'constancia', label:'Constância', color:'#8e8e93', hint:'ativas no ciclo' },\n];\nconst DIM_BY = {}; DIMS.forEach(d => DIM_BY[d.k] = d);\nconst TINTS = ['#e6f0ff','#e6f7ea','#fff2e0','#f6ecfd','#ffe9ee','#e6f5f9','#f2f2f7'];\n\nconst esc = s => (s == null ? '' : String(s)).replace(/[&<>\"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\nconst digits = s => String(s == null ? '' : s).replace(/[^0-9]/g, '');\nconst letters = s => String(s == null ? '' : s).replace(/[^A-Za-zÀ-ÿ]/g, '');\nconst hash = s => { let h = 0; const t = String(s || ''); for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return Math.abs(h); };\nconst titleCase = s => String(s || '').toLowerCase().replace(/(^|[\\s'-])([a-zà-ÿ])/g, (m, a, b) => a + b.toUpperCase());\n\nfunction rowsOf(resp) {\n  const c = (resp.columns || []).map(x => x.name);\n  return (resp.rows || []).map(a => Object.fromEntries(c.map((k, i) => [k, a[i]])));\n}\nasync function q(sql) {\n  const ref = d2.params.consultaRush || RUSH_FALLBACK;\n  let dbx;\n  try {\n    const res = await d2.rush(ref).invoke({ sql });\n    dbx = res.data.result.data;              // envelope do listener: res.data, depois o payload do Databricks\n  } catch (e) {\n    throw new Error('consulta indisponível (rush ' + ref + '): ' + (e && e.message ? e.message : e));\n  }\n  if (dbx.state && dbx.state !== 'SUCCEEDED') {\n    throw new Error('Databricks ' + dbx.state + ': ' + ((dbx.error && dbx.error.message) || 'erro desconhecido'));\n  }\n  return dbx;\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%;--bg:#f5f5f7;--panel:#ffffff;--panel2:#f2f2f7;--line:#e5e5ea;--ink:#1c1c1e;--mut:#6e6e73;--acc:#007aff;--closing:#ff3b30}\n  *{box-sizing:border-box}\n  .root{display:flex;flex-direction:column;height:100%;background:var(--bg);color:var(--ink);\n    font:13px/1.45 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Segoe UI\",Roboto,sans-serif;-webkit-font-smoothing:antialiased}\n  header{padding:10px 16px;background:rgba(246,246,248,.85);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);\n    border-bottom:1px solid var(--line);display:flex;align-items:center;gap:10px;flex-wrap:wrap;flex:0 0 auto}\n  header h1{font-size:14.5px;margin:0;font-weight:600;letter-spacing:-.1px;color:var(--ink)}\n  .spacer{flex:1}\n  .pill{font-size:10.5px;padding:2.5px 9px;border-radius:99px;border:1px solid var(--line);color:var(--mut);background:var(--panel2);white-space:nowrap}\n  .pill b{color:var(--ink);font-weight:600}\n  .status{font-size:11px;color:var(--mut)}\n  .status.err{color:var(--closing)}\n  .btn{font:inherit;font-size:12px;padding:4.5px 11px;border-radius:7px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}\n  .btn:hover{border-color:#d5d5da;background:#fbfbfd}\n  .btn:active{background:#f0f0f4}\n  .btn:disabled{opacity:.5;cursor:progress}\n  .howto{padding:8px 16px;background:var(--panel);border-bottom:1px solid var(--line);display:flex;gap:16px;flex-wrap:wrap;\n    font-size:11.5px;color:var(--mut);flex:0 0 auto}\n  .howto b{color:var(--ink)} .howto span{max-width:360px}\n  .body{flex:1;display:flex;min-height:0}\n  .stage{flex:1;position:relative;min-width:0}\n  canvas{width:100%;height:100%;display:block}\n  .side{width:334px;flex:0 0 334px;border-left:1px solid var(--line);padding:12px;display:flex;flex-direction:column;gap:12px;overflow:auto}\n  .card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:12px 13px}\n  .card h2{margin:0 0 9px;font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);font-weight:600}\n  .card h3{margin:0 0 2px;font-size:15px;font-weight:600;letter-spacing:-.2px}\n  .card .sub{font-size:11.5px;color:var(--mut);margin-bottom:10px}\n  .facts{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px}\n  .fact{background:var(--panel2);border-radius:8px;padding:6px 8px}\n  .fact b{display:block;font-size:13px;font-weight:600}\n  .fact span{font-size:9.5px;color:var(--mut);letter-spacing:.05em;text-transform:uppercase}\n  .conn{border-top:1px solid var(--line);padding-top:8px}\n  .conn h4{margin:0 0 4px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);font-weight:600}\n  .crow{display:flex;align-items:center;gap:8px;padding:5px 6px;margin:0 -6px;cursor:pointer;border-radius:7px}\n  .crow:hover{background:var(--panel2)}\n  .cdot{width:22px;height:22px;border-radius:50%;flex:0 0 22px;display:flex;align-items:center;justify-content:center;\n    font-size:9px;font-weight:600;color:#3a3a3c;border:1px solid rgba(60,60,67,.12)}\n  .cname{flex:1;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .tags{display:flex;gap:3px;flex-wrap:wrap;justify-content:flex-end;max-width:110px}\n  .tag{width:7px;height:7px;border-radius:50%}\n  .hintline{font-size:11.5px;color:var(--mut);line-height:1.5}\n  .hintline b{color:var(--ink)}\n  .legend{display:flex;flex-wrap:wrap;gap:6px}\n  .chip{display:inline-flex;align-items:center;gap:6px;background:var(--panel2);border:1px solid var(--line);border-radius:99px;\n    padding:4px 10px 4px 7px;font:inherit;font-size:11.5px;color:var(--ink);cursor:pointer;user-select:none;\n    transition:opacity .12s,border-color .12s,color .12s}\n  .chip i{width:8px;height:8px;border-radius:50%;flex:0 0 8px}\n  .chip b{font-weight:500;color:var(--mut);font-size:10px}\n  .chip:hover{border-color:var(--acc);color:var(--acc)}\n  .chip:focus-visible{outline:2px solid rgba(0,122,255,.35);outline-offset:1px}\n  .chip.off{opacity:.4}\n  .tip{position:absolute;pointer-events:none;background:rgba(28,28,30,.94);color:#fff;border-radius:8px;padding:6px 9px;\n    font-size:11.5px;line-height:1.35;opacity:0;transition:opacity .1s;max-width:230px;z-index:5;box-shadow:0 8px 24px rgba(0,0,0,.18)}\n  .tip.on{opacity:1}\n  .tip em{font-style:normal;color:#aeaeb2;display:block;font-size:10.5px;margin-top:2px}\n  `,\n\n  // Shell estático de propósito: há um <canvas> aqui, e qualquer leitura reativa dentro de\n  // render() re-executa o render inteiro e devolve o canvas ao default 300x150 (checklist §15).\n  // Tudo que muda — contagem, cards, chips — é escrito por DOM dentro de mounted().\n  render() {\n    const chips = DIMS.map(d =>\n      `<button class=\"chip\" data-k=\"${d.k}\" title=\"${esc(d.hint)}\"><i style=\"background:${d.color}\"></i>${d.label} <b data-n=\"${d.k}\"></b></button>`\n    ).join('');\n    return `<div class=\"root\">\n      <header>\n        <h1>Senso de rede — consultoras</h1>\n        <span class=\"pill\"><b id=\"count\">—</b> pessoas</span>\n        <span class=\"status\" id=\"status\">consultando…</span>\n        <div class=\"spacer\"></div>\n        <button class=\"btn\" id=\"reload\">Recarregar</button>\n      </header>\n      <div class=\"howto\">\n        <span><b>1.</b> Cada nó é uma CB — o tamanho segue os pontos do ciclo, o anel marca quem está ativa.</span>\n        <span><b>2.</b> Cada laço é uma afinidade calculada; as contas no meio contam quantas dimensões as duas dividem.</span>\n        <span><b>3.</b> Clique num nó pra ver quem ele alcança — arraste pra reorganizar.</span>\n        <span><b>4.</b> Ligue e desligue dimensões nas marcações à direita.</span>\n      </div>\n      <div class=\"body\">\n        <div class=\"stage\" data-d2-static>\n          <canvas id=\"net\"></canvas>\n          <div class=\"tip\" id=\"tip\"></div>\n        </div>\n        <aside class=\"side\">\n          <section class=\"card\">\n            <h2>Afinidades</h2>\n            <div class=\"legend\" id=\"legend\">${chips}</div>\n          </section>\n          <section class=\"card\" id=\"detail\"></section>\n        </aside>\n      </div>\n    </div>`;\n  },\n\n  mounted(d2, root) {\n    const $ = s => root.querySelector(s);\n    const cv = $('#net'), ctx = cv.getContext('2d'), tip = $('#tip');\n    const S = {\n      nodes: [], edges: [], adj: new Map(), W: {}, on: {},\n      hover: null, sel: null, drag: null, alpha: 0, w: 0, h: 0\n    };\n    DIMS.forEach(d => S.on[d.k] = true);\n\n    const say = (msg, err) => { const el = $('#status'); el.textContent = msg || ''; el.classList.toggle('err', !!err); };\n\n    // ---------- canvas ----------\n    function resize() {\n      const r = cv.getBoundingClientRect();\n      const dpr = Math.min(window.devicePixelRatio || 1, 2);\n      S.w = r.width; S.h = r.height;\n      cv.width = Math.max(1, Math.round(r.width * dpr));\n      cv.height = Math.max(1, Math.round(r.height * dpr));\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);   // o resize zera a matriz — reaplicar sempre\n    }\n    const ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(() => ro.disconnect());\n    resize();\n\n    // ---------- amostra ----------\n    async function nameColumn() {\n      // O nome da CB não está no contrato conhecido da tabela, então em vez de chutar um\n      // DS_NOME que pode não existir (e derrubar a query inteira), descobrimos a coluna.\n      try {\n        const d = await q(`DESCRIBE ${T}`);\n        const cols = rowsOf(d).map(r => String(r.col_name || r.name || '')).filter(Boolean);\n        const cand = cols.filter(c => /NOME|NAME/i.test(c) &&\n          !/LEADER|LIDER|MUNIC|ESTAD|SETOR|GRUPO|CANAL|MARCA|PAIS|REGIA|GV|NVL/i.test(c));\n        cand.sort((a, b) => (/CONSULT|CB|PESSOA/i.test(b) ? 1 : 0) - (/CONSULT|CB|PESSOA/i.test(a) ? 1 : 0));\n        return cand[0] || null;\n      } catch (e) { return null; }\n    }\n    function sqlFor(nomeCol, where, order, lim) {\n      const nome = nomeCol ? `MAX(${nomeCol}) nome,` : '';\n      return `SELECT CD_CONSULTORA cb, ${nome}\n        MAX(DS_MUNICIPIO) municipio, MAX(DS_ESTADO) uf, MAX(CD_SETOR) setor,\n        MAX(CD_GRUPO) grupo, MAX(DS_LEADER_NAME) lider, MAX(DS_NVL_PLAN_CRESC) nivel,\n        SUM(QT_PEDIDOS_CICLO) pedidos, SUM(QT_PONTOS_CICLO) pontos,\n        SUM(VL_PEDIDOS_NATURA_CICLO) rec_nat, SUM(VL_PEDIDOS_AVON_CICLO) rec_avon,\n        MAX(CASE WHEN ST_FEZ_PED_CICLO_ATUAL THEN 1 ELSE 0 END) ativa\n       FROM ${T} WHERE ${where} GROUP BY CD_CONSULTORA ${order} LIMIT ${lim}`;\n    }\n\n    // Faixas relativas à AMOSTRA, não absolutas: \"ritmo parecido\" só quer dizer alguma coisa\n    // comparado com as outras CBs desta consulta.\n    function bucketer(vals) {\n      const v = vals.filter(x => isFinite(x)).sort((a, b) => a - b);\n      if (!v.length) return () => 0;\n      const at = p => v[Math.min(v.length - 1, Math.floor(p * v.length))];\n      const q1 = at(.25), q2 = at(.5), q3 = at(.75);\n      return x => (x <= q1 ? 0 : x <= q2 ? 1 : x <= q3 ? 2 : 3);\n    }\n\n    // Amostra sintética — existe para revisar layout/física sem sessão do simulador (o listener do\n    // Databricks é gated). Nunca é o default e se anuncia na tela: um grafo bonito com gente que não\n    // existe é pior do que um erro honesto.\n    function demoRows(n) {\n      const NOMES = ['Ana Ribeiro','Marta Souza','Cláudia Lima','Rita Alves','Vera Nunes','Sônia Braga','Ivone Castro','Lúcia Prado','Neide Rocha','Tânia Melo','Dora Pires','Elis Faria','Bruna Antunes','Carla Moraes','Selma Dutra','Joana Bastos','Regina Paiva','Marli Cardoso','Fátima Goulart','Simone Teles'];\n      const MUN = ['Brasília','Formosa','Buritis','Unaí','Paracatu','Arinos','Riachinho','Bonfinópolis','Chapada Gaúcha','Urucuia'];\n      const NIV = ['Bronze','Cristal','Prata','Ouro','Diamante'];\n      const out = [];\n      for (let i = 0; i < n; i++) {\n        const h = hash('cb' + i);\n        const mun = MUN[h % MUN.length];\n        const setor = 3200 + ((h >> 3) % 6) * 7;\n        const grupo = 100 + ((h >> 5) % 14);\n        const escala = .25 + ((h >> 7) % 100) / 100 * 1.8;\n        out.push({\n          cb: 900000 + i,\n          nome: NOMES[h % NOMES.length] + ' ' + String.fromCharCode(65 + (h >> 11) % 26) + '.',\n          municipio: mun, uf: mun === 'Chapada Gaúcha' || mun === 'Urucuia' ? 'MG' : 'GO',\n          setor: setor, grupo: grupo, lider: 'Líder ' + grupo,\n          nivel: NIV[(h >> 13) % NIV.length],\n          pedidos: Math.round(4 + escala * 22), pontos: Math.round(120 + escala * 900),\n          rec_nat: Math.round(escala * 3200), rec_avon: Math.round(escala * 3200 * (((h >> 17) % 100) / 100)),\n          ativa: (h >> 19) % 10 > 2 ? 1 : 0\n        });\n      }\n      return out;\n    }\n\n    async function load() {\n      say('consultando o snapshot mais recente…');\n      const lim = Math.max(12, Math.min(400, +d2.param('limite', 82) || 82));\n      const setor = digits(d2.param('setor', ''));\n      const uf = letters(d2.param('uf', '')).toUpperCase();\n      const aleat = String(d2.param('amostra', 'top') || 'top').toLowerCase().indexOf('aleat') === 0;\n      let where = `CD_PAIS=1 AND CD_GRUPO>0 AND DT_HISTORICO=${SNAP}`;\n      if (setor) where += ` AND CD_SETOR=${setor}`;\n      if (uf) where += ` AND UPPER(DS_ESTADO)='${uf}'`;\n      const order = aleat ? 'ORDER BY RAND()' : 'ORDER BY pontos DESC';\n\n      // `amostra: \"demo\"` em vez de um param novo — a declaração já existe e o modo fica visível\n      // no mesmo lugar onde se escolhe top/aleatória, não escondido num booleano à parte.\n      const demo = String(d2.param('amostra', 'top') || '').toLowerCase().indexOf('demo') === 0;\n      let raw;\n      if (demo) {\n        raw = demoRows(lim);\n      } else {\n        const nc = await nameColumn();\n        const attempts = nc ? [nc, null] : [null];\n        let data = null, lastErr = null;\n        for (const col of attempts) {\n          try { data = await q(sqlFor(col, where, order, lim)); break; }\n          catch (e) { lastErr = e; }\n        }\n        if (!data) throw lastErr || new Error('sem resposta da consulta');\n        raw = rowsOf(data);\n      }\n      if (!raw.length) throw new Error('nenhuma CB encontrada com esses filtros' + (setor ? ' (setor ' + setor + ')' : '') + (uf ? ' (UF ' + uf + ')' : ''));\n\n      const bPed = bucketer(raw.map(r => +r.pedidos || 0));\n      const bPon = bucketer(raw.map(r => +r.pontos || 0));\n      const bRec = bucketer(raw.map(r => (+r.rec_nat || 0) + (+r.rec_avon || 0)));\n\n      S.nodes = raw.map((r, i) => {\n        const nat = +r.rec_nat || 0, avon = +r.rec_avon || 0, rec = nat + avon;\n        const nome = titleCase(r.nome || '');\n        const cb = String(r.cb == null ? '' : r.cb);\n        const ini = nome ? d2.fmt.initials(nome, 2) : cb.slice(-2);\n        return {\n          i, cb, nome, ini,\n          municipio: titleCase(r.municipio || ''), uf: String(r.uf || ''),\n          setor: String(r.setor == null ? '' : r.setor), grupo: String(r.grupo == null ? '' : r.grupo),\n          lider: titleCase(r.lider || ''), nivel: String(r.nivel || ''),\n          pedidos: +r.pedidos || 0, pontos: +r.pontos || 0, receita: rec,\n          ativa: +r.ativa === 1 ? 1 : 0,\n          bRitmo: bPed(+r.pedidos || 0), bAlcance: bPon(+r.pontos || 0), bPorte: bRec(rec),\n          bMarca: rec > 0 ? Math.min(3, Math.floor((nat / rec) * 4)) : null,\n          tint: TINTS[hash(String(r.setor) + '|' + String(r.grupo)) % TINTS.length],\n          r: 0, x: 0, y: 0, vx: 0, vy: 0\n        };\n      });\n\n      const maxP = Math.max(...S.nodes.map(n => n.pontos), 1);\n      S.nodes.forEach(n => {\n        n.r = 7 + 17 * Math.sqrt(Math.max(0, n.pontos) / maxP);\n        const a = (n.i / S.nodes.length) * Math.PI * 2, rad = 140 + (hash(n.cb) % 220);\n        n.x = S.w / 2 + Math.cos(a) * rad; n.y = S.h / 2 + Math.sin(a) * rad;\n      });\n\n      DIMS.forEach(d => S.W[d.k] = weightOf(d.k));\n      rebuild();\n      $('#count').textContent = d2.fmt.number(S.nodes.length);\n      say((demo ? '⚠ AMOSTRA SINTÉTICA (dadosDemo) — ' : '') + `${S.nodes.length} CBs · ${S.edges.length} laços · ${S.nodes.filter(n => n.ativa).length} ativas no ciclo`, demo);\n      renderDetail(null);\n    }\n\n    // ---------- afinidades ----------\n    function keyOf(k, n) {\n      if (k === 'origem') return n.municipio ? n.uf + '/' + n.municipio : null;\n      if (k === 'territorio') return n.setor || null;\n      if (k === 'rede') return n.grupo || null;\n      if (k === 'trajetoria') return n.nivel || null;\n      if (k === 'ritmo') return 'r' + n.bRitmo;\n      if (k === 'alcance') return 'a' + n.bAlcance;\n      if (k === 'porte') return 'p' + n.bPorte;\n      if (k === 'marca') return n.bMarca == null ? null : 'm' + n.bMarca;\n      if (k === 'constancia') return n.ativa ? 'ativa' : null;\n      return null;\n    }\n    // Peso = raridade. p = chance de dois nós sorteados caírem no mesmo grupo desta dimensão;\n    // log2(1/p) transforma isso em bits de informação, então \"mesma líder\" pesa muito mais que\n    // \"ambas ativas\" sem que ninguém precise arbitrar uma tabela de pesos à mão.\n    function weightOf(k) {\n      const g = {}, n = S.nodes.length;\n      S.nodes.forEach(x => { const key = keyOf(k, x); if (key != null) g[key] = (g[key] || 0) + 1; });\n      let p = 0; Object.values(g).forEach(c => { p += (c / n) * (c / n); });\n      return p > 0 ? Math.max(.35, Math.min(6, Math.log2(1 / p))) : 0;\n    }\n    function sharedDims(a, b) {\n      const out = [];\n      for (const d of DIMS) {\n        if (!S.on[d.k]) continue;\n        const ka = keyOf(d.k, a); if (ka == null) continue;\n        if (ka === keyOf(d.k, b)) out.push(d.k);\n      }\n      return out;\n    }\n    function rebuild() {\n      const ns = S.nodes, K = Math.max(1, Math.min(8, +d2.param('lacos', 4) || 4));\n      const cand = ns.map(() => []);\n      for (let i = 0; i < ns.length; i++) {\n        for (let j = i + 1; j < ns.length; j++) {\n          const dims = sharedDims(ns[i], ns[j]);\n          if (!dims.length) continue;\n          let sc = 0; for (const k of dims) sc += S.W[k] || 0;\n          if (sc <= 0) continue;\n          cand[i].push({ o: j, sc, dims }); cand[j].push({ o: i, sc, dims });\n        }\n      }\n      // Corte por nó, não global: um corte global deixaria as CBs de município grande com\n      // dezenas de laços e as demais órfãs — cada uma guarda seus K vínculos mais fortes.\n      const seen = new Set(), out = [];\n      cand.forEach((list, i) => {\n        list.sort((a, b) => b.sc - a.sc).slice(0, K).forEach(e => {\n          const a = Math.min(i, e.o), b = Math.max(i, e.o), key = a + ':' + b;\n          if (seen.has(key)) return; seen.add(key);\n          const top = e.dims.slice().sort((x, y) => (S.W[y] || 0) - (S.W[x] || 0))[0];\n          out.push({ a, b, sc: e.sc, dims: e.dims, top });\n        });\n      });\n      S.edges = out;\n      S.adj = new Map();\n      out.forEach((e, idx) => {\n        if (!S.adj.has(e.a)) S.adj.set(e.a, []);\n        if (!S.adj.has(e.b)) S.adj.set(e.b, []);\n        S.adj.get(e.a).push(idx); S.adj.get(e.b).push(idx);\n      });\n      const maxSc = Math.max(...out.map(e => e.sc), 1);\n      out.forEach(e => e.n = e.sc / maxSc);\n      const counts = {}; DIMS.forEach(d => counts[d.k] = 0);\n      out.forEach(e => e.dims.forEach(k => counts[k]++));\n      DIMS.forEach(d => { const el = root.querySelector(`[data-n=\"${d.k}\"]`); if (el) el.textContent = counts[d.k] ? counts[d.k] : ''; });\n      S.alpha = 1;\n    }\n\n    // ---------- física ----------\n    function tick() {\n      const ns = S.nodes, cx = S.w / 2, cy = S.h / 2;\n      if (S.alpha < .02 || !ns.length) return;\n      const k = S.alpha;\n      for (let i = 0; i < ns.length; i++) {\n        const a = ns[i];\n        for (let j = i + 1; j < ns.length; j++) {\n          const b = ns[j];\n          let dx = b.x - a.x, dy = b.y - a.y, d = Math.hypot(dx, dy);\n          if (d < .01) {   // coincidentes: desempata com um deslocamento determinístico\n            dx = ((hash(a.cb + b.cb) % 21) - 10) / 10;\n            dy = ((hash(b.cb + a.cb) % 21) - 10) / 10;\n            d = Math.hypot(dx, dy) || 1;\n          }\n          const ux = dx / d, uy = dy / d;\n          const rep = Math.min(4, 9000 / (d * d)) * k;\n          a.vx -= ux * rep; a.vy -= uy * rep;\n          b.vx += ux * rep; b.vy += uy * rep;\n          // Colisão dura, resolvida na POSIÇÃO e não na velocidade: só repulsão deixa os círculos\n          // se sobreporem enquanto o sistema esfria, e as iniciais viram sopa.\n          const min = a.r + b.r + 6;\n          if (d < min) {\n            const push = (min - d) * .5;\n            a.x -= ux * push; a.y -= uy * push;\n            b.x += ux * push; b.y += uy * push;\n          }\n        }\n      }\n      for (const e of S.edges) {\n        const a = ns[e.a], b = ns[e.b];\n        const dx = b.x - a.x, dy = b.y - a.y, d = Math.max(.01, Math.hypot(dx, dy));\n        const target = 170 - 95 * e.n;          // laço forte = mais curto\n        const f = (d - target) * .022 * (.4 + .6 * e.n) * k;\n        const ux = dx / d, uy = dy / d;\n        a.vx += ux * f; a.vy += uy * f;\n        b.vx -= ux * f; b.vy -= uy * f;\n      }\n      for (const n of ns) {\n        if (S.drag === n) { n.vx = 0; n.vy = 0; continue; }\n        n.vx += (cx - n.x) * .002 * k; n.vy += (cy - n.y) * .002 * k;\n        n.vx *= .84; n.vy *= .84;\n        n.x += Math.max(-9, Math.min(9, n.vx));\n        n.y += Math.max(-9, Math.min(9, n.vy));\n      }\n      // Recentrar + encolher, em vez de prender nas bordas: o clamp anterior deformava o grafo\n      // contra as margens e foi o que empurrou o desenho inteiro pra um canto.\n      let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n      for (const n of ns) {\n        x0 = Math.min(x0, n.x - n.r); y0 = Math.min(y0, n.y - n.r);\n        x1 = Math.max(x1, n.x + n.r); y1 = Math.max(y1, n.y + n.r);\n      }\n      const ox = cx - (x0 + x1) / 2, oy = cy - (y0 + y1) / 2;\n      for (const n of ns) { n.x += ox * .12; n.y += oy * .12; }\n      const sc = Math.min(1, (S.w - 48) / Math.max(1, x1 - x0), (S.h - 48) / Math.max(1, y1 - y0));\n      if (sc < 1) for (const n of ns) {\n        n.x = cx + (n.x - cx) * (1 - (1 - sc) * .1);\n        n.y = cy + (n.y - cy) * (1 - (1 - sc) * .1);\n      }\n      S.alpha *= .994;\n    }\n\n    // ---------- desenho ----------\n    function draw() {\n      ctx.clearRect(0, 0, S.w, S.h);\n      ctx.fillStyle = BG; ctx.fillRect(0, 0, S.w, S.h);\n      if (!S.nodes.length) return;\n      const focus = S.sel != null ? S.sel : S.hover;\n      const near = new Set();\n      if (focus != null) { near.add(focus); (S.adj.get(focus) || []).forEach(i => { near.add(S.edges[i].a); near.add(S.edges[i].b); }); }\n\n      S.edges.forEach((e, idx) => {\n        const a = S.nodes[e.a], b = S.nodes[e.b];\n        const lit = focus == null || e.a === focus || e.b === focus;\n        const col = (DIM_BY[e.top] || DIMS[0]).color;\n        ctx.globalAlpha = focus == null ? .16 + .34 * e.n : (lit ? .5 + .4 * e.n : .05);\n        ctx.strokeStyle = col;\n        ctx.lineWidth = .5 + 1.7 * e.n;\n        ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke();\n        // uma conta por dimensão compartilhada, no meio do laço — é a leitura rápida de\n        // \"quantas coisas estas duas têm em comum\" sem precisar do hover\n        const dims = e.dims;\n        for (let i = 0; i < dims.length; i++) {\n          const t = (i + 1) / (dims.length + 1);\n          ctx.globalAlpha = focus == null ? .55 : (lit ? .95 : .08);\n          ctx.fillStyle = (DIM_BY[dims[i]] || DIMS[0]).color;\n          ctx.beginPath(); ctx.arc(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, 1.9 + 1.3 * e.n, 0, 6.284); ctx.fill();\n        }\n      });\n\n      S.nodes.forEach(n => {\n        const dim = focus != null && !near.has(n.i);\n        ctx.globalAlpha = dim ? .18 : 1;\n        ctx.beginPath(); ctx.arc(n.x, n.y, n.r, 0, 6.284);\n        ctx.fillStyle = n.tint; ctx.fill();\n        ctx.lineWidth = n.i === focus ? 2.5 : 1;\n        ctx.strokeStyle = n.i === focus ? '#007aff' : (n.ativa ? 'rgba(60,60,67,.38)' : 'rgba(60,60,67,.14)');\n        ctx.stroke();\n        if (n.r >= 12) {   // nós pequenos ficam como pontos, sem inicial ilegível dentro\n          ctx.globalAlpha = dim ? .2 : .85;\n          ctx.fillStyle = '#3a3a3c';\n          ctx.font = `600 ${Math.round(n.r * .78)}px -apple-system,\"Segoe UI\",Roboto,sans-serif`;\n          ctx.textAlign = 'center'; ctx.textBaseline = 'middle';\n          ctx.fillText(n.ini, n.x, n.y + .5);\n        }\n      });\n      ctx.globalAlpha = 1;\n    }\n\n    d2.raf(() => { tick(); draw(); });\n\n    // ---------- interação ----------\n    function hit(px, py) {\n      let best = null, bd = Infinity;\n      for (const n of S.nodes) {\n        const d = Math.hypot(n.x - px, n.y - py);\n        if (d < n.r + 5 && d < bd) { bd = d; best = n; }\n      }\n      return best;\n    }\n    const rel = ev => { const r = cv.getBoundingClientRect(); return [ev.clientX - r.left, ev.clientY - r.top]; };\n\n    d2.on(cv, 'pointermove', ev => {\n      const [x, y] = rel(ev);\n      if (S.drag) { S.drag.x = x; S.drag.y = y; S.alpha = Math.max(S.alpha, .35); return; }\n      const n = hit(x, y);\n      S.hover = n ? n.i : null;\n      cv.style.cursor = n ? 'pointer' : 'default';\n      if (n) {\n        tip.classList.add('on');\n        tip.innerHTML = `<b>${esc(n.nome || 'CB ' + n.cb)}</b><em>${esc(n.municipio)}${n.uf ? '/' + esc(n.uf) : ''} · setor ${esc(n.setor)} · ${d2.fmt.number(n.pontos)} pontos</em>`;\n        const tw = tip.offsetWidth || 200;\n        tip.style.left = Math.max(6, Math.min(S.w - tw - 6, x + 14)) + 'px';\n        tip.style.top = Math.max(6, y - 42) + 'px';\n      } else tip.classList.remove('on');\n    });\n    d2.on(cv, 'pointerdown', ev => {\n      const [x, y] = rel(ev); const n = hit(x, y);\n      if (n) { S.drag = n; cv.setPointerCapture(ev.pointerId); }\n    });\n    d2.on(cv, 'pointerup', ev => {\n      const [x, y] = rel(ev); const n = hit(x, y);\n      S.drag = null;\n      if (n) { S.sel = (S.sel === n.i ? null : n.i); renderDetail(S.sel); }\n      else { S.sel = null; renderDetail(null); }\n    });\n    d2.on(cv, 'pointerleave', () => { S.hover = null; S.drag = null; tip.classList.remove('on'); });\n\n    d2.on('.chip', 'click', (e, el) => {\n      const k = el.dataset.k;\n      S.on[k] = !S.on[k];\n      el.classList.toggle('off', !S.on[k]);\n      if (S.nodes.length) { rebuild(); renderDetail(S.sel); }\n    });\n    d2.on('.crow', 'click', (e, el) => { S.sel = +el.dataset.i; renderDetail(S.sel); });\n    d2.on('#reload', 'click', (e, el) => {\n      el.disabled = true;\n      S.sel = null; S.hover = null;\n      load().catch(err => say(err && err.message ? err.message : String(err), true))\n        .then(() => { el.disabled = false; });\n    });\n\n    // ---------- painel ----------\n    function renderDetail(i) {\n      const el = $('#detail');\n      if (i == null || !S.nodes[i]) {\n        const on = DIMS.filter(d => S.on[d.k]);\n        el.innerHTML = `<div class=\"hintline\">Cada laço é uma afinidade calculada, não um cadastro: duas CBs se ligam quando compartilham origem, território, líder, trajetória ou um padrão de trabalho parecido.<br><br>O peso de cada dimensão é o inverso da sua frequência — por isso <b>${esc((on.slice().sort((a, b) => (S.W[b.k] || 0) - (S.W[a.k] || 0))[0] || {}).label || '—')}</b> puxa mais que as demais nesta amostra.<br><br>Clique num nó para ver quem ele alcança. Arraste para reorganizar.</div>`;\n        return;\n      }\n      const n = S.nodes[i];\n      const mine = (S.adj.get(i) || []).map(x => S.edges[x]).sort((a, b) => b.sc - a.sc).slice(0, 8);\n      const fact = (v, l) => `<div class=\"fact\"><b>${v}</b><span>${l}</span></div>`;        el.innerHTML = `<div>\n        <h3>${esc(n.nome || 'CB ' + n.cb)}</h3>\n        <div class=\"sub\">${esc(n.municipio)}${n.uf ? ' · ' + esc(n.uf) : ''} · setor ${esc(n.setor)}${n.lider ? ' · líder ' + esc(n.lider) : ''}</div>\n        <div class=\"facts\">\n          ${fact(d2.fmt.number(n.pontos), 'pontos')}\n          ${fact(d2.fmt.number(n.pedidos), 'pedidos')}\n          ${fact(d2.fmt.currency(n.receita, 'BRL'), 'receita')}\n          ${fact(esc(n.nivel || '—'), 'nível')}\n        </div>\n        <div class=\"conn\"><h4>${mine.length} afinidades mais fortes</h4>\n          ${mine.map(e => {\n            const o = S.nodes[e.a === i ? e.b : e.a];\n            return `<div class=\"crow\" data-i=\"${o.i}\" title=\"${esc(e.dims.map(k => (DIM_BY[k] || {}).hint).join(' · '))}\">\n              <span class=\"cdot\" style=\"background:${o.tint}\">${esc(o.ini)}</span>\n              <span class=\"cname\">${esc(o.nome || 'CB ' + o.cb)}</span>\n              <span class=\"tags\">${e.dims.map(k => `<i class=\"tag\" style=\"background:${(DIM_BY[k] || {}).color}\"></i>`).join('')}</span>\n            </div>`;\n          }).join('')}\n        </div>\n      </div>`;\n    }\n\n    load().catch(err => {\n      say(err && err.message ? err.message : String(err), true);\n      $('#detail').innerHTML = `<div class=\"hintline\">A consulta roda no listener do Databricks, que exige sessão do simulador. Se a mensagem acima fala em 401/permissão, é preciso estar logado no app.</div>`;\n    });\n  }\n};","at":"2026-08-07T05:00:41.560Z"}}},{"_id":"5ed9bed7b9e0891ce913e884","component":"data2-core-types@Component","params":{"package":"data2-core","component":"DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}},{"_id":"5f472ae463c680de4ff296e9","component":"data2-core-types@Component","params":{"package":"data2-core","component":"Data2TableDataSource","extends":"data2-core@DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}}]}},{"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}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_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":"6a7568efdc05e47d0b34ca18","component":"data2-core-primitives@Router","system":"6a57cfe9673eb7e3c5e57784","displayName":"Network-Router","params":{"routerType":{"component":"data2-core-primitives@String","params":{"content":"browser"}},"routes":[{"component":"data2-core-primitives@Route","params":{"path":"/","name":"Senso de rede — consultoras","content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"cLjnfyJtVTGInijJTZznT"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"timelineSimAuthFinal"}}}}]}}],"login":{"component":"data2-core-types@ArtboardReference","params":{"reference":"OjHwaLdtsQ130Tg6z4N_t"}}},"reference":"networkRouter"},{"_id":"6a74aea863667431f75e46f0","component":"data2-core-primitives@Artboard","system":"6a57cfe9673eb7e3c5e57784","displayName":"login","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"login"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1366,"height":768}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"OjHwaLdtsQ130Tg6z4N_t"},{"_id":"6a75621ae45e25fa9e450a5e","component":"data2-core-primitives@Artboard","system":"6a57cfe9673eb7e3c5e57784","displayName":"Senso de rede — CBs","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Senso de rede — CBs"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"cLjnfyJtVTGInijJTZznT"},{"_id":"edee90b922e5038ec1d1c76c","parent":"6a74aea863667431f75e46f0","component":"natura-geomarketing@timeline-sim-login","system":"6a57cfe9673eb7e3c5e57784","displayName":"Login form instance","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":1366,"height":768,"exactWidth":false,"exactHeight":false}}}},"reference":"Y87XaCN1xFnP4It1hGn6u"},{"_id":"58676499c376b2b87e908132","parent":"6a75621ae45e25fa9e450a5e","component":"natura-geomarketing@cb-senso-de-rede","system":"6a57cfe9673eb7e3c5e57784","displayName":"Senso de rede — instância","params":{"limite":82,"lacos":4,"amostra":"top","super":{"position":{"component":"data2-core-types@Position","params":{"x":-120,"y":-278,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true,"exactX":-120,"exactY":-278}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}}}},"reference":"dCsSLwSQPVM6ZNg0SICwe"}]}},{"_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":"6a7568f7dc05e47d0b34ca1f","displayName":"Network-App","reference":"MStFJxCPTrbzFORpDU4Mx","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"network"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Router","reference":"networkRouter"}}},"system":"6a57cfe9673eb7e3c5e57784","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a74ae2663667431f75e45b2","component":"data2-core@AuthenticationStrategy","system":"6a57cfe9673eb7e3c5e57784","params":{"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"AEKK_Z4OsZ2njfQxg7tS6"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"email"}},{"component":"data2-core-types@FieldReference","params":{"key":"password"}}],"email":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"password":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}}},"reference":"timelineSimAuthFinal"},{"_id":"6a74bb3c32c4b185b93738ce","component":"data2-core@DataSet","system":"6a57cfe9673eb7e3c5e57784","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"timeline-sim-users"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"pagination":{"size":20}},"reference":"AEKK_Z4OsZ2njfQxg7tS6"},{"_id":"6a74ae1b8bf67595bba6dcfb","component":"data2-core@Data2TableDataSource","system":"6a57cfe9673eb7e3c5e57784","params":{"table":"timeline-sim-users","schema":[{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"email","title":"email","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"password","title":"senha","component":"data2-core-types@Password"}},{"component":"data2-core-types@Field","params":{"key":"role","title":"Perfil","component":"data2-core-primitives@String"}}]},"reference":"timeline-sim-users"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}