{"context":{"domain":"gui"},"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":"6329b5535b2103060e128c0a","name":"gui","domain":"gui","visibility":"private","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T20:00:20.536Z","createdAt":"2022-09-20T12:42:59.116Z"}]}},{"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":"6a4d4c69c2b2e1501e2177cb","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"damas-board","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{height:100%;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;\n      box-sizing:border-box;padding:16px;background:#1a1410;font-family:system-ui,sans-serif}\n    .hud{display:flex;align-items:center;gap:16px;color:#f0e6d2;font-size:15px;font-weight:600}\n    .turn{display:flex;align-items:center;gap:8px;padding:6px 14px;border-radius:999px;background:#2a2018}\n    .dot{width:16px;height:16px;border-radius:50%;box-shadow:inset 0 0 0 2px rgba(0,0,0,.3)}\n    .dot.d{background:radial-gradient(circle at 35% 30%,#5a4a3a,#2a1d12)}\n    .dot.l{background:radial-gradient(circle at 35% 30%,#fff,#d9c9a8)}\n    .btn{padding:6px 14px;border:0;border-radius:999px;background:#c8a15a;color:#2a1d12;font-weight:700;cursor:pointer;font-size:14px}\n    .btn:hover{background:#d9b56a}\n    .win{color:#ffd479;font-weight:800}\n    .board{display:grid;grid-template-columns:repeat(8,1fr);grid-template-rows:repeat(8,1fr);\n      width:min(72vh,92vw);aspect-ratio:1;border:10px solid #4a3521;border-radius:8px;\n      box-shadow:0 12px 40px rgba(0,0,0,.5);overflow:hidden}\n    .sq{position:relative;display:flex;align-items:center;justify-content:center;cursor:default}\n    .sq.dark{background:#7d5a3c}\n    .sq.light{background:#e8d3b0}\n    .sq.playable{cursor:pointer}\n    .sq.sel{box-shadow:inset 0 0 0 4px #ffd479}\n    .sq.move::after{content:'';position:absolute;width:26%;height:26%;border-radius:50%;\n      background:rgba(255,212,121,.55)}\n    .pc{width:74%;height:74%;border-radius:50%;position:relative;\n      box-shadow:0 3px 6px rgba(0,0,0,.4),inset 0 -3px 6px rgba(0,0,0,.35),inset 0 3px 4px rgba(255,255,255,.25)}\n    .pc.d{background:radial-gradient(circle at 35% 30%,#6a5642,#241810)}\n    .pc.l{background:radial-gradient(circle at 35% 30%,#fff,#cdbb96)}\n    .pc.king::after{content:'♛';position:absolute;inset:0;display:flex;align-items:center;justify-content:center;\n      font-size:min(4.2vh,30px);color:#ffd479;text-shadow:0 1px 2px rgba(0,0,0,.5)}\n  `,\n  render(d2){\n    const S = d2.state\n    if(!S.board){ S.board = init(); S.turn = 'l'; S.sel = null; S.moves = []; S.winner = null }\n    const b = S.board, sel = S.sel, moves = S.moves\n    const cells = []\n    for(let r=0;r<8;r++)for(let c=0;c<8;c++){\n      const dark = (r+c)%2===1\n      const p = b[r][c]\n      const isSel = sel && sel.r===r && sel.c===c\n      const mv = moves.find(m=>m.r===r&&m.c===c)\n      const cls = ['sq', dark?'dark':'light']\n      if(dark) cls.push('playable')\n      if(isSel) cls.push('sel')\n      if(mv) cls.push('move')\n      const piece = p ? `<div class=\"pc ${p.side}${p.king?' king':''}\"></div>` : ''\n      cells.push(`<div class=\"${cls.join(' ')}\" data-key=\"${r}-${c}\" data-r=\"${r}\" data-c=\"${c}\">${piece}</div>`)\n    }\n    const label = S.turn==='l' ? 'Claras' : 'Escuras'\n    const hud = S.winner\n      ? `<span class=\"win\">🏆 ${S.winner==='l'?'Claras':'Escuras'} venceram!</span>`\n      : `<div class=\"turn\"><span class=\"dot ${S.turn}\"></span>Vez das ${label}</div>`\n    return `<div class=\"wrap\">\n      <div class=\"hud\">${hud}<button class=\"btn reset\">Reiniciar</button></div>\n      <div class=\"board\">${cells.join('')}</div>\n    </div>`\n  },\n  mounted(d2){\n    d2.on('.reset','click',()=>{ d2.state.board=init(); d2.state.turn='l'; d2.state.sel=null; d2.state.moves=[]; d2.state.winner=null })\n    d2.on('.sq','click',(e,el)=>{\n      const S=d2.state; if(S.winner) return\n      const r=+el.dataset.r, c=+el.dataset.c, b=S.board, p=b[r][c]\n      // clicking a legal destination?\n      const mv=(S.moves||[]).find(m=>m.r===r&&m.c===c)\n      if(mv && S.sel){ applyMove(d2,S.sel,mv); return }\n      // selecting own piece\n      if(p && p.side===S.turn){\n        const caps=allCaptures(b,S.turn)\n        let opts=movesFor(b,r,c)\n        if(caps.length){ // forced capture: only capture moves allowed\n          opts=opts.filter(m=>m.cap)\n          if(!opts.length){ return } // this piece can't capture but another must\n        }\n        S.sel={r,c}; S.moves=opts; d2.refresh()\n      } else { S.sel=null; S.moves=[]; d2.refresh() }\n    })\n  },\n}\nfunction init(){\n  const b=Array.from({length:8},()=>Array(8).fill(null))\n  for(let r=0;r<8;r++)for(let c=0;c<8;c++){\n    if((r+c)%2===1){\n      if(r<3) b[r][c]={side:'d',king:false}      // dark on top\n      else if(r>4) b[r][c]={side:'l',king:false}  // light on bottom\n    }\n  }\n  return b\n}\nfunction movesFor(b,r,c){\n  const p=b[r][c]; if(!p) return []\n  const dirs = p.king ? [[1,1],[1,-1],[-1,1],[-1,-1]]\n    : p.side==='l' ? [[-1,1],[-1,-1]] : [[1,1],[1,-1]]\n  const out=[]\n  // captures\n  for(const[dr,dc]of dirs){\n    const mr=r+dr,mc=c+dc,jr=r+2*dr,jc=c+2*dc\n    if(inb(jr,jc)&&b[jr][jc]===null&&inb(mr,mc)&&b[mr][mc]&&b[mr][mc].side!==p.side)\n      out.push({r:jr,c:jc,cap:{r:mr,c:mc}})\n  }\n  // simple slides (king can't slide multiple here — one-step diagonals, classic draughts man; king one-step)\n  for(const[dr,dc]of dirs){\n    const nr=r+dr,nc=c+dc\n    if(inb(nr,nc)&&b[nr][nc]===null) out.push({r:nr,c:nc,cap:null})\n  }\n  return out\n}\nfunction allCaptures(b,side){\n  const res=[]\n  for(let r=0;r<8;r++)for(let c=0;c<8;c++){\n    const p=b[r][c]\n    if(p&&p.side===side){ movesFor(b,r,c).forEach(m=>{ if(m.cap) res.push({from:{r,c},to:m}) }) }\n  }\n  return res\n}\nfunction applyMove(d2,from,mv){\n  const S=d2.state, b=S.board\n  const p=b[from.r][from.c]\n  b[mv.r][mv.c]=p; b[from.r][from.c]=null\n  if(mv.cap) b[mv.cap.r][mv.cap.c]=null\n  // king promotion\n  if(!p.king && ((p.side==='l'&&mv.r===0)||(p.side==='d'&&mv.r===7))) p.king=true\n  // chained capture?\n  if(mv.cap && !( (p.side==='l'&&mv.r===0)||(p.side==='d'&&mv.r===7) && !p.king )){\n    const more=movesFor(b,mv.r,mv.c).filter(m=>m.cap)\n    if(more.length){ S.sel={r:mv.r,c:mv.c}; S.moves=more; d2.refresh(); return }\n  }\n  // end turn\n  S.turn = S.turn==='l' ? 'd' : 'l'\n  S.sel=null; S.moves=[]\n  // win check: opponent has no pieces or no moves\n  const opp=S.turn\n  const hasPiece=b.flat().some(x=>x&&x.side===opp)\n  let canMove=false\n  if(hasPiece){ for(let r=0;r<8&&!canMove;r++)for(let c=0;c<8&&!canMove;c++){ const q=b[r][c]; if(q&&q.side===opp&&movesFor(b,r,c).length) canMove=true } }\n  if(!hasPiece||!canMove) S.winner = opp==='l'?'d':'l'\n  d2.refresh()\n}\nfunction inb(r,c){ return r>=0&&r<8&&c>=0&&c<8 }\n"}}}}}}}},{"_id":"6a4ed45f3f83b2328de3eb61","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"tic-tac-toe","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{position:relative;height:100%;box-sizing:border-box;overflow:hidden;\n      font-family:'Segoe UI',system-ui,sans-serif;\n      background:radial-gradient(circle at 50% 0%,#1e1b4b 0%,#0f172a 55%,#020617 100%);color:#e2e8f0}\n    .cv{position:absolute;inset:0;width:100%;height:100%;display:block}\n    .hud{position:absolute;top:20px;left:0;right:0;text-align:center;pointer-events:none;z-index:2}\n    .title{font-size:26px;font-weight:800;letter-spacing:.5px;\n      background:linear-gradient(90deg,#818cf8,#22d3ee);-webkit-background-clip:text;background-clip:text;color:transparent}\n    .status{margin-top:6px;font-size:16px;font-weight:600;min-height:22px;color:#cbd5e1;text-shadow:0 2px 12px #000}\n    .status b{color:#a5b4fc}\n    .foot{position:absolute;bottom:22px;left:0;right:0;text-align:center;z-index:2}\n    .btn{padding:11px 26px;border:0;border-radius:12px;cursor:pointer;font-size:15px;font-weight:700;\n      color:#0f172a;background:linear-gradient(90deg,#818cf8,#22d3ee);transition:opacity .2s;box-shadow:0 6px 24px rgba(34,211,238,.35)}\n    .btn:hover{opacity:.88}\n    .loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#94a3b8}\n  `,\n  render(d2){\n    const b = d2.state.board || Array(9).fill(null)\n    const winInfo = d2.state.winInfo || null\n    const winner = winInfo ? winInfo.player : null\n    const full = b.every(Boolean)\n    const turn = d2.state.turn || 'X'\n    let status\n    if (winner) status = `Venceu: <b>${winner}</b> 🎉 BOOM!`\n    else if (full) status = `Deu velha! 🤝 BOOM!`\n    else status = `Vez de: <b>${turn}</b>`\n    const libState = d2.lib.state('three@0.160.0')\n    const ready = !!d2.state._three\n    let overlay = ''\n    if (!ready) {\n      overlay = libState === 'error'\n        ? `<div class=\"loading\">falha ao carregar 3D: ${d2.lib.error('three@0.160.0') || 'tente recarregar'}</div>`\n        : '<div class=\"loading\">carregando 3D…</div>'\n    }\n    return `<div class=\"wrap\">\n      <canvas class=\"cv\"></canvas>\n      ${overlay}\n      <div class=\"hud\">\n        <div class=\"title\">Jogo da Velha 3D</div>\n        <div class=\"status\">${status}</div>\n      </div>\n      <div class=\"foot\"><button class=\"btn reset\">Reiniciar</button></div>\n    </div>`\n  },\n  mounted(d2){ this._init(d2) },\n  updated(d2){ this._init(d2) },\n  _init(d2){\n    if(d2.state._three) return\n    const THREE = d2.lib('three@0.160.0')\n    if(!THREE){ return } // reactive: render() shows loading/error; updated() retries when ready\n    if(!d2.$('.cv')) return // canvas not painted yet; updated() will retry\n    d2.state._three = true\n\n    const LINES=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]\n    const check=(b)=>{\n      for(const line of LINES){ const [a,c,d]=line\n        if(b[a]&&b[a]===b[c]&&b[a]===b[d]) return {player:b[a],line} }\n      return null\n    }\n    if(!d2.state.board){ d2.state.board=Array(9).fill(null); d2.state.turn='X'; d2.state.winInfo=null }\n\n    const canvas = d2.$('.cv')\n    const renderer = new THREE.WebGLRenderer({canvas,antialias:true,alpha:true})\n    const scene = new THREE.Scene()\n    const camera = new THREE.PerspectiveCamera(45,1,0.1,100)\n    camera.position.set(0,0,11)\n    scene.add(new THREE.AmbientLight(0xffffff,0.7))\n    const key = new THREE.DirectionalLight(0xa5b4fc,1.2); key.position.set(5,6,8); scene.add(key)\n    const rim = new THREE.DirectionalLight(0x22d3ee,0.8); rim.position.set(-6,-3,4); scene.add(rim)\n\n    const root = new THREE.Group(); scene.add(root)      // spins\n    const boardGrp = new THREE.Group(); root.add(boardGrp)\n\n    // 3x3 cell tiles\n    const tiles = []\n    const tileGeo = new THREE.BoxGeometry(1.7,1.7,0.28)\n    const idle = 0x1e2547, hover = 0x3b3f8f, win = 0x16a34a\n    for(let i=0;i<9;i++){\n      const col=i%3, rowN=Math.floor(i/3)\n      const mat=new THREE.MeshStandardMaterial({color:idle,metalness:.4,roughness:.35,\n        emissive:0x0b1030,emissiveIntensity:.4})\n      const m=new THREE.Mesh(tileGeo,mat)\n      m.position.set((col-1)*1.95,(1-rowN)*1.95,0)\n      m.userData={i,base:m.position.clone()}\n      boardGrp.add(m); tiles.push(m)\n    }\n    // marks group per cell (X / O built from bars)\n    const marks = Array(9).fill(null)\n    const makeMark=(player)=>{\n      const g=new THREE.Group()\n      const color = player==='X'?0xf472b6:0x22d3ee\n      const mat=new THREE.MeshStandardMaterial({color,emissive:color,emissiveIntensity:.5,metalness:.3,roughness:.3})\n      if(player==='X'){\n        const bar=new THREE.BoxGeometry(1.4,0.26,0.26)\n        const a=new THREE.Mesh(bar,mat); a.rotation.z=Math.PI/4\n        const b=new THREE.Mesh(bar,mat); b.rotation.z=-Math.PI/4\n        g.add(a,b)\n      } else {\n        const ring=new THREE.TorusGeometry(0.6,0.14,16,40)\n        g.add(new THREE.Mesh(ring,mat))\n      }\n      return g\n    }\n\n    // raycaster picking\n    const ray=new THREE.Raycaster(), pointer=new THREE.Vector2()\n    let hoverI=-1\n    const setPointer=(ev)=>{\n      const r=canvas.getBoundingClientRect()\n      pointer.x=((ev.clientX-r.left)/r.width)*2-1\n      pointer.y=-((ev.clientY-r.top)/r.height)*2+1\n    }\n    const pick=()=>{\n      ray.setFromCamera(pointer,camera)\n      const hit=ray.intersectObjects(tiles,false)\n      return hit.length?hit[0].object.userData.i:-1\n    }\n    d2.on(canvas,'mousemove',(ev)=>{ setPointer(ev); hoverI=pick() })\n    d2.on(canvas,'click',(ev)=>{\n      // state guard: block moves during explosion, or when game already decided\n      if(exploding || d2.state.winInfo) return\n      setPointer(ev); const i=pick()\n      if(i<0) return\n      const cur = d2.state.board || Array(9).fill(null)\n      if(cur[i]) return                 // cell taken — no-op, no state mutation\n      const player = d2.state.turn\n      const b = cur.slice(); b[i]=player // immutable copy, never mutate live state\n      // spawn mark for this move only\n      const mk=makeMark(player); mk.position.copy(tiles[i].userData.base); mk.position.z=0.3\n      mk.scale.setScalar(0.01); marks[i]=mk; boardGrp.add(mk)\n      const w=check(b)\n      const isDraw = !w && b.every(Boolean)\n      d2.setState({ board:b, winInfo:w, turn: player==='X'?'O':'X' })\n      // explosion ONLY on a decisive end (win or full-board draw)\n      if(w || isDraw) triggerExplosion()\n    })\n\n    // explosion particles\n    let exploding=false, explodeT=0, debris=[]\n    const triggerExplosion=()=>{\n      if(exploding) return\n      exploding=true; explodeT=0\n      const pieces=[...tiles, ...marks.filter(Boolean)]\n      debris = pieces.map(obj=>{\n        boardGrp.remove(obj); scene.add(obj)\n        obj.position.copy(obj.getWorldPosition(new THREE.Vector3()))\n        return { obj,\n          vel:new THREE.Vector3((Math.random()-.5)*10,(Math.random()-.5)*10,(Math.random()*6+2)),\n          spin:new THREE.Vector3((Math.random()-.5)*.4,(Math.random()-.5)*.4,(Math.random()-.5)*.4) }\n      })\n    }\n    const rebuild=()=>{\n      // remove debris, reset tiles into board\n      debris.forEach(d=>scene.remove(d.obj))\n      marks.forEach(m=>m&&boardGrp.remove(m))\n      for(let i=0;i<9;i++) marks[i]=null\n      tiles.forEach(t=>{ boardGrp.add(t); t.position.copy(t.userData.base); t.rotation.set(0,0,0); t.material.color.setHex(idle) })\n      exploding=false; debris=[]\n    }\n\n    d2.on('.reset','click',()=>{\n      d2.setState({ board:Array(9).fill(null), turn:'X', winInfo:null })\n      rebuild()\n    })\n\n    // resize\n    const resize=()=>{\n      const w=Math.max(1,d2.root.clientWidth||600), h=Math.max(1,d2.root.clientHeight||600)\n      renderer.setPixelRatio(Math.min(devicePixelRatio,2))\n      renderer.setSize(w,h,false)\n      camera.aspect=w/h; camera.updateProjectionMatrix()\n      // keep the board fully framed regardless of aspect ratio — pull camera back for narrow/short viewports\n      const boardSpan = 6.4 // world units the 3x3 board+marks occupy\n      const vFov = camera.fov*Math.PI/180\n      const fitH = (boardSpan/2)/Math.tan(vFov/2)\n      const fitW = fitH/Math.min(1,camera.aspect)\n      camera.position.z = Math.max(9, fitH, fitW) + 1.2\n      camera.updateProjectionMatrix()\n    }\n    resize(); d2.on(window,'resize',resize)\n\n    // animation loop\n    d2.raf((t)=>{\n      root.rotation.y = t*0.0004\n      root.rotation.x = Math.sin(t*0.0003)*0.25\n      const winSet = d2.state.winInfo ? d2.state.winInfo.line : []\n      tiles.forEach(m=>{\n        if(!exploding){\n          const isWin=winSet.includes(m.userData.i)\n          const target = isWin?win : (m.userData.i===hoverI && !d2.state.board[m.userData.i] && !d2.state.winInfo ? hover : idle)\n          m.material.color.lerp(new THREE.Color(target),0.15)\n          const lift = (m.userData.i===hoverI && !exploding)?0.35:0\n          m.position.z += (lift - m.position.z)*0.2\n        }\n      })\n      marks.forEach(m=>{ if(m && m.scale.x<1) m.scale.lerp(new THREE.Vector3(1,1,1),0.25) })\n      if(exploding){\n        explodeT+=0.016\n        debris.forEach(d=>{\n          d.vel.z-=0.25 // gravity\n          d.obj.position.addScaledVector(d.vel,0.05)\n          d.obj.rotation.x+=d.spin.x; d.obj.rotation.y+=d.spin.y; d.obj.rotation.z+=d.spin.z\n        })\n      }\n      renderer.render(scene,camera)\n    })\n    d2.onUnmount(()=>{ renderer.dispose() })\n  },\n}"}}}}}}}},{"_id":"6a710d3395912880b321f0dd","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab-task-row","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .row{display:flex;align-items:center;gap:10px;height:100%;box-sizing:border-box;padding:10px 12px;\n       background:#fff;border:1px solid #e6e6ee;border-radius:10px;font:13px/1.35 -apple-system,Segoe UI,sans-serif}\n  .row[data-active]{border-color:#7c4dff;box-shadow:0 0 0 2px rgba(124,77,255,.15)}\n  .chip{flex:0 0 auto;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;\n        padding:3px 7px;border-radius:999px;cursor:pointer;user-select:none}\n  .chip[data-s=\"todo\"]{background:#eef1f7;color:#5b6478}\n  .chip[data-s=\"doing\"]{background:#fff3d6;color:#8a5b00}\n  .chip[data-s=\"done\"]{background:#e2f7e9;color:#186c3a}\n  .t{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;color:#161622}\n  .who{flex:0 0 auto;color:#7a7a8c;font-size:12px}\n  .due{flex:0 0 auto;color:#9a9aac;font-size:11px;font-variant-numeric:tabular-nums}\n  .due[data-late]{color:#c0392b;font-weight:700}\n  .p{flex:0 0 auto;font-size:11px;color:#7c4dff;font-weight:700}\n  .btn{flex:0 0 auto;border:1px solid #e0e0ea;background:#fafafe;cursor:pointer;font-size:11px;font-weight:700;padding:3px 8px;border-radius:6px;color:#6a6a80}\n  .btn:hover{background:#f2f2f7;color:#161622}\n  `,\n  render(d2) {\n    const r = d2.param('record') || {}\n    const esc = (s) => String(s == null ? '' : s).replace(/[<>&\"]/g, (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;' })[c])\n    const st = r.status || 'todo'\n    const active = d2.param('activeId') === r._id\n    const late = r.due && new Date(r.due) < new Date() && st !== 'done'\n    return `<div class=\"row\" ${active ? 'data-active' : ''}>\n      <span class=\"chip\" data-s=\"${esc(st)}\" title=\"click to cycle status\">${esc(st)}</span>\n      <span class=\"t\">${esc(r.title)}</span>\n      <span class=\"p\">P${esc(r.priority == null ? '-' : r.priority)}</span>\n      <span class=\"who\">${esc(r.assignee || '—')}</span>\n      <span class=\"due\" ${late ? 'data-late' : ''}>${r.due ? d2.fmt.date(r.due, { month: 'short', day: 'numeric' }) : '—'}</span>\n      <button class=\"btn open\" title=\"detail\">EDIT</button>\n      <button class=\"btn del\" title=\"delete\">DEL</button>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.chip', 'click', () => {\n      const rv = d2.emit('onCycle', (d2.param('record') || {})._id)\n      console.log('[row] emit onCycle ->', rv, 'paramKeys=', Object.keys(d2.params || {}), 'onCycleType=', typeof (d2.params || {}).onCycle)\n    })\n    d2.on('.open', 'click', () => d2.emit('onOpen', (d2.param('record') || {})._id))\n    d2.on('.del', 'click', () => d2.emit('onDelete', (d2.param('record') || {})._id))\n  },\n}"}},"record":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"record"}}}},"activeId":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"activeId"}}}},"onOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onOpen"}}}},"onCycle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCycle"}}}},"onDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDelete"}}}}}}}}}},{"_id":"6a710dc27c7babb663fe6caf","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_board","package":"gui","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 esc = (s) => String(s == null ? '' : s).replace(/[<>&\"]/g, (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;' })[c])\n\nconst modal = (d2, rec) => {\n  const mod = d2.params.dataset\n  const fTitle = d2.field(mod, 'title')\n  const fNotes = d2.field(mod, 'notes')\n  const fEst = d2.field(mod, 'estimate')\n  const ds = d2.data()\n  return `<div class=\"scrim\">\n    <div class=\"modal\">\n      <span class=\"lb\">Task detail · ${esc(rec._id)}</span>\n      <span class=\"lb\">Title</span>\n      <input class=\"in f\" data-f=\"title\" value=\"${esc(fTitle.value)}\"/>\n      <span class=\"lb\">Estimate (hours)</span>\n      <input class=\"in f\" data-f=\"estimate\" type=\"number\" value=\"${esc(fEst.value)}\"/>\n      <span class=\"lb\">Notes</span>\n      <textarea class=\"ta f\" data-f=\"notes\">${esc(fNotes.value)}</textarea>\n      <span class=\"lb\">editable=${String(fTitle.editable)} · def=${fTitle.def ? 'yes' : 'NULL'} · errors=${fTitle.errors ? esc(JSON.stringify(fTitle.errors)) : 'none'}</span>\n      <div class=\"mrow\">\n        <button class=\"b ghost editmode\">edit()</button>\n        <button class=\"b ghost close\">Close</button>\n        <button class=\"b save\" ${ds.mutating ? 'disabled' : ''}>${ds.mutating ? 'Saving…' : 'Save'}</button>\n      </div>\n    </div>\n  </div>`\n}\n\nexport default {\n  css: `\n  .wrap{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;height:100%;box-sizing:border-box;\n        background:#f6f6fa;font:13px -apple-system,Segoe UI,sans-serif;color:#161622}\n  .bar{display:flex;gap:8px;align-items:center;padding:12px 14px;border-bottom:1px solid #e6e6ee;background:#fff}\n  .h1{font-size:15px;font-weight:800;margin-right:auto}\n  .in{padding:7px 10px;border:1px solid #d5d5e0;border-radius:8px;font:13px inherit;min-width:200px}\n  .b{padding:7px 12px;border:0;border-radius:8px;background:#7c4dff;color:#fff;font-weight:700;cursor:pointer}\n  .b[disabled]{opacity:.45;cursor:default}\n  .b.ghost{background:#eeeef5;color:#4a4a5c}\n  .meta{display:flex;gap:12px;flex-wrap:wrap;padding:8px 14px;color:#7a7a8c;font-size:11px;border-bottom:1px solid #eeeef5;background:#fbfbfe}\n  .list{overflow:auto;padding:10px 14px;display:grid;grid-auto-rows:46px;gap:8px;align-content:start}\n  .foot{padding:8px 14px;border-top:1px solid #e6e6ee;background:#fff;display:flex;gap:8px;align-items:center;font-size:11px;color:#7a7a8c}\n  .empty{padding:24px;text-align:center;color:#9a9aac}\n  .err{color:#c0392b;font-weight:700}\n  .scrim{position:absolute;inset:0;background:rgba(18,18,30,.42);display:flex;align-items:center;justify-content:center;z-index:20}\n  .modal{width:min(440px,92%);background:#fff;border-radius:14px;padding:18px;box-shadow:0 24px 60px rgba(0,0,0,.28);display:flex;flex-direction:column;gap:6px}\n  .lb{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#9a9aac}\n  .ta{padding:8px 10px;border:1px solid #d5d5e0;border-radius:8px;font:13px inherit;min-height:64px;resize:vertical}\n  .mrow{display:flex;gap:8px;justify-content:flex-end;margin-top:6px}\n  `,\n  render(d2) {\n    const ds = d2.data()\n    if (!ds) return `<div class=\"empty\">No dataset bound (params.dataset is null).</div>`\n    if (ds.loading) return `<div class=\"empty\">Loading…</div>`\n    const status = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const q = String(d2.shared.get('cclab:q', '', { global: true }) || '').toLowerCase()\n    const all = ds.records()\n    let rows = all\n    if (status !== 'all') rows = rows.filter((r) => (r.status || 'todo') === status)\n    if (q) rows = rows.filter((r) => String(r.title || '').toLowerCase().includes(q))\n    const openId = d2.state.openId || ''\n    const pi = ds.pageInfo() || {}\n    const rowType = d2.param('row')\n    const cbs = {\n      onOpen: (id) => {\n        console.log('[board] onOpen fired id=', id)\n        d2.state.openId = id\n        d2.state.err = ''\n        try {\n          d2.data().select(id)\n          console.log('[board] select ok selected=', (d2.data().selected() || {})._id)\n        } catch (e) {\n          console.log('[board] select THREW', e && e.message)\n          d2.state.err = 'select: ' + (e && e.message)\n        }\n        console.log('[board] state.openId=', d2.state.openId, 'match=', !!d2.data().records().find((x) => x._id === d2.state.openId))\n      },\n      onCycle: async (id) => {\n        const order = ['todo', 'doing', 'done']\n        const rec = d2.data().records().find((r) => r._id === id)\n        if (!rec) return\n        const next = order[(order.indexOf(rec.status || 'todo') + 1) % order.length]\n        try {\n          await d2.data().update(Object.assign({}, rec, { status: next }))\n          d2.bus.emit('cclab:changed', { id: id, status: next })\n        } catch (e) { d2.state.err = 'cycle failed: ' + (e && e.message) }\n      },\n      onDelete: async (id) => {\n        const ok = await d2.dialog.confirm('Delete this task?')\n        if (!ok) return\n        try {\n          await d2.data().remove(id)\n          d2.bus.emit('cclab:changed', { id: id, deleted: true })\n        } catch (e) { d2.state.err = 'delete failed: ' + (e && e.message) }\n      },\n    }\n    const hosts = rows.map((r) => d2.render(rowType, Object.assign({ record: r, activeId: openId }, cbs), Object.assign({ key: r._id + '|' + (r.status || '') + '|' + String(r.title || '').length }, cbs))).join('')\n    const rec = openId ? all.find((r) => r._id === openId) : null\n    return `<div class=\"wrap\">\n      <div class=\"bar\">\n        <span class=\"h1\">Task board</span>\n        <input class=\"in new\" placeholder=\"New task title…\"/>\n        <button class=\"b add\" ${ds.mutating ? 'disabled' : ''}>${ds.mutating ? 'Saving…' : '+ Add'}</button>\n      </div>\n      <div class=\"meta\">\n        <span>${d2.fmt.pluralize(rows.length, 'task', 'tasks')} shown</span>\n        <span>of ${d2.fmt.number(all.length)} loaded / ${d2.fmt.number(pi.total == null ? 0 : pi.total)} total</span>\n        <span>filter: <b>${esc(status)}</b>${q ? ' · q=' + esc(q) : ''}</span>\n        ${ds.fetching ? '<span>fetching…</span>' : ''}\n        ${d2.state.err ? '<span class=\"err\">' + esc(d2.state.err) + '</span>' : ''}\n      </div>\n      <div class=\"list\">${hosts || '<div class=\"empty\">Nothing matches.</div>'}</div>\n      <div class=\"foot\">\n        <button class=\"b ghost loadall\">Load all pages</button>\n        <span>page ${pi.page == null ? 1 : pi.page} of ${pi.pages == null ? 1 : pi.pages} · size ${pi.size == null ? '?' : pi.size} · rows ${rows.length}</span>\n      </div>\n      ${rec ? modal(d2, rec) : ''}\n    </div>`\n  },\n  mounted(d2) {\n    const add = async () => {\n      const el = d2.$('.new')\n      const t = String((el && el.value) || '').trim()\n      if (!t) { d2.state.err = 'title required'; return }\n      d2.state.err = ''\n      try {\n        const out = await d2.data().create({ title: t, status: 'todo', priority: 3, assignee: 'unassigned' })\n        d2.log('create returned', out)\n        const el2 = d2.$('.new')\n        if (el2) el2.value = ''\n        d2.bus.emit('cclab:changed', { created: true })\n      } catch (e) { d2.state.err = 'create failed: ' + (e && e.message) }\n    }\n    d2.on('.add', 'click', add)\n    d2.on('.new', 'keydown', (e) => { if (e.key === 'Enter') add() })\n    d2.on('.f', 'input', (e, el) => {\n      const v = el.type === 'number' ? Number(el.value) : el.value\n      d2.field(d2.params.dataset, el.dataset.f).set(v)\n    })\n    d2.on('.editmode', 'click', () => { d2.data().edit(); d2.refresh() })\n    d2.on('.save', 'click', async () => {\n      try {\n        const out = await d2.data().save()\n        d2.log('save returned', out)\n        d2.state.openId = ''\n        d2.bus.emit('cclab:changed', { saved: true })\n      } catch (e) { d2.state.err = 'save failed: ' + (e && e.message) }\n    })\n    d2.on('.close', 'click', () => { d2.data().cancel(); d2.state.openId = '' })\n    d2.on('.loadall', 'click', async () => {\n      const out = await d2.data().loadAll()\n      d2.log('loadAll ->', out && out.length)\n    })\n    d2.bus.on('cclab:filter', () => d2.refresh())\n  },\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"row":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"row"}}}}}}}}}},{"_id":"6a710deac1dc93c9dc7d201f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_filter","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .fb{display:flex;align-items:center;gap:8px;height:100%;box-sizing:border-box;padding:0 14px;\n      background:#161622;color:#fff;font:13px -apple-system,Segoe UI,sans-serif}\n  .lb{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:#8b8ba6}\n  .pill{border:1px solid #33334a;background:transparent;color:#c9c9db;padding:5px 11px;border-radius:999px;\n        font-size:12px;font-weight:700;cursor:pointer;text-transform:capitalize}\n  .pill[data-on]{background:#7c4dff;border-color:#7c4dff;color:#fff}\n  .q{margin-left:auto;padding:6px 10px;border:1px solid #33334a;border-radius:8px;background:#0e0e18;color:#fff;\n     font:13px inherit;min-width:200px;outline:none}\n  .dbg{font-size:10px;color:#5c5c78;font-family:ui-monospace,monospace}\n  `,\n  render(d2) {\n    const cur = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const q = String(d2.shared.get('cclab:q', '', { global: true }) || '')\n    const opts = ['all', 'todo', 'doing', 'done']\n    return `<div class=\"fb\">\n      <span class=\"lb\">Filter</span>\n      ${opts.map((o) => `<button class=\"pill\" data-o=\"${o}\" ${o === cur ? 'data-on' : ''}>${o}</button>`).join('')}\n      <input class=\"q\" placeholder=\"search title…\" value=\"${q.replace(/\"/g, '&quot;')}\"/>\n      <span class=\"dbg\">shared:${cur}|${q ? q.length : 0}ch</span>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.pill', 'click', (e, el) => {\n      d2.shared.set('cclab:status', el.dataset.o, { global: true })\n      const n = d2.bus.emit('cclab:filter', { status: el.dataset.o })\n      d2.log('bus reached', n)\n    })\n    d2.on('.q', 'input', (e, el) => {\n      d2.shared.set('cclab:q', el.value, { global: true })\n      d2.bus.emit('cclab:filter', { q: el.value })\n    })\n  },\n}\n"}}}}}}}},{"_id":"6a710deb33e3af9321f61da2","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_stats","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .s{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;height:100%;box-sizing:border-box;padding:10px 14px;\n     background:#fff;border-bottom:1px solid #e6e6ee;font:13px -apple-system,Segoe UI,sans-serif}\n  .tile{display:flex;flex-direction:column;justify-content:center;gap:2px;padding:8px 12px;border-radius:10px;\n        background:#f6f6fa;border:1px solid #eeeef5}\n  .tile[data-on]{background:#f1ebff;border-color:#d5c4ff}\n  .tile b{font-size:20px;font-weight:800;color:#161622;line-height:1}\n  .tile span{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#9a9aac}\n  .warn{color:#c0392b}\n  `,\n  render(d2) {\n    const ds = d2.data()\n    if (!ds) return `<div class=\"s\"><div class=\"tile warn\"><b>!</b><span>no dataset</span></div></div>`\n    const rows = ds.records()\n    const by = { todo: 0, doing: 0, done: 0 }\n    let hrs = 0\n    rows.forEach((r) => {\n      const s = r.status || 'todo'\n      if (by[s] != null) by[s]++\n      hrs += Number(r.estimate) || 0\n    })\n    const cur = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const tiles = ['todo', 'doing', 'done'].map((k) => `<div class=\"tile\" ${cur === k ? 'data-on' : ''}><b>${by[k]}</b><span>${k}</span></div>`).join('')\n    return `<div class=\"s\">${tiles}\n      <div class=\"tile\"><b>${d2.fmt.number(hrs)}h</b><span>estimate</span></div>\n      <div class=\"tile\"><b>${d2.fmt.percent(rows.length ? by.done / rows.length : 0)}</b><span>complete</span></div>\n      <div class=\"tile\"><b>${d2.state.pings || 0}</b><span>bus pings</span></div>\n    </div>`\n  },\n  mounted(d2) {\n    d2.bus.on('cclab:changed', (p) => {\n      d2.state.pings = (d2.state.pings || 0) + 1\n      d2.state.last = JSON.stringify(p || {})\n    })\n  },\n}\n"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6ab459ea156b886f8631de98","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Aerodromos_do_Drive_teste","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f6f5f1;--ink:#1d1d1b;--mut:#77756f;--line:#e2e0d8;--acc:#1f5eff;--warn:#d9480f;--panel:#fff}\n  *{box-sizing:border-box}\n  .root{height:100%;display:grid;grid-template-columns:1fr 360px;grid-template-rows:auto 1fr;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif}\n  header{grid-column:1/3;padding:18px 24px 14px;border-bottom:1px solid var(--line);display:flex;align-items:baseline;gap:24px;flex-wrap:wrap}\n  h1{font-size:16px;margin:0;font-weight:600}\n  .src{color:var(--mut);font-size:11.5px;word-break:break-all}\n  .kpis{display:flex;gap:20px;margin-left:auto}\n  .kpi b{display:block;font-size:20px;font-weight:600;font-variant-numeric:tabular-nums}\n  .kpi span{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.4px}\n  .map{position:relative;padding:16px;min-height:0}\n  svg{width:100%;height:100%;display:block}\n  .dot{fill:var(--acc);fill-opacity:.55;cursor:pointer}\n  .dot.int{fill:var(--warn)}\n  .dot.sel{fill:var(--ink);fill-opacity:1}\n  .side{border-left:1px solid var(--line);display:flex;flex-direction:column;min-height:0;background:var(--panel)}\n  .side input{margin:12px;padding:8px 10px;border:1px solid var(--line);border-radius:6px;font:inherit;background:var(--bg)}\n  .list{overflow:auto;flex:1}\n  .row{padding:8px 14px;border-top:1px solid var(--line);cursor:pointer;display:grid;grid-template-columns:52px 1fr;gap:8px}\n  .row:hover{background:var(--bg)}\n  .row.sel{background:#e9efff}\n  .row code{font:11.5px ui-monospace,Menlo,monospace;color:var(--mut);padding-top:1px}\n  .row small{display:block;color:var(--mut)}\n  .tag{font-size:10.5px;color:var(--warn)}\n  .msg{grid-column:1/3;padding:40px;color:var(--mut)}\n  .err{color:var(--warn)}\n  .legend{position:absolute;left:28px;bottom:24px;font-size:11px;color:var(--mut);display:flex;gap:14px}\n  .legend i{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:0}\n  @media (max-width:760px){.root{grid-template-columns:1fr;grid-template-rows:auto 320px 1fr}header,.msg{grid-column:1}.side{border-left:none;border-top:1px solid var(--line)}.kpis{margin-left:0}}\n  `,\n  render(d2){\n    var s = d2.state;\n    var url = d2.param('arquivoUrl', '');\n    var head = '<header><h1>Aeródromos públicos · ANAC</h1><span class=\"src\">Drive: ' + esc(url || '(sem arquivo)') + '</span>';\n    if (s.error) return '<div class=\"root\">' + head + '</header><div class=\"msg err\">Falha ao ler o arquivo do Drive: ' + esc(s.error) + '</div></div>';\n    if (!s.rows) return '<div class=\"root\">' + head + '</header><div class=\"msg\">Lendo arquivo do Drive…</div></div>';\n\n    var rows = s.rows, q = (s.q || '').toLowerCase();\n    var shown = q ? rows.filter(function(r){ return r.hay.indexOf(q) >= 0; }) : rows;\n    var ufs = {}; rows.forEach(function(r){ ufs[r.uf] = 1; });\n    var nInt = rows.filter(function(r){ return r.interditado; }).length;\n\n    head += '<div class=\"kpis\">'\n      + '<div class=\"kpi\"><b>' + rows.length + '</b><span>aeródromos</span></div>'\n      + '<div class=\"kpi\"><b>' + Object.keys(ufs).length + '</b><span>UFs</span></div>'\n      + '<div class=\"kpi\"><b>' + nInt + '</b><span>interditados</span></div>'\n      + '<div class=\"kpi\"><b>' + d2.fmt.bytes(s.bytes || 0) + '</b><span>arquivo</span></div>'\n      + '</div></header>';\n\n    // Equiretangular simples sobre a caixa do Brasil\n    var W = 1000, H = 1000, x0 = -74.5, x1 = -28.5, y0 = -34.5, y1 = 5.8;\n    var px = function(lon){ return (lon - x0) / (x1 - x0) * W; };\n    var py = function(lat){ return (y1 - lat) / (y1 - y0) * H; };\n    var dots = shown.map(function(r){\n      var cls = 'dot' + (r.interditado ? ' int' : '') + (s.sel === r.id ? ' sel' : '');\n      return '<circle data-key=\"' + r.id + '\" data-id=\"' + r.id + '\" class=\"' + cls + '\" cx=\"' + px(r.lon).toFixed(1) + '\" cy=\"' + py(r.lat).toFixed(1) + '\" r=\"' + (s.sel === r.id ? 9 : 5) + '\"><title>' + esc(r.oaci + ' · ' + r.nome + ' — ' + r.mun + '/' + r.uf) + '</title></circle>';\n    }).join('');\n\n    var list = shown.map(function(r){\n      return '<div class=\"row' + (s.sel === r.id ? ' sel' : '') + '\" data-key=\"' + r.id + '\" data-id=\"' + r.id + '\"><code>' + esc(r.oaci) + '</code><div>' + esc(r.nome)\n        + (r.interditado ? ' <span class=\"tag\">interditado</span>' : '')\n        + '<small>' + esc(r.mun) + ' · ' + esc(r.uf) + ' · ' + esc(r.op) + '</small></div></div>';\n    }).join('');\n\n    return '<div class=\"root\">' + head\n      + '<div class=\"map\"><svg viewBox=\"0 0 ' + W + ' ' + H + '\" preserveAspectRatio=\"xMidYMid meet\">' + dots + '</svg>'\n      + '<div class=\"legend\"><span><i style=\"background:var(--acc)\"></i>cadastrado</span><span><i style=\"background:var(--warn)\"></i>interditado</span><span>' + shown.length + ' no filtro</span></div></div>'\n      + '<div class=\"side\"><input data-q placeholder=\"Buscar nome, município, UF ou código OACI\" value=\"' + esc(s.q || '') + '\"><div class=\"list\">' + list + '</div></div>'\n      + '</div>';\n\n    function esc(v){ return String(v == null ? '' : v).replace(/[&<>\"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]; }); }\n  },\n  mounted(d2){\n    var url = d2.param('arquivoUrl', '');\n    if (!url) { d2.state.error = 'parâmetro arquivoUrl vazio'; return; }\n    fetch(url).then(function(res){\n      if (!res.ok) throw new Error('HTTP ' + res.status);\n      return res.arrayBuffer();\n    }).then(function(buf){\n      // CSV da ANAC vem em ISO-8859-1, separador ';', 1ª linha = \"Atualizado em\", 2ª = cabeçalho\n      var text = new TextDecoder('iso-8859-1').decode(buf);\n      var lines = text.split(/\\r?\\n/).filter(function(l){ return l.trim(); });\n      var hi = lines.findIndex(function(l){ return l.indexOf('OACI') >= 0; });\n      var cols = lines[hi].split(';');\n      var ix = function(name){ return cols.indexOf(name); };\n      var iO = ix('Código OACI'), iN = ix('Nome'), iM = ix('Município'), iU = ix('UF'), iLa = ix('LATGEOPOINT'), iLo = ix('LONGEOPOINT'), iOp = ix('Operação Diurna'), iS = ix('Situação');\n      var rows = [];\n      lines.slice(hi + 1).forEach(function(l, k){\n        var c = l.split(';');\n        var lat = parseFloat(c[iLa]), lon = parseFloat(c[iLo]);\n        if (!isFinite(lat) || !isFinite(lon)) return;\n        var r = { id: 'a' + k, oaci: c[iO], nome: c[iN], mun: c[iM], uf: c[iU], lat: lat, lon: lon, op: c[iOp], interditado: /interdit/i.test(c[iS] || '') };\n        r.hay = (r.oaci + ' ' + r.nome + ' ' + r.mun + ' ' + r.uf).toLowerCase();\n        rows.push(r);\n      });\n      d2.setState({ rows: rows, bytes: buf.byteLength });\n    }).catch(function(e){ d2.state.error = (e && e.message) || String(e); });\n\n    d2.on('[data-q]', 'input', function(e){ d2.state.q = e.target.value; });\n    d2.on('[data-id]', 'click', function(e, el){ var id = el.getAttribute('data-id'); d2.state.sel = d2.state.sel === id ? null : id; });\n  }\n};"}},"arquivoUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivoUrl"}}}}}}}}}},{"_id":"6ab47623854a800547912d3f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Norte_do_Drive_teste","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:300px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:3px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  label .n{margin-left:auto;color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .err{color:#b42318}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}}\n  `,\n  render(){\n    function row(key, label){\n      return '<label><input type=\"checkbox\" data-layer=\"' + key + '\" checked><i style=\"background:var(--' + key + ')\"></i>' + label + '<span class=\"n\" data-n=\"' + key + '\">…</span></label>';\n    }\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Região Norte · OSM do Drive</h1><p class=\"sub\">GeoJSON estático no Drive, sem Overpass</p>'\n      + row('trunk', 'Rodovia expressa (trunk)')\n      + row('primary', 'Rodovia principal')\n      + row('secondary', 'Rodovia secundária')\n      + row('river', 'Rios permanentes')\n      + '<div class=\"sep\"></div><table><tr><th colspan=\"2\">Arquivos</th></tr>'\n      + '<tr><td>rodovias</td><td data-f=\"rodovias\">carregando…</td></tr><tr><td>rios</td><td data-f=\"rios\">carregando…</td></tr>'\n      + '<tr><th colspan=\"2\">Desenho</th></tr><tr><td>pontos na tela</td><td data-pts>…</td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr></table>'\n      + '<button data-reset>Recentrar</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var FILES = { rodovias: d2.param('rodoviasUrl', ''), rios: d2.param('riosUrl', '') };\n    var STYLE = {\n      river:     { color: '#2f7fd8', w: 0.8 },\n      secondary: { color: '#8a877f', w: 0.8 },\n      primary:   { color: '#e0822b', w: 1.3 },\n      trunk:     { color: '#c2410c', w: 2.0 }\n    };\n    var ORDER = ['river', 'secondary', 'primary', 'trunk'];\n    // Geometria fica fora do d2.state (é grande e não deve disparar re-render)\n    var geo = { river: [], secondary: [], primary: [], trunk: [] };\n    var bbox = [180, 90, -180, -90];\n    var view = null, cv = null, ctx = null, W = 0, H = 0, dpr = 1, raf = 0;\n    var layers = { trunk: true, primary: true, secondary: true, river: true };\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n\n    function ingest(json, kind){\n      var n = { trunk: 0, primary: 0, secondary: 0, river: 0 };\n      (json.features || []).forEach(function(f){\n        if (!f.geometry) return;\n        var key = kind === 'rios' ? 'river' : f.properties && f.properties.highway;\n        if (!geo[key]) return;\n        var lines = f.geometry.type === 'MultiLineString' ? f.geometry.coordinates : [f.geometry.coordinates];\n        lines.forEach(function(line){\n          var a = new Float32Array(line.length * 2);\n          for (var i = 0; i < line.length; i++){\n            var x = line[i][0], y = mercY(line[i][1]);\n            a[i * 2] = x; a[i * 2 + 1] = y;\n            if (x < bbox[0]) bbox[0] = x; if (x > bbox[2]) bbox[2] = x;\n            if (y < bbox[1]) bbox[1] = y; if (y > bbox[3]) bbox[3] = y;\n          }\n          geo[key].push(a);\n        });\n        n[key]++;\n      });\n      return n;\n    }\n\n    function fit(){\n      if (!W || bbox[0] > bbox[2]) return;\n      var k = Math.min(W / (bbox[2] - bbox[0]), H / (bbox[3] - bbox[1])) * 0.92;\n      view = { k: k, cx: (bbox[0] + bbox[2]) / 2, cy: (bbox[1] + bbox[3]) / 2 };\n    }\n\n    function draw(){\n      raf = 0;\n      if (!ctx || !view) return;\n      var t0 = performance.now();\n      var L = layers;\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      // janela visível em coordenadas do mapa, para pular linhas fora da tela\n      var vx0 = (0 - ox) / k, vx1 = (W - ox) / k, vy0 = (oy - H) / k, vy1 = oy / k;\n      var pts = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      ORDER.forEach(function(key){\n        if (!L[key]) return;\n        ctx.beginPath();\n        ctx.strokeStyle = STYLE[key].color;\n        ctx.lineWidth = STYLE[key].w * Math.max(1, Math.min(3, Math.log2(k / 20 + 1)));\n        var arr = geo[key];\n        for (var j = 0; j < arr.length; j++){\n          var a = arr[j], n = a.length, inside = false;\n          for (var i = 0; i < n; i += 2){ var x = a[i], y = a[i + 1]; if (x >= vx0 && x <= vx1 && y >= vy0 && y <= vy1){ inside = true; break; } }\n          if (!inside) continue;\n          ctx.moveTo(a[0] * k + ox, oy - a[1] * k);\n          for (var i2 = 2; i2 < n; i2 += 2) ctx.lineTo(a[i2] * k + ox, oy - a[i2 + 1] * k);\n          pts += n / 2;\n        }\n        ctx.stroke();\n      });\n      var ms = Math.round(performance.now() - t0);\n      txt('[data-pts]', d2.fmt.number(pts)); txt('[data-ms]', ms + ' ms');\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function resize(){\n      if (!cv) return;\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1;\n      W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view) fit();\n      schedule();\n    }\n\n    cv = d2.$('[data-cv]'); ctx = cv.getContext('2d');\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); });\n\n    // arrastar + pinça\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){\n        view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k;\n        view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k;\n      } else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]];\n        var d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(200000, view.k * d / last));\n        last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault();\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top;\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var gx = (mx - ox) / k, gy = (oy - my) / k;\n      var nk = Math.max(5, Math.min(200000, k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule();\n    }, { passive: false });\n\n    d2.on('[data-layer]', 'change', function(e, el){\n      layers[el.getAttribute('data-layer')] = el.checked;\n      schedule();\n    });\n    d2.on('[data-reset]', 'click', function(){ fit(); schedule(); });\n\n    // carrega os dois arquivos do Drive em paralelo\n    Object.keys(FILES).forEach(function(name){\n      var url = FILES[name];\n      var cell = d2.$('[data-f=\"' + name + '\"]');\n      var files = function(v){\n        if (!cell) return;\n        cell.className = v.error ? 'err' : '';\n        cell.textContent = v.error ? v.error : d2.fmt.bytes(v.bytes) + ' · ' + v.dl + ' ms · parse ' + v.parse + ' ms';\n      };\n      if (!url){ files({ error: 'parâmetro vazio' }); return; }\n      var t0 = performance.now(), t1;\n      fetch(url).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })\n        .then(function(body){\n          t1 = performance.now();\n          var json = JSON.parse(body);\n          var n = ingest(json, name);\n          var t2 = performance.now();\n          Object.keys(n).forEach(function(k){ if (n[k]) txt('[data-n=\"' + k + '\"]', d2.fmt.number(n[k])); });\n          files({ bytes: body.length, dl: Math.round(t1 - t0), parse: Math.round(t2 - t1) });\n          fit(); schedule();\n        })\n        .catch(function(e){ files({ error: (e && e.message) || String(e) }); });\n    });\n  }\n};"}},"rodoviasUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rodoviasUrl"}}}},"riosUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"riosUrl"}}}}}}}}}},{"_id":"6ab47c14ab5404c7a0311c0f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Brasil_do_Drive_streaming","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--motorway:#7a1f0e;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f;--tertiary:#b9b6ad}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:330px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:2px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  label .n{margin-left:auto;color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0;vertical-align:top}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .bar{height:4px;background:var(--line);border-radius:2px;margin:2px 0 6px;overflow:hidden}\n  .bar b{display:block;height:100%;width:0;background:var(--ink);transition:width .2s}\n  .err{color:#b42318}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}}\n  `,\n  render(){\n    var LAYERS = [['motorway','Autoestrada (motorway)'],['trunk','Rodovia expressa (trunk)'],['primary','Rodovia principal'],['secondary','Rodovia secundária'],['tertiary','Rodovia terciária'],['river','Rios']];\n    var rows = LAYERS.map(function(l){\n      return '<label><input type=\"checkbox\" data-layer=\"' + l[0] + '\" checked><i style=\"background:var(--' + l[0] + ')\"></i>' + l[1] + '<span class=\"n\" data-n=\"' + l[0] + '\">0</span></label>';\n    }).join('');\n    function file(name){\n      return '<tr><td>' + name + '</td><td data-f=\"' + name + '\">aguardando…</td></tr><tr><td colspan=\"2\"><div class=\"bar\"><b data-bar=\"' + name + '\"></b></div></td></tr>';\n    }\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Brasil inteiro · OSM do Drive</h1><p class=\"sub\">NDJSON bruto no Drive (sem simplificação), lido em streaming</p>'\n      + rows\n      + '<div class=\"sep\"></div><table><tr><th colspan=\"2\">Arquivos</th></tr>' + file('rodovias') + file('rios')\n      + '<tr><th colspan=\"2\">Memória e desenho</th></tr>'\n      + '<tr><td>pontos carregados</td><td data-loaded>0</td></tr>'\n      + '<tr><td>geometria em memória</td><td data-mem>0</td></tr>'\n      + '<tr><td>heap JS</td><td data-heap>—</td></tr>'\n      + '<tr><td>pontos desenhados</td><td data-pts>…</td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr></table>'\n      + '<button data-reset>Recentrar</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var FILES = { rodovias: d2.param('rodoviasUrl', ''), rios: d2.param('riosUrl', '') };\n    var STYLE = {\n      river:     { color: '#2f7fd8', w: 0.7 },\n      tertiary:  { color: '#b9b6ad', w: 0.6 },\n      secondary: { color: '#8a877f', w: 0.8 },\n      primary:   { color: '#e0822b', w: 1.1 },\n      trunk:     { color: '#c2410c', w: 1.6 },\n      motorway:  { color: '#7a1f0e', w: 2.0 }\n    };\n    var ORDER = ['river', 'tertiary', 'secondary', 'primary', 'trunk', 'motorway'];\n    var on = {}; ORDER.forEach(function(k){ on[k] = true; });\n\n    // Uma camada = coordenadas contíguas (Float32) + offsets por linha + bbox por linha.\n    // Nada disso passa por d2.state: são centenas de MB e não podem disparar re-render.\n    function Layer(){ this.xy = new Float32Array(1 << 16); this.n = 0; this.off = new Uint32Array(1 << 12); this.bb = new Float32Array(1 << 14); this.lines = 0; this.feats = 0; }\n    Layer.prototype.add = function(coords){\n      var need = this.n + coords.length * 2;\n      if (need > this.xy.length){ var nx = new Float32Array(Math.max(need, this.xy.length * 2)); nx.set(this.xy.subarray(0, this.n)); this.xy = nx; }\n      if ((this.lines + 1) * 2 > this.off.length){ var no = new Uint32Array(this.off.length * 2); no.set(this.off); this.off = no; }\n      if ((this.lines + 1) * 4 > this.bb.length){ var nb = new Float32Array(this.bb.length * 2); nb.set(this.bb); this.bb = nb; }\n      var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, p = this.n;\n      for (var i = 0; i < coords.length; i++){\n        var x = coords[i][0], y = mercY(coords[i][1]);\n        this.xy[p++] = x; this.xy[p++] = y;\n        if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y;\n      }\n      this.off[this.lines * 2] = this.n; this.off[this.lines * 2 + 1] = p;\n      var b = this.lines * 4; this.bb[b] = x0; this.bb[b + 1] = y0; this.bb[b + 2] = x1; this.bb[b + 3] = y1;\n      this.n = p; this.lines++;\n      if (x0 < ext[0]) ext[0] = x0; if (x1 > ext[2]) ext[2] = x1; if (y0 < ext[1]) ext[1] = y0; if (y1 > ext[3]) ext[3] = y1;\n    };\n    var geo = {}; ORDER.forEach(function(k){ geo[k] = new Layer(); });\n    var ext = [180, 90, -180, -90];\n    var view = null, cv = null, ctx = null, W = 0, H = 0, dpr = 1, raf = 0, userMoved = false;\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n    function mb(n){ return (n / 1048576).toFixed(n > 1e8 ? 0 : 1) + ' MB'; }\n\n    function fit(){\n      if (!W || ext[0] > ext[2]) return;\n      var k = Math.min(W / (ext[2] - ext[0]), H / (ext[3] - ext[1])) * 0.92;\n      view = { k: k, cx: (ext[0] + ext[2]) / 2, cy: (ext[1] + ext[3]) / 2 };\n    }\n\n    function draw(){\n      raf = 0;\n      if (!ctx || !view) return;\n      var t0 = performance.now();\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var vx0 = -ox / k, vx1 = (W - ox) / k, vy0 = (oy - H) / k, vy1 = oy / k;\n      var minSpan = 0.6 / k;          // linha menor que ~0,6 px inteira: não desenha\n      var drawn = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      ORDER.forEach(function(key){\n        if (!on[key]) return;\n        var L = geo[key], xy = L.xy, off = L.off, bb = L.bb;\n        ctx.beginPath();\n        ctx.strokeStyle = STYLE[key].color;\n        ctx.lineWidth = STYLE[key].w * Math.max(1, Math.min(3, Math.log2(k / 40 + 1)));\n        for (var j = 0; j < L.lines; j++){\n          var b = j * 4;\n          if (bb[b + 2] < vx0 || bb[b] > vx1 || bb[b + 3] < vy0 || bb[b + 1] > vy1) continue;\n          if (bb[b + 2] - bb[b] < minSpan && bb[b + 3] - bb[b + 1] < minSpan) continue;\n          var s = off[j * 2], e = off[j * 2 + 1];\n          var lx = xy[s] * k + ox, ly = oy - xy[s + 1] * k;\n          ctx.moveTo(lx, ly); drawn++;\n          for (var i = s + 2; i < e; i += 2){\n            var px = xy[i] * k + ox, py = oy - xy[i + 1] * k;\n            // descarta vértices a menos de ~0,7 px do último desenhado (exceto o último da linha)\n            if (i < e - 2 && Math.abs(px - lx) + Math.abs(py - ly) < 0.7) continue;\n            ctx.lineTo(px, py); lx = px; ly = py; drawn++;\n          }\n        }\n        ctx.stroke();\n      });\n      txt('[data-pts]', d2.fmt.number(drawn));\n      txt('[data-ms]', Math.round(performance.now() - t0) + ' ms');\n      if (performance.memory) txt('[data-heap]', mb(performance.memory.usedJSHeapSize));\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function resize(){\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1; W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view) fit();\n      schedule();\n    }\n\n    cv = d2.$('[data-cv]'); ctx = cv.getContext('2d');\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    var aborts = [];\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); aborts.forEach(function(a){ a.abort(); }); });\n\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; userMoved = true; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){\n        view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k;\n        view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k;\n      } else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]];\n        var d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(500000, view.k * d / last));\n        last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault(); userMoved = true;\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top;\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var gx = (mx - ox) / k, gy = (oy - my) / k;\n      var nk = Math.max(5, Math.min(500000, k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule();\n    }, { passive: false });\n    d2.on('[data-layer]', 'change', function(e, el){ on[el.getAttribute('data-layer')] = el.checked; schedule(); });\n    d2.on('[data-reset]', 'click', function(){ fit(); userMoved = false; schedule(); });\n\n    function stats(){\n      var pts = 0, bytes = 0;\n      ORDER.forEach(function(k){ var L = geo[k]; pts += L.n / 2; bytes += L.xy.byteLength + L.off.byteLength + L.bb.byteLength; txt('[data-n=\"' + k + '\"]', d2.fmt.number(L.feats)); });\n      txt('[data-loaded]', d2.fmt.number(pts)); txt('[data-mem]', mb(bytes));\n      if (performance.memory) txt('[data-heap]', mb(performance.memory.usedJSHeapSize));\n    }\n\n    // Streaming: lê o corpo aos pedaços, separa por linha, parseia 1 feição por vez.\n    function load(name, url){\n      var cell = d2.$('[data-f=\"' + name + '\"]'), bar = d2.$('[data-bar=\"' + name + '\"]');\n      if (!url){ cell.textContent = 'parâmetro vazio'; cell.className = 'err'; return; }\n      var ac = new AbortController(); aborts.push(ac);\n      var t0 = performance.now(), got = 0, total = 0, feats = 0, parseMs = 0, tail = '', lastUi = 0, lastDraw = 0;\n      var dec = new TextDecoder();\n      function eat(chunk, final){\n        var t = performance.now();\n        var s = tail + chunk, from = 0, nl;\n        while ((nl = s.indexOf('\\n', from)) >= 0){\n          if (nl > from){\n            var f = JSON.parse(s.slice(from, nl));\n            var g = f.geometry, key = name === 'rios' ? 'river' : (f.properties && f.properties.highway);\n            var L = geo[key];\n            if (g && L){\n              if (g.type === 'MultiLineString') g.coordinates.forEach(function(c){ L.add(c); }); else L.add(g.coordinates);\n              L.feats++;\n            }\n            feats++;\n          }\n          from = nl + 1;\n        }\n        tail = s.slice(from);\n        if (final && tail.trim()){ eat('\\n', false); }\n        parseMs += performance.now() - t;\n      }\n      function ui(done){\n        var el = (performance.now() - t0) / 1000;\n        cell.className = '';\n        cell.textContent = mb(got) + (total ? ' / ' + mb(total) : '') + ' · ' + d2.fmt.number(feats) + ' feições · ' + el.toFixed(1) + ' s' + (done ? ' (parse ' + (parseMs / 1000).toFixed(1) + ' s)' : '');\n        if (total) bar.style.width = (100 * got / total).toFixed(1) + '%';\n        stats();\n      }\n      fetch(url, { signal: ac.signal }).then(function(res){\n        if (!res.ok) throw new Error('HTTP ' + res.status);\n        total = +res.headers.get('content-length') || 0;\n        var reader = res.body.getReader();\n        function pump(){\n          return reader.read().then(function(r){\n            if (r.done){ eat(dec.decode(), true); ui(true); if (!userMoved) fit(); schedule(); return; }\n            got += r.value.byteLength;\n            eat(dec.decode(r.value, { stream: true }), false);\n            var now = performance.now();\n            if (now - lastUi > 250){ lastUi = now; ui(false); }\n            if (now - lastDraw > 1500){ lastDraw = now; if (!userMoved) fit(); schedule(); }\n            return pump();\n          });\n        }\n        return pump();\n      }).catch(function(e){ if (e && e.name === 'AbortError') return; cell.textContent = (e && e.message) || String(e); cell.className = 'err'; });\n    }\n    load('rodovias', FILES.rodovias);\n    load('rios', FILES.rios);\n  }\n};"}},"rodoviasUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rodoviasUrl"}}}},"riosUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"riosUrl"}}}}}}}}}},{"_id":"6ab4954aab5404c7a0314368","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Brasil_por_quadricula_Range","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--motorway:#7a1f0e;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f;--tertiary:#b9b6ad}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:340px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:2px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0;vertical-align:top}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .bar{height:4px;background:var(--line);border-radius:2px;margin:4px 0 2px;overflow:hidden}\n  .bar b{display:block;height:100%;width:0;background:var(--ink)}\n  .status{margin:0 0 8px;font-size:12px;padding:6px 8px;border-radius:6px;background:var(--bg)}\n  .status.warn{background:#fff4e5;color:#8a4b00}\n  .log{margin-top:8px;font:11px/1.45 ui-monospace,Menlo,monospace;color:var(--mut);max-height:110px;overflow:auto;white-space:pre}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px;margin-right:6px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}.log{display:none}}\n  `,\n  render(){\n    var LAYERS = [['motorway','Autoestrada'],['trunk','Rodovia expressa'],['primary','Rodovia principal'],['secondary','Rodovia secundária'],['tertiary','Rodovia terciária'],['river','Rios']];\n    var rows = LAYERS.map(function(l){\n      return '<label><input type=\"checkbox\" data-layer=\"' + l[0] + '\" checked><i style=\"background:var(--' + l[0] + ')\"></i>' + l[1] + '</label>';\n    }).join('');\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Brasil · leitura por quadrícula</h1><p class=\"sub\">1 arquivo no Drive + índice na table; baixa só os bytes da área visível (HTTP Range)</p>'\n      + '<div class=\"status\" data-status>carregando índice…</div>'\n      + rows\n      + '<div class=\"sep\"></div><table>'\n      + '<tr><th colspan=\"2\">Arquivo</th></tr>'\n      + '<tr><td>tamanho total</td><td data-total>…</td></tr>'\n      + '<tr><td>índice</td><td data-idx>…</td></tr>'\n      + '<tr><td>visão geral</td><td data-ov>…</td></tr>'\n      + '<tr><th colspan=\"2\">Área visível</th></tr>'\n      + '<tr><td>quadrículas na tela</td><td data-vis>…</td></tr>'\n      + '<tr><th colspan=\"2\">Baixado até agora</th></tr>'\n      + '<tr><td>quadrículas em memória</td><td data-loaded>0</td></tr>'\n      + '<tr><td>requisições Range</td><td data-reqs>0</td></tr>'\n      + '<tr><td>bytes baixados</td><td data-got>0</td></tr>'\n      + '<tr><td colspan=\"2\"><div class=\"bar\"><b data-bar></b></div></td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr>'\n      + '</table>'\n      + '<div class=\"log\" data-log></div>'\n      + '<button data-reset>Brasil inteiro</button><button data-sp>São Paulo</button><button data-bel>Belém</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom · quadrados = quadrículas do arquivo</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var IDX_URL = d2.param('indiceUrl', ''), DATA_URL = d2.param('arquivoUrl', ''), OV_URL = d2.param('visaoGeralUrl', '');\n    var START = String(d2.param('inicio', '') || '');       // \"lon,lat,larguraGraus\" opcional\n    var BUDGET = 48 * 1024 * 1024;   // teto de detalhe na tela: carrega do centro para as bordas até aqui\n    var CACHE = 160 * 1024 * 1024;   // acima disso, descarta quadrículas fora da tela\n    var GAP = 512 * 1024, MAXREQ = 16 * 1024 * 1024, PARALLEL = 4;\n    var STYLE = {\n      river: { color: '#2f7fd8', w: 0.7 }, tertiary: { color: '#b9b6ad', w: 0.6 }, secondary: { color: '#8a877f', w: 0.8 },\n      primary: { color: '#e0822b', w: 1.1 }, trunk: { color: '#c2410c', w: 1.6 }, motorway: { color: '#7a1f0e', w: 2.0 }\n    };\n    var ORDER = ['river', 'tertiary', 'secondary', 'primary', 'trunk', 'motorway'];\n    var on = {}; ORDER.forEach(function(k){ on[k] = true; });\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n    function mb(n){ return n >= 1048576 ? (n / 1048576).toFixed(n > 1e8 ? 0 : 1) + ' MB' : Math.round(n / 1024) + ' KB'; }\n    function log(s){ var el = d2.$('[data-log]'); if (!el) return; el.textContent = (s + '\\n' + el.textContent).split('\\n').slice(0, 30).join('\\n'); }\n    function status(s, warn){ var el = d2.$('[data-status]'); if (el){ el.textContent = s; el.className = 'status' + (warn ? ' warn' : ''); } }\n\n    function Layer(){ this.xy = new Float32Array(1024); this.n = 0; this.off = new Uint32Array(64); this.bb = new Float32Array(128); this.lines = 0; }\n    Layer.prototype.add = function(c){\n      var need = this.n + c.length * 2;\n      if (need > this.xy.length){ var nx = new Float32Array(Math.max(need, this.xy.length * 2)); nx.set(this.xy.subarray(0, this.n)); this.xy = nx; }\n      if ((this.lines + 1) * 2 > this.off.length){ var no = new Uint32Array(this.off.length * 2); no.set(this.off); this.off = no; }\n      if ((this.lines + 1) * 4 > this.bb.length){ var nb = new Float32Array(this.bb.length * 2); nb.set(this.bb); this.bb = nb; }\n      var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, p = this.n;\n      for (var i = 0; i < c.length; i++){\n        var x = c[i][0], y = mercY(c[i][1]); this.xy[p++] = x; this.xy[p++] = y;\n        if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y;\n      }\n      this.off[this.lines * 2] = this.n; this.off[this.lines * 2 + 1] = p;\n      var b = this.lines * 4; this.bb[b] = x0; this.bb[b + 1] = y0; this.bb[b + 2] = x1; this.bb[b + 3] = y1;\n      this.n = p; this.lines++;\n    };\n\n    var tiles = [], total = 0, got = 0, reqs = 0, memBytes = 0;\n    var view = null, cv = d2.$('[data-cv]'), ctx = cv.getContext('2d'), W = 0, H = 0, dpr = 1, raf = 0, planT = 0, inflight = 0, queue = [];\n    var ext = [180, 90, -180, -90];\n    var overview = null;   // camadas leves do Brasil inteiro, desenhadas onde não há quadrícula carregada\n\n    function fitTo(x0, y0, x1, y1){\n      if (!W) return;\n      var k = Math.min(W / (x1 - x0), H / (y1 - y0)) * 0.92;\n      view = { k: k, cx: (x0 + x1) / 2, cy: (y0 + y1) / 2 };\n    }\n    function goTo(lon, lat, spanDeg){ var y = mercY(lat); fitTo(lon - spanDeg / 2, y - spanDeg / 2, lon + spanDeg / 2, y + spanDeg / 2); schedule(); plan(); }\n    function fitAll(){ fitTo(ext[0], ext[1], ext[2], ext[3]); schedule(); plan(); }\n\n    function visibleWin(){\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      return { k: k, ox: ox, oy: oy, x0: -ox / k, x1: (W - ox) / k, y0: (oy - H) / k, y1: oy / k };\n    }\n    function inView(t, v){ return !(t.mb[2] < v.x0 || t.mb[0] > v.x1 || t.mb[3] < v.y0 || t.mb[1] > v.y1); }\n\n    function draw(){\n      raf = 0;\n      if (!view) return;\n      var t0 = performance.now(), v = visibleWin(), k = v.k, ox = v.ox, oy = v.oy;\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var vis = tiles.filter(function(t){ return inView(t, v); });\n      // grade: quadrículas não baixadas em cinza, baixando em âmbar\n      vis.forEach(function(t){\n        var x = t.cell[0] * k + ox, y = oy - t.cell[3] * k, w = (t.cell[2] - t.cell[0]) * k, h = (t.cell[3] - t.cell[1]) * k;\n        if (t.state === 'loading'){ ctx.fillStyle = 'rgba(224,130,43,.14)'; ctx.fillRect(x, y, w, h); }\n        else if (t.state !== 'loaded'){ ctx.fillStyle = 'rgba(29,29,27,' + Math.min(0.10, 0.02 + t.len / 4194304 * 0.08).toFixed(3) + ')'; ctx.fillRect(x, y, w, h); }\n        ctx.strokeStyle = 'rgba(29,29,27,.18)'; ctx.lineWidth = 1; ctx.strokeRect(x + 0.5, y + 0.5, w, h);\n        if (t.state !== 'loaded' && w > 46 && h > 18){ ctx.fillStyle = 'rgba(29,29,27,.55)'; ctx.font = '10px -apple-system,sans-serif'; ctx.fillText(mb(t.len), x + 4, y + 13); }\n      });\n      var minSpan = 0.6 / k, drawn = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      var lw = function(key){ return STYLE[key].w * Math.max(1, Math.min(2.2, Math.log2(k / 40 + 1))); };\n      // 1) visão geral só onde a quadrícula ainda não chegou (clip), para a tela nunca ficar vazia\n      var pending = vis.filter(function(t){ return t.state !== 'loaded'; });\n      if (overview && pending.length){\n        ctx.save(); ctx.beginPath();\n        pending.forEach(function(t){ ctx.rect(t.cell[0] * k + ox, oy - t.cell[3] * k, (t.cell[2] - t.cell[0]) * k, (t.cell[3] - t.cell[1]) * k); });\n        ctx.clip();\n        ORDER.forEach(function(key){\n          var L = overview[key]; if (!on[key] || !L) return;\n          ctx.beginPath(); ctx.strokeStyle = STYLE[key].color; ctx.lineWidth = lw(key);\n          drawn += lines(L);\n          ctx.stroke();\n        });\n        ctx.restore();\n      }\n      function lines(L){\n        var xy = L.xy, off = L.off, bb = L.bb, c = 0;\n        for (var j = 0; j < L.lines; j++){\n          var b = j * 4;\n          if (bb[b + 2] < v.x0 || bb[b] > v.x1 || bb[b + 3] < v.y0 || bb[b + 1] > v.y1) continue;\n          if (bb[b + 2] - bb[b] < minSpan && bb[b + 3] - bb[b + 1] < minSpan) continue;\n          var s = off[j * 2], e = off[j * 2 + 1], lx = xy[s] * k + ox, ly = oy - xy[s + 1] * k;\n          ctx.moveTo(lx, ly); c++;\n          for (var i = s + 2; i < e; i += 2){\n            var px = xy[i] * k + ox, py = oy - xy[i + 1] * k;\n            if (i < e - 2 && Math.abs(px - lx) + Math.abs(py - ly) < 0.7) continue;\n            ctx.lineTo(px, py); lx = px; ly = py; c++;\n          }\n        }\n        return c;\n      }\n      // 2) detalhe completo das quadrículas carregadas\n      ORDER.forEach(function(key){\n        if (!on[key]) return;\n        ctx.beginPath(); ctx.strokeStyle = STYLE[key].color; ctx.lineWidth = lw(key);\n        vis.forEach(function(t){\n          if (t.state !== 'loaded') return;\n          var L = t.geo[key]; if (!L) return;\n          drawn += lines(L);\n        });\n        ctx.stroke();\n      });\n      txt('[data-ms]', Math.round(performance.now() - t0) + ' ms · ' + d2.fmt.number(drawn) + ' pts');\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function stats(){\n      txt('[data-loaded]', tiles.filter(function(t){ return t.state === 'loaded'; }).length + ' (' + mb(memBytes) + ')');\n      txt('[data-reqs]', d2.fmt.number(reqs));\n      txt('[data-got]', mb(got) + ' de ' + mb(total) + ' (' + (100 * got / (total || 1)).toFixed(2) + '%)');\n      var bar = d2.$('[data-bar]'); if (bar) bar.style.width = Math.min(100, 100 * got / (total || 1)) + '%';\n    }\n\n    // decide o que baixar para a área visível\n    function plan(){\n      clearTimeout(planT);\n      planT = setTimeout(function(){\n        if (!view || !tiles.length) return;\n        var v = visibleWin();\n        var vis = tiles.filter(function(t){ return inView(t, v); });\n        var visBytes = vis.reduce(function(a, t){ return a + t.len; }, 0);\n        txt('[data-vis]', vis.length + ' · ' + mb(visBytes));\n        // mais perto do centro primeiro; soma o que já está na tela e para no limite\n        var cx = view.cx, cy = view.cy;\n        var dist = function(t){ var x = (t.cell[0] + t.cell[2]) / 2 - cx, y = (t.cell[1] + t.cell[3]) / 2 - cy; return x * x + y * y; };\n        var used = vis.filter(function(t){ return t.state; }).reduce(function(a, t){ return a + t.len; }, 0);\n        var need = [], skipped = 0;\n        vis.filter(function(t){ return !t.state; }).sort(function(a, b){ return dist(a) - dist(b); }).forEach(function(t){\n          if (used + t.len <= BUDGET){ need.push(t); used += t.len; } else skipped++;\n        });\n        var partial = skipped ? ' · ' + skipped + ' quadrículas nas bordas ficam na visão geral (aproxime para detalhar)' : '';\n        if (!need.length){ status((inflight ? 'baixando…' : skipped ? 'detalhe carregado no centro' : 'área visível completa') + partial, !!skipped && !inflight); return; }\n        evict(v);\n        // junta quadrículas contíguas no arquivo em uma requisição só\n        need.sort(function(a, b){ return a.off - b.off; });\n        var groups = [], g = null;\n        need.forEach(function(t){\n          if (g && t.off - g.end <= GAP && t.off + t.len - g.start <= MAXREQ){ g.tiles.push(t); g.end = t.off + t.len; }\n          else { g = { start: t.off, end: t.off + t.len, tiles: [t] }; groups.push(g); }\n        });\n        groups.forEach(function(gr){ gr.tiles.forEach(function(t){ t.state = 'loading'; }); queue.push(gr); });\n        status('baixando ' + need.length + ' quadrículas em ' + groups.length + ' requisições…' + partial);\n        pump(); schedule();\n      }, 250);\n    }\n\n    function evict(v){\n      if (memBytes <= CACHE) return;\n      tiles.filter(function(t){ return t.state === 'loaded' && !inView(t, v); })\n        .sort(function(a, b){ return a.used - b.used; })\n        .some(function(t){ memBytes -= t.mem; t.geo = null; t.state = null; t.mem = 0; return memBytes <= CACHE * 0.7; });\n    }\n\n    function pump(){\n      while (inflight < PARALLEL && queue.length){\n        (function(gr){\n          inflight++; reqs++;\n          var t0 = performance.now();\n          fetch(DATA_URL, { headers: { Range: 'bytes=' + gr.start + '-' + (gr.end - 1) } })\n            .then(function(r){\n              if (r.status !== 206 && r.status !== 200) throw new Error('HTTP ' + r.status);\n              if (r.status === 200) throw new Error('servidor ignorou Range (200)');\n              return r.arrayBuffer();\n            })\n            .then(function(buf){\n              var ms = Math.round(performance.now() - t0);\n              got += buf.byteLength;\n              var dec = new TextDecoder(), tp = performance.now();\n              gr.tiles.forEach(function(t){\n                var text = dec.decode(new Uint8Array(buf, t.off - gr.start, t.len));\n                var geo = {}, mem = 0, s = 0, nl;\n                while ((nl = text.indexOf('\\n', s)) >= 0){\n                  if (nl > s){ var f = JSON.parse(text.slice(s, nl)); (geo[f.k] || (geo[f.k] = new Layer())).add(f.c); }\n                  s = nl + 1;\n                }\n                Object.keys(geo).forEach(function(kk){ var L = geo[kk]; mem += L.xy.byteLength + L.off.byteLength + L.bb.byteLength; });\n                t.geo = geo; t.mem = mem; t.state = 'loaded'; t.used = performance.now(); memBytes += mem;\n              });\n              log('bytes=' + gr.start + '-' + (gr.end - 1) + '  ' + mb(buf.byteLength) + '  ' + gr.tiles.length + ' quadr.  ' + ms + ' ms rede + ' + Math.round(performance.now() - tp) + ' ms parse');\n            })\n            .catch(function(e){ gr.tiles.forEach(function(t){ t.state = null; }); log('erro: ' + ((e && e.message) || e)); status('erro ao baixar: ' + ((e && e.message) || e), true); })\n            .then(function(){ inflight--; stats(); schedule(); if (!queue.length && !inflight) plan(); pump(); });\n        })(queue.shift());\n      }\n    }\n\n    function resize(){\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1; W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view && tiles.length) start();\n      schedule();\n    }\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); clearTimeout(planT); queue.length = 0; });\n\n    function start(){\n      var p = START.split(',').map(Number);\n      if (p.length === 3 && p.every(isFinite)) goTo(p[0], p[1], p[2]); else fitAll();\n    }\n\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){ view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k; view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k; }\n      else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]], d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(500000, view.k * d / last)); last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule(); plan();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault();\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top, v = visibleWin();\n      var gx = (mx - v.ox) / v.k, gy = (v.oy - my) / v.k, nk = Math.max(5, Math.min(500000, v.k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule(); plan();\n    }, { passive: false });\n    d2.on('[data-layer]', 'change', function(e, el){ on[el.getAttribute('data-layer')] = el.checked; schedule(); });\n    d2.on('[data-reset]', 'click', fitAll);\n    d2.on('[data-sp]', 'click', function(){ goTo(-46.63, -23.55, 0.9); });\n    d2.on('[data-bel]', 'click', function(){ goTo(-48.48, -1.40, 1.2); });\n\n    var IDX_DS = d2.data('indice');\n    if (!IDX_DS && (!IDX_URL || !DATA_URL)){ status('parâmetros indice (table) ou indiceUrl/arquivoUrl vazios', true); return; }\n    if (OV_URL){\n      var to = performance.now();\n      fetch(OV_URL).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }).then(function(body){\n        var tp = performance.now(), lines = body.split('\\n'), q = JSON.parse(lines[0]).q, ov = {}, n = 0;\n        for (var i = 1; i < lines.length; i++){\n          if (!lines[i]) continue;\n          var f = JSON.parse(lines[i]), d = f.c, c = [], x = 0, y = 0;\n          for (var j = 0; j < d.length; j += 2){ x += d[j]; y += d[j + 1]; c.push([x * q, y * q]); }\n          (ov[f.k] || (ov[f.k] = new Layer())).add(c); n++;\n        }\n        overview = ov;\n        txt('[data-ov]', d2.fmt.number(n) + ' trechos · ' + Math.round(tp - to) + ' ms + ' + Math.round(performance.now() - tp) + ' ms');\n        schedule();\n      }).catch(function(e){ txt('[data-ov]', 'erro: ' + ((e && e.message) || e)); });\n    } else txt('[data-ov]', '—');\n    var ti = performance.now();\n    // índice: da table (coluna arquivo = file) quando o parâmetro indice vier; senão, do JSON no Drive\n    // loadAll() não faz nada se a busca inicial do dataset ainda está em andamento: espera todas as linhas chegarem\n    function waitAll(ds){\n      return new Promise(function(res, rej){\n        var t0 = Date.now();\n        (function tick(){\n          var n = ds.records().length, tot = (ds.pageInfo() || {}).total || 0, busy = ds.fetching || ds.mutating;\n          if (!busy && n && n >= tot) return res(ds.records());\n          if (!busy && n < tot) ds.loadAll();\n          if (Date.now() - t0 > 30000) return rej(new Error('tempo esgotado lendo a table (' + n + '/' + tot + ')'));\n          setTimeout(tick, 150);\n        })();\n      });\n    }\n    var loadIndex = IDX_DS\n      ? waitAll(IDX_DS).then(function(rows){\n          rows = rows.slice().sort(function(a, b){ return a.ordem - b.ordem; });\n          if (rows[0] && rows[0].arquivo && rows[0].arquivo.fileUrl) DATA_URL = rows[0].arquivo.fileUrl;\n          var tot = rows.reduce(function(s, r){ return s + r.bytes; }, 0);\n          txt('[data-idx]', 'table · ' + rows.length + ' linhas em ' + Math.round(performance.now() - ti) + ' ms');\n          return { total: tot, fromTable: true, tiles: rows.map(function(r){ return { off: r.byteInicio, len: r.bytes, n: r.feicoes, bb: [r.lonMin, r.latMin, r.lonMax, r.latMax], cell: [r.celulaLon, r.celulaLat, r.celulaGraus] }; }) };\n        })\n      : fetch(IDX_URL).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); });\n    loadIndex.then(function(idx){\n      total = idx.total;\n      tiles = idx.tiles.map(function(t){\n        var c = t.cell, mbx = [t.bb[0], mercY(t.bb[1]), t.bb[2], mercY(t.bb[3])];\n        if (mbx[0] < ext[0]) ext[0] = mbx[0]; if (mbx[2] > ext[2]) ext[2] = mbx[2]; if (mbx[1] < ext[1]) ext[1] = mbx[1]; if (mbx[3] > ext[3]) ext[3] = mbx[3];\n        return { off: t.off, len: t.len, n: t.n, mb: mbx, cell: [c[0], mercY(c[1]), c[0] + c[2], mercY(c[1] + c[2])], state: null, geo: null, mem: 0, used: 0 };\n      });\n      txt('[data-total]', mb(total) + ' · ' + tiles.length + ' quadrículas');\n      if (!idx.fromTable) txt('[data-idx]', mb(JSON.stringify(idx).length) + ' em ' + Math.round(performance.now() - ti) + ' ms');\n      stats();\n      if (W) start();\n    }).catch(function(e){ status('falha ao ler índice: ' + ((e && e.message) || e), true); });\n  }\n};"}},"indice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"indice"}}}},"indiceUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"indiceUrl"}}}},"arquivoUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivoUrl"}}}},"visaoGeralUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"visaoGeralUrl"}}}},"inicio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"inicio"}}}}}}}}}},{"_id":"6ab49e7b5e1ce1556917cdd4","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Doc_OSM_sem_Overpass","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--w:#ffffff;--g:#f5f5f7;--ink:#1d1d1f;--mut:#6e6e73;--line:#d2d2d7;--rust:#c2410c;--moss:#3f7d20}\n  *{box-sizing:border-box}\n  .doc{height:100%;overflow:auto;background:var(--w);color:var(--ink);font:17px/1.47 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Helvetica Neue\",Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}\n  nav{position:sticky;top:0;z-index:5;background:rgba(255,255,255,.86);backdrop-filter:saturate(180%) blur(16px);-webkit-backdrop-filter:saturate(180%) blur(16px);border-bottom:1px solid rgba(0,0,0,.08)}\n  nav .in{max-width:1024px;margin:0 auto;height:48px;padding:0 22px;display:flex;align-items:center;gap:22px;font-size:12px}\n  nav .t{font-size:17px;font-weight:600;letter-spacing:-.02em;margin-right:auto}\n  nav a{color:var(--ink);opacity:.8;cursor:pointer}\n  nav a:hover{opacity:1;color:var(--rust)}\n  .band{padding:110px 22px 96px}\n  .band.g{background:var(--g)}\n  .in{max-width:1024px;margin:0 auto}\n  .c{text-align:center}\n  .eyebrow{font-size:17px;font-weight:600;color:var(--rust);margin:0 0 6px}\n  h1{font-size:72px;line-height:1.05;letter-spacing:-.025em;font-weight:700;margin:0 auto 14px;max-width:880px}\n  h2{font-size:48px;line-height:1.08;letter-spacing:-.02em;font-weight:700;margin:0 auto 14px;max-width:820px}\n  .sub{font-size:21px;line-height:1.38;color:var(--mut);max-width:680px;margin:0 auto;font-weight:400;letter-spacing:.004em}\n  .sub b{color:var(--ink);font-weight:600}\n  .stage{position:relative;margin:56px auto 0;max-width:1024px}\n  .stage canvas{display:block;width:100%}\n  .stage.wide{max-width:none;width:min(1300px,calc(100vw - 44px));left:50%;transform:translateX(-50%)}\n  .cap{font-size:14px;color:var(--mut);text-align:center;margin:14px auto 0;max-width:620px}\n  .hud{position:absolute;left:0;top:0;font:13px/1.5 -apple-system,BlinkMacSystemFont,sans-serif;color:var(--mut);pointer-events:none;text-align:left}\n  .hud b{color:var(--ink);font-weight:600}\n  .hud.big{font-size:15px}\n  .tip{position:absolute;pointer-events:none;background:rgba(29,29,31,.94);color:#f5f5f7;border-radius:12px;padding:10px 13px;font:12px/1.55 ui-monospace,Menlo,monospace;white-space:nowrap;transform:translate(-50%,calc(-100% - 16px));opacity:0;transition:opacity .15s;text-align:left}\n  .tip.on{opacity:1}\n  .tip b{font:600 13px -apple-system,sans-serif;color:#fff}\n  .tip span{color:#a1a1a6}\n  .ctrl{display:flex;justify-content:center;gap:4px;margin:26px auto 0;background:#e8e8ed;border-radius:99px;padding:4px;width:max-content}\n  .ctrl button{font:14px -apple-system,sans-serif;border:0;background:transparent;border-radius:99px;padding:7px 16px;cursor:pointer;color:var(--ink)}\n  .ctrl button.on{background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12)}\n  .reqlog{position:absolute;left:0;bottom:6px;font:12px/1.7 ui-monospace,Menlo,monospace;color:var(--mut);pointer-events:none;text-align:left}\n  .reqlog div{opacity:0;transform:translateY(4px);transition:all .3s}\n  .reqlog div.in{opacity:1;transform:none}\n  .stats{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin:64px auto 0;max-width:900px;text-align:center}\n  .stats b{display:block;font-size:56px;line-height:1.05;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n  .stats span{display:block;font-size:15px;color:var(--mut);margin-top:6px}\n  .stats .rust b{color:var(--rust)}\n  .versus{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:900px;margin:56px auto 0}\n  .versus > div{background:var(--w);border-radius:22px;padding:34px 30px;text-align:center}\n  .band:not(.g) .versus > div{background:var(--g)}\n  .versus small{display:block;font-size:14px;color:var(--mut);font-weight:600}\n  .versus b{display:block;font-size:64px;line-height:1.05;font-weight:700;letter-spacing:-.025em;margin:8px 0 4px}\n  .versus span{font-size:15px;color:var(--mut)}\n  .versus .hi b{color:var(--rust)}\n  .tiles{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:1024px;margin:56px auto 0}\n  .tile{background:var(--g);border-radius:22px;padding:34px 32px}\n  .band.g .tile{background:var(--w)}\n  .tile h3{font-size:24px;line-height:1.17;letter-spacing:-.01em;margin:0 0 10px;font-weight:700}\n  .tile p{font-size:16px;color:var(--mut);margin:0}\n  .tile .n{font-size:40px;font-weight:700;letter-spacing:-.02em;color:var(--ink);margin-bottom:8px;font-variant-numeric:tabular-nums}\n  code,.mono{font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}\n  code{color:var(--ink)}\n  .specs{max-width:1024px;margin:56px auto 0}\n  .spec{display:grid;grid-template-columns:240px 1fr;gap:24px;padding:22px 0;border-top:1px solid var(--line)}\n  .spec h4{margin:0;font-size:17px;font-weight:600}\n  .spec .v{font-size:15px;color:var(--ink)}\n  .spec .v .note{color:var(--mut)}\n  table{width:100%;border-collapse:collapse;font-size:14px;font-variant-numeric:tabular-nums}\n  td{padding:9px 12px 9px 0;border-bottom:1px solid #e8e8ed;vertical-align:top}\n  td.r{text-align:right;padding-right:0}\n  th{text-align:left;font-size:12px;color:var(--mut);font-weight:600;padding:0 12px 8px 0}\n  th.r{text-align:right;padding-right:0}\n  .type{font:12px ui-monospace,Menlo,monospace;color:var(--mut)}\n  .type.file{color:var(--rust);font-weight:600}\n  .filechip{color:#0066cc;text-decoration:none;font-size:14px;white-space:nowrap}\n  .filechip:hover{text-decoration:underline}\n  .filechip i{display:none}\n  .note{font-size:13px;color:var(--mut)}\n  .foot{background:var(--g);padding:26px 22px 60px;font-size:12px;color:var(--mut);border-top:1px solid var(--line)}\n  @media (max-width:820px){\n    h1{font-size:44px} h2{font-size:32px} .sub{font-size:18px} .band{padding:72px 18px 64px}\n    .stats{grid-template-columns:1fr} .stats b{font-size:44px} .versus,.tiles{grid-template-columns:1fr} .versus b{font-size:48px}\n    .spec{grid-template-columns:1fr;gap:8px} .reqlog,.hide-sm{display:none} nav a{display:none}\n  }\n  `,\n  render(){\n    return '<div class=\"doc\" data-doc>'\n      + '<nav><div class=\"in\"><span class=\"t\">OSM sem Overpass</span><a data-go=\"s1\">Hoje</a><a data-go=\"s2\">Arquivo</a><a data-go=\"s3\">Índice</a><a data-go=\"s4\">Leitura</a><a data-go=\"s4b\">Exemplo</a><a data-go=\"s5\">Medições</a><a data-go=\"s7\">Especificações</a></div></nav>'\n\n      + '<div class=\"band\"><div class=\"in c\">'\n      + '<p class=\"eyebrow\">Estudo de dados · natura-geomarketing</p>'\n      + '<h1>Rodovias e rios do Brasil, lidos direto do Drive.</h1>'\n      + '<p class=\"sub\">Um arquivo de 600 MB e uma table de 884 linhas. O mapa baixa só o pedaço que está na tela.</p>'\n      + '<div class=\"stage\" data-st=\"hero\"><canvas data-cv=\"hero\" height=\"600\"></canvas><div class=\"hud\" data-hud=\"hero\">lendo a table…</div><div class=\"tip\" data-tip=\"hero\"></div></div>'\n      + '<p class=\"cap\">Cada bloco é uma linha da table. A altura é o tamanho da quadrícula no arquivo. Passe o mouse.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s1\"><div class=\"in c\">'\n      + '<h2>Hoje, cada etapa espera o Overpass.</h2>'\n      + '<p class=\"sub\">O painel Camadas consulta o Overpass, do OpenStreetMap. Quando ele está cheio, responde 504 e o simulador tenta de novo, sem limite.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"today\" height=\"260\"></canvas><div class=\"hud\" data-hud=\"today\"></div></div>'\n      + '<div class=\"stats\"><div class=\"rust\"><b>1.157</b><span>chamadas à Rush osmOverpassProxy</span></div><div class=\"rust\"><b>504</b><span>resposta da maioria das chamadas de 14/09</span></div><div><b>0</b><span>cálculos de fechamento que usam essa geometria</span></div></div>'\n      + '</div></div>'\n\n      + '<div class=\"band\" id=\"s2\"><div class=\"in c\">'\n      + '<h2>O Brasil inteiro, filtrado e separado por área.</h2>'\n      + '<p class=\"sub\">Partimos dos <b>2,1 GB</b> do OpenStreetMap. Ficam rodovias e rios, em quadrículas de até 4 MB gravadas em ordem Z. Vizinhas no mapa ficam vizinhas no arquivo.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"pipe\" height=\"280\"></canvas></div>'\n      + '<p class=\"cap\">Volume de cada cubo proporcional ao tamanho em bytes.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s3\"><div class=\"in c\">'\n      + '<h2>Uma table diz onde começa cada pedaço.</h2>'\n      + '<p class=\"sub\">Cada linha guarda o byte inicial e o tamanho de uma quadrícula. A coluna <b>arquivo</b> é do tipo file e aponta para o arquivo no Drive.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"strip\" height=\"130\"></canvas><div class=\"hud\" data-hud=\"strip\"></div></div>'\n      + '<p class=\"cap\">Os 600 MB de ponta a ponta, uma fatia por linha. Passe o mouse no mapa do topo e a fatia correspondente sobe aqui.</p>'\n      + '<div class=\"specs\" style=\"text-align:left\">'\n      + '<div class=\"spec\"><h4>Colunas</h4><div class=\"v\"><table><tbody>'\n      + '<tr><td class=\"mono\">arquivo</td><td><span class=\"type file\">file</span></td><td>osm-brasil-quadriculas.ndjson, geoDatasetsDrive</td></tr>'\n      + '<tr><td class=\"mono\">byteInicio, bytes</td><td><span class=\"type\">number</span></td><td>onde a quadrícula começa e quantos bytes ler</td></tr>'\n      + '<tr><td class=\"mono\">range</td><td><span class=\"type\">string</span></td><td>header pronto, bytes=início-fim</td></tr>'\n      + '<tr><td class=\"mono\">lonMin, latMin, lonMax, latMax</td><td><span class=\"type\">number</span></td><td>caixa das linhas, para saber se está na tela</td></tr>'\n      + '<tr><td class=\"mono\">celulaLon, celulaLat, celulaGraus, nivel</td><td><span class=\"type\">number</span></td><td>célula do quadtree</td></tr>'\n      + '<tr><td class=\"mono\">feicoes, rodovias, rios, ordem</td><td><span class=\"type\">number</span></td><td>contagens e posição no arquivo</td></tr>'\n      + '</tbody></table></div></div>'\n      + '<div class=\"spec\"><h4>Amostra</h4><div class=\"v\"><table><thead><tr><th>Quadrícula</th><th class=\"r\">Byte inicial</th><th class=\"r\">Tamanho</th><th class=\"hide-sm\" style=\"padding-left:28px\">Range</th></tr></thead><tbody data-sample><tr><td colspan=\"4\" class=\"note\">lendo…</td></tr></tbody></table></div></div>'\n      + '</div></div></div>'\n\n      + '<div class=\"band\" id=\"s4\"><div class=\"in c\">'\n      + '<h2>A tela pede só os bytes dela.</h2>'\n      + '<p class=\"sub\">O componente acha as quadrículas visíveis, carrega do centro para fora até 48 MB e junta as vizinhas numa requisição só.</p>'\n      + '<div class=\"ctrl\" data-ctrl></div>'\n      + '<div class=\"stage\" style=\"margin-top:28px\"><canvas data-cv=\"read\" height=\"440\"></canvas><div class=\"hud big\" data-hud=\"read\"></div><div class=\"reqlog\" data-reqlog></div></div>'\n      + '<p class=\"cap\">Grupos e tamanhos calculados a partir das linhas da table, com as regras do componente.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s4b\"><div class=\"in c\">'\n      + '<h2>São Paulo, montada camada por camada.</h2>'\n      + '<p class=\"sub\">Este mapa foi baixado agora, do mesmo arquivo, com Range. As 10 quadrículas da cidade estão em sequência no arquivo e chegam numa requisição só.</p>'\n      + '<div class=\"stage wide\"><canvas data-cv=\"layers\" height=\"900\"></canvas><div class=\"hud big\" data-hud=\"layers\"></div></div>'\n      + '<p class=\"cap\">Cada plano é uma camada desenhada com os dados baixados. Com os planos abertos, cada quadrícula acende junto com a linha dela na table e o trecho que ocupa no arquivo, na ordem em que foi gravada. <a class=\"filechip\" href=\"https://gui.data2.qa/preview/ebL6zNvLBCPbgjFylsmk6\" target=\"_blank\" rel=\"noopener\">Abrir o mapa interativo</a></p>'\n      + '</div></div>'\n\n      + '<div class=\"band\" id=\"s5\"><div class=\"in c\">'\n      + '<h2>De 742 MB para 20 MB.</h2>'\n      + '<p class=\"sub\">Medido no render de verificação, com São Paulo na tela.</p>'\n      + '<div class=\"versus\"><div><small>Arquivo inteiro em streaming</small><b>742 MB</b><span>17 s para baixar e ler · 600 MB de memória na aba</span></div><div class=\"hi\"><small>Por quadrícula</small><b>20 MB</b><span>12 quadrículas em 3 requisições · 3,4% do arquivo</span></div></div>'\n      + '<div class=\"stage\"><canvas data-cv=\"gzip\" height=\"440\"></canvas></div>'\n      + '<p class=\"cap\">O CloudFront comprime um arquivo lido inteiro só quando ele tem até 10 MB e é servido como application/json. Cada cubo tem volume proporcional aos bytes.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s6\"><div class=\"in c\">'\n      + '<h2>Quatro detalhes que definiram o formato.</h2>'\n      + '<div class=\"tiles\" style=\"text-align:left\">'\n      + '<div class=\"tile\"><div class=\"n\">Range</div><h3>Geometria em arquivo, índice em table.</h3><p>Desenhar sempre pede tudo de uma área, e um GET com Range entrega isso. Milhões de linhas de geometria numa table precisariam de paginação.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">10 MB</div><h3>O limite da compressão.</h3><p>Rodovias do Norte, 4,6 MB, trafegaram 0,7 MB. Rios do Norte, 12,1 MB, trafegaram inteiros.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">application/json</div><h3>O content-type decide.</h3><p>O mesmo arquivo de 8,3 MB veio inteiro como application/x-ndjson e com 1,2 MB como application/json.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">145 de 14.716</div><h3>Rio permanente vem do IBGE.</h3><p>Poucos rios do OSM têm a tag intermittent. O BC250 do IBGE traz o regime de cada trecho e já está no Drive.</p></div>'\n      + '</div></div></div>'\n\n      + '<div class=\"band\" id=\"s7\"><div class=\"in\">'\n      + '<h2 class=\"c\">Especificações</h2>'\n      + '<div class=\"specs\">'\n      + '<div class=\"spec\"><h4>Fonte</h4><div class=\"v\">OpenStreetMap Brasil via Geofabrik, 2,1 GB, atualizado diariamente<br><span class=\"note\">osmium tags-filter: highway motorway a tertiary, waterway=river</span></div></div>'\n      + '<div class=\"spec\"><h4>Arquivo</h4><div class=\"v\">osm-brasil-quadriculas.ndjson, 600 MB, 884 quadrículas<br><span class=\"note\">células de 1° divididas até 4 MB, ordem Z, mediana 370 KB</span></div></div>'\n      + '<div class=\"spec\"><h4>Índice</h4><div class=\"v\">Table Quadrículas OSM Brasil, 884 linhas, coluna arquivo do tipo file<br><span class=\"note\">acesso restrito ao builder e às Rushes do system</span></div></div>'\n      + '<div class=\"spec\"><h4>Visão geral</h4><div class=\"v\">osm-brasil-visao-geral.json, 8,3 MB no Drive, 1,2 MB na rede<br><span class=\"note\">rodovias principais e rios, coordenadas a 0,01°</span></div></div>'\n      + '<div class=\"spec\"><h4>Leitura</h4><div class=\"v\">Até 48 MB por vista, 4 requisições em paralelo, grupos de até 16 MB<br><span class=\"note\">blocos a menos de 512 KB viram uma requisição</span></div></div>'\n      + '<div class=\"spec\"><h4>Próximos passos</h4><div class=\"v\">Ligar a leitura por quadrícula no simulador, com cache no IndexedDB<br><span class=\"note\">gerar rios do BC250 e municípios da malha IBGE 2025 no mesmo formato; apagar overpassRoads e overpassClient</span></div></div>'\n      + '<div class=\"spec\"><h4 data-catsum>Arquivos</h4><div class=\"v\"><table><thead><tr><th>Arquivo</th><th class=\"hide-sm\">Papel</th><th class=\"r\">Tamanho</th></tr></thead><tbody data-cat><tr><td colspan=\"3\" class=\"note\">lendo…</td></tr></tbody></table></div></div>'\n      + '</div></div></div>'\n      + '<div class=\"foot\"><div class=\"in\">OpenStreetMap © colaboradores do OSM, licença ODbL, via Geofabrik. IBGE BC250 2025 e malha municipal 2025. ANAC dados abertos. OurAirports. Workspace Estudo geomarketing · Overpass → Drive, system gui.</div></div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var DOC = d2.$('[data-doc]');\n    var qd = d2.data('quadriculas'), ad = d2.data('arquivos');\n    var Q = [], A = [], hoverQ = null;\n\n    // ---------- utilidades ----------\n    function esc(v){ return String(v == null ? '' : v).replace(/[&<>\"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]; }); }\n    function nf(n){ return Number(n || 0).toLocaleString('pt-BR'); }\n    function mb(n){ if (n >= 1e9) return (n / 1e9).toFixed(1).replace('.', ',') + ' GB'; if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e8 ? 0 : 1).replace('.', ',') + ' MB'; if (n >= 1e3) return Math.round(n / 1e3) + ' KB'; return Math.round(n) + ' B'; }\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function ease(t){ t = Math.max(0, Math.min(1, t)); return 1 - Math.pow(1 - t, 3); }\n    function inout(t){ t = Math.max(0, Math.min(1, t)); return t < .5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }\n    function rgb(c){ return 'rgb(' + Math.round(c[0]) + ',' + Math.round(c[1]) + ',' + Math.round(c[2]) + ')'; }\n    function mix(a, b, t){ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t]; }\n    function shade(c, k){ return [c[0] * k, c[1] * k, c[2] * k]; }\n    var C0 = [242, 230, 216], C1 = [224, 130, 43], C2 = [122, 31, 14], INK = [29, 29, 31], RUST = [194, 65, 12], BLUE = [0, 113, 227], PAPER = [255, 255, 255], GRAY = [210, 210, 215];\n    function ramp(t){ t = Math.max(0, Math.min(1, t)); return t < .5 ? mix(C0, C1, t * 2) : mix(C1, C2, (t - .5) * 2); }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n\n    // canvas com DPR e laço de animação que pausa fora da tela\n    var scenes = [];\n    function scene(name, draw){\n      var cv = d2.$('[data-cv=\"' + name + '\"]'); if (!cv) return null;\n      var s = { cv: cv, ctx: cv.getContext('2d'), W: 0, H: +cv.getAttribute('height'), vis: false, t0: 0, draw: draw, name: name };\n      s.fit = function(){ var dpr = window.devicePixelRatio || 1, w = cv.getBoundingClientRect().width; s.W = w; s.dpr = dpr; cv.style.height = s.H + 'px'; cv.width = Math.round(w * dpr); cv.height = Math.round(s.H * dpr); s.ctx.setTransform(dpr, 0, 0, dpr, 0, 0); };\n      scenes.push(s); return s;\n    }\n    var ro = new ResizeObserver(function(){ scenes.forEach(function(s){ s.fit(); }); });\n    var io = new IntersectionObserver(function(es){ es.forEach(function(e){ scenes.forEach(function(s){ if (s.cv === e.target){ s.vis = e.isIntersecting; if (s.vis && !s.t0) s.t0 = performance.now(); } }); }); }, { root: DOC, threshold: 0.05 });\n    var stop = d2.raf(function(t){ scenes.forEach(function(s){ if (s.vis && s.W){ s.ctx.clearRect(0, 0, s.W, s.H); s.draw(s, (t - s.t0) / 1000); } }); });\n    d2.onUnmount(function(){ ro.disconnect(); io.disconnect(); if (stop) stop(); });\n\n    // projeção oblíqua (cabinet): x para a direita, y para o fundo (sobe à direita), z para cima\n    function ob(o, x, y, z){ return [o[0] + x + y * 0.55, o[1] - z - y * 0.32]; }\n    function poly(ctx, pts, fill, stroke){ ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]); for (var i = 1; i < pts.length; i++) ctx.lineTo(pts[i][0], pts[i][1]); ctx.closePath(); if (fill){ ctx.fillStyle = fill; ctx.fill(); } if (stroke){ ctx.strokeStyle = stroke; ctx.stroke(); } }\n    // caixa oblíqua: base em (x,y,z), dimensões w (x), d (y), h (z)\n    function obox(ctx, o, x, y, z, w, d, h, col, edge){\n      var P = function(a, b, c){ return ob(o, x + a, y + b, z + c); };\n      poly(ctx, [P(0, 0, 0), P(w, 0, 0), P(w, 0, h), P(0, 0, h)], rgb(col), edge);\n      poly(ctx, [P(w, 0, 0), P(w, d, 0), P(w, d, h), P(w, 0, h)], rgb(shade(col, .78)), edge);\n      poly(ctx, [P(0, 0, h), P(w, 0, h), P(w, d, h), P(0, d, h)], rgb(mix(col, [255, 255, 255], .18)), edge);\n    }\n    function label(ctx, x, y, s, size, color, align, weight){ ctx.font = (weight || 500) + ' ' + (size || 12) + 'px -apple-system,BlinkMacSystemFont,sans-serif'; ctx.fillStyle = color || '#3d3c38'; ctx.textAlign = align || 'left'; ctx.fillText(s, x, y); }\n    function mono(ctx, x, y, s, size, color, align){ ctx.font = (size || 11.5) + 'px ui-monospace,Menlo,monospace'; ctx.fillStyle = color || '#3d3c38'; ctx.textAlign = align || 'left'; ctx.fillText(s, x, y); }\n\n    // ================= 1. mapa isométrico das quadrículas =================\n    var hero = scene('hero', function(s, t){\n      var ctx = s.ctx;\n      if (!Q.length){ label(ctx, s.W / 2, s.H / 2, 'lendo a table…', 14, '#6e6e73', 'center'); return; }\n      if (!s.prisms) buildHero(s);\n      var rot = -Math.PI / 4 + Math.sin(t * 0.22) * 0.22;\n      if (!s.hover) s.lastRot = rot; else rot = s.lastRot;\n      var cr = Math.cos(rot), sr = Math.sin(rot);\n      var sc = s.scale, cx = s.W / 2, cy = s.H * 0.47;\n      function P(x, y, z){ var xr = x * cr - y * sr, yr = x * sr + y * cr; return [cx + (xr - yr) * 0.866 * sc, cy + (xr + yr) * 0.5 * sc - z * sc]; }\n      // faces visíveis dependem da rotação\n      var nrm = [[1, 0], [0, 1], [-1, 0], [0, -1]].map(function(n){ var xr = n[0] * cr - n[1] * sr, yr = n[0] * sr + n[1] * cr; return xr + yr > 0.02; });\n      var items = s.prisms.map(function(p){ var xr = p.cx * cr - p.cy * sr, yr = p.cx * sr + p.cy * cr; p.depth = xr + yr; return p; }).sort(function(a, b){ return a.depth - b.depth; });\n      // sombra do chão\n      ctx.fillStyle = 'rgba(29,29,31,.05)';\n      items.forEach(function(p){ var a = P(p.x0, p.y0, 0), b = P(p.x1, p.y0, 0), c = P(p.x1, p.y1, 0), e = P(p.x0, p.y1, 0); ctx.beginPath(); ctx.moveTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); ctx.lineTo(c[0], c[1]); ctx.lineTo(e[0], e[1]); ctx.closePath(); ctx.fill(); });\n      s.tops = [];\n      items.forEach(function(p){\n        var grow = ease((t - p.delay) / 1.1), h = p.h * grow;\n        var on = hoverQ === p.r.ordem, col = on ? INK : p.col;\n        var c = [[p.x0, p.y0], [p.x1, p.y0], [p.x1, p.y1], [p.x0, p.y1]];\n        // lados: face i liga c[i] -> c[i+1]; normais na ordem (0,-1),(1,0),(0,1),(-1,0)\n        var faceN = [3, 0, 1, 2];\n        for (var i = 0; i < 4; i++){\n          if (!nrm[faceN[i]]) continue;\n          var a = c[i], b = c[(i + 1) % 4];\n          var k = faceN[i] === 0 || faceN[i] === 2 ? .8 : .66;\n          poly(ctx, [P(a[0], a[1], 0), P(b[0], b[1], 0), P(b[0], b[1], h), P(a[0], a[1], h)], rgb(shade(col, k)));\n        }\n        var top = c.map(function(q){ return P(q[0], q[1], h); });\n        poly(ctx, top, rgb(on ? INK : mix(col, [255, 255, 255], .08)), 'rgba(29,29,31,.10)');\n        s.tops.push({ p: p, pts: top });\n      });\n      var loaded = s.prisms.filter(function(p){ return t > p.delay + 1.1; }).length;\n      var el = d2.$('[data-hud=\"hero\"]');\n      if (el && !s.hudDone){ el.innerHTML = '<b>' + nf(Math.min(loaded, s.prisms.length)) + '</b> de ' + nf(s.prisms.length) + ' linhas · <b>' + mb(s.sum * Math.min(1, loaded / s.prisms.length)) + '</b> de ' + mb(s.sum); if (loaded >= s.prisms.length) s.hudDone = true; }\n    });\n    function buildHero(s){\n      var lon0 = -52, y0 = mercY(-14), maxB = 1;\n      Q.forEach(function(r){ if (r.bytes > maxB) maxB = r.bytes; });\n      s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0);\n      s.prisms = Q.map(function(r){\n        var x0 = r.celulaLon - lon0, x1 = x0 + r.celulaGraus, yT = y0 - mercY(r.celulaLat + r.celulaGraus), yB = y0 - mercY(r.celulaLat);\n        var g = r.celulaGraus * 0.06, cxp = (x0 + x1) / 2, cyp = (yT + yB) / 2;\n        var tn = Math.sqrt(r.bytes / maxB);\n        return { r: r, x0: x0 + g, x1: x1 - g, y0: yT + g, y1: yB - g, cx: cxp, cy: cyp, h: 0.15 + tn * 6.5, col: ramp(tn), delay: 0.15 + Math.hypot(cxp, cyp) * 0.045 + (r.ordem % 7) * 0.02 };\n      });\n      s.scale = Math.min(s.W / 60, s.H / 40);\n    }\n    function inPoly(x, y, pts){ var c = false; for (var i = 0, j = pts.length - 1; i < pts.length; j = i++){ var xi = pts[i][0], yi = pts[i][1], xj = pts[j][0], yj = pts[j][1]; if (((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) c = !c; } return c; }\n    if (hero){\n      d2.on(hero.cv, 'mousemove', function(e){\n        if (!hero.tops) return;\n        var r = hero.cv.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top, hit = null;\n        for (var i = hero.tops.length - 1; i >= 0; i--){ if (inPoly(x, y, hero.tops[i].pts)){ hit = hero.tops[i].p; break; } }\n        var tip = d2.$('[data-tip=\"hero\"]');\n        hero.hover = !!hit; hoverQ = hit ? hit.r.ordem : null;\n        if (!hit){ tip.className = 'tip'; return; }\n        var q = hit.r;\n        tip.innerHTML = '<b>' + esc(q.quadricula) + '</b><br><span>tamanho</span> ' + mb(q.bytes) + ' <span>·</span> ' + nf(q.feicoes) + ' <span>trechos</span><br><span>Range:</span> ' + esc(q.range) + '<br><span>' + nf(q.rodovias) + ' rodovias · ' + nf(q.rios) + ' rios · nível ' + q.nivel + '</span>';\n        tip.style.left = x + 'px'; tip.style.top = y + 'px'; tip.className = 'tip on';\n      });\n      d2.on(hero.cv, 'mouseleave', function(){ hero.hover = false; hoverQ = null; var tip = d2.$('[data-tip=\"hero\"]'); if (tip) tip.className = 'tip'; });\n    }\n\n    // ================= 2. como é hoje =================\n    var today = scene('today', function(s, t){\n      var ctx = s.ctx, W = s.W, o = [0, 205];\n      var nodes = [{ x: W * 0.04, w: W * 0.2, name: 'Simulador', sub: 'navegador', col: PAPER }, { x: W * 0.39, w: W * 0.2, name: 'osmOverpassProxy', sub: 'Rush', col: [255, 241, 230] }, { x: W * 0.74, w: W * 0.22, name: 'overpass-api.de', sub: 'servidor público', col: [255, 241, 230] }];\n      nodes.forEach(function(n){ obox(ctx, o, n.x, 0, 0, n.w, 70, 26, n.col, 'rgba(29,29,31,.18)'); var p = ob(o, n.x + 14, 0, 0); label(ctx, p[0], p[1] + 22, n.name, 13, '#1d1d1f', 'left', 600); label(ctx, p[0], p[1] + 38, n.sub, 11.5, '#6e6e73'); });\n      // pacotes: ida até o overpass; a maioria volta como 504 e reinicia\n      var period = 2.6, n = 4, total = 0;\n      for (var k = 0; k < n; k++){\n        var tt = t + k * period / n, cyc = Math.floor(tt / period), ph = (tt % period) / period;\n        var fails = ((cyc * 7 + k * 3) % 10) < 8;\n        total += cyc;\n        var xA = nodes[0].x + nodes[0].w * 0.6, xB = nodes[2].x + nodes[2].w * 0.35, x, z, col;\n        if (ph < 0.55){ x = xA + (xB - xA) * inout(ph / 0.55); z = 40 + Math.sin(ph / 0.55 * Math.PI) * 26; col = INK; }\n        else { var q = (ph - 0.55) / 0.45; x = xB - (xB - xA) * inout(q) * (fails ? 0.62 : 1); z = 40 + Math.sin(q * Math.PI) * 18; col = fails ? RUST : [63, 125, 32]; }\n        obox(ctx, o, x - 7, 28, z, 14, 14, 12, col);\n        if (ph >= 0.55 && ph < 0.72){ var pp = ob(o, xB, 30, 78); label(ctx, pp[0], pp[1], fails ? '504 server too busy' : '200 OK', 12, fails ? '#c2410c' : '#3f7d20', 'center', 600); }\n      }\n      var hud = d2.$('[data-hud=\"today\"]'); if (hud) hud.innerHTML = 'consultas <b>' + nf(total + n) + '</b> · respostas 504 <b style=\"color:#c2410c\">' + nf(Math.round(total * 0.8)) + '</b> · cada falha volta para a fila';\n    });\n\n    // ================= 3. do PBF às quadrículas =================\n    var pipe = scene('pipe', function(s, t){\n      var ctx = s.ctx, W = s.W, base = 196, maxSide = 150;\n      var items = [\n        { name: 'brazil-latest.osm.pbf', size: 2086611725, sub: 'Geofabrik', col: BLUE },\n        { name: 'rodovias + rios', size: 742236000, sub: 'osmium tags-filter', col: GRAY },\n        { name: 'quadrículas', size: 599981073, sub: 'tile.mjs · 884 blocos', col: RUST },\n        { name: 'visão geral', size: 8336717, sub: '0,01° · 1,2 MB na rede', col: C1 },\n        { name: 'índice', size: 143706, sub: 'table · 884 linhas', col: [91, 58, 160] }\n      ];\n      var xs = [0.04, 0.26, 0.48, 0.70, 0.86];\n      items.forEach(function(it, i){\n        var side = Math.max(7, maxSide * Math.cbrt(it.size / items[0].size));\n        var g = ease((t - i * 0.55) / 0.8), sd = side * g, x = W * xs[i], o = [x, base];\n        if (g > 0){\n          if (i === 2){\n            // o cubo das quadrículas é feito de fatias\n            var sl = 12, w = sd / sl;\n            for (var k = 0; k < sl; k++) obox(ctx, o, k * w, 0, Math.sin(t * 2 + k * .7) * 2 * g, w - 1, sd * .55, sd * .55, mix(C1, C2, k / sl));\n          } else obox(ctx, o, 0, 0, 0, sd, sd * .55, sd * .55, it.col, 'rgba(29,29,31,.12)');\n        }\n        label(ctx, x, base + 26, it.name, 13, '#1d1d1f', 'left', 600);\n        mono(ctx, x, base + 44, mb(it.size * g), 12, '#c2410c');\n        label(ctx, x, base + 60, it.sub, 11.5, '#6e6e73');\n        if (i < 4 && g > .5){ var ax = x + side + 18, ax2 = W * xs[i + 1] - 14; ctx.strokeStyle = 'rgba(29,29,31,.5)'; ctx.setLineDash([4, 4]); ctx.lineDashOffset = -t * 20; ctx.beginPath(); ctx.moveTo(ax, base - 30); ctx.lineTo(ax2, base - 30); ctx.stroke(); ctx.setLineDash([]); }\n      });\n    });\n\n    // ================= 4. faixa do arquivo =================\n    var strip = scene('strip', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      if (!Q.length) return;\n      if (!s.sorted){ s.sorted = Q.slice().sort(function(a, b){ return a.byteInicio - b.byteInicio; }); s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0); s.maxB = Q.reduce(function(m, r){ return Math.max(m, r.bytes); }, 1); }\n      var o = [10, 96], L = W - 50, acc = 0, sel = null;\n      s.sorted.forEach(function(r){\n        var x = acc / s.sum * L, w = r.bytes / s.sum * L; acc += r.bytes;\n        var on = hoverQ === r.ordem, lift = on ? 16 : 0;\n        if (on) sel = { x: x, w: w, r: r };\n        obox(ctx, o, x, 0, lift, Math.max(.5, w), 34, 18, on ? INK : ramp(Math.sqrt(r.bytes / s.maxB)));\n      });\n      [0, .25, .5, .75, 1].forEach(function(f){ var p = ob(o, f * L, 0, 0); mono(ctx, p[0], p[1] + 16, mb(f * s.sum), 11, '#6e6e73', f === 1 ? 'right' : f ? 'center' : 'left'); });\n      var hud = d2.$('[data-hud=\"strip\"]');\n      if (hud) hud.innerHTML = sel ? '<b>' + esc(sel.r.quadricula) + '</b> · bytes ' + nf(sel.r.byteInicio) + ' a ' + nf(sel.r.byteInicio + sel.r.bytes - 1) + ' · ' + mb(sel.r.bytes) : 'osm-brasil-quadriculas.ndjson · ' + mb(s.sum) + ' · 884 fatias';\n    });\n\n    // ================= 5. uma leitura =================\n    var SCN = [\n      { id: 'sp', name: 'São Paulo', box: [-47.08, -23.9, -46.18, -23.2] },\n      { id: 'bel', name: 'Belém', box: [-49.08, -2.0, -47.88, -0.8] },\n      { id: 'larga', name: 'Zoom largo, 6°', box: [-50.5, -25.5, -44.5, -19.5] }\n    ];\n    function planScenario(sc){\n      var b = sc.box, cx = (b[0] + b[2]) / 2, cy = (b[1] + b[3]) / 2;\n      var vis = Q.filter(function(r){ return !(r.lonMax < b[0] || r.lonMin > b[2] || r.latMax < b[1] || r.latMin > b[3]); });\n      vis.sort(function(a, c){ var da = Math.pow(a.celulaLon + a.celulaGraus / 2 - cx, 2) + Math.pow(a.celulaLat + a.celulaGraus / 2 - cy, 2), dc = Math.pow(c.celulaLon + c.celulaGraus / 2 - cx, 2) + Math.pow(c.celulaLat + c.celulaGraus / 2 - cy, 2); return da - dc; });\n      var used = 0, need = [], skip = 0, BUDGET = 48 * 1048576;\n      vis.forEach(function(r){ if (used + r.bytes <= BUDGET){ need.push(r); used += r.bytes; } else skip++; });\n      need.sort(function(a, c){ return a.byteInicio - c.byteInicio; });\n      var groups = [], g = null;\n      need.forEach(function(r){ if (g && r.byteInicio - g.end <= 524288 && r.byteInicio + r.bytes - g.start <= 16777216){ g.tiles.push(r); g.end = r.byteInicio + r.bytes; } else { g = { start: r.byteInicio, end: r.byteInicio + r.bytes, tiles: [r] }; groups.push(g); } });\n      return { vis: vis.length, need: need, skip: skip, groups: groups, bytes: used, ids: need.reduce(function(m, r){ m[r.ordem] = 1; return m; }, {}) };\n    }\n    var readState = { i: 0, start: 0, plans: null, logged: -1 };\n    function setScenario(i, now){\n      readState.i = i; readState.start = now; readState.logged = -1;\n      var log = d2.$('[data-reqlog]'); if (log) log.innerHTML = '';\n      var c = d2.$('[data-ctrl]'); if (c) Array.prototype.forEach.call(c.children, function(b, k){ b.className = k === i ? 'on' : ''; });\n    }\n    var read = scene('read', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      if (!Q.length) return;\n      if (!readState.plans){\n        readState.plans = SCN.map(planScenario); s.sorted = Q.slice().sort(function(a, b){ return a.byteInicio - b.byteInicio; }); s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0);\n        var c = d2.$('[data-ctrl]'); if (c && !c.children.length) c.innerHTML = SCN.map(function(sc, k){ return '<button data-scn=\"' + k + '\">' + sc.name + '</button>'; }).join('');\n        setScenario(0, t);\n      }\n      var DUR = 7.5, lt = t - readState.start;\n      if (lt > DUR && !s.pinned){ setScenario((readState.i + 1) % SCN.length, t); lt = 0; }\n      var sc = SCN[readState.i], plan = readState.plans[readState.i];\n      var o = [10, 330], L = W - 110, acc = 0;\n      // navegador\n      var bx = W - 240, bo = [bx, 170];\n      var bt = ob(bo, 0, 80, 70); label(ctx, bt[0], bt[1] - 12, 'Navegador', 13, '#1d1d1f', 'left', 600);\n      // fundo e piso do navegador (a frente é de vidro, desenhada depois da pilha)\n      var G = function(a, b, c){ return ob(bo, a, b, c); };\n      poly(ctx, [G(0, 80, 0), G(180, 80, 0), G(180, 80, 70), G(0, 80, 70)], 'rgba(0,113,227,.06)', 'rgba(0,113,227,.25)');\n      poly(ctx, [G(0, 0, 0), G(180, 0, 0), G(180, 80, 0), G(0, 80, 0)], 'rgba(0,113,227,.08)', 'rgba(0,113,227,.25)');\n      // barra do arquivo com fatias; as selecionadas acendem e sobem\n      var lift = ease((lt - 0.8) / 0.9);\n      s.sorted.forEach(function(r){\n        var x = acc / s.sum * L, w = r.bytes / s.sum * L; acc += r.bytes;\n        var on = plan.ids[r.ordem];\n        var col = on ? mix(GRAY, RUST, ease((lt - 0.4) / 0.5)) : GRAY;\n        obox(ctx, o, x, 0, on ? lift * 34 : 0, Math.max(.45, w), 40, 20, col);\n      });\n      var po = ob(o, 0, 0, 0); label(ctx, po[0], po[1] + 22, 'osm-brasil-quadriculas.ndjson · 600 MB no Drive', 12.5, '#1d1d1f', 'left', 600);\n      // grupos viajam como pacotes com tamanho proporcional\n      var got = 0, n = plan.groups.length, t1 = 1.9, span = 3.2;\n      plan.groups.forEach(function(g, k){\n        var st = t1 + k * (span / Math.max(1, n)), q = ease((lt - st) / 1.1);\n        var gx = (g.start / s.sum) * L + ((g.end - g.start) / s.sum * L) / 2;\n        var side = 8 + 30 * Math.cbrt((g.end - g.start) / 16777216);\n        if (lt > st){\n          var sx = o[0] + gx, sy = o[1] - 60, ex = bx + 60, ey = 170 - 50;\n          var x = sx + (ex - sx) * q, y = sy + (ey - sy) * q - Math.sin(q * Math.PI) * 90;\n          if (q < 1) obox(ctx, [x, y], -side / 2, 0, 0, side, side * .8, side * .8, RUST);\n          if (q >= 1) got += g.end - g.start;\n          if (q > 0.05 && readState.logged < k){\n            readState.logged = k;\n            var log = d2.$('[data-reqlog]');\n            if (log){ var dv = document.createElement('div'); dv.textContent = 'GET Range: bytes=' + g.start + '-' + (g.end - 1) + ' · ' + mb(g.end - g.start) + ' · ' + g.tiles.length + ' quadr.'; log.appendChild(dv); while (log.children.length > 6) log.removeChild(log.firstChild); requestAnimationFrame(function(){ dv.className = 'in'; }); }\n          }\n        }\n      });\n      // pilha de bytes recebidos dentro do navegador\n      var fill = got / Math.max(1, plan.bytes);\n      if (fill > 0) obox(ctx, bo, 20, 20, 0, 140, 40, 56 * fill, RUST);\n      poly(ctx, [G(0, 0, 0), G(180, 0, 0), G(180, 0, 70), G(0, 0, 70)], 'rgba(255,255,255,.28)', 'rgba(0,113,227,.45)');\n      poly(ctx, [G(180, 0, 0), G(180, 80, 0), G(180, 80, 70), G(180, 0, 70)], 'rgba(0,113,227,.07)', 'rgba(0,113,227,.45)');\n      poly(ctx, [G(0, 0, 70), G(180, 0, 70), G(180, 80, 70), G(0, 80, 70)], 'rgba(0,113,227,.04)', 'rgba(0,113,227,.45)');\n      mono(ctx, bx, 170 + 26, mb(got) + ' de ' + mb(plan.bytes) + ' recebidos', 13, '#1d1d1f');\n      var hud = d2.$('[data-hud=\"read\"]');\n      if (hud) hud.innerHTML = '<b>' + sc.name + '</b><br>' + plan.vis + ' quadrículas na tela · ' + plan.need.length + ' baixadas' + (plan.skip ? ' · ' + plan.skip + ' na visão geral' : '') + '<br>' + plan.groups.length + ' requisições · ' + mb(plan.bytes) + ' de ' + mb(s.sum) + ' (' + (100 * plan.bytes / s.sum).toFixed(1).replace('.', ',') + '%)';\n    });\n    d2.on('[data-scn]', 'click', function(e, el){ if (!read) return; read.pinned = true; setScenario(+el.getAttribute('data-scn'), (performance.now() - read.t0) / 1000); });\n\n    // ================= 6. compressão =================\n    var gz = scene('gzip', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      var rows = [\n        { name: 'rodovias-norte.geojson', ct: 'application/json', a: 4557306, b: 733586, size: true, type: true },\n        { name: 'rios-norte.geojson', ct: 'application/json', a: 12064515, b: 12064515, size: false, type: true },\n        { name: 'visao-geral.ndjson', ct: 'application/x-ndjson', a: 8336717, b: 8336717, size: true, type: false },\n        { name: 'visao-geral.json', ct: 'application/json', a: 8336717, b: 1229098, size: true, type: true }\n      ];\n      var xIn = W * 0.30, xGate = W * 0.56, xOut = W * 0.80, top = 70, lane = 88, maxSide = 46, big = 12064515;\n      function side(bytes){ return Math.max(6, maxSide * Math.cbrt(bytes / big)); }\n      label(ctx, xIn, 28, 'No Drive', 13, '#6e6e73', 'center', 600);\n      label(ctx, xGate, 28, 'CloudFront', 13, '#6e6e73', 'center', 600);\n      label(ctx, xOut, 28, 'Na rede', 13, '#6e6e73', 'center', 600);\n      // portão do CloudFront atravessando todas as faixas\n      var gTop = top - 26, gBot = top + lane * rows.length - 30;\n      obox(ctx, [xGate - 6, gBot], 0, 0, 0, 12, 30, gBot - gTop, [232, 232, 237], 'rgba(29,29,31,.12)');\n      rows.forEach(function(r, i){\n        var y = top + i * lane, pass = r.size && r.type;\n        label(ctx, 20, y + 4, r.name, 14, '#1d1d1f', 'left', 600);\n        mono(ctx, 20, y + 22, r.ct, 12, '#6e6e73');\n        ctx.strokeStyle = 'rgba(29,29,31,.10)'; ctx.setLineDash([3, 5]); ctx.beginPath(); ctx.moveTo(xIn, y + 14); ctx.lineTo(xOut + 60, y + 14); ctx.stroke(); ctx.setLineDash([]);\n        // o cubo sai do Drive, passa pelo portão e chega à rede; encolhe no portão se passar nas duas checagens\n        var cyc = 5.2, ph = ((t + i * 0.45) % cyc) / cyc, travel = ease(Math.min(1, ph / 0.62));\n        var x = xIn + (xOut - xIn) * travel, crossed = x > xGate;\n        var shrink = pass ? ease((x - xGate) / 40) : 0;\n        var bytes = r.a + (r.b - r.a) * shrink, sd = side(bytes);\n        var col = !crossed ? GRAY : pass ? [63, 125, 32] : RUST;\n        if (ph < 0.92) obox(ctx, [x - sd / 2, y + 22], 0, 0, 0, sd, sd * .7, sd * .7, col);\n        // tamanhos fixos nas pontas\n        mono(ctx, xIn, y + 46, mb(r.a), 12.5, '#6e6e73', 'center');\n        var near = ph > 0.55;\n        mono(ctx, xOut, y + 46, mb(r.b) + (pass ? '  −' + Math.round(100 * (1 - r.b / r.a)) + '%' : ''), 12.5, pass ? '#3f7d20' : '#c2410c', 'center');\n        // checagens ao lado do portão\n        var lit = x > xGate - 30;\n        function check(dx, ok, txt){\n          var c = !lit ? '#aeaeb2' : ok ? '#3f7d20' : '#c2410c';\n          ctx.fillStyle = !lit ? 'rgba(174,174,178,.14)' : ok ? 'rgba(63,125,32,.10)' : 'rgba(194,65,12,.10)';\n          ctx.beginPath(); if (ctx.roundRect) ctx.roundRect(xGate + dx, y - 12, 104, 22, 11); else ctx.rect(xGate + dx, y - 12, 104, 22); ctx.fill();\n          label(ctx, xGate + dx + 52, y + 3, (lit ? (ok ? '✓ ' : '× ') : '') + txt, 12, c, 'center', 600);\n        }\n        check(-118, r.size, 'até 10 MB');\n        check(14, r.type, 'tipo json');\n        if (near && ph < 0.92 && !pass){ label(ctx, xOut, y - 2, 'passa inteiro', 11.5, '#c2410c', 'center'); }\n      });\n    });\n\n    // ================= 7. exemplo aplicado: São Paulo em camadas, baixada agora por Range =================\n    var EX = { box: [-46.95, -23.78, -46.3, -23.35], layers: null, loading: false, err: null, stats: null };\n    var LDEF = [\n      { id: 'base', name: 'Quadrículas do índice', keys: [] },\n      { id: 'river', name: 'Rios', keys: ['river'], color: '#0071e3', w: 1.6 },\n      { id: 'local', name: 'Rodovias locais', keys: ['secondary', 'tertiary'], color: '#8e8e93', w: 1.0 },\n      { id: 'main', name: 'Rodovias principais', keys: ['primary', 'trunk', 'motorway'], color: '#c2410c', w: 2.6 }\n    ];\n    function loadExample(){\n      if (EX.loading) return; EX.loading = true;\n      var b = EX.box, url = Q[0] && Q[0].arquivo && Q[0].arquivo.fileUrl;\n      if (!url){ EX.err = 'arquivo sem fileUrl'; return; }\n      var tiles = Q.filter(function(r){ return !(r.lonMax < b[0] || r.lonMin > b[2] || r.latMax < b[1] || r.latMin > b[3]); }).sort(function(a, c){ return a.byteInicio - c.byteInicio; });\n      var groups = [], g = null;\n      tiles.forEach(function(r){ if (g && r.byteInicio - g.end <= 524288){ g.end = r.byteInicio + r.bytes; g.n++; } else { g = { start: r.byteInicio, end: r.byteInicio + r.bytes, n: 1 }; groups.push(g); } });\n      var t0 = performance.now(), got = 0, feats = {}, bytesBy = {};\n      EX.progress = { tiles: tiles.length, groups: groups.length, total: groups.reduce(function(s, x){ return s + x.end - x.start; }, 0), got: 0 };\n      Promise.all(groups.map(function(gr){\n        return fetch(url, { headers: { Range: 'bytes=' + gr.start + '-' + (gr.end - 1) } }).then(function(r){\n          if (r.status !== 206) throw new Error('HTTP ' + r.status);\n          var reader = r.body.getReader(), chunks = [];\n          function pump(){ return reader.read().then(function(x){ if (x.done) return; chunks.push(x.value); got += x.value.byteLength; EX.progress.got = got; return pump(); }); }\n          return pump().then(function(){ return new Blob(chunks).text(); });\n        }).then(function(text){\n          text.split('\\n').forEach(function(line){ if (!line) return; var f = JSON.parse(line); (feats[f.k] || (feats[f.k] = [])).push(f.c); bytesBy[f.k] = (bytesBy[f.k] || 0) + line.length + 1; });\n        });\n      })).then(function(){\n        EX.stats = { tiles: tiles.length, groups: groups.length, bytes: got, ms: Math.round(performance.now() - t0), feats: Object.keys(feats).reduce(function(s, k){ return s + feats[k].length; }, 0) };\n        buildLayers(tiles, feats, bytesBy);\n      }).catch(function(e){ EX.err = (e && e.message) || String(e); });\n    }\n    function buildLayers(tiles, feats, bytesBy){\n      var S = 1000, b = EX.box, my0 = mercY(b[1]), my1 = mercY(b[3]);\n      var k = Math.min(S / (b[2] - b[0]), S / (my1 - my0)), ox = (S - (b[2] - b[0]) * k) / 2, oy = (S - (my1 - my0) * k) / 2;\n      function X(lon){ return ox + (lon - b[0]) * k; }\n      function Y(lat){ return oy + (my1 - mercY(lat)) * k; }\n      EX.layers = LDEF.map(function(L){\n        var c = document.createElement('canvas'); c.width = S; c.height = S;\n        var x = c.getContext('2d'); x.lineCap = 'round'; x.lineJoin = 'round';\n        x.save(); x.beginPath(); x.rect(0, 0, S, S); x.clip();\n        var n = 0, bytes = 0;\n        if (L.id === 'base'){\n          x.fillStyle = '#ffffff'; x.fillRect(0, 0, S, S);\n          tiles.forEach(function(r){\n            var x0 = X(r.celulaLon), x1 = X(r.celulaLon + r.celulaGraus), y0 = Y(r.celulaLat + r.celulaGraus), y1 = Y(r.celulaLat);\n            x.fillStyle = rgb(mix(C0, [255, 255, 255], .35)); x.fillRect(x0 + 3, y0 + 3, x1 - x0 - 6, y1 - y0 - 6);\n            x.strokeStyle = '#c2410c'; x.lineWidth = 3; x.setLineDash([14, 10]); x.strokeRect(x0 + 3, y0 + 3, x1 - x0 - 6, y1 - y0 - 6); x.setLineDash([]);\n            x.fillStyle = '#9a3412'; x.font = '600 26px -apple-system,sans-serif'; x.fillText(r.quadricula.split(' · ')[0], x0 + 16, y0 + 40);\n            x.font = '24px ui-monospace,Menlo,monospace'; x.fillText(mb(r.bytes), x0 + 16, y0 + 70);\n          });\n          L.info = tiles.length + ' linhas · ' + mb(EX.stats.bytes);\n        } else {\n          x.strokeStyle = L.color; x.lineWidth = L.w * 2;\n          x.beginPath();\n          L.keys.forEach(function(key){\n            (feats[key] || []).forEach(function(line){ n++; for (var i = 0; i < line.length; i++){ var px = X(line[i][0]), py = Y(line[i][1]); if (i) x.lineTo(px, py); else x.moveTo(px, py); } });\n            bytes += bytesBy[key] || 0;\n          });\n          x.stroke();\n          L.info = nf(n) + ' trechos · ' + mb(bytes);\n        }\n        x.restore();\n        return { name: L.name, info: L.info, canvas: c };\n      });\n      // quadrículas em coordenadas do plano (0..1000), na ordem em que estão no arquivo\n      EX.tq = tiles.map(function(r){ return { r: r, x0: X(r.celulaLon), x1: X(r.celulaLon + r.celulaGraus), y0: Y(r.celulaLat + r.celulaGraus), y1: Y(r.celulaLat) }; });\n      EX.range = [tiles[0].byteInicio, tiles[tiles.length - 1].byteInicio + tiles[tiles.length - 1].bytes];\n    }\n    // quadrícula no plano → linha da table → trecho do arquivo, uma de cada vez, na ordem Z\n    function drawIndexLogic(ctx, s, ip, st, exv, eyv, cx, size){\n      var O = s.O0, n = EX.tq.length, STEP = 0.62;\n      // recorta cada quadrícula no limite do plano (as de 0,5° passam da borda da cidade)\n      function cl(v){ return Math.max(0, Math.min(1000, v)); }\n      var TQ = EX.tq.map(function(q){ return { r: q.r, x0: cl(q.x0), x1: cl(q.x1), y0: cl(q.y0), y1: cl(q.y1) }; });\n      function PB(u, v){ return [O[0] + u * exv[0] + v * eyv[0], O[1] + u * exv[1] + v * eyv[1]]; }\n      var cur = st < 0 ? -1 : Math.min(n, Math.floor(st / STEP)), done = st >= n * STEP + 0.3;\n      ctx.globalAlpha = ip;\n      // quadrículas percorridas acendem no plano, e o caminho da ordem Z vai sendo traçado\n      var path = [];\n      TQ.forEach(function(q, j){\n        if (j > cur) return;\n        var on = j === cur && !done;\n        poly(ctx, [PB(q.x0, q.y0), PB(q.x1, q.y0), PB(q.x1, q.y1), PB(q.x0, q.y1)], on ? 'rgba(194,65,12,.42)' : 'rgba(194,65,12,.16)', on ? '#c2410c' : 'rgba(194,65,12,.5)');\n        path.push(PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2));\n      });\n      if (path.length > 1){ ctx.strokeStyle = '#1d1d1f'; ctx.lineWidth = 1.6; ctx.setLineDash([]); ctx.beginPath(); path.forEach(function(p, k){ if (k) ctx.lineTo(p[0], p[1]); else ctx.moveTo(p[0], p[1]); }); ctx.stroke(); path.forEach(function(p){ ctx.fillStyle = '#1d1d1f'; ctx.beginPath(); ctx.arc(p[0], p[1], 3, 0, Math.PI * 2); ctx.fill(); }); ctx.lineWidth = 1; }\n      // table do índice na coluna da esquerda\n      var TW = 250, xT = Math.max(12, cx - 0.866 * size - 70 - TW), yT = s.H * 0.30, RH = 23;\n      label(ctx, xT, yT - 30, 'Table Quadrículas OSM Brasil', 14, '#1d1d1f', 'left', 600);\n      mono(ctx, xT, yT - 12, 'quadricula      byteInicio    bytes', 11, '#6e6e73');\n      TQ.forEach(function(q, j){\n        var y = yT + j * RH, on = j === cur && !done, seen = j <= cur;\n        if (on){ ctx.fillStyle = 'rgba(194,65,12,.12)'; ctx.fillRect(xT - 6, y - 4, TW + 12, RH); }\n        mono(ctx, xT, y + 12, q.r.quadricula.split(' · ')[0], 12, seen ? '#1d1d1f' : '#aeaeb2');\n        mono(ctx, xT + 96, y + 12, nf(q.r.byteInicio), 12, seen ? '#1d1d1f' : '#aeaeb2');\n        mono(ctx, xT + TW, y + 12, mb(q.r.bytes), 12, seen ? '#c2410c' : '#aeaeb2', 'right');\n        if (on){\n          var c = PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2);\n          ctx.strokeStyle = 'rgba(194,65,12,.7)'; ctx.beginPath(); ctx.moveTo(xT + TW + 8, y + 8); ctx.bezierCurveTo(xT + TW + 80, y + 8, c[0] - 80, c[1], c[0], c[1]); ctx.stroke();\n        }\n      });\n      // trecho do arquivo ocupado por essas linhas, e onde ele fica nos 600 MB\n      var yS = yT + n * RH + 34, a0 = EX.range[0], a1 = EX.range[1];\n      label(ctx, xT, yS - 8, 'No arquivo', 14, '#1d1d1f', 'left', 600);\n      TQ.forEach(function(q, j){\n        var x = xT + (q.r.byteInicio - a0) / (a1 - a0) * TW, w = q.r.bytes / (a1 - a0) * TW;\n        var on = j === cur && !done, seen = j <= cur;\n        obox(ctx, [xT, yS + 22], x - xT, 0, on ? 8 : 0, Math.max(1, w - 1), 14, 12, on ? INK : seen ? RUST : GRAY);\n        if (on){ var c2 = PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2); ctx.strokeStyle = 'rgba(29,29,31,.35)'; ctx.setLineDash([3, 4]); ctx.beginPath(); ctx.moveTo(x + w / 2, yS + 2); ctx.bezierCurveTo(x + w / 2, yS - 60, c2[0] - 120, c2[1] + 40, c2[0], c2[1]); ctx.stroke(); ctx.setLineDash([]); }\n      });\n      var tot = Q.reduce(function(sum, r){ return sum + r.bytes; }, 0);\n      ctx.fillStyle = '#e8e8ed'; ctx.fillRect(xT, yS + 44, TW, 4);\n      ctx.fillStyle = '#c2410c'; ctx.fillRect(xT + a0 / tot * TW, yS + 44, Math.max(2, (a1 - a0) / tot * TW), 4);\n      mono(ctx, xT, yS + 62, mb(a0) + ' a ' + mb(a1) + ' dos ' + mb(tot), 11, '#6e6e73');\n      // no fim, os blocos contíguos viram uma requisição\n      if (done){\n        var f = ease((st - n * STEP - 0.3) / 0.6);\n        ctx.globalAlpha = ip * f;\n        label(ctx, xT, yS + 96, n + ' blocos seguidos no arquivo', 14, '#1d1d1f', 'left', 600);\n        mono(ctx, xT, yS + 116, 'Range: bytes=' + a0 + '-' + (a1 - 1), 12, '#c2410c');\n        mono(ctx, xT, yS + 134, '1 requisição · ' + mb(a1 - a0), 12, '#6e6e73');\n      }\n      ctx.globalAlpha = 1;\n    }\n    var layersScene = scene('layers', function(s, t){\n      var ctx = s.ctx, W = s.W, H = s.H;\n      var hud = d2.$('[data-hud=\"layers\"]');\n      if (!EX.layers){\n        if (Q.length) loadExample();\n        var pr = EX.progress;\n        label(ctx, W / 2, H / 2, EX.err ? 'erro: ' + EX.err : pr ? 'baixando ' + mb(pr.got) + ' de ' + mb(pr.total) + '…' : 'lendo a table…', 15, '#6e6e73', 'center');\n        if (pr){ var bw = 280, f = pr.got / Math.max(1, pr.total); ctx.fillStyle = '#e8e8ed'; ctx.fillRect(W / 2 - bw / 2, H / 2 + 16, bw, 4); ctx.fillStyle = '#c2410c'; ctx.fillRect(W / 2 - bw / 2, H / 2 + 16, bw * f, 4); }\n        return;\n      }\n      if (!s.t1) s.t1 = t;\n      // 0-1,2 junto · 1,2-3,4 abre · 3,4-6,6 legendas · 6,6-15,4 lógica do índice · 15,6-17,6 fecha\n      var CYC = 19, lt = (t - s.t1) % CYC, e;\n      if (lt < 1.2) e = 0; else if (lt < 3.4) e = inout((lt - 1.2) / 2.2); else if (lt < 15.6) e = 1; else if (lt < 17.6) e = 1 - inout((lt - 15.6) / 2); else e = 0;\n      var ip = Math.max(0, Math.min(inout((lt - 6.6) / 0.8), 1 - inout((lt - 14.6) / 0.8)));\n      // pilha centralizada; coluna de legendas fora dela, à direita\n      var colW = 230, size = Math.min((W - 2 * colW - 80) / 1.732, 460), a = size / 1000, gap = 88;\n      var exv = [0.866 * a, 0.5 * a], eyv = [-0.866 * a, 0.5 * a];\n      var cx = W / 2, top0 = H - 50 - size, legX = cx + 0.866 * size + 70;\n      EX.layers.forEach(function(L, i){\n        var z = i * gap * e + Math.sin(t * 1.2 + i) * 2 * e;\n        var O = [cx, top0 - z];\n        if (i === 0) s.O0 = O;\n        ctx.globalAlpha = i === 0 ? 1 : 1 - 0.82 * ip;\n        var c1 = [O[0] + 1000 * exv[0], O[1] + 1000 * exv[1]], c3 = [O[0] + 1000 * eyv[0], O[1] + 1000 * eyv[1]], c2 = [c1[0] + 1000 * eyv[0], c1[1] + 1000 * eyv[1]];\n        if (i === 0){ ctx.fillStyle = 'rgba(29,29,31,.06)'; poly(ctx, [[O[0], O[1] + 14], [c1[0], c1[1] + 14], [c2[0], c2[1] + 14], [c3[0], c3[1] + 14]], 'rgba(29,29,31,.06)'); }\n        else poly(ctx, [O, c1, c2, c3], 'rgba(255,255,255,' + (0.5 * e).toFixed(3) + ')');\n        ctx.save(); ctx.setTransform(s.dpr * exv[0], s.dpr * exv[1], s.dpr * eyv[0], s.dpr * eyv[1], s.dpr * O[0], s.dpr * O[1]); ctx.drawImage(L.canvas, 0, 0); ctx.restore();\n        poly(ctx, [O, c1, c2, c3], null, 'rgba(29,29,31,' + (0.10 + 0.14 * e).toFixed(3) + ')');\n        var la = (e > 0.55 ? (e - 0.55) / 0.45 : 0) * (i === 0 ? 1 : 1 - 0.82 * ip);\n        ctx.globalAlpha = 1;\n        if (la > 0){\n          ctx.globalAlpha = la;\n          ctx.strokeStyle = 'rgba(29,29,31,.28)'; ctx.beginPath(); ctx.moveTo(c1[0] + 6, c1[1]); ctx.lineTo(legX - 12, c1[1]); ctx.stroke();\n          ctx.fillStyle = 'rgba(29,29,31,.45)'; ctx.beginPath(); ctx.arc(c1[0] + 6, c1[1], 2.5, 0, Math.PI * 2); ctx.fill();\n          label(ctx, legX, c1[1] - 3, L.name, 15, '#1d1d1f', 'left', 600);\n          mono(ctx, legX, c1[1] + 16, L.info, 12.5, i === 0 ? '#c2410c' : '#6e6e73');\n          ctx.globalAlpha = 1;\n        }\n      });\n      ctx.globalAlpha = 1;\n      if (ip > 0.01 && EX.tq) drawIndexLogic(ctx, s, ip, lt - 7.4, exv, eyv, cx, size);\n      if (hud && EX.stats) hud.innerHTML = '<b>São Paulo</b><br>' + EX.stats.tiles + ' quadrículas · ' + EX.stats.groups + ' requisição · ' + mb(EX.stats.bytes) + ' baixados agora em ' + (EX.stats.ms / 1000).toFixed(1).replace('.', ',') + ' s<br>' + nf(EX.stats.feats) + ' trechos desenhados';\n    });\n\n    scenes.forEach(function(s){ s.fit(); ro.observe(s.cv); io.observe(s.cv); });\n\n    // ---------- dados das tables ----------\n    function waitAll(ds){\n      return new Promise(function(res, rej){\n        var t0 = Date.now();\n        (function tick(){\n          var n = ds.records().length, tot = (ds.pageInfo() || {}).total || 0, busy = ds.fetching || ds.mutating;\n          if (!busy && n && n >= tot) return res(ds.records());\n          if (!busy && n < tot) ds.loadAll();\n          if (Date.now() - t0 > 30000) return rej(new Error('tempo esgotado'));\n          setTimeout(tick, 150);\n        })();\n      });\n    }\n    function chip(f, lab){ return f && f.fileUrl ? '<a class=\"filechip\" href=\"' + esc(f.fileUrl) + '\" target=\"_blank\" rel=\"noopener\"><i></i>' + esc(lab || f.name) + '</a>' : '<span class=\"note\">—</span>'; }\n    if (qd) waitAll(qd).then(function(rows){\n      Q = rows.slice().sort(function(a, b){ return a.ordem - b.ordem; });\n      var sample = Q.filter(function(r, i){ return i % 110 === 0; }).slice(0, 8);\n      var el = d2.$('[data-sample]');\n      if (el) el.innerHTML = sample.map(function(r){ return '<tr><td class=\"mono\">' + esc(r.quadricula) + '</td><td class=\"mono r\">' + nf(r.byteInicio) + '</td><td class=\"r\">' + mb(r.bytes) + '</td><td class=\"mono hide-sm\" style=\"padding-left:28px;color:#6e6e73\">' + esc(r.range) + '</td></tr>'; }).join('');\n    }).catch(function(e){ txt('[data-hud=\"hero\"]', 'erro lendo a table: ' + e.message); });\n    var ORDER = ['processado', 'índice', 'fonte', 'script', 'documentação'];\n    if (ad) waitAll(ad).then(function(rows){\n      A = rows.slice().sort(function(a, b){ return (ORDER.indexOf(a.papel) - ORDER.indexOf(b.papel)) || (a.ordem - b.ordem); });\n      var tot = A.reduce(function(s, r){ return s + (r.bytes || 0); }, 0);\n      var sum = d2.$('[data-catsum]'); if (sum) sum.innerHTML = 'Arquivos<br><span class=\"note\" style=\"font-weight:400\">' + A.length + ' no Drive, ' + mb(tot) + '</span>';\n      var el = d2.$('[data-cat]');\n      if (el) el.innerHTML = A.map(function(r){ var comp = r.trafegado && r.trafegado < r.bytes * .9 ? '<div class=\"note\">' + mb(r.trafegado) + ' na rede</div>' : ''; return '<tr><td>' + chip(r.arquivo, r.nome) + '<div class=\"note\">' + esc(r.formato) + '</div></td><td class=\"hide-sm note\">' + esc(r.papel) + '</td><td class=\"r\">' + mb(r.bytes) + comp + '</td></tr>'; }).join('');\n    });\n\n    d2.on('[data-go]', 'click', function(e, el){ var sec = d2.$('#' + el.getAttribute('data-go')); if (sec) DOC.scrollTo({ top: sec.offsetTop - 56, behavior: 'smooth' }); });\n  }\n};"}},"quadriculas":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quadriculas"}}}},"arquivos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivos"}}}}}}}}}},{"_id":"6ab5d640dc8440a163c02c82","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"F1_Bento_Dashboard","package":"gui","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 BASE = 'https://api.jolpi.ca/ergast/f1/';\nconst esc = s => String(s ?? '').replace(/[&<>\"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\nconst raceTs = r => new Date(r.date + 'T' + (r.time || '23:59:00Z')).getTime();\n\nasync function get(path) {\n  const res = await fetch(BASE + path);\n  if (!res.ok) throw new Error('HTTP ' + res.status + ' em ' + path);\n  const j = await res.json();\n  return j.MRData;\n}\n\nasync function load(d2) {\n  const season = String(d2.param('season', 'current') || 'current');\n  d2.setState({ loading: true, error: null });\n  try {\n    const [ds, cs, last, cal] = await Promise.all([\n      get(season + '/driverStandings.json'),\n      get(season + '/constructorStandings.json'),\n      get(season + '/last/results.json'),\n      get(season + '/races.json?limit=100')\n    ]);\n    const dl = (ds.StandingsTable.StandingsLists || [])[0] || {};\n    const cl = (cs.StandingsTable.StandingsLists || [])[0] || {};\n    d2.setState({\n      loading: false,\n      data: {\n        season: dl.season || ds.StandingsTable.season || season,\n        round: Number(dl.round || 0),\n        drivers: dl.DriverStandings || [],\n        teams: cl.ConstructorStandings || [],\n        last: ((last.RaceTable && last.RaceTable.Races) || [])[0] || null,\n        races: (cal.RaceTable && cal.RaceTable.Races) || []\n      }\n    });\n  } catch (e) {\n    d2.setState({ loading: false, error: String((e && e.message) || e) });\n  }\n}\n\nexport default {\n  css: `\n    :host{display:block;width:100%;height:100%}\n    *{box-sizing:border-box}\n    .wrap{width:100%;height:100%;background:#111412;color:#E7ECE8;font-family:Inter,system-ui,-apple-system,sans-serif;padding:24px;display:flex;flex-direction:column;gap:16px;overflow:hidden}\n    .hdr{display:flex;align-items:baseline;justify-content:space-between}\n    .hdr h1{margin:0;font-size:22px;font-weight:700;letter-spacing:-.01em}\n    .hdr h1 span{color:#A3E635}\n    .sub{color:#8A948D;font-size:12px}\n    .grid{flex:1;min-height:0;display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(3,1fr);gap:14px}\n    .card{background:#1C211E;border:1px solid #262C28;border-radius:16px;padding:18px;display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden;position:relative}\n    .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:#8A948D;margin-bottom:10px}\n    .big{grid-column:3/5;grid-row:1/3}\n    .c-lead{grid-column:1;grid-row:1}\n    .c-gap{grid-column:2;grid-row:1}\n    .c-next{grid-column:1/3;grid-row:2}\n    .wide{grid-column:3/5}\n    .wide3{grid-column:2/4}\n    .lime{background:#A3E635;color:#111412;border-color:#A3E635}\n    .lime .lbl,.lime .muted{color:#111412;opacity:.65}\n    .mono{font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace}\n    .muted{color:#8A948D}\n    .lst{display:flex;flex-direction:column;flex:1;min-height:0;justify-content:space-between}\n    .row{display:grid;grid-template-columns:26px 1fr 64px;align-items:center;gap:10px;font-size:13px}\n    .row .pos{color:#8A948D;font-weight:600}\n    .row .nm{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .row .nm b{font-weight:600}\n    .row .tm{font-size:11px;color:#8A948D;margin-left:6px}\n    .row .pts{text-align:right;font-weight:600}\n    .bar{height:5px;background:#262C28;border-radius:3px;margin-top:5px;overflow:hidden}\n    .bar i{display:block;height:100%;background:#4B5A4F;border-radius:3px}\n    .row.p1 .pos,.row.p1 .pts{color:#A3E635}\n    .row.p1 .bar i{background:#A3E635}\n    .kpi{font-size:46px;font-weight:700;line-height:1;letter-spacing:-.02em}\n    .kpi small{font-size:15px;font-weight:600;margin-left:4px}\n    .yel{color:#FACC15}\n    .grn{color:#4ADE80}\n    .grow{flex:1}\n    .next{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex:1}\n    .next h2{margin:0 0 4px;font-size:22px;font-weight:700}\n    .cd{display:flex;gap:12px}\n    .cd div{text-align:center}\n    .cd b{display:block;font-size:40px;line-height:1;color:#FACC15}\n    .cd span{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:#8A948D}\n    .ring{display:flex;align-items:center;gap:16px;flex:1}\n    .pod{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;align-items:end;flex:1}\n    .step{border-radius:10px;background:#262C28;padding:10px;display:flex;flex-direction:column;justify-content:flex-end;min-width:0}\n    .step.s1{background:#A3E635;color:#111412;height:100%}\n    .step.s2{height:80%}\n    .step.s3{height:62%}\n    .step .p{font-size:22px;font-weight:700}\n    .step .d{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .step .t{font-size:11px;opacity:.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .trow{display:grid;grid-template-columns:18px 1fr auto;gap:8px;font-size:12px;align-items:center}\n    .trow:first-child{color:#4ADE80;font-weight:600}\n    .skel{background:linear-gradient(90deg,#1C211E,#232925,#1C211E);background-size:200% 100%;animation:sh 1.4s infinite}\n    @keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}\n    .err{margin:auto;text-align:center;max-width:420px}\n    .btn{margin-top:14px;background:#A3E635;color:#111412;border:0;border-radius:999px;padding:10px 18px;font-weight:700;cursor:pointer}\n  `,\n\n  render(d2) {\n    const s = d2.state;\n    const now = s.now || Date.now();\n\n    if (s.error) {\n      return `<div class=\"wrap\"><div class=\"err\"><div class=\"lbl\">Erro ao carregar dados da F1</div><div class=\"muted\">${esc(s.error)}</div><button class=\"btn retry\">Tentar de novo</button></div></div>`;\n    }\n\n    if (!s.data) {\n      return `<div class=\"wrap\"><div class=\"hdr\"><h1>F1 <span>Dashboard</span></h1><div class=\"sub\">carregando…</div></div>\n        <div class=\"grid\">\n          <div class=\"card big skel\"></div><div class=\"card c-lead skel\"></div><div class=\"card c-gap skel\"></div>\n          <div class=\"card c-next skel\"></div><div class=\"card skel\"></div><div class=\"card wide3 skel\"></div><div class=\"card skel\"></div>\n        </div></div>`;\n    }\n\n    const D = s.data;\n    const drivers = D.drivers.slice(0, 10);\n    const maxPts = Math.max(1, ...drivers.map(x => Number(x.points)));\n    const p1 = D.drivers[0], p2 = D.drivers[1];\n    const total = D.races.length || 0;\n    const nextRace = D.races.find(r => raceTs(r) > now) || null;\n\n    const standings = drivers.length ? drivers.map((x, i) => {\n      const team = (x.Constructors && x.Constructors[0] && x.Constructors[0].name) || '';\n      const w = (Number(x.points) / maxPts * 100).toFixed(1);\n      return `<div class=\"row ${i === 0 ? 'p1' : ''}\" data-key=\"${esc(x.Driver.driverId)}\">\n        <div class=\"pos mono\">${esc(x.position)}</div>\n        <div><div class=\"nm\"><b>${esc(x.Driver.givenName)} ${esc(x.Driver.familyName)}</b><span class=\"tm\">${esc(team)}</span></div><div class=\"bar\"><i style=\"width:${w}%\"></i></div></div>\n        <div class=\"pts mono\">${esc(x.points)}</div>\n      </div>`;\n    }).join('') : `<div class=\"muted\">Sem classificação ainda.</div>`;\n\n    const leader = p1 ? `<div class=\"lbl\">Líder do campeonato</div>\n      <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:space-between\">\n        <div style=\"font-size:18px;font-weight:700\">${esc(p1.Driver.givenName)} ${esc(p1.Driver.familyName)}</div>\n        <div><div class=\"kpi mono\">${esc(p1.points)}<small>pts</small></div>\n        <div class=\"muted\" style=\"font-size:12px;margin-top:6px\">${esc(p1.wins)} vitórias · ${esc((p1.Constructors[0] || {}).name || '')}</div></div>\n      </div>` : `<div class=\"lbl\">Líder</div><div class=\"muted\">—</div>`;\n\n    const gapCard = (p1 && p2) ? `<div class=\"lbl\">Vantagem sobre P2</div>\n      <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:space-between\">\n        <div style=\"font-size:14px\" class=\"muted\">vs ${esc(p2.Driver.givenName)} ${esc(p2.Driver.familyName)}</div>\n        <div class=\"kpi mono yel\">+${esc(Number(p1.points) - Number(p2.points))}<small>pts</small></div>\n      </div>` : `<div class=\"lbl\">Vantagem</div><div class=\"muted\">—</div>`;\n\n    let nextCard;\n    if (nextRace) {\n      const ms = Math.max(0, raceTs(nextRace) - now);\n      const dd = Math.floor(ms / 864e5), hh = Math.floor(ms % 864e5 / 36e5), mm = Math.floor(ms % 36e5 / 6e4);\n      const when = new Date(raceTs(nextRace)).toLocaleString('pt-BR', { weekday: 'short', day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });\n      nextCard = `<div class=\"lbl\">Próxima corrida · Round ${esc(nextRace.round)}</div>\n        <div class=\"next\">\n          <div style=\"min-width:0\"><h2>${esc(nextRace.raceName)}</h2>\n            <div class=\"muted\" style=\"font-size:13px\">${esc(nextRace.Circuit.circuitName)} — ${esc(nextRace.Circuit.Location.locality)}, ${esc(nextRace.Circuit.Location.country)}</div>\n            <div style=\"font-size:13px;margin-top:6px\">${esc(when)}</div></div>\n          <div class=\"cd mono\"><div><b>${dd}</b><span>dias</span></div><div><b>${hh}</b><span>horas</span></div><div><b>${mm}</b><span>min</span></div></div>\n        </div>`;\n    } else {\n      nextCard = `<div class=\"lbl\">Próxima corrida</div><div class=\"next\"><h2>Temporada encerrada 🏁</h2></div>`;\n    }\n\n    const pairs = D.teams.map(t => {\n      const cid = t.Constructor.constructorId;\n      const ds = D.drivers.filter(x => (x.Constructors || []).some(c => c.constructorId === cid)).slice(0, 2);\n      return { team: t.Constructor.name, ds };\n    }).filter(p => p.ds.length === 2).slice(0, 5);\n    let duel;\n    if (pairs.length) {\n      const idx = (s.duelIdx || 0) % pairs.length;\n      const p = pairs[idx], a = p.ds[0], b = p.ds[1];\n      const pa = Number(a.points), pb = Number(b.points);\n      const share = (pa + pb) ? (pa / (pa + pb) * 100).toFixed(1) : 50;\n      duel = `<div class=\"lbl\">Duelo interno · ${esc(p.team)}</div>\n        <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:center;gap:10px\">\n          <div style=\"display:flex;justify-content:space-between;font-size:13px;font-weight:600\"><span>${esc(a.Driver.familyName)}</span><span class=\"muted\">${esc(b.Driver.familyName)}</span></div>\n          <div style=\"display:flex;height:12px;border-radius:6px;overflow:hidden;background:#262C28\"><div style=\"width:${share}%;background:#A3E635;transition:width .6s ease\"></div><div style=\"flex:1;background:#4B5A4F\"></div></div>\n          <div class=\"mono\" style=\"display:flex;justify-content:space-between;font-size:28px;font-weight:700;line-height:1\"><span>${esc(pa)}</span><span class=\"muted\">${esc(pb)}</span></div>\n          <div class=\"muted\" style=\"display:flex;justify-content:space-between;font-size:11px\"><span>${esc(a.wins)} vitórias</span><span>${esc(b.wins)} vitórias</span></div>\n        </div>\n        <div style=\"display:flex;gap:6px;justify-content:center\">${pairs.map((q, i) => `<button class=\"dot\" data-duel=\"${i}\" title=\"${esc(q.team)}\" style=\"width:8px;height:8px;border-radius:50%;border:0;padding:0;cursor:pointer;background:${i === idx ? '#A3E635' : '#4B5A4F'}\"></button>`).join('')}</div>`;\n    } else {\n      duel = `<div class=\"lbl\">Duelo interno</div><div class=\"muted\">Sem dados ainda.</div>`;\n    }\n\n    let podium;\n    if (D.last && D.last.Results && D.last.Results.length) {\n      const res = D.last.Results.slice(0, 3);\n      const order = [res[1], res[0], res[2]].filter(Boolean);\n      podium = `<div class=\"lbl\">Última corrida · ${esc(D.last.raceName)}</div>\n        <div class=\"pod\">${order.map(r => {\n          const cls = r.position === '1' ? 's1' : r.position === '2' ? 's2' : 's3';\n          const t = (r.Time && r.Time.time) || r.status || '';\n          return `<div class=\"step ${cls}\" data-key=\"${esc(r.Driver.driverId)}\"><div class=\"p mono\">P${esc(r.position)}</div><div class=\"d\">${esc(r.Driver.givenName)} ${esc(r.Driver.familyName)}</div><div class=\"t\">${esc(r.Constructor.name)} · ${esc(t)}</div></div>`;\n        }).join('')}</div>`;\n    } else {\n      podium = `<div class=\"lbl\">Última corrida</div><div class=\"muted\">Nenhuma corrida disputada ainda.</div>`;\n    }\n\n    const teams = `<div class=\"lbl\">Construtores</div>\n      <div class=\"lst\">${D.teams.slice(0, 6).map(t => `<div class=\"trow\" data-key=\"${esc(t.Constructor.constructorId)}\"><span class=\"mono\">${esc(t.position)}</span><span style=\"white-space:nowrap;overflow:hidden;text-overflow:ellipsis\">${esc(t.Constructor.name)}</span><span class=\"mono\">${esc(t.points)}</span></div>`).join('') || '<div class=\"muted\">—</div>'}</div>`;\n\n    return `<div class=\"wrap\" data-ready=\"1\">\n      <div class=\"hdr\"><h1>F1 <span>Dashboard</span> · ${esc(D.season)}</h1><div class=\"sub\">dados: Jolpica F1 API · atualizado ${esc(new Date(now).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }))}</div></div>\n      <div class=\"grid\">\n        <div class=\"card big\"><div class=\"lbl\">Campeonato de pilotos · Top 10</div><div class=\"lst\">${standings}</div></div>\n        <div class=\"card lime c-lead\">${leader}</div>\n        <div class=\"card c-gap\">${gapCard}</div>\n        <div class=\"card c-next\">${nextCard}</div>\n        <div class=\"card\">${duel}</div>\n        <div class=\"card wide3\">${podium}</div>\n        <div class=\"card\">${teams}</div>\n      </div>\n    </div>`;\n  },\n\n  mounted(d2) {\n    d2.state.now = Date.now();\n    load(d2);\n    const t = setInterval(() => { d2.state.now = Date.now(); }, 30000);\n    d2.onUnmount(() => clearInterval(t));\n    d2.on('.retry', 'click', () => load(d2));\n    const rot = setInterval(() => { d2.state.duelIdx = (d2.state.duelIdx || 0) + 1; }, 8000);\n    d2.onUnmount(() => clearInterval(rot));\n    d2.on('[data-duel]', 'click', (e) => { const el = e.target.closest('[data-duel]'); if (el) d2.state.duelIdx = Number(el.dataset.duel); });\n  }\n};"}},"season":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"season"}}}}}}}}}},{"_id":"6ab6e7c6e92b0a71790318bb","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Geo_Quadriculas_Dashboard","package":"gui","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 LIB = { url: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', global: 'L', css: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' }\nconst C = { fundo: '#f4f1ea', painel: '#ffffff', texto: '#2b2a28', rod: '#c2410c', rio: '#0e7490', dest: '#65a30d', muted: '#8a857c', borda: '#e4ded2' }\nconst NIV = ['#84cc16', '#65a30d', '#3f6212', '#1a2e05']\nconst TILES = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'\nconst MIN_ZOOM_VET = 7\nlet G = null\n\nfunction isRio(p) {\n  if (!p || typeof p !== 'object') return false\n  if (p.waterway) return true\n  if (p.highway) return false\n  for (const k of ['k', 'tipo', 'camada', 'kind', 'layer', 't', 'classe', 'class', 'cat']) {\n    const v = String(p[k] == null ? '' : p[k]).toLowerCase()\n    if (!v) continue\n    if (/rio|river|water|stream|canal|hidro|agua|água/.test(v)) return true\n    if (/rod|road|highway|via|estrada/.test(v)) return false\n  }\n  return false\n}\nfunction toLines(coords) {\n  if (!Array.isArray(coords) || !coords.length) return []\n  if (typeof coords[0][0] === 'number') return [coords.map(c => [c[1], c[0]])]\n  return coords.flatMap(toLines)\n}\nfunction parseObj(o, out) {\n  if (!o || typeof o !== 'object') return\n  if (o.type === 'FeatureCollection' && Array.isArray(o.features)) { o.features.forEach(f => parseObj(f, out)); return }\n  if (Array.isArray(o.features)) { o.features.forEach(f => parseObj(f, out)); return }\n  const g = o.geometry || o.g || (o.coordinates ? o : null)\n  const coords = g ? g.coordinates : (o.c || o.coords)\n  if (!coords) return\n  const p = o.properties || o.p || o\n  const lines = toLines(coords)\n  if (!lines.length) return\n  ;(isRio(p) ? out.rios : out.rods).push(...lines)\n}\nfunction bytesFmt(n) { if (!n) return '0 B'; const u = ['B', 'KB', 'MB', 'GB']; let i = 0; while (n >= 1024 && i < 3) { n /= 1024; i++ } return n.toFixed(i ? 1 : 0) + ' ' + u[i] }\nfunction numFmt(n) { return (n || 0).toLocaleString('pt-BR') }\nfunction heatColor(t) {\n  const a = [253, 230, 138], b = [194, 65, 12]\n  return 'rgb(' + a.map((v, i) => Math.round(v + (b[i] - v) * t)).join(',') + ')'\n}\n\nfunction build(d2, L, mapEl, miniEl) {\n  const g = { mapEl, cells: [], vis: { base: true, rodovias: true, rios: true, grade: true, heat: false }, nivel: '', loaded: 0, downloaded: 0, queue: [], active: 0 }\n  G = g\n  const map = L.map(mapEl, { preferCanvas: true, zoomSnap: 0.5, zoomControl: false }).setView([-15, -52], 4)\n  L.control.zoom({ position: 'topright' }).addTo(map)\n  g.map = map\n  g.L = L\n  g.renderer = L.canvas({ padding: 0.4 })\n  g.base = L.tileLayer(TILES, { attribution: '© OpenStreetMap', maxZoom: 19, className: 'soft' }).addTo(map)\n  g.gridLayer = L.layerGroup().addTo(map)\n  g.heatLayer = L.layerGroup()\n  g.rodLayer = L.layerGroup().addTo(map)\n  g.rioLayer = L.layerGroup().addTo(map)\n\n  const mini = L.map(miniEl, { zoomControl: false, attributionControl: false, dragging: false, scrollWheelZoom: false, doubleClickZoom: false, boxZoom: false, keyboard: false, touchZoom: false }).setView([-15, -52], 2)\n  L.tileLayer(TILES, { className: 'soft' }).addTo(mini)\n  g.mini = mini\n  g.miniRect = L.rectangle(map.getBounds(), { color: C.dest, weight: 1.5, fillOpacity: 0.2, interactive: false }).addTo(mini)\n  mini.on('click', e => map.setView(e.latlng, Math.max(map.getZoom(), 6)))\n\n  map.on('moveend zoomend', () => onMove(d2))\n  const ro = new ResizeObserver(() => { map.invalidateSize(); mini.invalidateSize() })\n  ro.observe(mapEl)\n  d2.onUnmount(() => { try { ro.disconnect(); map.remove(); mini.remove() } catch (e) {} ; if (G === g) G = null })\n  setTimeout(() => { map.invalidateSize(); mini.invalidateSize() }, 50)\n\n  wireUI(d2)\n  renderLayersPanel(d2)\n  loadIndex(d2)\n}\n\nasync function loadIndex(d2) {\n  const g = G\n  toast(d2, 'Carregando índice de quadrículas…')\n  const ds = d2.data('dataset')\n  if (!ds) { toast(d2, 'Parâmetro dataset não configurado'); return }\n  let recs = []\n  try { recs = await ds.loadAll() } catch (e) { d2.log('loadAll falhou', e) }\n  if (!recs || !recs.length) recs = ds.records()\n  for (let i = 0; i < 40 && (!recs || recs.length < 2); i++) {\n    await new Promise(r => setTimeout(r, 500))\n    if (i === 4) { try { ds.refresh() } catch (e) {} }\n    if (i === 10) { try { recs = await ds.loadAll() } catch (e) {} }\n    if (!recs || recs.length < 2) recs = ds.records()\n  }\n  if (g !== G) return\n  if (!recs || !recs.length) { toast(d2, '⚠️ Não consegui ler o índice (0 registros)'); return }\n  const L = g.L\n  g.cells = recs.map(r => {\n    const s = Number(r.celulaGraus) || 1, lon = Number(r.celulaLon), lat = Number(r.celulaLat)\n    return { r, nivel: Number(r.nivel) || 0, bounds: L.latLngBounds([lat, lon], [lat + s, lon + s]), state: null }\n  }).filter(c => !isNaN(c.bounds.getSouth()))\n  g.maxFeat = Math.max(1, ...g.cells.map(c => c.r.feicoes || 0))\n  drawGrid(d2)\n  toast(d2, g.cells.length + ' quadrículas carregadas', 2500)\n  onMove(d2)\n}\n\nfunction visibleCells() { const g = G; return g.cells.filter(c => g.nivel === '' || String(c.nivel) === g.nivel) }\n\nfunction drawGrid(d2) {\n  const g = G, L = g.L\n  g.gridLayer.clearLayers(); g.heatLayer.clearLayers()\n  const lmax = Math.log(g.maxFeat + 1)\n  visibleCells().forEach(c => {\n    const col = NIV[Math.min(3, c.nivel)]\n    const rect = L.rectangle(c.bounds, { renderer: g.renderer, color: col, weight: c.nivel ? 1.2 : 0.8, opacity: 0.75, fillColor: col, fillOpacity: 0.03 })\n    rect.on('click', () => openCell(d2, c))\n    g.gridLayer.addLayer(rect)\n    const t = Math.log((c.r.feicoes || 0) + 1) / lmax\n    g.heatLayer.addLayer(L.rectangle(c.bounds, { renderer: g.renderer, stroke: false, fillColor: heatColor(t), fillOpacity: 0.15 + 0.55 * t, interactive: false }))\n  })\n}\n\nfunction popupHtml(c) {\n  const r = c.r\n  const st = c.state === 'ok' ? '✅ vetores carregados' : c.state === 'loading' ? '⏳ carregando vetores…' : c.state === 'erro' ? '⚠️ ' + (c.err || 'erro ao carregar') : '—'\n  return '<div class=\"pop\"><b>' + (r.quadricula || '') + '</b>'\n    + '<div class=\"pr\"><span>Nível</span><b>' + c.nivel + ' · ' + (r.celulaGraus || '') + '°</b></div>'\n    + '<div class=\"pr\"><span>Feições</span><b>' + numFmt(r.feicoes) + '</b></div>'\n    + '<div class=\"pr\"><span style=\"color:' + C.rod + '\">Rodovias</span><b>' + numFmt(r.rodovias) + '</b></div>'\n    + '<div class=\"pr\"><span style=\"color:' + C.rio + '\">Rios</span><b>' + numFmt(r.rios) + '</b></div>'\n    + '<div class=\"pr\"><span>Tamanho</span><b>' + bytesFmt(r.bytes) + '</b></div>'\n    + '<div class=\"pr\"><span>Range</span><code>' + (r.range || '') + '</code></div>'\n    + '<div class=\"ps\">' + st + '</div></div>'\n}\nfunction openCell(d2, c) {\n  const g = G\n  c.popup = g.L.popup({ maxWidth: 260 }).setLatLng(c.bounds.getCenter()).setContent(popupHtml(c)).openOn(g.map)\n  enqueue(d2, c, true)\n}\n\nfunction enqueue(d2, c, front) {\n  const g = G\n  if (c.state) return\n  c.state = 'queued'\n  front ? g.queue.unshift(c) : g.queue.push(c)\n  pump(d2)\n}\nfunction pump(d2) {\n  const g = G\n  while (g.active < 4 && g.queue.length) { const c = g.queue.shift(); g.active++; loadCell(d2, c).finally(() => { g.active--; pump(d2); renderStats(d2) }) }\n}\nasync function loadCell(d2, c) {\n  const g = G, L = g.L, r = c.r\n  c.state = 'loading'; refreshPopup(c)\n  const url = r.arquivo && r.arquivo.fileUrl\n  if (!url || !r.range) { c.state = 'erro'; c.err = 'sem arquivo/range'; refreshPopup(c); return }\n  try {\n    const res = await fetch(url, { headers: { Range: r.range } })\n    if (res.status !== 206) { try { res.body && res.body.cancel() } catch (e) {} ; throw new Error('servidor não aceitou Range (HTTP ' + res.status + ')') }\n    const txt = await res.text()\n    if (g !== G) return\n    g.downloaded += r.bytes || txt.length\n    const out = { rods: [], rios: [] }\n    txt.split('\\n').forEach(line => { line = line.trim(); if (!line) return; try { parseObj(JSON.parse(line), out) } catch (e) {} })\n    if (out.rods.length) g.rodLayer.addLayer(L.polyline(out.rods, { renderer: g.renderer, color: C.rod, weight: 1.4, opacity: 0.9, interactive: false }))\n    if (out.rios.length) g.rioLayer.addLayer(L.polyline(out.rios, { renderer: g.renderer, color: C.rio, weight: 1.6, opacity: 0.85, interactive: false }))\n    c.state = 'ok'; g.loaded++\n  } catch (e) { c.state = 'erro'; c.err = e.message; toast(d2, '⚠️ ' + e.message, 4000) }\n  refreshPopup(c)\n}\nfunction refreshPopup(c) { if (c.popup && c.popup.isOpen && c.popup.isOpen()) c.popup.setContent(popupHtml(c)) }\n\nfunction onMove(d2) {\n  const g = G\n  if (!g) return\n  const b = g.map.getBounds()\n  g.miniRect.setBounds(b)\n  g.mini.setView(b.getCenter(), Math.max(1, Math.min(5, g.map.getZoom() - 4)), { animate: false })\n  renderStats(d2)\n  if (!g.cells.length) return\n  if (!(g.vis.rodovias || g.vis.rios)) return\n  if (g.map.getZoom() < MIN_ZOOM_VET) { toast(d2, 'Dá zoom (≥ ' + MIN_ZOOM_VET + ') pra carregar rodovias e rios · ou clica numa quadrícula'); return }\n  toast(d2, '')\n  const ctr = b.getCenter()\n  visibleCells().filter(c => !c.state && b.intersects(c.bounds))\n    .sort((a, z) => a.bounds.getCenter().distanceTo(ctr) - z.bounds.getCenter().distanceTo(ctr))\n    .slice(0, 16).forEach(c => enqueue(d2, c))\n}\n\nfunction toast(d2, msg, ms) {\n  const el = d2.$('#toast'); if (!el) return\n  el.textContent = msg; el.style.display = msg ? 'block' : 'none'\n  clearTimeout(el._t); if (msg && ms) el._t = setTimeout(() => { el.style.display = 'none' }, ms)\n}\n\nfunction renderStats(d2) {\n  const g = G, el = d2.$('#stats'); if (!g || !el) return\n  const b = g.map.getBounds()\n  const vis = visibleCells().filter(c => b.intersects(c.bounds))\n  const s = vis.reduce((a, c) => { a.f += c.r.feicoes || 0; a.r += c.r.rodovias || 0; a.w += c.r.rios || 0; a.by += c.r.bytes || 0; return a }, { f: 0, r: 0, w: 0, by: 0 })\n  const carregando = g.active + g.queue.length\n  el.innerHTML =\n    '<div class=\"kpi\"><span>Quadrículas</span><b>' + numFmt(vis.length) + '</b></div>'\n    + '<div class=\"kpi\"><span>Feições</span><b>' + numFmt(s.f) + '</b></div>'\n    + '<div class=\"kpi\"><span><i style=\"background:' + C.rod + '\"></i>Rodovias</span><b>' + numFmt(s.r) + '</b></div>'\n    + '<div class=\"kpi\"><span><i style=\"background:' + C.rio + '\"></i>Rios</span><b>' + numFmt(s.w) + '</b></div>'\n    + '<div class=\"kpi\"><span>Tamanho da área</span><b>' + bytesFmt(s.by) + '</b></div>'\n    + '<div class=\"kpi hi\"><span>Baixado até agora</span><b>' + bytesFmt(g.downloaded) + '</b></div>'\n    + '<div class=\"sub\">' + g.loaded + ' quadrícula(s) com vetores' + (carregando ? ' · ' + carregando + ' na fila' : '') + '</div>'\n}\n\nfunction renderLayersPanel(d2) {\n  const g = G, el = d2.$('#layers'); if (!el) return\n  const items = [\n    ['base', 'Mapa-base', '<i class=\"sw\" style=\"background:linear-gradient(135deg,#eceae4,#d9d6cf)\"></i>'],\n    ['rodovias', 'Rodovias', '<i class=\"ln\" style=\"background:' + C.rod + '\"></i>'],\n    ['rios', 'Rios', '<i class=\"ln\" style=\"background:' + C.rio + '\"></i>'],\n    ['grade', 'Grade das quadrículas', '<i class=\"sw\" style=\"border:2px solid ' + NIV[1] + '\"></i>'],\n    ['heat', 'Heatmap de densidade', '<i class=\"sw\" style=\"background:linear-gradient(90deg,' + heatColor(0) + ',' + heatColor(1) + ')\"></i>']\n  ]\n  el.innerHTML = items.map(([k, t, ic]) => '<label class=\"tg' + (g.vis[k] ? ' on' : '') + '\"><input type=\"checkbox\" data-layer=\"' + k + '\"' + (g.vis[k] ? ' checked' : '') + '>' + ic + '<span>' + t + '</span></label>').join('')\n}\n\nfunction applyVis() {\n  const g = G, m = g.map\n  const set = (lay, on) => { on ? (m.hasLayer(lay) || lay.addTo(m)) : m.removeLayer(lay) }\n  set(g.base, g.vis.base); set(g.heatLayer, g.vis.heat); set(g.gridLayer, g.vis.grade); set(g.rioLayer, g.vis.rios); set(g.rodLayer, g.vis.rodovias)\n  if (g.vis.heat) g.heatLayer.bringToBack && g.heatLayer.eachLayer(l => l.bringToBack())\n}\n\nfunction doSearch(d2, raw) {\n  const g = G, box = d2.$('#qres'); if (!box) return\n  const q = String(raw || '').trim()\n  if (!q) { box.innerHTML = ''; return }\n  const m = q.match(/^(-?\\d+(?:[.,]\\d+)?)\\s*[\\/;\\s]\\s*(-?\\d+(?:[.,]\\d+)?)$/)\n  let hits = []\n  if (m) {\n    const lon = parseFloat(m[1].replace(',', '.')), lat = parseFloat(m[2].replace(',', '.'))\n    const pt = g.L.latLng(lat, lon)\n    hits = g.cells.filter(c => c.bounds.contains(pt) || (c.r.quadricula || '').includes(m[1] + '/' + m[2]))\n    if (!hits.length) { g.map.flyTo(pt, 8); box.innerHTML = '<div class=\"qe\">Sem quadrícula aí — centralizei no ponto</div>'; return }\n  } else {\n    let qq = q.toLowerCase()\n    const qm = qq.match(/^q\\s*0*(\\d+)$/)\n    hits = g.cells.filter(c => {\n      const n = (c.r.quadricula || '').toLowerCase()\n      if (qm) { const nm = n.match(/^q0*(\\d+)/); return nm && nm[1] === qm[1] }\n      return n.includes(qq)\n    })\n  }\n  hits.sort((a, z) => z.nivel - a.nivel)\n  box.innerHTML = hits.length ? hits.slice(0, 6).map(c => '<button class=\"qr\" data-q=\"' + g.cells.indexOf(c) + '\"><b>' + (c.r.quadricula || '').split(' · ')[0] + '</b><span>' + (c.r.quadricula || '').split(' · ').slice(1).join(' · ') + ' · ' + numFmt(c.r.feicoes) + ' feições</span></button>').join('') + (hits.length > 6 ? '<div class=\"qe\">+' + (hits.length - 6) + ' resultados</div>' : '') : '<div class=\"qe\">Nada encontrado</div>'\n  if (hits.length === 1) goCell(d2, hits[0])\n}\nfunction goCell(d2, c) { G.map.flyToBounds(c.bounds.pad(0.3), { duration: 0.8 }); setTimeout(() => openCell(d2, c), 850) }\n\nfunction wireUI(d2) {\n  const g = G\n  d2.on('#layers input', 'change', e => { const k = e.target.dataset.layer; g.vis[k] = e.target.checked; e.target.closest('.tg').classList.toggle('on', e.target.checked); applyVis(); onMove(d2) })\n  d2.on('#q', 'keydown', e => { if (e.key === 'Enter') doSearch(d2, e.target.value) })\n  d2.on('#q', 'input', e => { clearTimeout(g._qt); g._qt = setTimeout(() => doSearch(d2, e.target.value), 250) })\n  d2.on('#qres .qr', 'click', e => { const b = e.target.closest('.qr'); const c = g.cells[Number(b.dataset.q)]; if (c) goCell(d2, c) })\n  d2.on('#niv', 'change', e => { g.nivel = e.target.value; drawGrid(d2); renderStats(d2); onMove(d2) })\n  d2.on('#reset', 'click', () => g.map.flyTo([-15, -52], 4))\n}\n\nfunction init(d2) {\n  const L = d2.lib(LIB)\n  if (!L) return\n  const mapEl = d2.$('#map'), miniEl = d2.$('#mini')\n  if (!mapEl || !miniEl) return\n  if (G && G.mapEl === mapEl && mapEl.querySelector('.leaflet-pane')) return\n  if (G) { try { G.map.remove(); G.mini.remove() } catch (e) {} ; G = null }\n  build(d2, L, mapEl, miniEl)\n}\n\nexport default {\n  css: `\n    :host{display:block;width:100%;height:100%}\n    *{box-sizing:border-box}\n    .app{position:relative;width:100%;height:100%;display:grid;grid-template-columns:minmax(0,1fr) 22%;gap:2.5%;padding:2.2% 2.5%;background:${C.fundo};color:${C.texto};font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px}\n    .mapwrap{position:relative;border-radius:14px;overflow:hidden;background:${C.painel};box-shadow:0 1px 0 ${C.borda},0 8px 24px rgba(60,50,30,.08);min-height:300px}\n    #map{position:absolute;inset:0;background:#efece5}\n    .tag{position:absolute;left:14px;top:12px;z-index:500;background:${C.painel};padding:6px 10px;border-radius:8px;font-weight:700;letter-spacing:.06em;font-size:11px;box-shadow:0 2px 8px rgba(0,0,0,.08)}\n    #mini{position:absolute;right:12px;bottom:26px;width:170px;height:130px;z-index:600;border-radius:10px;overflow:hidden;border:2px solid ${C.painel};box-shadow:0 4px 14px rgba(0,0,0,.15);cursor:pointer}\n    #toast{display:none;position:absolute;left:50%;top:12px;transform:translateX(-50%);z-index:700;background:${C.texto};color:${C.fundo};padding:7px 12px;border-radius:999px;font-size:12px;white-space:nowrap;max-width:80%;overflow:hidden;text-overflow:ellipsis}\n    .side{display:grid;grid-template-rows:auto minmax(0,1fr);gap:4%;min-height:0}\n    .card{background:${C.painel};border-radius:14px;padding:14px;box-shadow:0 1px 0 ${C.borda},0 8px 24px rgba(60,50,30,.06);min-height:0;overflow:auto}\n    h4{margin:0 0 10px;font-size:11px;letter-spacing:.08em;font-weight:800;color:${C.texto}}\n    h5{margin:14px 0 6px;font-size:10px;letter-spacing:.08em;color:${C.muted};font-weight:700;text-transform:uppercase}\n    input[type=text],select{width:100%;padding:8px 10px;border:1px solid ${C.borda};border-radius:8px;background:${C.fundo};color:${C.texto};font:inherit;font-size:12px;outline:none}\n    input[type=text]:focus,select:focus{border-color:${C.dest}}\n    .row{display:flex;gap:6px;margin-top:6px}\n    #qres{margin-top:6px;display:flex;flex-direction:column;gap:4px}\n    .qr{display:flex;flex-direction:column;align-items:flex-start;text-align:left;border:1px solid ${C.borda};background:${C.painel};border-radius:8px;padding:6px 8px;cursor:pointer;font:inherit;color:${C.texto}}\n    .qr:hover{border-color:${C.dest};background:#f7fbef}\n    .qr span{font-size:11px;color:${C.muted}}\n    .qe{font-size:11px;color:${C.muted};padding:2px}\n    .tg{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:8px;cursor:pointer;user-select:none;color:${C.muted}}\n    .tg.on{color:${C.texto};background:#f3f7ea}\n    .tg input{accent-color:${C.dest};margin:0}\n    .ln{display:inline-block;width:16px;height:3px;border-radius:2px}\n    .sw{display:inline-block;width:16px;height:12px;border-radius:3px}\n    .lg{display:flex;align-items:center;gap:6px;font-size:11px;color:${C.muted};margin:3px 0}\n    .lg i{display:inline-block;width:12px;height:12px;border-radius:3px}\n    .grad{height:8px;border-radius:4px;background:linear-gradient(90deg,${heatColor(0)},${heatColor(1)});margin:4px 0 2px}\n    .gl{display:flex;justify-content:space-between;font-size:10px;color:${C.muted}}\n    .kpi{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px dashed ${C.borda};font-size:12px}\n    .kpi span{color:${C.muted};display:flex;align-items:center;gap:6px}\n    .kpi i{display:inline-block;width:10px;height:3px;border-radius:2px}\n    .kpi.hi b{color:${C.dest}}\n    .sub{font-size:10px;color:${C.muted};margin-top:6px}\n    .btn{border:1px solid ${C.borda};background:${C.fundo};border-radius:8px;padding:6px 10px;font:inherit;font-size:11px;cursor:pointer;color:${C.texto}}\n    .loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:${C.fundo};color:${C.muted};z-index:900;font-size:13px}\n    .pop{font-family:Inter,system-ui,sans-serif;font-size:12px;min-width:200px;color:${C.texto}}\n    .pop>b{display:block;font-size:13px;margin-bottom:6px}\n    .pr{display:flex;justify-content:space-between;gap:10px;padding:2px 0}\n    .pr span{color:${C.muted}}\n    .pr code{font-size:10px}\n    .ps{margin-top:6px;font-size:11px;color:${C.muted}}\n    .leaflet-container{font-family:inherit}\n    .soft{filter:grayscale(.75) sepia(.18) brightness(1.06) contrast(.92)}\n  `,\n  render(d2) {\n    const L = d2.lib(LIB)\n    const st = d2.lib.state(LIB)\n    const overlay = L ? '' : '<div class=\"loading\">' + (st === 'error' ? 'Falha ao carregar o mapa: ' + d2.lib.error(LIB) : 'Carregando mapa…') + '</div>'\n    return `<div class=\"app\">\n      <div class=\"mapwrap\">\n        <div id=\"map\"></div>\n        <div class=\"tag\">MAPA</div>\n        <div id=\"toast\"></div>\n        <div id=\"mini\" title=\"Minimapa — clica pra ir\"></div>\n      </div>\n      <div class=\"side\">\n        <div class=\"card\">\n          <h4>FILTRO</h4>\n          <input type=\"text\" id=\"q\" placeholder=\"Busca: Q123 ou -46/-24\" autocomplete=\"off\">\n          <div id=\"qres\"></div>\n          <div class=\"row\">\n            <select id=\"niv\">\n              <option value=\"\">Todos os níveis</option>\n              <option value=\"0\">Nível 0 · 1°</option>\n              <option value=\"1\">Nível 1 · 0,5°</option>\n              <option value=\"2\">Nível 2 · 0,25°</option>\n              <option value=\"3\">Nível 3 · 0,125°</option>\n            </select>\n            <button class=\"btn\" id=\"reset\" title=\"Ver Brasil inteiro\">Brasil</button>\n          </div>\n        </div>\n        <div class=\"card\">\n          <h4>CAMADAS</h4>\n          <div id=\"layers\"></div>\n          <h5>Legenda</h5>\n          <div class=\"lg\"><i style=\"height:3px;background:${C.rod}\"></i>Rodovias</div>\n          <div class=\"lg\"><i style=\"height:3px;background:${C.rio}\"></i>Rios</div>\n          <div class=\"lg\">${NIV.map((c, i) => '<i style=\"border:2px solid ' + c + '\" title=\"nível ' + i + '\"></i>').join('')}Grade · nível 0 → 3</div>\n          <div class=\"grad\"></div><div class=\"gl\"><span>poucas feições</span><span>muitas</span></div>\n          <h5>Área visível</h5>\n          <div id=\"stats\"><div class=\"sub\">—</div></div>\n        </div>\n      </div>\n      ${overlay}\n    </div>`\n  },\n  mounted(d2) { init(d2) },\n  updated(d2) { init(d2) }\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6abee6b456470c27067490ad","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Hello_World","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .hello { font-family: sans-serif; font-size: 28px; font-weight: 600; padding: 24px; text-align: center; }\n  `,\n  render(d2) {\n    return `<div class=\"hello\">Hello, world! 👋</div>`;\n  }\n};"}}}}}}}},{"_id":"6ace621ef9fc7612597d19e7","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"kStateOfAi02","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"function esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n  });\n}\n\nvar PAUTA = [\n  ['01', 'Escrever “feito com IA” no rótulo ajuda a vender?',\n    'A Migrolino colocou o aviso na embalagem e virou piada no Reddit. Um especialista ouvido pela imprensa suíça disse que a IA entra no desenvolvimento de produtos o tempo todo e quase nunca aparece no rótulo.'],\n  ['02', 'Quem fica com o que a IA economizou?',\n    'Se a receita, a lata e o nome saíram de um prompt em dois dias, o cliente quer saber por que o preço é o mesmo. Quando a economia fica toda com a empresa, para o cliente sobra o risco de comprar algo que ninguém provou.'],\n  ['03', 'Quando a imagem gerada vira propaganda enganosa?',\n    'Foto de comida sempre foi produzida: óleo de motor no lugar de calda, purê no lugar de sorvete. Mesmo assim havia uma panqueca de verdade na frente da câmera. Uma imagem gerada mostra um produto que a cozinha nunca fez. Vale para cardápio, catálogo, provador virtual e embalagem.'],\n  ['04', 'O que você não deixa a IA fazer no seu trabalho?',\n    'O tempero final, a mixagem, o traço, o acabamento da peça. Cada convidado traz a parte do ofício que segue sem IA e explica por quê.']\n];\n\nvar CASOS = [\n  {\n    where: 'Suíça · rede de conveniência', name: 'Migrolino',\n    body: [\n      'Lançou um sanduíche de frango com aviso na embalagem de que a receita foi gerada por IA. Foi o segundo produto da rede feito assim.',\n      'Depois das piadas, a empresa disse que a IA serve para transformar tendências de mercado em receita, que funcionários fazem os ajustes finais e que o aviso existe para mostrar a abordagem inovadora.'\n    ],\n    src: [['20 Minuten', 'https://www.20min.ch/story/migrolino-ki-generiertes-poulet-sandwich-sorgt-fuer-haeme-103324194']]\n  },\n  {\n    where: 'Suíça · 2023 · bebida', name: 'Vivi Nova',\n    body: [\n      'Refrigerante da Vivi Kola vendido na Migros. O ChatGPT escolheu os ingredientes e o Midjourney desenhou a lata. Do conceito ao produto foram dois dias.',\n      'A tabela nutricional ficou com pessoas, porque a IA não dava conta do cálculo. As avaliações saíram divididas.'\n    ],\n    src: [['FoodNavigator', 'https://www.foodnavigator.com/Article/2023/06/09/vivi-nova-ai-designs-beverage-in-switzerland'], ['SIAL', 'https://newsroom.sialparis.com/?p=20334']]\n  },\n  {\n    where: 'EUA · 2026 · restaurantes', name: 'Wonder Create',\n    body: [\n      'Marc Lore, CEO da Wonder, anunciou uma ferramenta que monta um restaurante inteiro a partir de um prompt: nome, marca, cardápio, fotos e preço, por US$ 10 por mês.'\n    ],\n    quote: '“Build me a fast-casual Mexican restaurant for Gen Z, for people that love cycling.”',\n    after: 'É o exemplo que ele repete em várias entrevistas. Numa delas, perguntaram para quem era o produto. Ele respondeu explicando como funciona o prompt.',\n    src: [['Semafor', 'https://www.semafor.com/article/05/05/2026/im-the-ipo-guy-serial-entrepreneur-marc-lore-on-his-plan-to-take-wonder-public']]\n  }\n];\n\nvar RODA = ['Chefs', 'Músicos', 'Designers', 'Criadores', 'Artesãos'];\n\nvar MEDIA = 'https://gui.data2.media/file/';\nvar FOTOS = [\n  ['fIi16oJRutNWEKc8NyqGI', 'big', 'Painel com Rafa Cunha, Babi Vallim e Aline Sordili'],\n  ['iC0jQ8SJLyo5n2uECSy6u', '', 'Renato Shirakashi e o experimento Mirokami'],\n  ['dHeIpxYwQ2znOqKtFUeUl', '', 'David Lojudice e as sociedades de agentes'],\n  ['0z9NZDU9X29I3UpY5gjKn', '', 'Painel'],\n  ['K8hGOxCBtzdOCMYaUh_nX', '', 'Plateia'],\n  ['P-jsWJo2gXKAJIP_xzeua', 'wide', 'Sala cheia'],\n  ['JHvCplvr08sRKHQx9U1RG', '', 'Speakers e hosts no fim da noite'],\n  ['obksUWINAW0YZzHFh1GWy', '', 'Happy hour']\n];\n\nvar ED01 = [\n  ['Renato Shirakashi', 'O experimento Mirokami: números, erros e aprendizados'],\n  ['David Lojudice', 'O que aprendeu colocando LLMs para conviver'],\n  ['Rafa Cunha', 'Painel sobre gestão de times de IA'],\n  ['Babi Vallim', 'Painel, pela Hera.Build'],\n  ['Aline Sordili', 'Painel, co-fundadora do R7.com']\n];\n\nfunction link(label, href) {\n  return '<a href=\"' + esc(href) + '\" target=\"_blank\" rel=\"noopener\">' + esc(label) + '</a>';\n}\n\nexport default {\n  css: `\n    :host { display:block; width:100%; height:100%; }\n    * { margin:0; padding:0; box-sizing:border-box; }\n    a { color:inherit; -webkit-tap-highlight-color:transparent; }\n    .page {\n      --black:#0a0a0a; --white:#fff; --off:#f7f7f7; --gray:#666; --line:#e0e0e0;\n      position:relative; height:100%; overflow-y:auto; overflow-x:hidden; background:var(--white); color:var(--black);\n      font-family:'Inter', system-ui, sans-serif; -webkit-font-smoothing:antialiased; scroll-behavior:smooth;\n    }\n    .g { font-family:'Space Grotesk', 'Inter', sans-serif; }\n    .serif { font-family:'Source Serif 4', Georgia, serif; }\n\n    nav { position:sticky; top:0; z-index:10; padding:1.2rem 3rem; display:flex; justify-content:space-between; align-items:center;\n      background:rgba(255,255,255,.92); backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px); border-bottom:1px solid var(--line); }\n    .logo { font-weight:700; font-size:1rem; letter-spacing:2px; cursor:pointer; }\n    .links { display:flex; gap:2rem; }\n    .links span { color:var(--gray); font-size:.8rem; letter-spacing:1px; text-transform:uppercase; cursor:pointer; transition:color .3s; }\n    .links span:hover { color:var(--black); }\n    .badge { background:var(--black); color:var(--white); padding:.4rem 1rem; font-size:.7rem; font-weight:600; letter-spacing:1px; text-transform:uppercase; }\n\n    .hero { min-height:calc(100vh - 60px); display:grid; grid-template-columns:1fr auto; align-items:end; gap:3rem; padding:4rem 3rem 3rem; border-bottom:2px solid var(--black); }\n    .ed { font-size:.75rem; letter-spacing:3px; text-transform:uppercase; color:var(--gray); margin-bottom:1.2rem; }\n    h1 { font-size:clamp(4rem,12vw,10rem); font-weight:700; line-height:.9; letter-spacing:-4px; }\n    .builders { margin-top:1.5rem; font-size:clamp(1rem,2vw,1.3rem); line-height:1.6; }\n    .builders .tol { background:var(--black); color:var(--white); padding:.1em .4em; }\n    .entry { margin-top:3rem; border-top:2px solid var(--black); padding-top:1.5rem; max-width:760px; }\n    .entry-head { display:flex; align-items:baseline; gap:1rem; flex-wrap:wrap; }\n    .word { font-size:clamp(2.6rem,6vw,4.5rem); line-height:1; }\n    .pron { font-style:italic; color:var(--gray); font-size:1.2rem; }\n    .cls { font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); }\n    .def { font-size:clamp(1.15rem,2.2vw,1.5rem); line-height:1.5; margin-top:1rem; }\n    .def b { font-family:'Inter', sans-serif; font-size:.9em; margin-right:.4rem; }\n    .src { font-size:.78rem; color:var(--gray); margin-top:.8rem; }\n    .src a { color:var(--gray); }\n    .meta { text-align:right; padding-bottom:.5rem; }\n    .meta div { font-weight:700; font-size:1.1rem; margin-bottom:.3rem; }\n    .meta .muted { color:var(--gray); font-weight:500; }\n    .invite { margin-top:1.2rem; font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); border:1px solid var(--black); display:inline-block; padding:.4rem .8rem; }\n\n    section { padding:6rem 3rem; max-width:1200px; margin:0 auto; }\n    hr { border:none; height:1px; background:var(--line); max-width:1200px; margin:0 auto; }\n    .label { font-size:.7rem; letter-spacing:3px; text-transform:uppercase; color:var(--gray); margin-bottom:1.5rem; }\n    h2 { font-size:clamp(2rem,5vw,3.5rem); font-weight:700; line-height:1.1; margin-bottom:2rem; }\n\n    .mani { font-size:clamp(1.2rem,2.5vw,1.8rem); line-height:1.6; color:var(--gray); max-width:860px; }\n    .mani + .mani { margin-top:1.6rem; }\n    .mani em { color:var(--black); font-style:normal; font-weight:600; }\n\n    .pauta { border-top:2px solid var(--black); }\n    .pi { display:grid; grid-template-columns:120px 1fr; border-bottom:1px solid var(--line); transition:background .3s; }\n    .pi:last-child { border-bottom:2px solid var(--black); }\n    .pi:hover { background:var(--off); }\n    .pn { font-weight:700; font-size:1.6rem; padding:1.6rem 1.2rem; border-right:2px solid var(--black); }\n    .pb { padding:1.6rem 1.5rem; }\n    .pq { font-weight:700; font-size:clamp(1.15rem,2.2vw,1.5rem); line-height:1.3; }\n    .pd { font-size:.92rem; color:var(--gray); line-height:1.6; margin-top:.5rem; max-width:720px; }\n\n    .casos { display:grid; grid-template-columns:repeat(3,1fr); border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .caso { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2rem; display:flex; flex-direction:column; transition:background .3s; }\n    .caso:hover { background:var(--off); }\n    .cw { font-size:.7rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); }\n    .cn { font-weight:700; font-size:1.6rem; margin:.4rem 0 1rem; }\n    .caso p { font-size:.92rem; line-height:1.6; color:#333; }\n    .caso p + p { margin-top:.8rem; }\n    .caso .q { border-left:3px solid var(--black); padding-left:.9rem; font-style:italic; font-size:1rem; color:var(--black); }\n    .cs { margin-top:auto; padding-top:1.4rem; font-size:.75rem; color:var(--gray); }\n    .cs a { color:var(--gray); }\n    .note { margin-top:1.5rem; font-size:.85rem; color:var(--gray); line-height:1.6; max-width:760px; }\n    .note a { color:var(--black); }\n\n    .hosts { display:grid; grid-template-columns:1fr 1fr; border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .host { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2.5rem 2rem; transition:background .3s; }\n    .host:hover { background:var(--off); }\n    .hn { font-size:1.6rem; font-weight:700; }\n    .hc { font-size:.8rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); margin-top:.3rem; }\n    .roda { border:2px dashed var(--black); padding:2.5rem 2rem; margin-top:3rem; display:flex; justify-content:space-between; align-items:center; gap:2rem; flex-wrap:wrap; }\n    .rt { font-size:1.4rem; font-weight:700; }\n    .chips { display:flex; flex-wrap:wrap; gap:.6rem; margin:1rem 0 .6rem; }\n    .chips span { border:1.5px solid var(--black); padding:.5rem 1rem; font-weight:600; font-size:1rem; transition:all .25s; }\n    .chips span:hover { background:var(--black); color:var(--white); }\n    .rx { font-size:.92rem; color:var(--gray); line-height:1.6; max-width:560px; }\n    .tag { font-size:.7rem; letter-spacing:2px; text-transform:uppercase; background:var(--black); color:var(--white); padding:.4rem .8rem; white-space:nowrap; }\n\n    .ed01 { background:var(--black); color:var(--white); padding-top:5rem; }\n    .ed01-head { max-width:1200px; margin:0 auto 2rem; padding:0 3rem; display:flex; justify-content:space-between; align-items:flex-end; gap:1rem; flex-wrap:wrap; }\n    .mosaic { max-width:1200px; margin:0 auto; padding:0 3rem; display:grid; grid-template-columns:repeat(4,1fr); grid-auto-rows:210px; gap:6px; }\n    .ph { position:relative; overflow:hidden; background:#1a1a1a; }\n    .ph.big { grid-column:span 2; grid-row:span 2; }\n    .ph.wide { grid-column:span 2; }\n    .ph img { width:100%; height:100%; object-fit:cover; display:block; filter:grayscale(100%) contrast(1.05); transition:filter .5s, transform .8s; }\n    .ph:hover img { filter:grayscale(0%); transform:scale(1.02); }\n    .ph figcaption { position:absolute; left:0; right:0; bottom:0; padding:1.4rem .9rem .7rem; font-size:.75rem; color:var(--white); background:linear-gradient(transparent, rgba(0,0,0,.75)); opacity:0; transition:opacity .4s; }\n    .ph:hover figcaption { opacity:1; }\n    .credit { max-width:1200px; margin:.9rem auto 0; padding:0 3rem; font-size:.72rem; color:#888; letter-spacing:.5px; }\n    .ed01-in { max-width:1200px; margin:0 auto; padding:4rem 3rem; display:grid; grid-template-columns:240px 1fr; gap:2rem; align-items:start; }\n    .ed01 .label { color:#999; margin-bottom:.5rem; }\n    .ed01-t { font-size:1.6rem; font-weight:700; }\n    .ed01 li { list-style:none; display:grid; grid-template-columns:220px 1fr; gap:1rem; padding:.8rem 0; border-bottom:1px solid #2a2a2a; font-size:.95rem; }\n    .ed01 li:last-child { border-bottom:none; }\n    .ed01 .who { font-weight:700; }\n    .ed01 .what { color:#aaa; }\n\n    .details { display:grid; grid-template-columns:repeat(3,1fr); border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .dc { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2rem; transition:background .3s; }\n    .dc:hover { background:var(--off); }\n    .dc h3 { font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); margin-bottom:.8rem; font-weight:700; }\n    .dc p { font-size:1rem; line-height:1.6; }\n    .dc b { font-weight:700; }\n\n    footer { background:var(--black); color:var(--white); padding:3rem; display:flex; justify-content:space-between; align-items:center; gap:1rem; flex-wrap:wrap; border-top:1px solid #2a2a2a; }\n    .fl { font-size:1.2rem; font-weight:700; letter-spacing:2px; }\n    .ft { font-size:.8rem; color:#999; }\n\n    @supports (animation-timeline: view()) {\n      @media (prefers-reduced-motion: no-preference) {\n        .rv { animation: rise linear both; animation-timeline: view(); animation-range: entry 0% entry 35%; }\n        @keyframes rise { from { opacity:0; transform:translateY(30px); } to { opacity:1; transform:none; } }\n      }\n    }\n\n    @media (max-width: 900px) {\n      .casos { grid-template-columns:1fr; }\n      .details { grid-template-columns:1fr; }\n      .ed01-in { grid-template-columns:1fr; }\n      .mosaic { grid-template-columns:repeat(2,1fr); grid-auto-rows:170px; }\n    }\n    @media (max-width: 768px) {\n      nav { padding:1rem 16px; }\n      .links { display:none; }\n      .hero { grid-template-columns:1fr; padding:3rem 16px 2.5rem; min-height:auto; }\n      h1 { letter-spacing:-2px; }\n      .meta { text-align:left; }\n      section { padding:4rem 16px; }\n      .pi { grid-template-columns:56px 1fr; }\n      .pn { font-size:1.1rem; padding:1.4rem .6rem; }\n      .pb { padding:1.4rem 1rem; }\n      .hosts { grid-template-columns:1fr; }\n      .caso, .host, .dc { padding:1.6rem 1.2rem; }\n      .roda { padding:1.8rem 1.2rem; }\n      .ed01-in { padding:3rem 16px; }\n      .ed01 { padding-top:3.5rem; }\n      .ed01-head, .mosaic, .credit { padding-left:16px; padding-right:16px; }\n      .mosaic { grid-auto-rows:130px; gap:4px; }\n      .ph figcaption { opacity:1; font-size:.68rem; padding:1rem .6rem .5rem; }\n      .ed01 li { grid-template-columns:1fr; gap:.2rem; }\n      footer { padding:2rem 16px; }\n    }\n  `,\n\n  render(d2) {\n    var data = d2.param('dataEvento', 'Terça, 3 de novembro de 2026');\n    var local = d2.param('localEvento', '');\n\n    var h = '<div class=\"page\" data-scroller>';\n\n    h += '<nav><span class=\"logo g\" data-go=\"top\">STATE OF AI</span><div class=\"links\">' +\n      '<span data-go=\"manifesto\">Manifesto</span><span data-go=\"pauta\">Pauta</span>' +\n      '<span data-go=\"casos\">Casos</span><span data-go=\"ed01\">Edição 01</span><span data-go=\"info\">Info</span></div>' +\n      '<span class=\"badge\">Somente convidados</span></nav>';\n\n    h += '<header class=\"hero\" id=\"top\"><div>' +\n      '<div class=\"ed\">Edição 02</div>' +\n      '<h1 class=\"g\">STATE<br>OF AI</h1>' +\n      '<div class=\"builders\">Builders allowed.<br><span class=\"tol\">Providers tolerated.</span></div>' +\n      '<div class=\"entry\"><div class=\"entry-head\">' +\n      '<span class=\"word serif\">slop</span><span class=\"pron serif\">/ˈslɑːp/</span><span class=\"cls\">substantivo</span></div>' +\n      '<p class=\"def serif\"><b>1.</b>conteúdo digital de baixa qualidade, geralmente produzido em quantidade por meio de inteligência artificial.</p>' +\n      '<p class=\"src\">Merriam-Webster, palavra do ano de 2025. ' + link('AP', 'https://abc7news.com/amp/post/merriam-websters-2025-word-year-is-slop/18288780/') + '</p>' +\n      '</div></div>' +\n      '<div class=\"meta g\"><div>' + esc(data) + '</div><div>Roda de conversa</div>' +\n      '<div class=\"muted\">' + (local ? esc(local) : 'Local no convite') + '</div>' +\n      '<span class=\"invite\">Evento fechado para convidados</span></div></header>';\n\n    h += '<section id=\"manifesto\" class=\"rv\"><div class=\"label\">Manifesto</div>' +\n      '<p class=\"mani\">Na edição 01 a conversa ficou dentro da empresa: onde aplicar IA, como montar o time, <em>o que não automatizar</em>.</p>' +\n      '<p class=\"mani\">A IA já faz a foto do cardápio, a receita do sanduíche, a lata do refrigerante e a música de fundo do anúncio. Quem percebe primeiro é quem faz isso há anos: <em>chefs, músicos, designers, criadores e artesãos</em>. O trabalho deles é o primeiro que a IA imita, e eles reconhecem uma cópia mal feita de longe.</p>' +\n      '<p class=\"mani\">Por isso a edição 02 não tem palco nem palestra. Vai ser <em>uma roda de conversa</em> com essas pessoas. Cada uma conta onde a IA entrou no próprio ofício, onde foi barrada e como os clientes reagiram.</p>' +\n      '</section><hr>';\n\n    h += '<section id=\"pauta\" class=\"rv\"><div class=\"label\">Pauta</div><h2 class=\"g\">Quatro perguntas<br>para a roda.</h2><div class=\"pauta\">';\n    PAUTA.forEach(function (p) {\n      h += '<div class=\"pi\" data-key=\"p' + p[0] + '\"><div class=\"pn g\">' + p[0] + '</div><div class=\"pb\">' +\n        '<div class=\"pq g\">' + esc(p[1]) + '</div><div class=\"pd\">' + esc(p[2]) + '</div></div></div>';\n    });\n    h += '</div></section><hr>';\n\n    h += '<section id=\"casos\" class=\"rv\"><div class=\"label\">Casos</div><h2 class=\"g\">Três casos que<br>vamos discutir.</h2><div class=\"casos\">';\n    CASOS.forEach(function (c) {\n      h += '<article class=\"caso\" data-key=\"' + esc(c.name) + '\"><div class=\"cw\">' + esc(c.where) + '</div><div class=\"cn g\">' + esc(c.name) + '</div>';\n      c.body.forEach(function (t) { h += '<p>' + esc(t) + '</p>'; });\n      if (c.quote) h += '<p class=\"q serif\">' + esc(c.quote) + '</p>';\n      if (c.after) h += '<p>' + esc(c.after) + '</p>';\n      h += '<div class=\"cs\">Fonte: ' + c.src.map(function (s) { return link(s[0], s[1]); }).join(' · ') + '</div></article>';\n    });\n    h += '</div><p class=\"note\">A pauta saiu de um ' + link('vídeo sobre IA na comida', 'https://www.youtube.com/watch?v=O3aybGjkrVY') +\n      ' que circulou entre os hosts. Conferimos cada caso nas fontes originais antes de colocar aqui.</p></section><hr>';\n\n    h += '<section id=\"hosts\" class=\"rv\"><div class=\"label\">Hosts</div><h2 class=\"g\">Quem está por trás.</h2><div class=\"hosts\">' +\n      '<div class=\"host\"><div class=\"hn g\">Thaís Fein</div><div class=\"hc\">Grupo Boticário</div></div>' +\n      '<div class=\"host\"><div class=\"hn g\">Guilherme Souza</div><div class=\"hc\">Data2</div></div></div>' +\n      '<div class=\"roda\"><div><div class=\"rt g\">Na roda</div><div class=\"chips g\">' +\n      RODA.map(function (r) { return '<span>' + esc(r) + '</span>'; }).join('') +\n      '</div><p class=\"rx\">Os convites estão saindo agora. Os nomes aparecem aqui conforme cada pessoa confirmar.</p></div>' +\n      '<span class=\"tag\">Em breve</span></div></section>';\n\n    h += '<div class=\"ed01\" id=\"ed01\"><div class=\"ed01-head\"><div><div class=\"label\">Edição 01</div><div class=\"ed01-t g\">A primeira noite.</div></div></div>' +\n      '<div class=\"mosaic\">' + FOTOS.map(function (f) {\n        return '<figure class=\"ph ' + f[1] + '\" data-key=\"' + f[0] + '\"><img src=\"' + MEDIA + f[0] + '\" alt=\"' + esc(f[2]) + '\" loading=\"lazy\" decoding=\"async\"><figcaption>' + esc(f[2]) + '</figcaption></figure>';\n      }).join('') + '</div>' +\n      '<p class=\"credit\">Fotos: Alécio Cezar Batista Oliveira</p>' +\n      '<div class=\"ed01-in\"><div><div class=\"label\">Na edição 01</div><div class=\"ed01-t g\">Quem já falou aqui.</div></div><ul>';\n    ED01.forEach(function (e) { h += '<li><span class=\"who g\">' + esc(e[0]) + '</span><span class=\"what\">' + esc(e[1]) + '</span></li>'; });\n    h += '</ul></div></div>';\n\n    h += '<section id=\"info\" class=\"rv\"><div class=\"label\">Informações</div><h2 class=\"g\">Informações.</h2><div class=\"details\">' +\n      '<div class=\"dc\"><h3 class=\"g\">Acesso</h3><p><b>Evento fechado.</b><br>Somente para convidados.<br>Não há venda de ingressos.</p></div>' +\n      '<div class=\"dc\"><h3 class=\"g\">Para quem</h3><p>Gente de produto, marca, tecnologia e dados, sentada na mesma roda com quem vive do próprio ofício.</p></div>' +\n      '<div class=\"dc\"><h3 class=\"g\">Data e local</h3><p><b>' + esc(data) + '.</b><br>' + (local ? esc(local) : 'O endereço vai junto com o convite.') + '</p></div>' +\n      '</div></section>';\n\n    h += '<footer><div class=\"fl g\">STATE OF AI BR</div><div class=\"ft\">Edição 02 · stateofai.com.br</div></footer>';\n    h += '</div>';\n    return h;\n  },\n\n  mounted(d2) {\n    var id = 'stateofai-fonts';\n    if (!document.getElementById(id)) {\n      var l = document.createElement('link');\n      l.id = id; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Source+Serif+4:ital,wght@0,400;1,400&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('[data-go]', 'click', function (e, el) {\n      var target = el.getAttribute('data-go');\n      var scroller = d2.$('[data-scroller]');\n      var node = d2.$('#' + target);\n      if (!scroller || !node) return;\n      var navH = target === 'top' ? 0 : 64;\n      scroller.scrollTo({ top: target === 'top' ? 0 : node.offsetTop - navH, behavior: 'smooth' });\n    });\n  }\n};\n"}},"dataEvento":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataEvento"}}}},"localEvento":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"localEvento"}}}}}}}}}},{"_id":"5ecd05ded68ce41360f74b43","component":"data2-core-types@Component","params":{"extendable":{"placeholder":"children"},"component":"Block","package":"data2-core-primitives"}},{"_id":"60019e844369e2af95919c94","component":"data2-core-types@Component","params":{"definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"html@iframe","params":{"frameBorder":"0","src":{"component":"data2-core-flow@If","params":{"compare":[{"component":"data2-core-types@Reference","params":{"reference":"builder"}}],"then":"about:blank","else":{"component":"data2-core-types@Reference","params":{"reference":"url"}}}},"allow":"autoplay","allowFullScreen":{"component":"data2-core-types@Reference","params":{"reference":"allowFullScreen"}},"loading":"lazy","style":{"position":"absolute","width":"100%","height":"100%","border":"none","padding":"none","overflow":"auto"}}}}},"component":"IframeBlock","package":"data2-blocks","extends":"data2-core-primitives@Block"}}]}},{"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":"696030260ef36bdeb18a6aee","component":"data2-core-primitives@Artboard","system":"6329b5535b2103060e128c0a","displayName":"stateofai","params":{"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1366,"height":768}},"fill":{"params":{"backgrounds":[{"params":{"color":{"disabled":false,"component":"data2-core-types@Color","params":{"rgba":"rgba(0,0,0,1)"}},"gradient":{"disabled":true,"component":"data2-core-types@ColorGradient","params":{}},"image":{"disabled":true,"component":"data2-core-types@String","params":{}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"}},"reference":"8hF7AqB45M1DjgIsYi0-X"},{"_id":"6961148a2bf3e20fd6a095b5","parent":"696030260ef36bdeb18a6aee","component":"data2-blocks@IframeBlock","system":"6329b5535b2103060e128c0a","params":{"super":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1366,"height":768,"exactWidth":false,"exactHeight":false}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"x":0,"y":0,"exactX":0,"exactY":0,"top":true,"left":true,"bottom":true,"growV":false,"right":true,"growH":false}},"font":{"disabled":false,"component":"data2-core-web@GoogleFont","params":{"fontFamily":"Montserrat"}},"textColor":{"disabled":false,"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,1)"}},"textFormat":{"disabled":false,"component":"data2-core-types@TextFormat","params":{"horizontalAlignment":"center","textOverflow":false,"verticalAlignment":"middle","textTransform":"uppercase","wordWrap":"break-word","fontWeight":700}},"textSize":{"params":{"components":[{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"__DeviceController"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"width"}}}},{"component":"data2-core-primitives@Number","params":{"content":"5"}}]},"component":"data2-core-numerical-operators@Divide"},"childrenMode":"rows"},"url":{"component":"data2-core-types@FileComponentUrlReference","params":{"reference":"XS6RCOXH4eKRJEh2oSaqL"}}}}]}},{"_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":"6960301c0ef36bdeb18a6aed","displayName":"stateofai","reference":"ifOeeSyfMPBWVPj5tal8A","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"stateofai"}},"entryPoint":{"disabled":false,"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"8hF7AqB45M1DjgIsYi0-X"}}},"system":"6329b5535b2103060e128c0a","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}