{"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":"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":{}}}}