{"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  ui: {\n    theme: 'dark',\n    accent: '#22d3ee',\n    radius: 'lg',\n    font: '\"Segoe UI\", system-ui, sans-serif',\n    tokens: { bg: '#020617', surface: '#0f172a', line: '#1e2547', text: '#e2e8f0' }\n  },\n  css: `\n    .ttt-wrap{position:relative;height:100%;box-sizing:border-box;overflow:hidden;\n      font-family:var(--font);\n      background:radial-gradient(circle at 50% 0%,var(--line) 0%,var(--surface) 55%,var(--bg) 100%);color:var(--text)}\n    .ttt-cv{position:absolute;inset:0;width:100%;height:100%;display:block}\n    .ttt-hud{position:absolute;top:20px;left:0;right:0;text-align:center;pointer-events:none;z-index:2}\n    .ttt-title{font-size:26px;font-weight:800;letter-spacing:.5px;\n      background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 45%,var(--text)),var(--accent));-webkit-background-clip:text;background-clip:text;color:transparent}\n    .ttt-status{margin-top:6px;font-size:16px;font-weight:600;min-height:22px;color:color-mix(in srgb,var(--text) 82%,var(--bg));text-shadow:0 2px 12px var(--bg)}\n    .ttt-status b{color:color-mix(in srgb,var(--accent) 40%,var(--text))}\n    .ttt-foot{position:absolute;bottom:22px;left:0;right:0;text-align:center;z-index:2}\n    .ttt-foot .btn{padding:11px 26px;border-radius:var(--r-lg);font-size:15px;font-weight:700;\n      box-shadow:0 6px 24px color-mix(in srgb,var(--accent) 35%,transparent)}\n    .ttt-loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--muted)}\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=\"ttt-loading\">falha ao carregar 3D: ${d2.lib.error('three@0.160.0') || 'tente recarregar'}</div>`\n        : '<div class=\"ttt-loading\">carregando 3D…</div>'\n    }\n    return `<div class=\"ttt-wrap\">\n      <canvas class=\"ttt-cv\"></canvas>\n      ${overlay}\n      <div class=\"ttt-hud\">\n        <div class=\"ttt-title\">Jogo da Velha 3D</div>\n        <div class=\"ttt-status\">${status}</div>\n      </div>\n      <div class=\"ttt-foot\"><button class=\"btn pri 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.$('.ttt-cv')) return // canvas not painted yet; updated() will retry\n    d2.state._three = true\n\n    // theme colours for the 3D scene come from the kit's custom properties\n    const cs = getComputedStyle(d2.$('.ttt-wrap'))\n    const cvar = (n, f) => (cs.getPropertyValue(n) || '').trim() || f\n    const accentC = new THREE.Color(cvar('--accent', '#22d3ee'))\n    const idleC = new THREE.Color(cvar('--line', '#1e2547'))\n    const hoverC = new THREE.Color(0x3b3f8f)\n    const winC = new THREE.Color(0x16a34a)\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.$('.ttt-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(accentC,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    for(let i=0;i<9;i++){\n      const col=i%3, rowN=Math.floor(i/3)\n      const mat=new THREE.MeshStandardMaterial({color:idleC.clone(),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' ? new THREE.Color(0xf472b6) : accentC.clone()\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.copy(idleC) })\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?winC : (m.userData.i===hoverI && !d2.state.board[m.userData.i] && !d2.state.winInfo ? hoverC : idleC)\n          m.material.color.lerp(target,0.15)\n          const lift = (m.userData.i===hoverI && !exploding)?0.35:0\n          m.position.z += (lift - m.position.z)*0.2\n        }\n      })\n      marks.forEach(m=>{ if(m && m.scale.x<1) m.scale.lerp(new THREE.Vector3(1,1,1),0.25) })\n      if(exploding){\n        explodeT+=0.016\n        debris.forEach(d=>{\n          d.vel.z-=0.25 // gravity\n          d.obj.position.addScaledVector(d.vel,0.05)\n          d.obj.rotation.x+=d.spin.x; d.obj.rotation.y+=d.spin.y; d.obj.rotation.z+=d.spin.z\n        })\n      }\n      renderer.render(scene,camera)\n    })\n    d2.onUnmount(()=>{ renderer.dispose() })\n  },\n}"}}}}}}}},{"_id":"6a710d3395912880b321f0dd","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab-task-row","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .row{display:flex;align-items:center;gap:10px;height:100%;box-sizing:border-box;padding:10px 12px;\n       background:#fff;border:1px solid #e6e6ee;border-radius:10px;font:13px/1.35 -apple-system,Segoe UI,sans-serif}\n  .row[data-active]{border-color:#7c4dff;box-shadow:0 0 0 2px rgba(124,77,255,.15)}\n  .chip{flex:0 0 auto;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;\n        padding:3px 7px;border-radius:999px;cursor:pointer;user-select:none}\n  .chip[data-s=\"todo\"]{background:#eef1f7;color:#5b6478}\n  .chip[data-s=\"doing\"]{background:#fff3d6;color:#8a5b00}\n  .chip[data-s=\"done\"]{background:#e2f7e9;color:#186c3a}\n  .t{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;color:#161622}\n  .who{flex:0 0 auto;color:#7a7a8c;font-size:12px}\n  .due{flex:0 0 auto;color:#9a9aac;font-size:11px;font-variant-numeric:tabular-nums}\n  .due[data-late]{color:#c0392b;font-weight:700}\n  .p{flex:0 0 auto;font-size:11px;color:#7c4dff;font-weight:700}\n  .btn{flex:0 0 auto;border:1px solid #e0e0ea;background:#fafafe;cursor:pointer;font-size:11px;font-weight:700;padding:3px 8px;border-radius:6px;color:#6a6a80}\n  .btn:hover{background:#f2f2f7;color:#161622}\n  `,\n  render(d2) {\n    const r = d2.param('record') || {}\n    const esc = (s) => String(s == null ? '' : s).replace(/[<>&\"]/g, (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;' })[c])\n    const st = r.status || 'todo'\n    const active = d2.param('activeId') === r._id\n    const late = r.due && new Date(r.due) < new Date() && st !== 'done'\n    return `<div class=\"row\" ${active ? 'data-active' : ''}>\n      <span class=\"chip\" data-s=\"${esc(st)}\" title=\"click to cycle status\">${esc(st)}</span>\n      <span class=\"t\">${esc(r.title)}</span>\n      <span class=\"p\">P${esc(r.priority == null ? '-' : r.priority)}</span>\n      <span class=\"who\">${esc(r.assignee || '—')}</span>\n      <span class=\"due\" ${late ? 'data-late' : ''}>${r.due ? d2.fmt.date(r.due, { month: 'short', day: 'numeric' }) : '—'}</span>\n      <button class=\"btn open\" title=\"detail\">EDIT</button>\n      <button class=\"btn del\" title=\"delete\">DEL</button>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.chip', 'click', () => {\n      const rv = d2.emit('onCycle', (d2.param('record') || {})._id)\n      console.log('[row] emit onCycle ->', rv, 'paramKeys=', Object.keys(d2.params || {}), 'onCycleType=', typeof (d2.params || {}).onCycle)\n    })\n    d2.on('.open', 'click', () => d2.emit('onOpen', (d2.param('record') || {})._id))\n    d2.on('.del', 'click', () => d2.emit('onDelete', (d2.param('record') || {})._id))\n  },\n}"}},"record":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"record"}}}},"activeId":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"activeId"}}}},"onOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onOpen"}}}},"onCycle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCycle"}}}},"onDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDelete"}}}}}}}}}},{"_id":"6a710dc27c7babb663fe6caf","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_board","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const esc = (s) => String(s == null ? '' : s).replace(/[<>&\"]/g, (c) => ({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '\"': '&quot;' })[c])\n\nconst modal = (d2, rec) => {\n  const mod = d2.params.dataset\n  const fTitle = d2.field(mod, 'title')\n  const fNotes = d2.field(mod, 'notes')\n  const fEst = d2.field(mod, 'estimate')\n  const ds = d2.data()\n  return `<div class=\"scrim\">\n    <div class=\"modal\">\n      <span class=\"lb\">Task detail · ${esc(rec._id)}</span>\n      <span class=\"lb\">Title</span>\n      <input class=\"in f\" data-f=\"title\" value=\"${esc(fTitle.value)}\"/>\n      <span class=\"lb\">Estimate (hours)</span>\n      <input class=\"in f\" data-f=\"estimate\" type=\"number\" value=\"${esc(fEst.value)}\"/>\n      <span class=\"lb\">Notes</span>\n      <textarea class=\"ta f\" data-f=\"notes\">${esc(fNotes.value)}</textarea>\n      <span class=\"lb\">editable=${String(fTitle.editable)} · def=${fTitle.def ? 'yes' : 'NULL'} · errors=${fTitle.errors ? esc(JSON.stringify(fTitle.errors)) : 'none'}</span>\n      <div class=\"mrow\">\n        <button class=\"b ghost editmode\">edit()</button>\n        <button class=\"b ghost close\">Close</button>\n        <button class=\"b save\" ${ds.mutating ? 'disabled' : ''}>${ds.mutating ? 'Saving…' : 'Save'}</button>\n      </div>\n    </div>\n  </div>`\n}\n\nexport default {\n  css: `\n  .wrap{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;height:100%;box-sizing:border-box;\n        background:#f6f6fa;font:13px -apple-system,Segoe UI,sans-serif;color:#161622}\n  .bar{display:flex;gap:8px;align-items:center;padding:12px 14px;border-bottom:1px solid #e6e6ee;background:#fff}\n  .h1{font-size:15px;font-weight:800;margin-right:auto}\n  .in{padding:7px 10px;border:1px solid #d5d5e0;border-radius:8px;font:13px inherit;min-width:200px}\n  .b{padding:7px 12px;border:0;border-radius:8px;background:#7c4dff;color:#fff;font-weight:700;cursor:pointer}\n  .b[disabled]{opacity:.45;cursor:default}\n  .b.ghost{background:#eeeef5;color:#4a4a5c}\n  .meta{display:flex;gap:12px;flex-wrap:wrap;padding:8px 14px;color:#7a7a8c;font-size:11px;border-bottom:1px solid #eeeef5;background:#fbfbfe}\n  .list{overflow:auto;padding:10px 14px;display:grid;grid-auto-rows:46px;gap:8px;align-content:start}\n  .foot{padding:8px 14px;border-top:1px solid #e6e6ee;background:#fff;display:flex;gap:8px;align-items:center;font-size:11px;color:#7a7a8c}\n  .empty{padding:24px;text-align:center;color:#9a9aac}\n  .err{color:#c0392b;font-weight:700}\n  .scrim{position:absolute;inset:0;background:rgba(18,18,30,.42);display:flex;align-items:center;justify-content:center;z-index:20}\n  .modal{width:min(440px,92%);background:#fff;border-radius:14px;padding:18px;box-shadow:0 24px 60px rgba(0,0,0,.28);display:flex;flex-direction:column;gap:6px}\n  .lb{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:#9a9aac}\n  .ta{padding:8px 10px;border:1px solid #d5d5e0;border-radius:8px;font:13px inherit;min-height:64px;resize:vertical}\n  .mrow{display:flex;gap:8px;justify-content:flex-end;margin-top:6px}\n  `,\n  render(d2) {\n    const ds = d2.data()\n    if (!ds) return `<div class=\"empty\">No dataset bound (params.dataset is null).</div>`\n    if (ds.loading) return `<div class=\"empty\">Loading…</div>`\n    const status = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const q = String(d2.shared.get('cclab:q', '', { global: true }) || '').toLowerCase()\n    const all = ds.records()\n    let rows = all\n    if (status !== 'all') rows = rows.filter((r) => (r.status || 'todo') === status)\n    if (q) rows = rows.filter((r) => String(r.title || '').toLowerCase().includes(q))\n    const openId = d2.state.openId || ''\n    const pi = ds.pageInfo() || {}\n    const rowType = d2.param('row')\n    const cbs = {\n      onOpen: (id) => {\n        console.log('[board] onOpen fired id=', id)\n        d2.state.openId = id\n        d2.state.err = ''\n        try {\n          d2.data().select(id)\n          console.log('[board] select ok selected=', (d2.data().selected() || {})._id)\n        } catch (e) {\n          console.log('[board] select THREW', e && e.message)\n          d2.state.err = 'select: ' + (e && e.message)\n        }\n        console.log('[board] state.openId=', d2.state.openId, 'match=', !!d2.data().records().find((x) => x._id === d2.state.openId))\n      },\n      onCycle: async (id) => {\n        const order = ['todo', 'doing', 'done']\n        const rec = d2.data().records().find((r) => r._id === id)\n        if (!rec) return\n        const next = order[(order.indexOf(rec.status || 'todo') + 1) % order.length]\n        try {\n          await d2.data().update(Object.assign({}, rec, { status: next }))\n          d2.bus.emit('cclab:changed', { id: id, status: next })\n        } catch (e) { d2.state.err = 'cycle failed: ' + (e && e.message) }\n      },\n      onDelete: async (id) => {\n        const ok = await d2.dialog.confirm('Delete this task?')\n        if (!ok) return\n        try {\n          await d2.data().remove(id)\n          d2.bus.emit('cclab:changed', { id: id, deleted: true })\n        } catch (e) { d2.state.err = 'delete failed: ' + (e && e.message) }\n      },\n    }\n    const hosts = rows.map((r) => d2.render(rowType, Object.assign({ record: r, activeId: openId }, cbs), Object.assign({ key: r._id + '|' + (r.status || '') + '|' + String(r.title || '').length }, cbs))).join('')\n    const rec = openId ? all.find((r) => r._id === openId) : null\n    return `<div class=\"wrap\">\n      <div class=\"bar\">\n        <span class=\"h1\">Task board</span>\n        <input class=\"in new\" placeholder=\"New task title…\"/>\n        <button class=\"b add\" ${ds.mutating ? 'disabled' : ''}>${ds.mutating ? 'Saving…' : '+ Add'}</button>\n      </div>\n      <div class=\"meta\">\n        <span>${d2.fmt.pluralize(rows.length, 'task', 'tasks')} shown</span>\n        <span>of ${d2.fmt.number(all.length)} loaded / ${d2.fmt.number(pi.total == null ? 0 : pi.total)} total</span>\n        <span>filter: <b>${esc(status)}</b>${q ? ' · q=' + esc(q) : ''}</span>\n        ${ds.fetching ? '<span>fetching…</span>' : ''}\n        ${d2.state.err ? '<span class=\"err\">' + esc(d2.state.err) + '</span>' : ''}\n      </div>\n      <div class=\"list\">${hosts || '<div class=\"empty\">Nothing matches.</div>'}</div>\n      <div class=\"foot\">\n        <button class=\"b ghost loadall\">Load all pages</button>\n        <span>page ${pi.page == null ? 1 : pi.page} of ${pi.pages == null ? 1 : pi.pages} · size ${pi.size == null ? '?' : pi.size} · rows ${rows.length}</span>\n      </div>\n      ${rec ? modal(d2, rec) : ''}\n    </div>`\n  },\n  mounted(d2) {\n    const add = async () => {\n      const el = d2.$('.new')\n      const t = String((el && el.value) || '').trim()\n      if (!t) { d2.state.err = 'title required'; return }\n      d2.state.err = ''\n      try {\n        const out = await d2.data().create({ title: t, status: 'todo', priority: 3, assignee: 'unassigned' })\n        d2.log('create returned', out)\n        const el2 = d2.$('.new')\n        if (el2) el2.value = ''\n        d2.bus.emit('cclab:changed', { created: true })\n      } catch (e) { d2.state.err = 'create failed: ' + (e && e.message) }\n    }\n    d2.on('.add', 'click', add)\n    d2.on('.new', 'keydown', (e) => { if (e.key === 'Enter') add() })\n    d2.on('.f', 'input', (e, el) => {\n      const v = el.type === 'number' ? Number(el.value) : el.value\n      d2.field(d2.params.dataset, el.dataset.f).set(v)\n    })\n    d2.on('.editmode', 'click', () => { d2.data().edit(); d2.refresh() })\n    d2.on('.save', 'click', async () => {\n      try {\n        const out = await d2.data().save()\n        d2.log('save returned', out)\n        d2.state.openId = ''\n        d2.bus.emit('cclab:changed', { saved: true })\n      } catch (e) { d2.state.err = 'save failed: ' + (e && e.message) }\n    })\n    d2.on('.close', 'click', () => { d2.data().cancel(); d2.state.openId = '' })\n    d2.on('.loadall', 'click', async () => {\n      const out = await d2.data().loadAll()\n      d2.log('loadAll ->', out && out.length)\n    })\n    d2.bus.on('cclab:filter', () => d2.refresh())\n  },\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"row":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"row"}}}}}}}}}},{"_id":"6a710deac1dc93c9dc7d201f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_filter","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .fb{display:flex;align-items:center;gap:8px;height:100%;box-sizing:border-box;padding:0 14px;\n      background:#161622;color:#fff;font:13px -apple-system,Segoe UI,sans-serif}\n  .lb{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:#8b8ba6}\n  .pill{border:1px solid #33334a;background:transparent;color:#c9c9db;padding:5px 11px;border-radius:999px;\n        font-size:12px;font-weight:700;cursor:pointer;text-transform:capitalize}\n  .pill[data-on]{background:#7c4dff;border-color:#7c4dff;color:#fff}\n  .q{margin-left:auto;padding:6px 10px;border:1px solid #33334a;border-radius:8px;background:#0e0e18;color:#fff;\n     font:13px inherit;min-width:200px;outline:none}\n  .dbg{font-size:10px;color:#5c5c78;font-family:ui-monospace,monospace}\n  `,\n  render(d2) {\n    const cur = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const q = String(d2.shared.get('cclab:q', '', { global: true }) || '')\n    const opts = ['all', 'todo', 'doing', 'done']\n    return `<div class=\"fb\">\n      <span class=\"lb\">Filter</span>\n      ${opts.map((o) => `<button class=\"pill\" data-o=\"${o}\" ${o === cur ? 'data-on' : ''}>${o}</button>`).join('')}\n      <input class=\"q\" placeholder=\"search title…\" value=\"${q.replace(/\"/g, '&quot;')}\"/>\n      <span class=\"dbg\">shared:${cur}|${q ? q.length : 0}ch</span>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.pill', 'click', (e, el) => {\n      d2.shared.set('cclab:status', el.dataset.o, { global: true })\n      const n = d2.bus.emit('cclab:filter', { status: el.dataset.o })\n      d2.log('bus reached', n)\n    })\n    d2.on('.q', 'input', (e, el) => {\n      d2.shared.set('cclab:q', el.value, { global: true })\n      d2.bus.emit('cclab:filter', { q: el.value })\n    })\n  },\n}\n"}}}}}}}},{"_id":"6a710deb33e3af9321f61da2","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"cclab_stats","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .s{display:grid;grid-template-columns:repeat(6,1fr);gap:10px;height:100%;box-sizing:border-box;padding:10px 14px;\n     background:#fff;border-bottom:1px solid #e6e6ee;font:13px -apple-system,Segoe UI,sans-serif}\n  .tile{display:flex;flex-direction:column;justify-content:center;gap:2px;padding:8px 12px;border-radius:10px;\n        background:#f6f6fa;border:1px solid #eeeef5}\n  .tile[data-on]{background:#f1ebff;border-color:#d5c4ff}\n  .tile b{font-size:20px;font-weight:800;color:#161622;line-height:1}\n  .tile span{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#9a9aac}\n  .warn{color:#c0392b}\n  `,\n  render(d2) {\n    const ds = d2.data()\n    if (!ds) return `<div class=\"s\"><div class=\"tile warn\"><b>!</b><span>no dataset</span></div></div>`\n    const rows = ds.records()\n    const by = { todo: 0, doing: 0, done: 0 }\n    let hrs = 0\n    rows.forEach((r) => {\n      const s = r.status || 'todo'\n      if (by[s] != null) by[s]++\n      hrs += Number(r.estimate) || 0\n    })\n    const cur = d2.shared.get('cclab:status', 'all', { global: true }) || 'all'\n    const tiles = ['todo', 'doing', 'done'].map((k) => `<div class=\"tile\" ${cur === k ? 'data-on' : ''}><b>${by[k]}</b><span>${k}</span></div>`).join('')\n    return `<div class=\"s\">${tiles}\n      <div class=\"tile\"><b>${d2.fmt.number(hrs)}h</b><span>estimate</span></div>\n      <div class=\"tile\"><b>${d2.fmt.percent(rows.length ? by.done / rows.length : 0)}</b><span>complete</span></div>\n      <div class=\"tile\"><b>${d2.state.pings || 0}</b><span>bus pings</span></div>\n    </div>`\n  },\n  mounted(d2) {\n    d2.bus.on('cclab:changed', (p) => {\n      d2.state.pings = (d2.state.pings || 0) + 1\n      d2.state.last = JSON.stringify(p || {})\n    })\n  },\n}\n"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6ab459ea156b886f8631de98","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Aerodromos_do_Drive_teste","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f6f5f1;--ink:#1d1d1b;--mut:#77756f;--line:#e2e0d8;--acc:#1f5eff;--warn:#d9480f;--panel:#fff}\n  *{box-sizing:border-box}\n  .root{height:100%;display:grid;grid-template-columns:1fr 360px;grid-template-rows:auto 1fr;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif}\n  header{grid-column:1/3;padding:18px 24px 14px;border-bottom:1px solid var(--line);display:flex;align-items:baseline;gap:24px;flex-wrap:wrap}\n  h1{font-size:16px;margin:0;font-weight:600}\n  .src{color:var(--mut);font-size:11.5px;word-break:break-all}\n  .kpis{display:flex;gap:20px;margin-left:auto}\n  .kpi b{display:block;font-size:20px;font-weight:600;font-variant-numeric:tabular-nums}\n  .kpi span{color:var(--mut);font-size:11px;text-transform:uppercase;letter-spacing:.4px}\n  .map{position:relative;padding:16px;min-height:0}\n  svg{width:100%;height:100%;display:block}\n  .dot{fill:var(--acc);fill-opacity:.55;cursor:pointer}\n  .dot.int{fill:var(--warn)}\n  .dot.sel{fill:var(--ink);fill-opacity:1}\n  .side{border-left:1px solid var(--line);display:flex;flex-direction:column;min-height:0;background:var(--panel)}\n  .side input{margin:12px;padding:8px 10px;border:1px solid var(--line);border-radius:6px;font:inherit;background:var(--bg)}\n  .list{overflow:auto;flex:1}\n  .row{padding:8px 14px;border-top:1px solid var(--line);cursor:pointer;display:grid;grid-template-columns:52px 1fr;gap:8px}\n  .row:hover{background:var(--bg)}\n  .row.sel{background:#e9efff}\n  .row code{font:11.5px ui-monospace,Menlo,monospace;color:var(--mut);padding-top:1px}\n  .row small{display:block;color:var(--mut)}\n  .tag{font-size:10.5px;color:var(--warn)}\n  .msg{grid-column:1/3;padding:40px;color:var(--mut)}\n  .err{color:var(--warn)}\n  .legend{position:absolute;left:28px;bottom:24px;font-size:11px;color:var(--mut);display:flex;gap:14px}\n  .legend i{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px;vertical-align:0}\n  @media (max-width:760px){.root{grid-template-columns:1fr;grid-template-rows:auto 320px 1fr}header,.msg{grid-column:1}.side{border-left:none;border-top:1px solid var(--line)}.kpis{margin-left:0}}\n  `,\n  render(d2){\n    var s = d2.state;\n    var url = d2.param('arquivoUrl', '');\n    var head = '<header><h1>Aeródromos públicos · ANAC</h1><span class=\"src\">Drive: ' + esc(url || '(sem arquivo)') + '</span>';\n    if (s.error) return '<div class=\"root\">' + head + '</header><div class=\"msg err\">Falha ao ler o arquivo do Drive: ' + esc(s.error) + '</div></div>';\n    if (!s.rows) return '<div class=\"root\">' + head + '</header><div class=\"msg\">Lendo arquivo do Drive…</div></div>';\n\n    var rows = s.rows, q = (s.q || '').toLowerCase();\n    var shown = q ? rows.filter(function(r){ return r.hay.indexOf(q) >= 0; }) : rows;\n    var ufs = {}; rows.forEach(function(r){ ufs[r.uf] = 1; });\n    var nInt = rows.filter(function(r){ return r.interditado; }).length;\n\n    head += '<div class=\"kpis\">'\n      + '<div class=\"kpi\"><b>' + rows.length + '</b><span>aeródromos</span></div>'\n      + '<div class=\"kpi\"><b>' + Object.keys(ufs).length + '</b><span>UFs</span></div>'\n      + '<div class=\"kpi\"><b>' + nInt + '</b><span>interditados</span></div>'\n      + '<div class=\"kpi\"><b>' + d2.fmt.bytes(s.bytes || 0) + '</b><span>arquivo</span></div>'\n      + '</div></header>';\n\n    // Equiretangular simples sobre a caixa do Brasil\n    var W = 1000, H = 1000, x0 = -74.5, x1 = -28.5, y0 = -34.5, y1 = 5.8;\n    var px = function(lon){ return (lon - x0) / (x1 - x0) * W; };\n    var py = function(lat){ return (y1 - lat) / (y1 - y0) * H; };\n    var dots = shown.map(function(r){\n      var cls = 'dot' + (r.interditado ? ' int' : '') + (s.sel === r.id ? ' sel' : '');\n      return '<circle data-key=\"' + r.id + '\" data-id=\"' + r.id + '\" class=\"' + cls + '\" cx=\"' + px(r.lon).toFixed(1) + '\" cy=\"' + py(r.lat).toFixed(1) + '\" r=\"' + (s.sel === r.id ? 9 : 5) + '\"><title>' + esc(r.oaci + ' · ' + r.nome + ' — ' + r.mun + '/' + r.uf) + '</title></circle>';\n    }).join('');\n\n    var list = shown.map(function(r){\n      return '<div class=\"row' + (s.sel === r.id ? ' sel' : '') + '\" data-key=\"' + r.id + '\" data-id=\"' + r.id + '\"><code>' + esc(r.oaci) + '</code><div>' + esc(r.nome)\n        + (r.interditado ? ' <span class=\"tag\">interditado</span>' : '')\n        + '<small>' + esc(r.mun) + ' · ' + esc(r.uf) + ' · ' + esc(r.op) + '</small></div></div>';\n    }).join('');\n\n    return '<div class=\"root\">' + head\n      + '<div class=\"map\"><svg viewBox=\"0 0 ' + W + ' ' + H + '\" preserveAspectRatio=\"xMidYMid meet\">' + dots + '</svg>'\n      + '<div class=\"legend\"><span><i style=\"background:var(--acc)\"></i>cadastrado</span><span><i style=\"background:var(--warn)\"></i>interditado</span><span>' + shown.length + ' no filtro</span></div></div>'\n      + '<div class=\"side\"><input data-q placeholder=\"Buscar nome, município, UF ou código OACI\" value=\"' + esc(s.q || '') + '\"><div class=\"list\">' + list + '</div></div>'\n      + '</div>';\n\n    function esc(v){ return String(v == null ? '' : v).replace(/[&<>\"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]; }); }\n  },\n  mounted(d2){\n    var url = d2.param('arquivoUrl', '');\n    if (!url) { d2.state.error = 'parâmetro arquivoUrl vazio'; return; }\n    fetch(url).then(function(res){\n      if (!res.ok) throw new Error('HTTP ' + res.status);\n      return res.arrayBuffer();\n    }).then(function(buf){\n      // CSV da ANAC vem em ISO-8859-1, separador ';', 1ª linha = \"Atualizado em\", 2ª = cabeçalho\n      var text = new TextDecoder('iso-8859-1').decode(buf);\n      var lines = text.split(/\\r?\\n/).filter(function(l){ return l.trim(); });\n      var hi = lines.findIndex(function(l){ return l.indexOf('OACI') >= 0; });\n      var cols = lines[hi].split(';');\n      var ix = function(name){ return cols.indexOf(name); };\n      var iO = ix('Código OACI'), iN = ix('Nome'), iM = ix('Município'), iU = ix('UF'), iLa = ix('LATGEOPOINT'), iLo = ix('LONGEOPOINT'), iOp = ix('Operação Diurna'), iS = ix('Situação');\n      var rows = [];\n      lines.slice(hi + 1).forEach(function(l, k){\n        var c = l.split(';');\n        var lat = parseFloat(c[iLa]), lon = parseFloat(c[iLo]);\n        if (!isFinite(lat) || !isFinite(lon)) return;\n        var r = { id: 'a' + k, oaci: c[iO], nome: c[iN], mun: c[iM], uf: c[iU], lat: lat, lon: lon, op: c[iOp], interditado: /interdit/i.test(c[iS] || '') };\n        r.hay = (r.oaci + ' ' + r.nome + ' ' + r.mun + ' ' + r.uf).toLowerCase();\n        rows.push(r);\n      });\n      d2.setState({ rows: rows, bytes: buf.byteLength });\n    }).catch(function(e){ d2.state.error = (e && e.message) || String(e); });\n\n    d2.on('[data-q]', 'input', function(e){ d2.state.q = e.target.value; });\n    d2.on('[data-id]', 'click', function(e, el){ var id = el.getAttribute('data-id'); d2.state.sel = d2.state.sel === id ? null : id; });\n  }\n};"}},"arquivoUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivoUrl"}}}}}}}}}},{"_id":"6ab47623854a800547912d3f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Norte_do_Drive_teste","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:300px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:3px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  label .n{margin-left:auto;color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .err{color:#b42318}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}}\n  `,\n  render(){\n    function row(key, label){\n      return '<label><input type=\"checkbox\" data-layer=\"' + key + '\" checked><i style=\"background:var(--' + key + ')\"></i>' + label + '<span class=\"n\" data-n=\"' + key + '\">…</span></label>';\n    }\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Região Norte · OSM do Drive</h1><p class=\"sub\">GeoJSON estático no Drive, sem Overpass</p>'\n      + row('trunk', 'Rodovia expressa (trunk)')\n      + row('primary', 'Rodovia principal')\n      + row('secondary', 'Rodovia secundária')\n      + row('river', 'Rios permanentes')\n      + '<div class=\"sep\"></div><table><tr><th colspan=\"2\">Arquivos</th></tr>'\n      + '<tr><td>rodovias</td><td data-f=\"rodovias\">carregando…</td></tr><tr><td>rios</td><td data-f=\"rios\">carregando…</td></tr>'\n      + '<tr><th colspan=\"2\">Desenho</th></tr><tr><td>pontos na tela</td><td data-pts>…</td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr></table>'\n      + '<button data-reset>Recentrar</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var FILES = { rodovias: d2.param('rodoviasUrl', ''), rios: d2.param('riosUrl', '') };\n    var STYLE = {\n      river:     { color: '#2f7fd8', w: 0.8 },\n      secondary: { color: '#8a877f', w: 0.8 },\n      primary:   { color: '#e0822b', w: 1.3 },\n      trunk:     { color: '#c2410c', w: 2.0 }\n    };\n    var ORDER = ['river', 'secondary', 'primary', 'trunk'];\n    // Geometria fica fora do d2.state (é grande e não deve disparar re-render)\n    var geo = { river: [], secondary: [], primary: [], trunk: [] };\n    var bbox = [180, 90, -180, -90];\n    var view = null, cv = null, ctx = null, W = 0, H = 0, dpr = 1, raf = 0;\n    var layers = { trunk: true, primary: true, secondary: true, river: true };\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n\n    function ingest(json, kind){\n      var n = { trunk: 0, primary: 0, secondary: 0, river: 0 };\n      (json.features || []).forEach(function(f){\n        if (!f.geometry) return;\n        var key = kind === 'rios' ? 'river' : f.properties && f.properties.highway;\n        if (!geo[key]) return;\n        var lines = f.geometry.type === 'MultiLineString' ? f.geometry.coordinates : [f.geometry.coordinates];\n        lines.forEach(function(line){\n          var a = new Float32Array(line.length * 2);\n          for (var i = 0; i < line.length; i++){\n            var x = line[i][0], y = mercY(line[i][1]);\n            a[i * 2] = x; a[i * 2 + 1] = y;\n            if (x < bbox[0]) bbox[0] = x; if (x > bbox[2]) bbox[2] = x;\n            if (y < bbox[1]) bbox[1] = y; if (y > bbox[3]) bbox[3] = y;\n          }\n          geo[key].push(a);\n        });\n        n[key]++;\n      });\n      return n;\n    }\n\n    function fit(){\n      if (!W || bbox[0] > bbox[2]) return;\n      var k = Math.min(W / (bbox[2] - bbox[0]), H / (bbox[3] - bbox[1])) * 0.92;\n      view = { k: k, cx: (bbox[0] + bbox[2]) / 2, cy: (bbox[1] + bbox[3]) / 2 };\n    }\n\n    function draw(){\n      raf = 0;\n      if (!ctx || !view) return;\n      var t0 = performance.now();\n      var L = layers;\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      // janela visível em coordenadas do mapa, para pular linhas fora da tela\n      var vx0 = (0 - ox) / k, vx1 = (W - ox) / k, vy0 = (oy - H) / k, vy1 = oy / k;\n      var pts = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      ORDER.forEach(function(key){\n        if (!L[key]) return;\n        ctx.beginPath();\n        ctx.strokeStyle = STYLE[key].color;\n        ctx.lineWidth = STYLE[key].w * Math.max(1, Math.min(3, Math.log2(k / 20 + 1)));\n        var arr = geo[key];\n        for (var j = 0; j < arr.length; j++){\n          var a = arr[j], n = a.length, inside = false;\n          for (var i = 0; i < n; i += 2){ var x = a[i], y = a[i + 1]; if (x >= vx0 && x <= vx1 && y >= vy0 && y <= vy1){ inside = true; break; } }\n          if (!inside) continue;\n          ctx.moveTo(a[0] * k + ox, oy - a[1] * k);\n          for (var i2 = 2; i2 < n; i2 += 2) ctx.lineTo(a[i2] * k + ox, oy - a[i2 + 1] * k);\n          pts += n / 2;\n        }\n        ctx.stroke();\n      });\n      var ms = Math.round(performance.now() - t0);\n      txt('[data-pts]', d2.fmt.number(pts)); txt('[data-ms]', ms + ' ms');\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function resize(){\n      if (!cv) return;\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1;\n      W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view) fit();\n      schedule();\n    }\n\n    cv = d2.$('[data-cv]'); ctx = cv.getContext('2d');\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); });\n\n    // arrastar + pinça\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){\n        view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k;\n        view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k;\n      } else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]];\n        var d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(200000, view.k * d / last));\n        last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault();\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top;\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var gx = (mx - ox) / k, gy = (oy - my) / k;\n      var nk = Math.max(5, Math.min(200000, k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule();\n    }, { passive: false });\n\n    d2.on('[data-layer]', 'change', function(e, el){\n      layers[el.getAttribute('data-layer')] = el.checked;\n      schedule();\n    });\n    d2.on('[data-reset]', 'click', function(){ fit(); schedule(); });\n\n    // carrega os dois arquivos do Drive em paralelo\n    Object.keys(FILES).forEach(function(name){\n      var url = FILES[name];\n      var cell = d2.$('[data-f=\"' + name + '\"]');\n      var files = function(v){\n        if (!cell) return;\n        cell.className = v.error ? 'err' : '';\n        cell.textContent = v.error ? v.error : d2.fmt.bytes(v.bytes) + ' · ' + v.dl + ' ms · parse ' + v.parse + ' ms';\n      };\n      if (!url){ files({ error: 'parâmetro vazio' }); return; }\n      var t0 = performance.now(), t1;\n      fetch(url).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })\n        .then(function(body){\n          t1 = performance.now();\n          var json = JSON.parse(body);\n          var n = ingest(json, name);\n          var t2 = performance.now();\n          Object.keys(n).forEach(function(k){ if (n[k]) txt('[data-n=\"' + k + '\"]', d2.fmt.number(n[k])); });\n          files({ bytes: body.length, dl: Math.round(t1 - t0), parse: Math.round(t2 - t1) });\n          fit(); schedule();\n        })\n        .catch(function(e){ files({ error: (e && e.message) || String(e) }); });\n    });\n  }\n};"}},"rodoviasUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rodoviasUrl"}}}},"riosUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"riosUrl"}}}}}}}}}},{"_id":"6ab47c14ab5404c7a0311c0f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Brasil_do_Drive_streaming","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--motorway:#7a1f0e;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f;--tertiary:#b9b6ad}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:330px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:2px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  label .n{margin-left:auto;color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0;vertical-align:top}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .bar{height:4px;background:var(--line);border-radius:2px;margin:2px 0 6px;overflow:hidden}\n  .bar b{display:block;height:100%;width:0;background:var(--ink);transition:width .2s}\n  .err{color:#b42318}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}}\n  `,\n  render(){\n    var LAYERS = [['motorway','Autoestrada (motorway)'],['trunk','Rodovia expressa (trunk)'],['primary','Rodovia principal'],['secondary','Rodovia secundária'],['tertiary','Rodovia terciária'],['river','Rios']];\n    var rows = LAYERS.map(function(l){\n      return '<label><input type=\"checkbox\" data-layer=\"' + l[0] + '\" checked><i style=\"background:var(--' + l[0] + ')\"></i>' + l[1] + '<span class=\"n\" data-n=\"' + l[0] + '\">0</span></label>';\n    }).join('');\n    function file(name){\n      return '<tr><td>' + name + '</td><td data-f=\"' + name + '\">aguardando…</td></tr><tr><td colspan=\"2\"><div class=\"bar\"><b data-bar=\"' + name + '\"></b></div></td></tr>';\n    }\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Brasil inteiro · OSM do Drive</h1><p class=\"sub\">NDJSON bruto no Drive (sem simplificação), lido em streaming</p>'\n      + rows\n      + '<div class=\"sep\"></div><table><tr><th colspan=\"2\">Arquivos</th></tr>' + file('rodovias') + file('rios')\n      + '<tr><th colspan=\"2\">Memória e desenho</th></tr>'\n      + '<tr><td>pontos carregados</td><td data-loaded>0</td></tr>'\n      + '<tr><td>geometria em memória</td><td data-mem>0</td></tr>'\n      + '<tr><td>heap JS</td><td data-heap>—</td></tr>'\n      + '<tr><td>pontos desenhados</td><td data-pts>…</td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr></table>'\n      + '<button data-reset>Recentrar</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var FILES = { rodovias: d2.param('rodoviasUrl', ''), rios: d2.param('riosUrl', '') };\n    var STYLE = {\n      river:     { color: '#2f7fd8', w: 0.7 },\n      tertiary:  { color: '#b9b6ad', w: 0.6 },\n      secondary: { color: '#8a877f', w: 0.8 },\n      primary:   { color: '#e0822b', w: 1.1 },\n      trunk:     { color: '#c2410c', w: 1.6 },\n      motorway:  { color: '#7a1f0e', w: 2.0 }\n    };\n    var ORDER = ['river', 'tertiary', 'secondary', 'primary', 'trunk', 'motorway'];\n    var on = {}; ORDER.forEach(function(k){ on[k] = true; });\n\n    // Uma camada = coordenadas contíguas (Float32) + offsets por linha + bbox por linha.\n    // Nada disso passa por d2.state: são centenas de MB e não podem disparar re-render.\n    function Layer(){ this.xy = new Float32Array(1 << 16); this.n = 0; this.off = new Uint32Array(1 << 12); this.bb = new Float32Array(1 << 14); this.lines = 0; this.feats = 0; }\n    Layer.prototype.add = function(coords){\n      var need = this.n + coords.length * 2;\n      if (need > this.xy.length){ var nx = new Float32Array(Math.max(need, this.xy.length * 2)); nx.set(this.xy.subarray(0, this.n)); this.xy = nx; }\n      if ((this.lines + 1) * 2 > this.off.length){ var no = new Uint32Array(this.off.length * 2); no.set(this.off); this.off = no; }\n      if ((this.lines + 1) * 4 > this.bb.length){ var nb = new Float32Array(this.bb.length * 2); nb.set(this.bb); this.bb = nb; }\n      var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, p = this.n;\n      for (var i = 0; i < coords.length; i++){\n        var x = coords[i][0], y = mercY(coords[i][1]);\n        this.xy[p++] = x; this.xy[p++] = y;\n        if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y;\n      }\n      this.off[this.lines * 2] = this.n; this.off[this.lines * 2 + 1] = p;\n      var b = this.lines * 4; this.bb[b] = x0; this.bb[b + 1] = y0; this.bb[b + 2] = x1; this.bb[b + 3] = y1;\n      this.n = p; this.lines++;\n      if (x0 < ext[0]) ext[0] = x0; if (x1 > ext[2]) ext[2] = x1; if (y0 < ext[1]) ext[1] = y0; if (y1 > ext[3]) ext[3] = y1;\n    };\n    var geo = {}; ORDER.forEach(function(k){ geo[k] = new Layer(); });\n    var ext = [180, 90, -180, -90];\n    var view = null, cv = null, ctx = null, W = 0, H = 0, dpr = 1, raf = 0, userMoved = false;\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n    function mb(n){ return (n / 1048576).toFixed(n > 1e8 ? 0 : 1) + ' MB'; }\n\n    function fit(){\n      if (!W || ext[0] > ext[2]) return;\n      var k = Math.min(W / (ext[2] - ext[0]), H / (ext[3] - ext[1])) * 0.92;\n      view = { k: k, cx: (ext[0] + ext[2]) / 2, cy: (ext[1] + ext[3]) / 2 };\n    }\n\n    function draw(){\n      raf = 0;\n      if (!ctx || !view) return;\n      var t0 = performance.now();\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var vx0 = -ox / k, vx1 = (W - ox) / k, vy0 = (oy - H) / k, vy1 = oy / k;\n      var minSpan = 0.6 / k;          // linha menor que ~0,6 px inteira: não desenha\n      var drawn = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      ORDER.forEach(function(key){\n        if (!on[key]) return;\n        var L = geo[key], xy = L.xy, off = L.off, bb = L.bb;\n        ctx.beginPath();\n        ctx.strokeStyle = STYLE[key].color;\n        ctx.lineWidth = STYLE[key].w * Math.max(1, Math.min(3, Math.log2(k / 40 + 1)));\n        for (var j = 0; j < L.lines; j++){\n          var b = j * 4;\n          if (bb[b + 2] < vx0 || bb[b] > vx1 || bb[b + 3] < vy0 || bb[b + 1] > vy1) continue;\n          if (bb[b + 2] - bb[b] < minSpan && bb[b + 3] - bb[b + 1] < minSpan) continue;\n          var s = off[j * 2], e = off[j * 2 + 1];\n          var lx = xy[s] * k + ox, ly = oy - xy[s + 1] * k;\n          ctx.moveTo(lx, ly); drawn++;\n          for (var i = s + 2; i < e; i += 2){\n            var px = xy[i] * k + ox, py = oy - xy[i + 1] * k;\n            // descarta vértices a menos de ~0,7 px do último desenhado (exceto o último da linha)\n            if (i < e - 2 && Math.abs(px - lx) + Math.abs(py - ly) < 0.7) continue;\n            ctx.lineTo(px, py); lx = px; ly = py; drawn++;\n          }\n        }\n        ctx.stroke();\n      });\n      txt('[data-pts]', d2.fmt.number(drawn));\n      txt('[data-ms]', Math.round(performance.now() - t0) + ' ms');\n      if (performance.memory) txt('[data-heap]', mb(performance.memory.usedJSHeapSize));\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function resize(){\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1; W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view) fit();\n      schedule();\n    }\n\n    cv = d2.$('[data-cv]'); ctx = cv.getContext('2d');\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    var aborts = [];\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); aborts.forEach(function(a){ a.abort(); }); });\n\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; userMoved = true; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){\n        view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k;\n        view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k;\n      } else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]];\n        var d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(500000, view.k * d / last));\n        last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault(); userMoved = true;\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top;\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      var gx = (mx - ox) / k, gy = (oy - my) / k;\n      var nk = Math.max(5, Math.min(500000, k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule();\n    }, { passive: false });\n    d2.on('[data-layer]', 'change', function(e, el){ on[el.getAttribute('data-layer')] = el.checked; schedule(); });\n    d2.on('[data-reset]', 'click', function(){ fit(); userMoved = false; schedule(); });\n\n    function stats(){\n      var pts = 0, bytes = 0;\n      ORDER.forEach(function(k){ var L = geo[k]; pts += L.n / 2; bytes += L.xy.byteLength + L.off.byteLength + L.bb.byteLength; txt('[data-n=\"' + k + '\"]', d2.fmt.number(L.feats)); });\n      txt('[data-loaded]', d2.fmt.number(pts)); txt('[data-mem]', mb(bytes));\n      if (performance.memory) txt('[data-heap]', mb(performance.memory.usedJSHeapSize));\n    }\n\n    // Streaming: lê o corpo aos pedaços, separa por linha, parseia 1 feição por vez.\n    function load(name, url){\n      var cell = d2.$('[data-f=\"' + name + '\"]'), bar = d2.$('[data-bar=\"' + name + '\"]');\n      if (!url){ cell.textContent = 'parâmetro vazio'; cell.className = 'err'; return; }\n      var ac = new AbortController(); aborts.push(ac);\n      var t0 = performance.now(), got = 0, total = 0, feats = 0, parseMs = 0, tail = '', lastUi = 0, lastDraw = 0;\n      var dec = new TextDecoder();\n      function eat(chunk, final){\n        var t = performance.now();\n        var s = tail + chunk, from = 0, nl;\n        while ((nl = s.indexOf('\\n', from)) >= 0){\n          if (nl > from){\n            var f = JSON.parse(s.slice(from, nl));\n            var g = f.geometry, key = name === 'rios' ? 'river' : (f.properties && f.properties.highway);\n            var L = geo[key];\n            if (g && L){\n              if (g.type === 'MultiLineString') g.coordinates.forEach(function(c){ L.add(c); }); else L.add(g.coordinates);\n              L.feats++;\n            }\n            feats++;\n          }\n          from = nl + 1;\n        }\n        tail = s.slice(from);\n        if (final && tail.trim()){ eat('\\n', false); }\n        parseMs += performance.now() - t;\n      }\n      function ui(done){\n        var el = (performance.now() - t0) / 1000;\n        cell.className = '';\n        cell.textContent = mb(got) + (total ? ' / ' + mb(total) : '') + ' · ' + d2.fmt.number(feats) + ' feições · ' + el.toFixed(1) + ' s' + (done ? ' (parse ' + (parseMs / 1000).toFixed(1) + ' s)' : '');\n        if (total) bar.style.width = (100 * got / total).toFixed(1) + '%';\n        stats();\n      }\n      fetch(url, { signal: ac.signal }).then(function(res){\n        if (!res.ok) throw new Error('HTTP ' + res.status);\n        total = +res.headers.get('content-length') || 0;\n        var reader = res.body.getReader();\n        function pump(){\n          return reader.read().then(function(r){\n            if (r.done){ eat(dec.decode(), true); ui(true); if (!userMoved) fit(); schedule(); return; }\n            got += r.value.byteLength;\n            eat(dec.decode(r.value, { stream: true }), false);\n            var now = performance.now();\n            if (now - lastUi > 250){ lastUi = now; ui(false); }\n            if (now - lastDraw > 1500){ lastDraw = now; if (!userMoved) fit(); schedule(); }\n            return pump();\n          });\n        }\n        return pump();\n      }).catch(function(e){ if (e && e.name === 'AbortError') return; cell.textContent = (e && e.message) || String(e); cell.className = 'err'; });\n    }\n    load('rodovias', FILES.rodovias);\n    load('rios', FILES.rios);\n  }\n};"}},"rodoviasUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rodoviasUrl"}}}},"riosUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"riosUrl"}}}}}}}}}},{"_id":"6ab4954aab5404c7a0314368","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"OSM_Brasil_por_quadricula_Range","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--bg:#f4f3ee;--ink:#1d1d1b;--mut:#77756f;--line:#dedcd3;--panel:#fff;--river:#2f7fd8;--motorway:#7a1f0e;--trunk:#c2410c;--primary:#e0822b;--secondary:#8a877f;--tertiary:#b9b6ad}\n  *{box-sizing:border-box}\n  .root{position:relative;height:100%;background:var(--bg);color:var(--ink);font:13px/1.45 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;overflow:hidden}\n  canvas{position:absolute;inset:0;width:100%;height:100%;cursor:grab;touch-action:none}\n  canvas.drag{cursor:grabbing}\n  .panel{position:absolute;top:16px;left:16px;width:340px;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:14px 16px;box-shadow:0 1px 3px rgba(0,0,0,.06)}\n  h1{font-size:14px;margin:0 0 2px;font-weight:600}\n  .sub{color:var(--mut);font-size:11.5px;margin:0 0 10px}\n  label{display:flex;align-items:center;gap:8px;padding:2px 0;cursor:pointer}\n  label i{width:18px;height:3px;border-radius:2px;display:inline-block}\n  .sep{border-top:1px solid var(--line);margin:10px 0}\n  table{width:100%;border-collapse:collapse;font-size:12px;font-variant-numeric:tabular-nums}\n  td{padding:2px 0;vertical-align:top}\n  td:last-child{text-align:right}\n  th{text-align:left;font-weight:600;color:var(--mut);font-size:10.5px;text-transform:uppercase;letter-spacing:.4px;padding:4px 0 2px}\n  .bar{height:4px;background:var(--line);border-radius:2px;margin:4px 0 2px;overflow:hidden}\n  .bar b{display:block;height:100%;width:0;background:var(--ink)}\n  .status{margin:0 0 8px;font-size:12px;padding:6px 8px;border-radius:6px;background:var(--bg)}\n  .status.warn{background:#fff4e5;color:#8a4b00}\n  .log{margin-top:8px;font:11px/1.45 ui-monospace,Menlo,monospace;color:var(--mut);max-height:110px;overflow:auto;white-space:pre}\n  .hint{position:absolute;right:16px;bottom:14px;color:var(--mut);font-size:11.5px;background:rgba(244,243,238,.85);padding:4px 8px;border-radius:4px}\n  button{font:inherit;font-size:12px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:5px 10px;cursor:pointer;margin-top:10px;margin-right:6px}\n  @media (max-width:520px){.panel{left:8px;right:8px;top:8px;width:auto}.log{display:none}}\n  `,\n  render(){\n    var LAYERS = [['motorway','Autoestrada'],['trunk','Rodovia expressa'],['primary','Rodovia principal'],['secondary','Rodovia secundária'],['tertiary','Rodovia terciária'],['river','Rios']];\n    var rows = LAYERS.map(function(l){\n      return '<label><input type=\"checkbox\" data-layer=\"' + l[0] + '\" checked><i style=\"background:var(--' + l[0] + ')\"></i>' + l[1] + '</label>';\n    }).join('');\n    return '<div class=\"root\">'\n      + '<canvas data-cv></canvas>'\n      + '<div class=\"panel\"><h1>Brasil · leitura por quadrícula</h1><p class=\"sub\">1 arquivo no Drive + índice na table; baixa só os bytes da área visível (HTTP Range)</p>'\n      + '<div class=\"status\" data-status>carregando índice…</div>'\n      + rows\n      + '<div class=\"sep\"></div><table>'\n      + '<tr><th colspan=\"2\">Arquivo</th></tr>'\n      + '<tr><td>tamanho total</td><td data-total>…</td></tr>'\n      + '<tr><td>índice</td><td data-idx>…</td></tr>'\n      + '<tr><td>visão geral</td><td data-ov>…</td></tr>'\n      + '<tr><th colspan=\"2\">Área visível</th></tr>'\n      + '<tr><td>quadrículas na tela</td><td data-vis>…</td></tr>'\n      + '<tr><th colspan=\"2\">Baixado até agora</th></tr>'\n      + '<tr><td>quadrículas em memória</td><td data-loaded>0</td></tr>'\n      + '<tr><td>requisições Range</td><td data-reqs>0</td></tr>'\n      + '<tr><td>bytes baixados</td><td data-got>0</td></tr>'\n      + '<tr><td colspan=\"2\"><div class=\"bar\"><b data-bar></b></div></td></tr>'\n      + '<tr><td>último frame</td><td data-ms>…</td></tr>'\n      + '</table>'\n      + '<div class=\"log\" data-log></div>'\n      + '<button data-reset>Brasil inteiro</button><button data-sp>São Paulo</button><button data-bel>Belém</button></div>'\n      + '<div class=\"hint\">arraste para mover · roda do mouse / pinça para zoom · quadrados = quadrículas do arquivo</div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var IDX_URL = d2.param('indiceUrl', ''), DATA_URL = d2.param('arquivoUrl', ''), OV_URL = d2.param('visaoGeralUrl', '');\n    var START = String(d2.param('inicio', '') || '');       // \"lon,lat,larguraGraus\" opcional\n    var BUDGET = 48 * 1024 * 1024;   // teto de detalhe na tela: carrega do centro para as bordas até aqui\n    var CACHE = 160 * 1024 * 1024;   // acima disso, descarta quadrículas fora da tela\n    var GAP = 512 * 1024, MAXREQ = 16 * 1024 * 1024, PARALLEL = 4;\n    var STYLE = {\n      river: { color: '#2f7fd8', w: 0.7 }, tertiary: { color: '#b9b6ad', w: 0.6 }, secondary: { color: '#8a877f', w: 0.8 },\n      primary: { color: '#e0822b', w: 1.1 }, trunk: { color: '#c2410c', w: 1.6 }, motorway: { color: '#7a1f0e', w: 2.0 }\n    };\n    var ORDER = ['river', 'tertiary', 'secondary', 'primary', 'trunk', 'motorway'];\n    var on = {}; ORDER.forEach(function(k){ on[k] = true; });\n\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n    function mb(n){ return n >= 1048576 ? (n / 1048576).toFixed(n > 1e8 ? 0 : 1) + ' MB' : Math.round(n / 1024) + ' KB'; }\n    function log(s){ var el = d2.$('[data-log]'); if (!el) return; el.textContent = (s + '\\n' + el.textContent).split('\\n').slice(0, 30).join('\\n'); }\n    function status(s, warn){ var el = d2.$('[data-status]'); if (el){ el.textContent = s; el.className = 'status' + (warn ? ' warn' : ''); } }\n\n    function Layer(){ this.xy = new Float32Array(1024); this.n = 0; this.off = new Uint32Array(64); this.bb = new Float32Array(128); this.lines = 0; }\n    Layer.prototype.add = function(c){\n      var need = this.n + c.length * 2;\n      if (need > this.xy.length){ var nx = new Float32Array(Math.max(need, this.xy.length * 2)); nx.set(this.xy.subarray(0, this.n)); this.xy = nx; }\n      if ((this.lines + 1) * 2 > this.off.length){ var no = new Uint32Array(this.off.length * 2); no.set(this.off); this.off = no; }\n      if ((this.lines + 1) * 4 > this.bb.length){ var nb = new Float32Array(this.bb.length * 2); nb.set(this.bb); this.bb = nb; }\n      var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9, p = this.n;\n      for (var i = 0; i < c.length; i++){\n        var x = c[i][0], y = mercY(c[i][1]); this.xy[p++] = x; this.xy[p++] = y;\n        if (x < x0) x0 = x; if (x > x1) x1 = x; if (y < y0) y0 = y; if (y > y1) y1 = y;\n      }\n      this.off[this.lines * 2] = this.n; this.off[this.lines * 2 + 1] = p;\n      var b = this.lines * 4; this.bb[b] = x0; this.bb[b + 1] = y0; this.bb[b + 2] = x1; this.bb[b + 3] = y1;\n      this.n = p; this.lines++;\n    };\n\n    var tiles = [], total = 0, got = 0, reqs = 0, memBytes = 0;\n    var view = null, cv = d2.$('[data-cv]'), ctx = cv.getContext('2d'), W = 0, H = 0, dpr = 1, raf = 0, planT = 0, inflight = 0, queue = [];\n    var ext = [180, 90, -180, -90];\n    var overview = null;   // camadas leves do Brasil inteiro, desenhadas onde não há quadrícula carregada\n\n    function fitTo(x0, y0, x1, y1){\n      if (!W) return;\n      var k = Math.min(W / (x1 - x0), H / (y1 - y0)) * 0.92;\n      view = { k: k, cx: (x0 + x1) / 2, cy: (y0 + y1) / 2 };\n    }\n    function goTo(lon, lat, spanDeg){ var y = mercY(lat); fitTo(lon - spanDeg / 2, y - spanDeg / 2, lon + spanDeg / 2, y + spanDeg / 2); schedule(); plan(); }\n    function fitAll(){ fitTo(ext[0], ext[1], ext[2], ext[3]); schedule(); plan(); }\n\n    function visibleWin(){\n      var k = view.k, ox = W / 2 - view.cx * k, oy = H / 2 + view.cy * k;\n      return { k: k, ox: ox, oy: oy, x0: -ox / k, x1: (W - ox) / k, y0: (oy - H) / k, y1: oy / k };\n    }\n    function inView(t, v){ return !(t.mb[2] < v.x0 || t.mb[0] > v.x1 || t.mb[3] < v.y0 || t.mb[1] > v.y1); }\n\n    function draw(){\n      raf = 0;\n      if (!view) return;\n      var t0 = performance.now(), v = visibleWin(), k = v.k, ox = v.ox, oy = v.oy;\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n      ctx.clearRect(0, 0, W, H);\n      var vis = tiles.filter(function(t){ return inView(t, v); });\n      // grade: quadrículas não baixadas em cinza, baixando em âmbar\n      vis.forEach(function(t){\n        var x = t.cell[0] * k + ox, y = oy - t.cell[3] * k, w = (t.cell[2] - t.cell[0]) * k, h = (t.cell[3] - t.cell[1]) * k;\n        if (t.state === 'loading'){ ctx.fillStyle = 'rgba(224,130,43,.14)'; ctx.fillRect(x, y, w, h); }\n        else if (t.state !== 'loaded'){ ctx.fillStyle = 'rgba(29,29,27,' + Math.min(0.10, 0.02 + t.len / 4194304 * 0.08).toFixed(3) + ')'; ctx.fillRect(x, y, w, h); }\n        ctx.strokeStyle = 'rgba(29,29,27,.18)'; ctx.lineWidth = 1; ctx.strokeRect(x + 0.5, y + 0.5, w, h);\n        if (t.state !== 'loaded' && w > 46 && h > 18){ ctx.fillStyle = 'rgba(29,29,27,.55)'; ctx.font = '10px -apple-system,sans-serif'; ctx.fillText(mb(t.len), x + 4, y + 13); }\n      });\n      var minSpan = 0.6 / k, drawn = 0;\n      ctx.lineJoin = 'round'; ctx.lineCap = 'round';\n      var lw = function(key){ return STYLE[key].w * Math.max(1, Math.min(2.2, Math.log2(k / 40 + 1))); };\n      // 1) visão geral só onde a quadrícula ainda não chegou (clip), para a tela nunca ficar vazia\n      var pending = vis.filter(function(t){ return t.state !== 'loaded'; });\n      if (overview && pending.length){\n        ctx.save(); ctx.beginPath();\n        pending.forEach(function(t){ ctx.rect(t.cell[0] * k + ox, oy - t.cell[3] * k, (t.cell[2] - t.cell[0]) * k, (t.cell[3] - t.cell[1]) * k); });\n        ctx.clip();\n        ORDER.forEach(function(key){\n          var L = overview[key]; if (!on[key] || !L) return;\n          ctx.beginPath(); ctx.strokeStyle = STYLE[key].color; ctx.lineWidth = lw(key);\n          drawn += lines(L);\n          ctx.stroke();\n        });\n        ctx.restore();\n      }\n      function lines(L){\n        var xy = L.xy, off = L.off, bb = L.bb, c = 0;\n        for (var j = 0; j < L.lines; j++){\n          var b = j * 4;\n          if (bb[b + 2] < v.x0 || bb[b] > v.x1 || bb[b + 3] < v.y0 || bb[b + 1] > v.y1) continue;\n          if (bb[b + 2] - bb[b] < minSpan && bb[b + 3] - bb[b + 1] < minSpan) continue;\n          var s = off[j * 2], e = off[j * 2 + 1], lx = xy[s] * k + ox, ly = oy - xy[s + 1] * k;\n          ctx.moveTo(lx, ly); c++;\n          for (var i = s + 2; i < e; i += 2){\n            var px = xy[i] * k + ox, py = oy - xy[i + 1] * k;\n            if (i < e - 2 && Math.abs(px - lx) + Math.abs(py - ly) < 0.7) continue;\n            ctx.lineTo(px, py); lx = px; ly = py; c++;\n          }\n        }\n        return c;\n      }\n      // 2) detalhe completo das quadrículas carregadas\n      ORDER.forEach(function(key){\n        if (!on[key]) return;\n        ctx.beginPath(); ctx.strokeStyle = STYLE[key].color; ctx.lineWidth = lw(key);\n        vis.forEach(function(t){\n          if (t.state !== 'loaded') return;\n          var L = t.geo[key]; if (!L) return;\n          drawn += lines(L);\n        });\n        ctx.stroke();\n      });\n      txt('[data-ms]', Math.round(performance.now() - t0) + ' ms · ' + d2.fmt.number(drawn) + ' pts');\n    }\n    function schedule(){ if (!raf) raf = requestAnimationFrame(draw); }\n\n    function stats(){\n      txt('[data-loaded]', tiles.filter(function(t){ return t.state === 'loaded'; }).length + ' (' + mb(memBytes) + ')');\n      txt('[data-reqs]', d2.fmt.number(reqs));\n      txt('[data-got]', mb(got) + ' de ' + mb(total) + ' (' + (100 * got / (total || 1)).toFixed(2) + '%)');\n      var bar = d2.$('[data-bar]'); if (bar) bar.style.width = Math.min(100, 100 * got / (total || 1)) + '%';\n    }\n\n    // decide o que baixar para a área visível\n    function plan(){\n      clearTimeout(planT);\n      planT = setTimeout(function(){\n        if (!view || !tiles.length) return;\n        var v = visibleWin();\n        var vis = tiles.filter(function(t){ return inView(t, v); });\n        var visBytes = vis.reduce(function(a, t){ return a + t.len; }, 0);\n        txt('[data-vis]', vis.length + ' · ' + mb(visBytes));\n        // mais perto do centro primeiro; soma o que já está na tela e para no limite\n        var cx = view.cx, cy = view.cy;\n        var dist = function(t){ var x = (t.cell[0] + t.cell[2]) / 2 - cx, y = (t.cell[1] + t.cell[3]) / 2 - cy; return x * x + y * y; };\n        var used = vis.filter(function(t){ return t.state; }).reduce(function(a, t){ return a + t.len; }, 0);\n        var need = [], skipped = 0;\n        vis.filter(function(t){ return !t.state; }).sort(function(a, b){ return dist(a) - dist(b); }).forEach(function(t){\n          if (used + t.len <= BUDGET){ need.push(t); used += t.len; } else skipped++;\n        });\n        var partial = skipped ? ' · ' + skipped + ' quadrículas nas bordas ficam na visão geral (aproxime para detalhar)' : '';\n        if (!need.length){ status((inflight ? 'baixando…' : skipped ? 'detalhe carregado no centro' : 'área visível completa') + partial, !!skipped && !inflight); return; }\n        evict(v);\n        // junta quadrículas contíguas no arquivo em uma requisição só\n        need.sort(function(a, b){ return a.off - b.off; });\n        var groups = [], g = null;\n        need.forEach(function(t){\n          if (g && t.off - g.end <= GAP && t.off + t.len - g.start <= MAXREQ){ g.tiles.push(t); g.end = t.off + t.len; }\n          else { g = { start: t.off, end: t.off + t.len, tiles: [t] }; groups.push(g); }\n        });\n        groups.forEach(function(gr){ gr.tiles.forEach(function(t){ t.state = 'loading'; }); queue.push(gr); });\n        status('baixando ' + need.length + ' quadrículas em ' + groups.length + ' requisições…' + partial);\n        pump(); schedule();\n      }, 250);\n    }\n\n    function evict(v){\n      if (memBytes <= CACHE) return;\n      tiles.filter(function(t){ return t.state === 'loaded' && !inView(t, v); })\n        .sort(function(a, b){ return a.used - b.used; })\n        .some(function(t){ memBytes -= t.mem; t.geo = null; t.state = null; t.mem = 0; return memBytes <= CACHE * 0.7; });\n    }\n\n    function pump(){\n      while (inflight < PARALLEL && queue.length){\n        (function(gr){\n          inflight++; reqs++;\n          var t0 = performance.now();\n          fetch(DATA_URL, { headers: { Range: 'bytes=' + gr.start + '-' + (gr.end - 1) } })\n            .then(function(r){\n              if (r.status !== 206 && r.status !== 200) throw new Error('HTTP ' + r.status);\n              if (r.status === 200) throw new Error('servidor ignorou Range (200)');\n              return r.arrayBuffer();\n            })\n            .then(function(buf){\n              var ms = Math.round(performance.now() - t0);\n              got += buf.byteLength;\n              var dec = new TextDecoder(), tp = performance.now();\n              gr.tiles.forEach(function(t){\n                var text = dec.decode(new Uint8Array(buf, t.off - gr.start, t.len));\n                var geo = {}, mem = 0, s = 0, nl;\n                while ((nl = text.indexOf('\\n', s)) >= 0){\n                  if (nl > s){ var f = JSON.parse(text.slice(s, nl)); (geo[f.k] || (geo[f.k] = new Layer())).add(f.c); }\n                  s = nl + 1;\n                }\n                Object.keys(geo).forEach(function(kk){ var L = geo[kk]; mem += L.xy.byteLength + L.off.byteLength + L.bb.byteLength; });\n                t.geo = geo; t.mem = mem; t.state = 'loaded'; t.used = performance.now(); memBytes += mem;\n              });\n              log('bytes=' + gr.start + '-' + (gr.end - 1) + '  ' + mb(buf.byteLength) + '  ' + gr.tiles.length + ' quadr.  ' + ms + ' ms rede + ' + Math.round(performance.now() - tp) + ' ms parse');\n            })\n            .catch(function(e){ gr.tiles.forEach(function(t){ t.state = null; }); log('erro: ' + ((e && e.message) || e)); status('erro ao baixar: ' + ((e && e.message) || e), true); })\n            .then(function(){ inflight--; stats(); schedule(); if (!queue.length && !inflight) plan(); pump(); });\n        })(queue.shift());\n      }\n    }\n\n    function resize(){\n      var r = cv.getBoundingClientRect();\n      dpr = window.devicePixelRatio || 1; W = r.width; H = r.height;\n      cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n      if (!view && tiles.length) start();\n      schedule();\n    }\n    var ro = new ResizeObserver(resize); ro.observe(cv);\n    d2.onUnmount(function(){ ro.disconnect(); if (raf) cancelAnimationFrame(raf); clearTimeout(planT); queue.length = 0; });\n\n    function start(){\n      var p = START.split(',').map(Number);\n      if (p.length === 3 && p.every(isFinite)) goTo(p[0], p[1], p[2]); else fitAll();\n    }\n\n    var ptrs = {}, last = null;\n    d2.on(cv, 'pointerdown', function(e){ cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = { x: e.clientX, y: e.clientY }; cv.classList.add('drag'); last = null; });\n    d2.on(cv, 'pointermove', function(e){\n      if (!ptrs[e.pointerId] || !view) return;\n      var ids = Object.keys(ptrs);\n      if (ids.length === 1){ view.cx -= (e.clientX - ptrs[e.pointerId].x) / view.k; view.cy += (e.clientY - ptrs[e.pointerId].y) / view.k; }\n      else if (ids.length === 2){\n        var o = ptrs[ids[0] == e.pointerId ? ids[1] : ids[0]], d = Math.hypot(e.clientX - o.x, e.clientY - o.y);\n        if (last) view.k = Math.max(5, Math.min(500000, view.k * d / last)); last = d;\n      }\n      ptrs[e.pointerId] = { x: e.clientX, y: e.clientY };\n      schedule(); plan();\n    });\n    function up(e){ delete ptrs[e.pointerId]; last = null; if (!Object.keys(ptrs).length) cv.classList.remove('drag'); }\n    d2.on(cv, 'pointerup', up); d2.on(cv, 'pointercancel', up);\n    d2.on(cv, 'wheel', function(e){\n      if (!view) return;\n      e.preventDefault();\n      var r = cv.getBoundingClientRect(), mx = e.clientX - r.left, my = e.clientY - r.top, v = visibleWin();\n      var gx = (mx - v.ox) / v.k, gy = (v.oy - my) / v.k, nk = Math.max(5, Math.min(500000, v.k * Math.exp(-e.deltaY * 0.0015)));\n      view.k = nk; view.cx = gx - (mx - W / 2) / nk; view.cy = gy + (my - H / 2) / nk;\n      schedule(); plan();\n    }, { passive: false });\n    d2.on('[data-layer]', 'change', function(e, el){ on[el.getAttribute('data-layer')] = el.checked; schedule(); });\n    d2.on('[data-reset]', 'click', fitAll);\n    d2.on('[data-sp]', 'click', function(){ goTo(-46.63, -23.55, 0.9); });\n    d2.on('[data-bel]', 'click', function(){ goTo(-48.48, -1.40, 1.2); });\n\n    var IDX_DS = d2.data('indice');\n    if (!IDX_DS && (!IDX_URL || !DATA_URL)){ status('parâmetros indice (table) ou indiceUrl/arquivoUrl vazios', true); return; }\n    if (OV_URL){\n      var to = performance.now();\n      fetch(OV_URL).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); }).then(function(body){\n        var tp = performance.now(), lines = body.split('\\n'), q = JSON.parse(lines[0]).q, ov = {}, n = 0;\n        for (var i = 1; i < lines.length; i++){\n          if (!lines[i]) continue;\n          var f = JSON.parse(lines[i]), d = f.c, c = [], x = 0, y = 0;\n          for (var j = 0; j < d.length; j += 2){ x += d[j]; y += d[j + 1]; c.push([x * q, y * q]); }\n          (ov[f.k] || (ov[f.k] = new Layer())).add(c); n++;\n        }\n        overview = ov;\n        txt('[data-ov]', d2.fmt.number(n) + ' trechos · ' + Math.round(tp - to) + ' ms + ' + Math.round(performance.now() - tp) + ' ms');\n        schedule();\n      }).catch(function(e){ txt('[data-ov]', 'erro: ' + ((e && e.message) || e)); });\n    } else txt('[data-ov]', '—');\n    var ti = performance.now();\n    // índice: da table (coluna arquivo = file) quando o parâmetro indice vier; senão, do JSON no Drive\n    // loadAll() não faz nada se a busca inicial do dataset ainda está em andamento: espera todas as linhas chegarem\n    function waitAll(ds){\n      return new Promise(function(res, rej){\n        var t0 = Date.now();\n        (function tick(){\n          var n = ds.records().length, tot = (ds.pageInfo() || {}).total || 0, busy = ds.fetching || ds.mutating;\n          if (!busy && n && n >= tot) return res(ds.records());\n          if (!busy && n < tot) ds.loadAll();\n          if (Date.now() - t0 > 30000) return rej(new Error('tempo esgotado lendo a table (' + n + '/' + tot + ')'));\n          setTimeout(tick, 150);\n        })();\n      });\n    }\n    var loadIndex = IDX_DS\n      ? waitAll(IDX_DS).then(function(rows){\n          rows = rows.slice().sort(function(a, b){ return a.ordem - b.ordem; });\n          if (rows[0] && rows[0].arquivo && rows[0].arquivo.fileUrl) DATA_URL = rows[0].arquivo.fileUrl;\n          var tot = rows.reduce(function(s, r){ return s + r.bytes; }, 0);\n          txt('[data-idx]', 'table · ' + rows.length + ' linhas em ' + Math.round(performance.now() - ti) + ' ms');\n          return { total: tot, fromTable: true, tiles: rows.map(function(r){ return { off: r.byteInicio, len: r.bytes, n: r.feicoes, bb: [r.lonMin, r.latMin, r.lonMax, r.latMax], cell: [r.celulaLon, r.celulaLat, r.celulaGraus] }; }) };\n        })\n      : fetch(IDX_URL).then(function(r){ if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); });\n    loadIndex.then(function(idx){\n      total = idx.total;\n      tiles = idx.tiles.map(function(t){\n        var c = t.cell, mbx = [t.bb[0], mercY(t.bb[1]), t.bb[2], mercY(t.bb[3])];\n        if (mbx[0] < ext[0]) ext[0] = mbx[0]; if (mbx[2] > ext[2]) ext[2] = mbx[2]; if (mbx[1] < ext[1]) ext[1] = mbx[1]; if (mbx[3] > ext[3]) ext[3] = mbx[3];\n        return { off: t.off, len: t.len, n: t.n, mb: mbx, cell: [c[0], mercY(c[1]), c[0] + c[2], mercY(c[1] + c[2])], state: null, geo: null, mem: 0, used: 0 };\n      });\n      txt('[data-total]', mb(total) + ' · ' + tiles.length + ' quadrículas');\n      if (!idx.fromTable) txt('[data-idx]', mb(JSON.stringify(idx).length) + ' em ' + Math.round(performance.now() - ti) + ' ms');\n      stats();\n      if (W) start();\n    }).catch(function(e){ status('falha ao ler índice: ' + ((e && e.message) || e), true); });\n  }\n};"}},"indice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"indice"}}}},"indiceUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"indiceUrl"}}}},"arquivoUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivoUrl"}}}},"visaoGeralUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"visaoGeralUrl"}}}},"inicio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"inicio"}}}}}}}}}},{"_id":"6ab49e7b5e1ce1556917cdd4","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Doc_OSM_sem_Overpass","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  :host{display:block;height:100%;--w:#ffffff;--g:#f5f5f7;--ink:#1d1d1f;--mut:#6e6e73;--line:#d2d2d7;--rust:#c2410c;--moss:#3f7d20}\n  *{box-sizing:border-box}\n  .doc{height:100%;overflow:auto;background:var(--w);color:var(--ink);font:17px/1.47 -apple-system,BlinkMacSystemFont,\"SF Pro Text\",\"Helvetica Neue\",Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased}\n  nav{position:sticky;top:0;z-index:5;background:rgba(255,255,255,.86);backdrop-filter:saturate(180%) blur(16px);-webkit-backdrop-filter:saturate(180%) blur(16px);border-bottom:1px solid rgba(0,0,0,.08)}\n  nav .in{max-width:1024px;margin:0 auto;height:48px;padding:0 22px;display:flex;align-items:center;gap:22px;font-size:12px}\n  nav .t{font-size:17px;font-weight:600;letter-spacing:-.02em;margin-right:auto}\n  nav a{color:var(--ink);opacity:.8;cursor:pointer}\n  nav a:hover{opacity:1;color:var(--rust)}\n  .band{padding:110px 22px 96px}\n  .band.g{background:var(--g)}\n  .in{max-width:1024px;margin:0 auto}\n  .c{text-align:center}\n  .eyebrow{font-size:17px;font-weight:600;color:var(--rust);margin:0 0 6px}\n  h1{font-size:72px;line-height:1.05;letter-spacing:-.025em;font-weight:700;margin:0 auto 14px;max-width:880px}\n  h2{font-size:48px;line-height:1.08;letter-spacing:-.02em;font-weight:700;margin:0 auto 14px;max-width:820px}\n  .sub{font-size:21px;line-height:1.38;color:var(--mut);max-width:680px;margin:0 auto;font-weight:400;letter-spacing:.004em}\n  .sub b{color:var(--ink);font-weight:600}\n  .stage{position:relative;margin:56px auto 0;max-width:1024px}\n  .stage canvas{display:block;width:100%}\n  .stage.wide{max-width:none;width:min(1300px,calc(100vw - 44px));left:50%;transform:translateX(-50%)}\n  .cap{font-size:14px;color:var(--mut);text-align:center;margin:14px auto 0;max-width:620px}\n  .hud{position:absolute;left:0;top:0;font:13px/1.5 -apple-system,BlinkMacSystemFont,sans-serif;color:var(--mut);pointer-events:none;text-align:left}\n  .hud b{color:var(--ink);font-weight:600}\n  .hud.big{font-size:15px}\n  .tip{position:absolute;pointer-events:none;background:rgba(29,29,31,.94);color:#f5f5f7;border-radius:12px;padding:10px 13px;font:12px/1.55 ui-monospace,Menlo,monospace;white-space:nowrap;transform:translate(-50%,calc(-100% - 16px));opacity:0;transition:opacity .15s;text-align:left}\n  .tip.on{opacity:1}\n  .tip b{font:600 13px -apple-system,sans-serif;color:#fff}\n  .tip span{color:#a1a1a6}\n  .ctrl{display:flex;justify-content:center;gap:4px;margin:26px auto 0;background:#e8e8ed;border-radius:99px;padding:4px;width:max-content}\n  .ctrl button{font:14px -apple-system,sans-serif;border:0;background:transparent;border-radius:99px;padding:7px 16px;cursor:pointer;color:var(--ink)}\n  .ctrl button.on{background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.12)}\n  .reqlog{position:absolute;left:0;bottom:6px;font:12px/1.7 ui-monospace,Menlo,monospace;color:var(--mut);pointer-events:none;text-align:left}\n  .reqlog div{opacity:0;transform:translateY(4px);transition:all .3s}\n  .reqlog div.in{opacity:1;transform:none}\n  .stats{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin:64px auto 0;max-width:900px;text-align:center}\n  .stats b{display:block;font-size:56px;line-height:1.05;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n  .stats span{display:block;font-size:15px;color:var(--mut);margin-top:6px}\n  .stats .rust b{color:var(--rust)}\n  .versus{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:900px;margin:56px auto 0}\n  .versus > div{background:var(--w);border-radius:22px;padding:34px 30px;text-align:center}\n  .band:not(.g) .versus > div{background:var(--g)}\n  .versus small{display:block;font-size:14px;color:var(--mut);font-weight:600}\n  .versus b{display:block;font-size:64px;line-height:1.05;font-weight:700;letter-spacing:-.025em;margin:8px 0 4px}\n  .versus span{font-size:15px;color:var(--mut)}\n  .versus .hi b{color:var(--rust)}\n  .tiles{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:1024px;margin:56px auto 0}\n  .tile{background:var(--g);border-radius:22px;padding:34px 32px}\n  .band.g .tile{background:var(--w)}\n  .tile h3{font-size:24px;line-height:1.17;letter-spacing:-.01em;margin:0 0 10px;font-weight:700}\n  .tile p{font-size:16px;color:var(--mut);margin:0}\n  .tile .n{font-size:40px;font-weight:700;letter-spacing:-.02em;color:var(--ink);margin-bottom:8px;font-variant-numeric:tabular-nums}\n  code,.mono{font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace}\n  code{color:var(--ink)}\n  .specs{max-width:1024px;margin:56px auto 0}\n  .spec{display:grid;grid-template-columns:240px 1fr;gap:24px;padding:22px 0;border-top:1px solid var(--line)}\n  .spec h4{margin:0;font-size:17px;font-weight:600}\n  .spec .v{font-size:15px;color:var(--ink)}\n  .spec .v .note{color:var(--mut)}\n  table{width:100%;border-collapse:collapse;font-size:14px;font-variant-numeric:tabular-nums}\n  td{padding:9px 12px 9px 0;border-bottom:1px solid #e8e8ed;vertical-align:top}\n  td.r{text-align:right;padding-right:0}\n  th{text-align:left;font-size:12px;color:var(--mut);font-weight:600;padding:0 12px 8px 0}\n  th.r{text-align:right;padding-right:0}\n  .type{font:12px ui-monospace,Menlo,monospace;color:var(--mut)}\n  .type.file{color:var(--rust);font-weight:600}\n  .filechip{color:#0066cc;text-decoration:none;font-size:14px;white-space:nowrap}\n  .filechip:hover{text-decoration:underline}\n  .filechip i{display:none}\n  .note{font-size:13px;color:var(--mut)}\n  .foot{background:var(--g);padding:26px 22px 60px;font-size:12px;color:var(--mut);border-top:1px solid var(--line)}\n  @media (max-width:820px){\n    h1{font-size:44px} h2{font-size:32px} .sub{font-size:18px} .band{padding:72px 18px 64px}\n    .stats{grid-template-columns:1fr} .stats b{font-size:44px} .versus,.tiles{grid-template-columns:1fr} .versus b{font-size:48px}\n    .spec{grid-template-columns:1fr;gap:8px} .reqlog,.hide-sm{display:none} nav a{display:none}\n  }\n  `,\n  render(){\n    return '<div class=\"doc\" data-doc>'\n      + '<nav><div class=\"in\"><span class=\"t\">OSM sem Overpass</span><a data-go=\"s1\">Hoje</a><a data-go=\"s2\">Arquivo</a><a data-go=\"s3\">Índice</a><a data-go=\"s4\">Leitura</a><a data-go=\"s4b\">Exemplo</a><a data-go=\"s5\">Medições</a><a data-go=\"s7\">Especificações</a></div></nav>'\n\n      + '<div class=\"band\"><div class=\"in c\">'\n      + '<p class=\"eyebrow\">Estudo de dados · natura-geomarketing</p>'\n      + '<h1>Rodovias e rios do Brasil, lidos direto do Drive.</h1>'\n      + '<p class=\"sub\">Um arquivo de 600 MB e uma table de 884 linhas. O mapa baixa só o pedaço que está na tela.</p>'\n      + '<div class=\"stage\" data-st=\"hero\"><canvas data-cv=\"hero\" height=\"600\"></canvas><div class=\"hud\" data-hud=\"hero\">lendo a table…</div><div class=\"tip\" data-tip=\"hero\"></div></div>'\n      + '<p class=\"cap\">Cada bloco é uma linha da table. A altura é o tamanho da quadrícula no arquivo. Passe o mouse.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s1\"><div class=\"in c\">'\n      + '<h2>Hoje, cada etapa espera o Overpass.</h2>'\n      + '<p class=\"sub\">O painel Camadas consulta o Overpass, do OpenStreetMap. Quando ele está cheio, responde 504 e o simulador tenta de novo, sem limite.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"today\" height=\"260\"></canvas><div class=\"hud\" data-hud=\"today\"></div></div>'\n      + '<div class=\"stats\"><div class=\"rust\"><b>1.157</b><span>chamadas à Rush osmOverpassProxy</span></div><div class=\"rust\"><b>504</b><span>resposta da maioria das chamadas de 14/09</span></div><div><b>0</b><span>cálculos de fechamento que usam essa geometria</span></div></div>'\n      + '</div></div>'\n\n      + '<div class=\"band\" id=\"s2\"><div class=\"in c\">'\n      + '<h2>O Brasil inteiro, filtrado e separado por área.</h2>'\n      + '<p class=\"sub\">Partimos dos <b>2,1 GB</b> do OpenStreetMap. Ficam rodovias e rios, em quadrículas de até 4 MB gravadas em ordem Z. Vizinhas no mapa ficam vizinhas no arquivo.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"pipe\" height=\"280\"></canvas></div>'\n      + '<p class=\"cap\">Volume de cada cubo proporcional ao tamanho em bytes.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s3\"><div class=\"in c\">'\n      + '<h2>Uma table diz onde começa cada pedaço.</h2>'\n      + '<p class=\"sub\">Cada linha guarda o byte inicial e o tamanho de uma quadrícula. A coluna <b>arquivo</b> é do tipo file e aponta para o arquivo no Drive.</p>'\n      + '<div class=\"stage\"><canvas data-cv=\"strip\" height=\"130\"></canvas><div class=\"hud\" data-hud=\"strip\"></div></div>'\n      + '<p class=\"cap\">Os 600 MB de ponta a ponta, uma fatia por linha. Passe o mouse no mapa do topo e a fatia correspondente sobe aqui.</p>'\n      + '<div class=\"specs\" style=\"text-align:left\">'\n      + '<div class=\"spec\"><h4>Colunas</h4><div class=\"v\"><table><tbody>'\n      + '<tr><td class=\"mono\">arquivo</td><td><span class=\"type file\">file</span></td><td>osm-brasil-quadriculas.ndjson, geoDatasetsDrive</td></tr>'\n      + '<tr><td class=\"mono\">byteInicio, bytes</td><td><span class=\"type\">number</span></td><td>onde a quadrícula começa e quantos bytes ler</td></tr>'\n      + '<tr><td class=\"mono\">range</td><td><span class=\"type\">string</span></td><td>header pronto, bytes=início-fim</td></tr>'\n      + '<tr><td class=\"mono\">lonMin, latMin, lonMax, latMax</td><td><span class=\"type\">number</span></td><td>caixa das linhas, para saber se está na tela</td></tr>'\n      + '<tr><td class=\"mono\">celulaLon, celulaLat, celulaGraus, nivel</td><td><span class=\"type\">number</span></td><td>célula do quadtree</td></tr>'\n      + '<tr><td class=\"mono\">feicoes, rodovias, rios, ordem</td><td><span class=\"type\">number</span></td><td>contagens e posição no arquivo</td></tr>'\n      + '</tbody></table></div></div>'\n      + '<div class=\"spec\"><h4>Amostra</h4><div class=\"v\"><table><thead><tr><th>Quadrícula</th><th class=\"r\">Byte inicial</th><th class=\"r\">Tamanho</th><th class=\"hide-sm\" style=\"padding-left:28px\">Range</th></tr></thead><tbody data-sample><tr><td colspan=\"4\" class=\"note\">lendo…</td></tr></tbody></table></div></div>'\n      + '</div></div></div>'\n\n      + '<div class=\"band\" id=\"s4\"><div class=\"in c\">'\n      + '<h2>A tela pede só os bytes dela.</h2>'\n      + '<p class=\"sub\">O componente acha as quadrículas visíveis, carrega do centro para fora até 48 MB e junta as vizinhas numa requisição só.</p>'\n      + '<div class=\"ctrl\" data-ctrl></div>'\n      + '<div class=\"stage\" style=\"margin-top:28px\"><canvas data-cv=\"read\" height=\"440\"></canvas><div class=\"hud big\" data-hud=\"read\"></div><div class=\"reqlog\" data-reqlog></div></div>'\n      + '<p class=\"cap\">Grupos e tamanhos calculados a partir das linhas da table, com as regras do componente.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s4b\"><div class=\"in c\">'\n      + '<h2>São Paulo, montada camada por camada.</h2>'\n      + '<p class=\"sub\">Este mapa foi baixado agora, do mesmo arquivo, com Range. As 10 quadrículas da cidade estão em sequência no arquivo e chegam numa requisição só.</p>'\n      + '<div class=\"stage wide\"><canvas data-cv=\"layers\" height=\"900\"></canvas><div class=\"hud big\" data-hud=\"layers\"></div></div>'\n      + '<p class=\"cap\">Cada plano é uma camada desenhada com os dados baixados. Com os planos abertos, cada quadrícula acende junto com a linha dela na table e o trecho que ocupa no arquivo, na ordem em que foi gravada. <a class=\"filechip\" href=\"https://gui.data2.qa/preview/ebL6zNvLBCPbgjFylsmk6\" target=\"_blank\" rel=\"noopener\">Abrir o mapa interativo</a></p>'\n      + '</div></div>'\n\n      + '<div class=\"band\" id=\"s5\"><div class=\"in c\">'\n      + '<h2>De 742 MB para 20 MB.</h2>'\n      + '<p class=\"sub\">Medido no render de verificação, com São Paulo na tela.</p>'\n      + '<div class=\"versus\"><div><small>Arquivo inteiro em streaming</small><b>742 MB</b><span>17 s para baixar e ler · 600 MB de memória na aba</span></div><div class=\"hi\"><small>Por quadrícula</small><b>20 MB</b><span>12 quadrículas em 3 requisições · 3,4% do arquivo</span></div></div>'\n      + '<div class=\"stage\"><canvas data-cv=\"gzip\" height=\"440\"></canvas></div>'\n      + '<p class=\"cap\">O CloudFront comprime um arquivo lido inteiro só quando ele tem até 10 MB e é servido como application/json. Cada cubo tem volume proporcional aos bytes.</p>'\n      + '</div></div>'\n\n      + '<div class=\"band g\" id=\"s6\"><div class=\"in c\">'\n      + '<h2>Quatro detalhes que definiram o formato.</h2>'\n      + '<div class=\"tiles\" style=\"text-align:left\">'\n      + '<div class=\"tile\"><div class=\"n\">Range</div><h3>Geometria em arquivo, índice em table.</h3><p>Desenhar sempre pede tudo de uma área, e um GET com Range entrega isso. Milhões de linhas de geometria numa table precisariam de paginação.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">10 MB</div><h3>O limite da compressão.</h3><p>Rodovias do Norte, 4,6 MB, trafegaram 0,7 MB. Rios do Norte, 12,1 MB, trafegaram inteiros.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">application/json</div><h3>O content-type decide.</h3><p>O mesmo arquivo de 8,3 MB veio inteiro como application/x-ndjson e com 1,2 MB como application/json.</p></div>'\n      + '<div class=\"tile\"><div class=\"n\">145 de 14.716</div><h3>Rio permanente vem do IBGE.</h3><p>Poucos rios do OSM têm a tag intermittent. O BC250 do IBGE traz o regime de cada trecho e já está no Drive.</p></div>'\n      + '</div></div></div>'\n\n      + '<div class=\"band\" id=\"s7\"><div class=\"in\">'\n      + '<h2 class=\"c\">Especificações</h2>'\n      + '<div class=\"specs\">'\n      + '<div class=\"spec\"><h4>Fonte</h4><div class=\"v\">OpenStreetMap Brasil via Geofabrik, 2,1 GB, atualizado diariamente<br><span class=\"note\">osmium tags-filter: highway motorway a tertiary, waterway=river</span></div></div>'\n      + '<div class=\"spec\"><h4>Arquivo</h4><div class=\"v\">osm-brasil-quadriculas.ndjson, 600 MB, 884 quadrículas<br><span class=\"note\">células de 1° divididas até 4 MB, ordem Z, mediana 370 KB</span></div></div>'\n      + '<div class=\"spec\"><h4>Índice</h4><div class=\"v\">Table Quadrículas OSM Brasil, 884 linhas, coluna arquivo do tipo file<br><span class=\"note\">acesso restrito ao builder e às Rushes do system</span></div></div>'\n      + '<div class=\"spec\"><h4>Visão geral</h4><div class=\"v\">osm-brasil-visao-geral.json, 8,3 MB no Drive, 1,2 MB na rede<br><span class=\"note\">rodovias principais e rios, coordenadas a 0,01°</span></div></div>'\n      + '<div class=\"spec\"><h4>Leitura</h4><div class=\"v\">Até 48 MB por vista, 4 requisições em paralelo, grupos de até 16 MB<br><span class=\"note\">blocos a menos de 512 KB viram uma requisição</span></div></div>'\n      + '<div class=\"spec\"><h4>Próximos passos</h4><div class=\"v\">Ligar a leitura por quadrícula no simulador, com cache no IndexedDB<br><span class=\"note\">gerar rios do BC250 e municípios da malha IBGE 2025 no mesmo formato; apagar overpassRoads e overpassClient</span></div></div>'\n      + '<div class=\"spec\"><h4 data-catsum>Arquivos</h4><div class=\"v\"><table><thead><tr><th>Arquivo</th><th class=\"hide-sm\">Papel</th><th class=\"r\">Tamanho</th></tr></thead><tbody data-cat><tr><td colspan=\"3\" class=\"note\">lendo…</td></tr></tbody></table></div></div>'\n      + '</div></div></div>'\n      + '<div class=\"foot\"><div class=\"in\">OpenStreetMap © colaboradores do OSM, licença ODbL, via Geofabrik. IBGE BC250 2025 e malha municipal 2025. ANAC dados abertos. OurAirports. Workspace Estudo geomarketing · Overpass → Drive, system gui.</div></div>'\n      + '</div>';\n  },\n  mounted(d2){\n    var DOC = d2.$('[data-doc]');\n    var qd = d2.data('quadriculas'), ad = d2.data('arquivos');\n    var Q = [], A = [], hoverQ = null;\n\n    // ---------- utilidades ----------\n    function esc(v){ return String(v == null ? '' : v).replace(/[&<>\"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]; }); }\n    function nf(n){ return Number(n || 0).toLocaleString('pt-BR'); }\n    function mb(n){ if (n >= 1e9) return (n / 1e9).toFixed(1).replace('.', ',') + ' GB'; if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e8 ? 0 : 1).replace('.', ',') + ' MB'; if (n >= 1e3) return Math.round(n / 1e3) + ' KB'; return Math.round(n) + ' B'; }\n    function mercY(lat){ var r = lat * Math.PI / 180; return Math.log(Math.tan(Math.PI / 4 + r / 2)) * 180 / Math.PI; }\n    function ease(t){ t = Math.max(0, Math.min(1, t)); return 1 - Math.pow(1 - t, 3); }\n    function inout(t){ t = Math.max(0, Math.min(1, t)); return t < .5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }\n    function rgb(c){ return 'rgb(' + Math.round(c[0]) + ',' + Math.round(c[1]) + ',' + Math.round(c[2]) + ')'; }\n    function mix(a, b, t){ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t, a[2] + (b[2] - a[2]) * t]; }\n    function shade(c, k){ return [c[0] * k, c[1] * k, c[2] * k]; }\n    var C0 = [242, 230, 216], C1 = [224, 130, 43], C2 = [122, 31, 14], INK = [29, 29, 31], RUST = [194, 65, 12], BLUE = [0, 113, 227], PAPER = [255, 255, 255], GRAY = [210, 210, 215];\n    function ramp(t){ t = Math.max(0, Math.min(1, t)); return t < .5 ? mix(C0, C1, t * 2) : mix(C1, C2, (t - .5) * 2); }\n    function txt(sel, v){ var el = d2.$(sel); if (el) el.textContent = v; }\n\n    // canvas com DPR e laço de animação que pausa fora da tela\n    var scenes = [];\n    function scene(name, draw){\n      var cv = d2.$('[data-cv=\"' + name + '\"]'); if (!cv) return null;\n      var s = { cv: cv, ctx: cv.getContext('2d'), W: 0, H: +cv.getAttribute('height'), vis: false, t0: 0, draw: draw, name: name };\n      s.fit = function(){ var dpr = window.devicePixelRatio || 1, w = cv.getBoundingClientRect().width; s.W = w; s.dpr = dpr; cv.style.height = s.H + 'px'; cv.width = Math.round(w * dpr); cv.height = Math.round(s.H * dpr); s.ctx.setTransform(dpr, 0, 0, dpr, 0, 0); };\n      scenes.push(s); return s;\n    }\n    var ro = new ResizeObserver(function(){ scenes.forEach(function(s){ s.fit(); }); });\n    var io = new IntersectionObserver(function(es){ es.forEach(function(e){ scenes.forEach(function(s){ if (s.cv === e.target){ s.vis = e.isIntersecting; if (s.vis && !s.t0) s.t0 = performance.now(); } }); }); }, { root: DOC, threshold: 0.05 });\n    var stop = d2.raf(function(t){ scenes.forEach(function(s){ if (s.vis && s.W){ s.ctx.clearRect(0, 0, s.W, s.H); s.draw(s, (t - s.t0) / 1000); } }); });\n    d2.onUnmount(function(){ ro.disconnect(); io.disconnect(); if (stop) stop(); });\n\n    // projeção oblíqua (cabinet): x para a direita, y para o fundo (sobe à direita), z para cima\n    function ob(o, x, y, z){ return [o[0] + x + y * 0.55, o[1] - z - y * 0.32]; }\n    function poly(ctx, pts, fill, stroke){ ctx.beginPath(); ctx.moveTo(pts[0][0], pts[0][1]); for (var i = 1; i < pts.length; i++) ctx.lineTo(pts[i][0], pts[i][1]); ctx.closePath(); if (fill){ ctx.fillStyle = fill; ctx.fill(); } if (stroke){ ctx.strokeStyle = stroke; ctx.stroke(); } }\n    // caixa oblíqua: base em (x,y,z), dimensões w (x), d (y), h (z)\n    function obox(ctx, o, x, y, z, w, d, h, col, edge){\n      var P = function(a, b, c){ return ob(o, x + a, y + b, z + c); };\n      poly(ctx, [P(0, 0, 0), P(w, 0, 0), P(w, 0, h), P(0, 0, h)], rgb(col), edge);\n      poly(ctx, [P(w, 0, 0), P(w, d, 0), P(w, d, h), P(w, 0, h)], rgb(shade(col, .78)), edge);\n      poly(ctx, [P(0, 0, h), P(w, 0, h), P(w, d, h), P(0, d, h)], rgb(mix(col, [255, 255, 255], .18)), edge);\n    }\n    function label(ctx, x, y, s, size, color, align, weight){ ctx.font = (weight || 500) + ' ' + (size || 12) + 'px -apple-system,BlinkMacSystemFont,sans-serif'; ctx.fillStyle = color || '#3d3c38'; ctx.textAlign = align || 'left'; ctx.fillText(s, x, y); }\n    function mono(ctx, x, y, s, size, color, align){ ctx.font = (size || 11.5) + 'px ui-monospace,Menlo,monospace'; ctx.fillStyle = color || '#3d3c38'; ctx.textAlign = align || 'left'; ctx.fillText(s, x, y); }\n\n    // ================= 1. mapa isométrico das quadrículas =================\n    var hero = scene('hero', function(s, t){\n      var ctx = s.ctx;\n      if (!Q.length){ label(ctx, s.W / 2, s.H / 2, 'lendo a table…', 14, '#6e6e73', 'center'); return; }\n      if (!s.prisms) buildHero(s);\n      var rot = -Math.PI / 4 + Math.sin(t * 0.22) * 0.22;\n      if (!s.hover) s.lastRot = rot; else rot = s.lastRot;\n      var cr = Math.cos(rot), sr = Math.sin(rot);\n      var sc = s.scale, cx = s.W / 2, cy = s.H * 0.47;\n      function P(x, y, z){ var xr = x * cr - y * sr, yr = x * sr + y * cr; return [cx + (xr - yr) * 0.866 * sc, cy + (xr + yr) * 0.5 * sc - z * sc]; }\n      // faces visíveis dependem da rotação\n      var nrm = [[1, 0], [0, 1], [-1, 0], [0, -1]].map(function(n){ var xr = n[0] * cr - n[1] * sr, yr = n[0] * sr + n[1] * cr; return xr + yr > 0.02; });\n      var items = s.prisms.map(function(p){ var xr = p.cx * cr - p.cy * sr, yr = p.cx * sr + p.cy * cr; p.depth = xr + yr; return p; }).sort(function(a, b){ return a.depth - b.depth; });\n      // sombra do chão\n      ctx.fillStyle = 'rgba(29,29,31,.05)';\n      items.forEach(function(p){ var a = P(p.x0, p.y0, 0), b = P(p.x1, p.y0, 0), c = P(p.x1, p.y1, 0), e = P(p.x0, p.y1, 0); ctx.beginPath(); ctx.moveTo(a[0], a[1]); ctx.lineTo(b[0], b[1]); ctx.lineTo(c[0], c[1]); ctx.lineTo(e[0], e[1]); ctx.closePath(); ctx.fill(); });\n      s.tops = [];\n      items.forEach(function(p){\n        var grow = ease((t - p.delay) / 1.1), h = p.h * grow;\n        var on = hoverQ === p.r.ordem, col = on ? INK : p.col;\n        var c = [[p.x0, p.y0], [p.x1, p.y0], [p.x1, p.y1], [p.x0, p.y1]];\n        // lados: face i liga c[i] -> c[i+1]; normais na ordem (0,-1),(1,0),(0,1),(-1,0)\n        var faceN = [3, 0, 1, 2];\n        for (var i = 0; i < 4; i++){\n          if (!nrm[faceN[i]]) continue;\n          var a = c[i], b = c[(i + 1) % 4];\n          var k = faceN[i] === 0 || faceN[i] === 2 ? .8 : .66;\n          poly(ctx, [P(a[0], a[1], 0), P(b[0], b[1], 0), P(b[0], b[1], h), P(a[0], a[1], h)], rgb(shade(col, k)));\n        }\n        var top = c.map(function(q){ return P(q[0], q[1], h); });\n        poly(ctx, top, rgb(on ? INK : mix(col, [255, 255, 255], .08)), 'rgba(29,29,31,.10)');\n        s.tops.push({ p: p, pts: top });\n      });\n      var loaded = s.prisms.filter(function(p){ return t > p.delay + 1.1; }).length;\n      var el = d2.$('[data-hud=\"hero\"]');\n      if (el && !s.hudDone){ el.innerHTML = '<b>' + nf(Math.min(loaded, s.prisms.length)) + '</b> de ' + nf(s.prisms.length) + ' linhas · <b>' + mb(s.sum * Math.min(1, loaded / s.prisms.length)) + '</b> de ' + mb(s.sum); if (loaded >= s.prisms.length) s.hudDone = true; }\n    });\n    function buildHero(s){\n      var lon0 = -52, y0 = mercY(-14), maxB = 1;\n      Q.forEach(function(r){ if (r.bytes > maxB) maxB = r.bytes; });\n      s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0);\n      s.prisms = Q.map(function(r){\n        var x0 = r.celulaLon - lon0, x1 = x0 + r.celulaGraus, yT = y0 - mercY(r.celulaLat + r.celulaGraus), yB = y0 - mercY(r.celulaLat);\n        var g = r.celulaGraus * 0.06, cxp = (x0 + x1) / 2, cyp = (yT + yB) / 2;\n        var tn = Math.sqrt(r.bytes / maxB);\n        return { r: r, x0: x0 + g, x1: x1 - g, y0: yT + g, y1: yB - g, cx: cxp, cy: cyp, h: 0.15 + tn * 6.5, col: ramp(tn), delay: 0.15 + Math.hypot(cxp, cyp) * 0.045 + (r.ordem % 7) * 0.02 };\n      });\n      s.scale = Math.min(s.W / 60, s.H / 40);\n    }\n    function inPoly(x, y, pts){ var c = false; for (var i = 0, j = pts.length - 1; i < pts.length; j = i++){ var xi = pts[i][0], yi = pts[i][1], xj = pts[j][0], yj = pts[j][1]; if (((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) c = !c; } return c; }\n    if (hero){\n      d2.on(hero.cv, 'mousemove', function(e){\n        if (!hero.tops) return;\n        var r = hero.cv.getBoundingClientRect(), x = e.clientX - r.left, y = e.clientY - r.top, hit = null;\n        for (var i = hero.tops.length - 1; i >= 0; i--){ if (inPoly(x, y, hero.tops[i].pts)){ hit = hero.tops[i].p; break; } }\n        var tip = d2.$('[data-tip=\"hero\"]');\n        hero.hover = !!hit; hoverQ = hit ? hit.r.ordem : null;\n        if (!hit){ tip.className = 'tip'; return; }\n        var q = hit.r;\n        tip.innerHTML = '<b>' + esc(q.quadricula) + '</b><br><span>tamanho</span> ' + mb(q.bytes) + ' <span>·</span> ' + nf(q.feicoes) + ' <span>trechos</span><br><span>Range:</span> ' + esc(q.range) + '<br><span>' + nf(q.rodovias) + ' rodovias · ' + nf(q.rios) + ' rios · nível ' + q.nivel + '</span>';\n        tip.style.left = x + 'px'; tip.style.top = y + 'px'; tip.className = 'tip on';\n      });\n      d2.on(hero.cv, 'mouseleave', function(){ hero.hover = false; hoverQ = null; var tip = d2.$('[data-tip=\"hero\"]'); if (tip) tip.className = 'tip'; });\n    }\n\n    // ================= 2. como é hoje =================\n    var today = scene('today', function(s, t){\n      var ctx = s.ctx, W = s.W, o = [0, 205];\n      var nodes = [{ x: W * 0.04, w: W * 0.2, name: 'Simulador', sub: 'navegador', col: PAPER }, { x: W * 0.39, w: W * 0.2, name: 'osmOverpassProxy', sub: 'Rush', col: [255, 241, 230] }, { x: W * 0.74, w: W * 0.22, name: 'overpass-api.de', sub: 'servidor público', col: [255, 241, 230] }];\n      nodes.forEach(function(n){ obox(ctx, o, n.x, 0, 0, n.w, 70, 26, n.col, 'rgba(29,29,31,.18)'); var p = ob(o, n.x + 14, 0, 0); label(ctx, p[0], p[1] + 22, n.name, 13, '#1d1d1f', 'left', 600); label(ctx, p[0], p[1] + 38, n.sub, 11.5, '#6e6e73'); });\n      // pacotes: ida até o overpass; a maioria volta como 504 e reinicia\n      var period = 2.6, n = 4, total = 0;\n      for (var k = 0; k < n; k++){\n        var tt = t + k * period / n, cyc = Math.floor(tt / period), ph = (tt % period) / period;\n        var fails = ((cyc * 7 + k * 3) % 10) < 8;\n        total += cyc;\n        var xA = nodes[0].x + nodes[0].w * 0.6, xB = nodes[2].x + nodes[2].w * 0.35, x, z, col;\n        if (ph < 0.55){ x = xA + (xB - xA) * inout(ph / 0.55); z = 40 + Math.sin(ph / 0.55 * Math.PI) * 26; col = INK; }\n        else { var q = (ph - 0.55) / 0.45; x = xB - (xB - xA) * inout(q) * (fails ? 0.62 : 1); z = 40 + Math.sin(q * Math.PI) * 18; col = fails ? RUST : [63, 125, 32]; }\n        obox(ctx, o, x - 7, 28, z, 14, 14, 12, col);\n        if (ph >= 0.55 && ph < 0.72){ var pp = ob(o, xB, 30, 78); label(ctx, pp[0], pp[1], fails ? '504 server too busy' : '200 OK', 12, fails ? '#c2410c' : '#3f7d20', 'center', 600); }\n      }\n      var hud = d2.$('[data-hud=\"today\"]'); if (hud) hud.innerHTML = 'consultas <b>' + nf(total + n) + '</b> · respostas 504 <b style=\"color:#c2410c\">' + nf(Math.round(total * 0.8)) + '</b> · cada falha volta para a fila';\n    });\n\n    // ================= 3. do PBF às quadrículas =================\n    var pipe = scene('pipe', function(s, t){\n      var ctx = s.ctx, W = s.W, base = 196, maxSide = 150;\n      var items = [\n        { name: 'brazil-latest.osm.pbf', size: 2086611725, sub: 'Geofabrik', col: BLUE },\n        { name: 'rodovias + rios', size: 742236000, sub: 'osmium tags-filter', col: GRAY },\n        { name: 'quadrículas', size: 599981073, sub: 'tile.mjs · 884 blocos', col: RUST },\n        { name: 'visão geral', size: 8336717, sub: '0,01° · 1,2 MB na rede', col: C1 },\n        { name: 'índice', size: 143706, sub: 'table · 884 linhas', col: [91, 58, 160] }\n      ];\n      var xs = [0.04, 0.26, 0.48, 0.70, 0.86];\n      items.forEach(function(it, i){\n        var side = Math.max(7, maxSide * Math.cbrt(it.size / items[0].size));\n        var g = ease((t - i * 0.55) / 0.8), sd = side * g, x = W * xs[i], o = [x, base];\n        if (g > 0){\n          if (i === 2){\n            // o cubo das quadrículas é feito de fatias\n            var sl = 12, w = sd / sl;\n            for (var k = 0; k < sl; k++) obox(ctx, o, k * w, 0, Math.sin(t * 2 + k * .7) * 2 * g, w - 1, sd * .55, sd * .55, mix(C1, C2, k / sl));\n          } else obox(ctx, o, 0, 0, 0, sd, sd * .55, sd * .55, it.col, 'rgba(29,29,31,.12)');\n        }\n        label(ctx, x, base + 26, it.name, 13, '#1d1d1f', 'left', 600);\n        mono(ctx, x, base + 44, mb(it.size * g), 12, '#c2410c');\n        label(ctx, x, base + 60, it.sub, 11.5, '#6e6e73');\n        if (i < 4 && g > .5){ var ax = x + side + 18, ax2 = W * xs[i + 1] - 14; ctx.strokeStyle = 'rgba(29,29,31,.5)'; ctx.setLineDash([4, 4]); ctx.lineDashOffset = -t * 20; ctx.beginPath(); ctx.moveTo(ax, base - 30); ctx.lineTo(ax2, base - 30); ctx.stroke(); ctx.setLineDash([]); }\n      });\n    });\n\n    // ================= 4. faixa do arquivo =================\n    var strip = scene('strip', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      if (!Q.length) return;\n      if (!s.sorted){ s.sorted = Q.slice().sort(function(a, b){ return a.byteInicio - b.byteInicio; }); s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0); s.maxB = Q.reduce(function(m, r){ return Math.max(m, r.bytes); }, 1); }\n      var o = [10, 96], L = W - 50, acc = 0, sel = null;\n      s.sorted.forEach(function(r){\n        var x = acc / s.sum * L, w = r.bytes / s.sum * L; acc += r.bytes;\n        var on = hoverQ === r.ordem, lift = on ? 16 : 0;\n        if (on) sel = { x: x, w: w, r: r };\n        obox(ctx, o, x, 0, lift, Math.max(.5, w), 34, 18, on ? INK : ramp(Math.sqrt(r.bytes / s.maxB)));\n      });\n      [0, .25, .5, .75, 1].forEach(function(f){ var p = ob(o, f * L, 0, 0); mono(ctx, p[0], p[1] + 16, mb(f * s.sum), 11, '#6e6e73', f === 1 ? 'right' : f ? 'center' : 'left'); });\n      var hud = d2.$('[data-hud=\"strip\"]');\n      if (hud) hud.innerHTML = sel ? '<b>' + esc(sel.r.quadricula) + '</b> · bytes ' + nf(sel.r.byteInicio) + ' a ' + nf(sel.r.byteInicio + sel.r.bytes - 1) + ' · ' + mb(sel.r.bytes) : 'osm-brasil-quadriculas.ndjson · ' + mb(s.sum) + ' · 884 fatias';\n    });\n\n    // ================= 5. uma leitura =================\n    var SCN = [\n      { id: 'sp', name: 'São Paulo', box: [-47.08, -23.9, -46.18, -23.2] },\n      { id: 'bel', name: 'Belém', box: [-49.08, -2.0, -47.88, -0.8] },\n      { id: 'larga', name: 'Zoom largo, 6°', box: [-50.5, -25.5, -44.5, -19.5] }\n    ];\n    function planScenario(sc){\n      var b = sc.box, cx = (b[0] + b[2]) / 2, cy = (b[1] + b[3]) / 2;\n      var vis = Q.filter(function(r){ return !(r.lonMax < b[0] || r.lonMin > b[2] || r.latMax < b[1] || r.latMin > b[3]); });\n      vis.sort(function(a, c){ var da = Math.pow(a.celulaLon + a.celulaGraus / 2 - cx, 2) + Math.pow(a.celulaLat + a.celulaGraus / 2 - cy, 2), dc = Math.pow(c.celulaLon + c.celulaGraus / 2 - cx, 2) + Math.pow(c.celulaLat + c.celulaGraus / 2 - cy, 2); return da - dc; });\n      var used = 0, need = [], skip = 0, BUDGET = 48 * 1048576;\n      vis.forEach(function(r){ if (used + r.bytes <= BUDGET){ need.push(r); used += r.bytes; } else skip++; });\n      need.sort(function(a, c){ return a.byteInicio - c.byteInicio; });\n      var groups = [], g = null;\n      need.forEach(function(r){ if (g && r.byteInicio - g.end <= 524288 && r.byteInicio + r.bytes - g.start <= 16777216){ g.tiles.push(r); g.end = r.byteInicio + r.bytes; } else { g = { start: r.byteInicio, end: r.byteInicio + r.bytes, tiles: [r] }; groups.push(g); } });\n      return { vis: vis.length, need: need, skip: skip, groups: groups, bytes: used, ids: need.reduce(function(m, r){ m[r.ordem] = 1; return m; }, {}) };\n    }\n    var readState = { i: 0, start: 0, plans: null, logged: -1 };\n    function setScenario(i, now){\n      readState.i = i; readState.start = now; readState.logged = -1;\n      var log = d2.$('[data-reqlog]'); if (log) log.innerHTML = '';\n      var c = d2.$('[data-ctrl]'); if (c) Array.prototype.forEach.call(c.children, function(b, k){ b.className = k === i ? 'on' : ''; });\n    }\n    var read = scene('read', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      if (!Q.length) return;\n      if (!readState.plans){\n        readState.plans = SCN.map(planScenario); s.sorted = Q.slice().sort(function(a, b){ return a.byteInicio - b.byteInicio; }); s.sum = Q.reduce(function(a, r){ return a + r.bytes; }, 0);\n        var c = d2.$('[data-ctrl]'); if (c && !c.children.length) c.innerHTML = SCN.map(function(sc, k){ return '<button data-scn=\"' + k + '\">' + sc.name + '</button>'; }).join('');\n        setScenario(0, t);\n      }\n      var DUR = 7.5, lt = t - readState.start;\n      if (lt > DUR && !s.pinned){ setScenario((readState.i + 1) % SCN.length, t); lt = 0; }\n      var sc = SCN[readState.i], plan = readState.plans[readState.i];\n      var o = [10, 330], L = W - 110, acc = 0;\n      // navegador\n      var bx = W - 240, bo = [bx, 170];\n      var bt = ob(bo, 0, 80, 70); label(ctx, bt[0], bt[1] - 12, 'Navegador', 13, '#1d1d1f', 'left', 600);\n      // fundo e piso do navegador (a frente é de vidro, desenhada depois da pilha)\n      var G = function(a, b, c){ return ob(bo, a, b, c); };\n      poly(ctx, [G(0, 80, 0), G(180, 80, 0), G(180, 80, 70), G(0, 80, 70)], 'rgba(0,113,227,.06)', 'rgba(0,113,227,.25)');\n      poly(ctx, [G(0, 0, 0), G(180, 0, 0), G(180, 80, 0), G(0, 80, 0)], 'rgba(0,113,227,.08)', 'rgba(0,113,227,.25)');\n      // barra do arquivo com fatias; as selecionadas acendem e sobem\n      var lift = ease((lt - 0.8) / 0.9);\n      s.sorted.forEach(function(r){\n        var x = acc / s.sum * L, w = r.bytes / s.sum * L; acc += r.bytes;\n        var on = plan.ids[r.ordem];\n        var col = on ? mix(GRAY, RUST, ease((lt - 0.4) / 0.5)) : GRAY;\n        obox(ctx, o, x, 0, on ? lift * 34 : 0, Math.max(.45, w), 40, 20, col);\n      });\n      var po = ob(o, 0, 0, 0); label(ctx, po[0], po[1] + 22, 'osm-brasil-quadriculas.ndjson · 600 MB no Drive', 12.5, '#1d1d1f', 'left', 600);\n      // grupos viajam como pacotes com tamanho proporcional\n      var got = 0, n = plan.groups.length, t1 = 1.9, span = 3.2;\n      plan.groups.forEach(function(g, k){\n        var st = t1 + k * (span / Math.max(1, n)), q = ease((lt - st) / 1.1);\n        var gx = (g.start / s.sum) * L + ((g.end - g.start) / s.sum * L) / 2;\n        var side = 8 + 30 * Math.cbrt((g.end - g.start) / 16777216);\n        if (lt > st){\n          var sx = o[0] + gx, sy = o[1] - 60, ex = bx + 60, ey = 170 - 50;\n          var x = sx + (ex - sx) * q, y = sy + (ey - sy) * q - Math.sin(q * Math.PI) * 90;\n          if (q < 1) obox(ctx, [x, y], -side / 2, 0, 0, side, side * .8, side * .8, RUST);\n          if (q >= 1) got += g.end - g.start;\n          if (q > 0.05 && readState.logged < k){\n            readState.logged = k;\n            var log = d2.$('[data-reqlog]');\n            if (log){ var dv = document.createElement('div'); dv.textContent = 'GET Range: bytes=' + g.start + '-' + (g.end - 1) + ' · ' + mb(g.end - g.start) + ' · ' + g.tiles.length + ' quadr.'; log.appendChild(dv); while (log.children.length > 6) log.removeChild(log.firstChild); requestAnimationFrame(function(){ dv.className = 'in'; }); }\n          }\n        }\n      });\n      // pilha de bytes recebidos dentro do navegador\n      var fill = got / Math.max(1, plan.bytes);\n      if (fill > 0) obox(ctx, bo, 20, 20, 0, 140, 40, 56 * fill, RUST);\n      poly(ctx, [G(0, 0, 0), G(180, 0, 0), G(180, 0, 70), G(0, 0, 70)], 'rgba(255,255,255,.28)', 'rgba(0,113,227,.45)');\n      poly(ctx, [G(180, 0, 0), G(180, 80, 0), G(180, 80, 70), G(180, 0, 70)], 'rgba(0,113,227,.07)', 'rgba(0,113,227,.45)');\n      poly(ctx, [G(0, 0, 70), G(180, 0, 70), G(180, 80, 70), G(0, 80, 70)], 'rgba(0,113,227,.04)', 'rgba(0,113,227,.45)');\n      mono(ctx, bx, 170 + 26, mb(got) + ' de ' + mb(plan.bytes) + ' recebidos', 13, '#1d1d1f');\n      var hud = d2.$('[data-hud=\"read\"]');\n      if (hud) hud.innerHTML = '<b>' + sc.name + '</b><br>' + plan.vis + ' quadrículas na tela · ' + plan.need.length + ' baixadas' + (plan.skip ? ' · ' + plan.skip + ' na visão geral' : '') + '<br>' + plan.groups.length + ' requisições · ' + mb(plan.bytes) + ' de ' + mb(s.sum) + ' (' + (100 * plan.bytes / s.sum).toFixed(1).replace('.', ',') + '%)';\n    });\n    d2.on('[data-scn]', 'click', function(e, el){ if (!read) return; read.pinned = true; setScenario(+el.getAttribute('data-scn'), (performance.now() - read.t0) / 1000); });\n\n    // ================= 6. compressão =================\n    var gz = scene('gzip', function(s, t){\n      var ctx = s.ctx, W = s.W;\n      var rows = [\n        { name: 'rodovias-norte.geojson', ct: 'application/json', a: 4557306, b: 733586, size: true, type: true },\n        { name: 'rios-norte.geojson', ct: 'application/json', a: 12064515, b: 12064515, size: false, type: true },\n        { name: 'visao-geral.ndjson', ct: 'application/x-ndjson', a: 8336717, b: 8336717, size: true, type: false },\n        { name: 'visao-geral.json', ct: 'application/json', a: 8336717, b: 1229098, size: true, type: true }\n      ];\n      var xIn = W * 0.30, xGate = W * 0.56, xOut = W * 0.80, top = 70, lane = 88, maxSide = 46, big = 12064515;\n      function side(bytes){ return Math.max(6, maxSide * Math.cbrt(bytes / big)); }\n      label(ctx, xIn, 28, 'No Drive', 13, '#6e6e73', 'center', 600);\n      label(ctx, xGate, 28, 'CloudFront', 13, '#6e6e73', 'center', 600);\n      label(ctx, xOut, 28, 'Na rede', 13, '#6e6e73', 'center', 600);\n      // portão do CloudFront atravessando todas as faixas\n      var gTop = top - 26, gBot = top + lane * rows.length - 30;\n      obox(ctx, [xGate - 6, gBot], 0, 0, 0, 12, 30, gBot - gTop, [232, 232, 237], 'rgba(29,29,31,.12)');\n      rows.forEach(function(r, i){\n        var y = top + i * lane, pass = r.size && r.type;\n        label(ctx, 20, y + 4, r.name, 14, '#1d1d1f', 'left', 600);\n        mono(ctx, 20, y + 22, r.ct, 12, '#6e6e73');\n        ctx.strokeStyle = 'rgba(29,29,31,.10)'; ctx.setLineDash([3, 5]); ctx.beginPath(); ctx.moveTo(xIn, y + 14); ctx.lineTo(xOut + 60, y + 14); ctx.stroke(); ctx.setLineDash([]);\n        // o cubo sai do Drive, passa pelo portão e chega à rede; encolhe no portão se passar nas duas checagens\n        var cyc = 5.2, ph = ((t + i * 0.45) % cyc) / cyc, travel = ease(Math.min(1, ph / 0.62));\n        var x = xIn + (xOut - xIn) * travel, crossed = x > xGate;\n        var shrink = pass ? ease((x - xGate) / 40) : 0;\n        var bytes = r.a + (r.b - r.a) * shrink, sd = side(bytes);\n        var col = !crossed ? GRAY : pass ? [63, 125, 32] : RUST;\n        if (ph < 0.92) obox(ctx, [x - sd / 2, y + 22], 0, 0, 0, sd, sd * .7, sd * .7, col);\n        // tamanhos fixos nas pontas\n        mono(ctx, xIn, y + 46, mb(r.a), 12.5, '#6e6e73', 'center');\n        var near = ph > 0.55;\n        mono(ctx, xOut, y + 46, mb(r.b) + (pass ? '  −' + Math.round(100 * (1 - r.b / r.a)) + '%' : ''), 12.5, pass ? '#3f7d20' : '#c2410c', 'center');\n        // checagens ao lado do portão\n        var lit = x > xGate - 30;\n        function check(dx, ok, txt){\n          var c = !lit ? '#aeaeb2' : ok ? '#3f7d20' : '#c2410c';\n          ctx.fillStyle = !lit ? 'rgba(174,174,178,.14)' : ok ? 'rgba(63,125,32,.10)' : 'rgba(194,65,12,.10)';\n          ctx.beginPath(); if (ctx.roundRect) ctx.roundRect(xGate + dx, y - 12, 104, 22, 11); else ctx.rect(xGate + dx, y - 12, 104, 22); ctx.fill();\n          label(ctx, xGate + dx + 52, y + 3, (lit ? (ok ? '✓ ' : '× ') : '') + txt, 12, c, 'center', 600);\n        }\n        check(-118, r.size, 'até 10 MB');\n        check(14, r.type, 'tipo json');\n        if (near && ph < 0.92 && !pass){ label(ctx, xOut, y - 2, 'passa inteiro', 11.5, '#c2410c', 'center'); }\n      });\n    });\n\n    // ================= 7. exemplo aplicado: São Paulo em camadas, baixada agora por Range =================\n    var EX = { box: [-46.95, -23.78, -46.3, -23.35], layers: null, loading: false, err: null, stats: null };\n    var LDEF = [\n      { id: 'base', name: 'Quadrículas do índice', keys: [] },\n      { id: 'river', name: 'Rios', keys: ['river'], color: '#0071e3', w: 1.6 },\n      { id: 'local', name: 'Rodovias locais', keys: ['secondary', 'tertiary'], color: '#8e8e93', w: 1.0 },\n      { id: 'main', name: 'Rodovias principais', keys: ['primary', 'trunk', 'motorway'], color: '#c2410c', w: 2.6 }\n    ];\n    function loadExample(){\n      if (EX.loading) return; EX.loading = true;\n      var b = EX.box, url = Q[0] && Q[0].arquivo && Q[0].arquivo.fileUrl;\n      if (!url){ EX.err = 'arquivo sem fileUrl'; return; }\n      var tiles = Q.filter(function(r){ return !(r.lonMax < b[0] || r.lonMin > b[2] || r.latMax < b[1] || r.latMin > b[3]); }).sort(function(a, c){ return a.byteInicio - c.byteInicio; });\n      var groups = [], g = null;\n      tiles.forEach(function(r){ if (g && r.byteInicio - g.end <= 524288){ g.end = r.byteInicio + r.bytes; g.n++; } else { g = { start: r.byteInicio, end: r.byteInicio + r.bytes, n: 1 }; groups.push(g); } });\n      var t0 = performance.now(), got = 0, feats = {}, bytesBy = {};\n      EX.progress = { tiles: tiles.length, groups: groups.length, total: groups.reduce(function(s, x){ return s + x.end - x.start; }, 0), got: 0 };\n      Promise.all(groups.map(function(gr){\n        return fetch(url, { headers: { Range: 'bytes=' + gr.start + '-' + (gr.end - 1) } }).then(function(r){\n          if (r.status !== 206) throw new Error('HTTP ' + r.status);\n          var reader = r.body.getReader(), chunks = [];\n          function pump(){ return reader.read().then(function(x){ if (x.done) return; chunks.push(x.value); got += x.value.byteLength; EX.progress.got = got; return pump(); }); }\n          return pump().then(function(){ return new Blob(chunks).text(); });\n        }).then(function(text){\n          text.split('\\n').forEach(function(line){ if (!line) return; var f = JSON.parse(line); (feats[f.k] || (feats[f.k] = [])).push(f.c); bytesBy[f.k] = (bytesBy[f.k] || 0) + line.length + 1; });\n        });\n      })).then(function(){\n        EX.stats = { tiles: tiles.length, groups: groups.length, bytes: got, ms: Math.round(performance.now() - t0), feats: Object.keys(feats).reduce(function(s, k){ return s + feats[k].length; }, 0) };\n        buildLayers(tiles, feats, bytesBy);\n      }).catch(function(e){ EX.err = (e && e.message) || String(e); });\n    }\n    function buildLayers(tiles, feats, bytesBy){\n      var S = 1000, b = EX.box, my0 = mercY(b[1]), my1 = mercY(b[3]);\n      var k = Math.min(S / (b[2] - b[0]), S / (my1 - my0)), ox = (S - (b[2] - b[0]) * k) / 2, oy = (S - (my1 - my0) * k) / 2;\n      function X(lon){ return ox + (lon - b[0]) * k; }\n      function Y(lat){ return oy + (my1 - mercY(lat)) * k; }\n      EX.layers = LDEF.map(function(L){\n        var c = document.createElement('canvas'); c.width = S; c.height = S;\n        var x = c.getContext('2d'); x.lineCap = 'round'; x.lineJoin = 'round';\n        x.save(); x.beginPath(); x.rect(0, 0, S, S); x.clip();\n        var n = 0, bytes = 0;\n        if (L.id === 'base'){\n          x.fillStyle = '#ffffff'; x.fillRect(0, 0, S, S);\n          tiles.forEach(function(r){\n            var x0 = X(r.celulaLon), x1 = X(r.celulaLon + r.celulaGraus), y0 = Y(r.celulaLat + r.celulaGraus), y1 = Y(r.celulaLat);\n            x.fillStyle = rgb(mix(C0, [255, 255, 255], .35)); x.fillRect(x0 + 3, y0 + 3, x1 - x0 - 6, y1 - y0 - 6);\n            x.strokeStyle = '#c2410c'; x.lineWidth = 3; x.setLineDash([14, 10]); x.strokeRect(x0 + 3, y0 + 3, x1 - x0 - 6, y1 - y0 - 6); x.setLineDash([]);\n            x.fillStyle = '#9a3412'; x.font = '600 26px -apple-system,sans-serif'; x.fillText(r.quadricula.split(' · ')[0], x0 + 16, y0 + 40);\n            x.font = '24px ui-monospace,Menlo,monospace'; x.fillText(mb(r.bytes), x0 + 16, y0 + 70);\n          });\n          L.info = tiles.length + ' linhas · ' + mb(EX.stats.bytes);\n        } else {\n          x.strokeStyle = L.color; x.lineWidth = L.w * 2;\n          x.beginPath();\n          L.keys.forEach(function(key){\n            (feats[key] || []).forEach(function(line){ n++; for (var i = 0; i < line.length; i++){ var px = X(line[i][0]), py = Y(line[i][1]); if (i) x.lineTo(px, py); else x.moveTo(px, py); } });\n            bytes += bytesBy[key] || 0;\n          });\n          x.stroke();\n          L.info = nf(n) + ' trechos · ' + mb(bytes);\n        }\n        x.restore();\n        return { name: L.name, info: L.info, canvas: c };\n      });\n      // quadrículas em coordenadas do plano (0..1000), na ordem em que estão no arquivo\n      EX.tq = tiles.map(function(r){ return { r: r, x0: X(r.celulaLon), x1: X(r.celulaLon + r.celulaGraus), y0: Y(r.celulaLat + r.celulaGraus), y1: Y(r.celulaLat) }; });\n      EX.range = [tiles[0].byteInicio, tiles[tiles.length - 1].byteInicio + tiles[tiles.length - 1].bytes];\n    }\n    // quadrícula no plano → linha da table → trecho do arquivo, uma de cada vez, na ordem Z\n    function drawIndexLogic(ctx, s, ip, st, exv, eyv, cx, size){\n      var O = s.O0, n = EX.tq.length, STEP = 0.62;\n      // recorta cada quadrícula no limite do plano (as de 0,5° passam da borda da cidade)\n      function cl(v){ return Math.max(0, Math.min(1000, v)); }\n      var TQ = EX.tq.map(function(q){ return { r: q.r, x0: cl(q.x0), x1: cl(q.x1), y0: cl(q.y0), y1: cl(q.y1) }; });\n      function PB(u, v){ return [O[0] + u * exv[0] + v * eyv[0], O[1] + u * exv[1] + v * eyv[1]]; }\n      var cur = st < 0 ? -1 : Math.min(n, Math.floor(st / STEP)), done = st >= n * STEP + 0.3;\n      ctx.globalAlpha = ip;\n      // quadrículas percorridas acendem no plano, e o caminho da ordem Z vai sendo traçado\n      var path = [];\n      TQ.forEach(function(q, j){\n        if (j > cur) return;\n        var on = j === cur && !done;\n        poly(ctx, [PB(q.x0, q.y0), PB(q.x1, q.y0), PB(q.x1, q.y1), PB(q.x0, q.y1)], on ? 'rgba(194,65,12,.42)' : 'rgba(194,65,12,.16)', on ? '#c2410c' : 'rgba(194,65,12,.5)');\n        path.push(PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2));\n      });\n      if (path.length > 1){ ctx.strokeStyle = '#1d1d1f'; ctx.lineWidth = 1.6; ctx.setLineDash([]); ctx.beginPath(); path.forEach(function(p, k){ if (k) ctx.lineTo(p[0], p[1]); else ctx.moveTo(p[0], p[1]); }); ctx.stroke(); path.forEach(function(p){ ctx.fillStyle = '#1d1d1f'; ctx.beginPath(); ctx.arc(p[0], p[1], 3, 0, Math.PI * 2); ctx.fill(); }); ctx.lineWidth = 1; }\n      // table do índice na coluna da esquerda\n      var TW = 250, xT = Math.max(12, cx - 0.866 * size - 70 - TW), yT = s.H * 0.30, RH = 23;\n      label(ctx, xT, yT - 30, 'Table Quadrículas OSM Brasil', 14, '#1d1d1f', 'left', 600);\n      mono(ctx, xT, yT - 12, 'quadricula      byteInicio    bytes', 11, '#6e6e73');\n      TQ.forEach(function(q, j){\n        var y = yT + j * RH, on = j === cur && !done, seen = j <= cur;\n        if (on){ ctx.fillStyle = 'rgba(194,65,12,.12)'; ctx.fillRect(xT - 6, y - 4, TW + 12, RH); }\n        mono(ctx, xT, y + 12, q.r.quadricula.split(' · ')[0], 12, seen ? '#1d1d1f' : '#aeaeb2');\n        mono(ctx, xT + 96, y + 12, nf(q.r.byteInicio), 12, seen ? '#1d1d1f' : '#aeaeb2');\n        mono(ctx, xT + TW, y + 12, mb(q.r.bytes), 12, seen ? '#c2410c' : '#aeaeb2', 'right');\n        if (on){\n          var c = PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2);\n          ctx.strokeStyle = 'rgba(194,65,12,.7)'; ctx.beginPath(); ctx.moveTo(xT + TW + 8, y + 8); ctx.bezierCurveTo(xT + TW + 80, y + 8, c[0] - 80, c[1], c[0], c[1]); ctx.stroke();\n        }\n      });\n      // trecho do arquivo ocupado por essas linhas, e onde ele fica nos 600 MB\n      var yS = yT + n * RH + 34, a0 = EX.range[0], a1 = EX.range[1];\n      label(ctx, xT, yS - 8, 'No arquivo', 14, '#1d1d1f', 'left', 600);\n      TQ.forEach(function(q, j){\n        var x = xT + (q.r.byteInicio - a0) / (a1 - a0) * TW, w = q.r.bytes / (a1 - a0) * TW;\n        var on = j === cur && !done, seen = j <= cur;\n        obox(ctx, [xT, yS + 22], x - xT, 0, on ? 8 : 0, Math.max(1, w - 1), 14, 12, on ? INK : seen ? RUST : GRAY);\n        if (on){ var c2 = PB((q.x0 + q.x1) / 2, (q.y0 + q.y1) / 2); ctx.strokeStyle = 'rgba(29,29,31,.35)'; ctx.setLineDash([3, 4]); ctx.beginPath(); ctx.moveTo(x + w / 2, yS + 2); ctx.bezierCurveTo(x + w / 2, yS - 60, c2[0] - 120, c2[1] + 40, c2[0], c2[1]); ctx.stroke(); ctx.setLineDash([]); }\n      });\n      var tot = Q.reduce(function(sum, r){ return sum + r.bytes; }, 0);\n      ctx.fillStyle = '#e8e8ed'; ctx.fillRect(xT, yS + 44, TW, 4);\n      ctx.fillStyle = '#c2410c'; ctx.fillRect(xT + a0 / tot * TW, yS + 44, Math.max(2, (a1 - a0) / tot * TW), 4);\n      mono(ctx, xT, yS + 62, mb(a0) + ' a ' + mb(a1) + ' dos ' + mb(tot), 11, '#6e6e73');\n      // no fim, os blocos contíguos viram uma requisição\n      if (done){\n        var f = ease((st - n * STEP - 0.3) / 0.6);\n        ctx.globalAlpha = ip * f;\n        label(ctx, xT, yS + 96, n + ' blocos seguidos no arquivo', 14, '#1d1d1f', 'left', 600);\n        mono(ctx, xT, yS + 116, 'Range: bytes=' + a0 + '-' + (a1 - 1), 12, '#c2410c');\n        mono(ctx, xT, yS + 134, '1 requisição · ' + mb(a1 - a0), 12, '#6e6e73');\n      }\n      ctx.globalAlpha = 1;\n    }\n    var layersScene = scene('layers', function(s, t){\n      var ctx = s.ctx, W = s.W, H = s.H;\n      var hud = d2.$('[data-hud=\"layers\"]');\n      if (!EX.layers){\n        if (Q.length) loadExample();\n        var pr = EX.progress;\n        label(ctx, W / 2, H / 2, EX.err ? 'erro: ' + EX.err : pr ? 'baixando ' + mb(pr.got) + ' de ' + mb(pr.total) + '…' : 'lendo a table…', 15, '#6e6e73', 'center');\n        if (pr){ var bw = 280, f = pr.got / Math.max(1, pr.total); ctx.fillStyle = '#e8e8ed'; ctx.fillRect(W / 2 - bw / 2, H / 2 + 16, bw, 4); ctx.fillStyle = '#c2410c'; ctx.fillRect(W / 2 - bw / 2, H / 2 + 16, bw * f, 4); }\n        return;\n      }\n      if (!s.t1) s.t1 = t;\n      // 0-1,2 junto · 1,2-3,4 abre · 3,4-6,6 legendas · 6,6-15,4 lógica do índice · 15,6-17,6 fecha\n      var CYC = 19, lt = (t - s.t1) % CYC, e;\n      if (lt < 1.2) e = 0; else if (lt < 3.4) e = inout((lt - 1.2) / 2.2); else if (lt < 15.6) e = 1; else if (lt < 17.6) e = 1 - inout((lt - 15.6) / 2); else e = 0;\n      var ip = Math.max(0, Math.min(inout((lt - 6.6) / 0.8), 1 - inout((lt - 14.6) / 0.8)));\n      // pilha centralizada; coluna de legendas fora dela, à direita\n      var colW = 230, size = Math.min((W - 2 * colW - 80) / 1.732, 460), a = size / 1000, gap = 88;\n      var exv = [0.866 * a, 0.5 * a], eyv = [-0.866 * a, 0.5 * a];\n      var cx = W / 2, top0 = H - 50 - size, legX = cx + 0.866 * size + 70;\n      EX.layers.forEach(function(L, i){\n        var z = i * gap * e + Math.sin(t * 1.2 + i) * 2 * e;\n        var O = [cx, top0 - z];\n        if (i === 0) s.O0 = O;\n        ctx.globalAlpha = i === 0 ? 1 : 1 - 0.82 * ip;\n        var c1 = [O[0] + 1000 * exv[0], O[1] + 1000 * exv[1]], c3 = [O[0] + 1000 * eyv[0], O[1] + 1000 * eyv[1]], c2 = [c1[0] + 1000 * eyv[0], c1[1] + 1000 * eyv[1]];\n        if (i === 0){ ctx.fillStyle = 'rgba(29,29,31,.06)'; poly(ctx, [[O[0], O[1] + 14], [c1[0], c1[1] + 14], [c2[0], c2[1] + 14], [c3[0], c3[1] + 14]], 'rgba(29,29,31,.06)'); }\n        else poly(ctx, [O, c1, c2, c3], 'rgba(255,255,255,' + (0.5 * e).toFixed(3) + ')');\n        ctx.save(); ctx.setTransform(s.dpr * exv[0], s.dpr * exv[1], s.dpr * eyv[0], s.dpr * eyv[1], s.dpr * O[0], s.dpr * O[1]); ctx.drawImage(L.canvas, 0, 0); ctx.restore();\n        poly(ctx, [O, c1, c2, c3], null, 'rgba(29,29,31,' + (0.10 + 0.14 * e).toFixed(3) + ')');\n        var la = (e > 0.55 ? (e - 0.55) / 0.45 : 0) * (i === 0 ? 1 : 1 - 0.82 * ip);\n        ctx.globalAlpha = 1;\n        if (la > 0){\n          ctx.globalAlpha = la;\n          ctx.strokeStyle = 'rgba(29,29,31,.28)'; ctx.beginPath(); ctx.moveTo(c1[0] + 6, c1[1]); ctx.lineTo(legX - 12, c1[1]); ctx.stroke();\n          ctx.fillStyle = 'rgba(29,29,31,.45)'; ctx.beginPath(); ctx.arc(c1[0] + 6, c1[1], 2.5, 0, Math.PI * 2); ctx.fill();\n          label(ctx, legX, c1[1] - 3, L.name, 15, '#1d1d1f', 'left', 600);\n          mono(ctx, legX, c1[1] + 16, L.info, 12.5, i === 0 ? '#c2410c' : '#6e6e73');\n          ctx.globalAlpha = 1;\n        }\n      });\n      ctx.globalAlpha = 1;\n      if (ip > 0.01 && EX.tq) drawIndexLogic(ctx, s, ip, lt - 7.4, exv, eyv, cx, size);\n      if (hud && EX.stats) hud.innerHTML = '<b>São Paulo</b><br>' + EX.stats.tiles + ' quadrículas · ' + EX.stats.groups + ' requisição · ' + mb(EX.stats.bytes) + ' baixados agora em ' + (EX.stats.ms / 1000).toFixed(1).replace('.', ',') + ' s<br>' + nf(EX.stats.feats) + ' trechos desenhados';\n    });\n\n    scenes.forEach(function(s){ s.fit(); ro.observe(s.cv); io.observe(s.cv); });\n\n    // ---------- dados das tables ----------\n    function waitAll(ds){\n      return new Promise(function(res, rej){\n        var t0 = Date.now();\n        (function tick(){\n          var n = ds.records().length, tot = (ds.pageInfo() || {}).total || 0, busy = ds.fetching || ds.mutating;\n          if (!busy && n && n >= tot) return res(ds.records());\n          if (!busy && n < tot) ds.loadAll();\n          if (Date.now() - t0 > 30000) return rej(new Error('tempo esgotado'));\n          setTimeout(tick, 150);\n        })();\n      });\n    }\n    function chip(f, lab){ return f && f.fileUrl ? '<a class=\"filechip\" href=\"' + esc(f.fileUrl) + '\" target=\"_blank\" rel=\"noopener\"><i></i>' + esc(lab || f.name) + '</a>' : '<span class=\"note\">—</span>'; }\n    if (qd) waitAll(qd).then(function(rows){\n      Q = rows.slice().sort(function(a, b){ return a.ordem - b.ordem; });\n      var sample = Q.filter(function(r, i){ return i % 110 === 0; }).slice(0, 8);\n      var el = d2.$('[data-sample]');\n      if (el) el.innerHTML = sample.map(function(r){ return '<tr><td class=\"mono\">' + esc(r.quadricula) + '</td><td class=\"mono r\">' + nf(r.byteInicio) + '</td><td class=\"r\">' + mb(r.bytes) + '</td><td class=\"mono hide-sm\" style=\"padding-left:28px;color:#6e6e73\">' + esc(r.range) + '</td></tr>'; }).join('');\n    }).catch(function(e){ txt('[data-hud=\"hero\"]', 'erro lendo a table: ' + e.message); });\n    var ORDER = ['processado', 'índice', 'fonte', 'script', 'documentação'];\n    if (ad) waitAll(ad).then(function(rows){\n      A = rows.slice().sort(function(a, b){ return (ORDER.indexOf(a.papel) - ORDER.indexOf(b.papel)) || (a.ordem - b.ordem); });\n      var tot = A.reduce(function(s, r){ return s + (r.bytes || 0); }, 0);\n      var sum = d2.$('[data-catsum]'); if (sum) sum.innerHTML = 'Arquivos<br><span class=\"note\" style=\"font-weight:400\">' + A.length + ' no Drive, ' + mb(tot) + '</span>';\n      var el = d2.$('[data-cat]');\n      if (el) el.innerHTML = A.map(function(r){ var comp = r.trafegado && r.trafegado < r.bytes * .9 ? '<div class=\"note\">' + mb(r.trafegado) + ' na rede</div>' : ''; return '<tr><td>' + chip(r.arquivo, r.nome) + '<div class=\"note\">' + esc(r.formato) + '</div></td><td class=\"hide-sm note\">' + esc(r.papel) + '</td><td class=\"r\">' + mb(r.bytes) + comp + '</td></tr>'; }).join('');\n    });\n\n    d2.on('[data-go]', 'click', function(e, el){ var sec = d2.$('#' + el.getAttribute('data-go')); if (sec) DOC.scrollTo({ top: sec.offsetTop - 56, behavior: 'smooth' }); });\n  }\n};"}},"quadriculas":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quadriculas"}}}},"arquivos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arquivos"}}}}}}}}}},{"_id":"6ab5d640dc8440a163c02c82","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"F1_Bento_Dashboard","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const BASE = 'https://api.jolpi.ca/ergast/f1/';\nconst esc = s => String(s ?? '').replace(/[&<>\"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\nconst raceTs = r => new Date(r.date + 'T' + (r.time || '23:59:00Z')).getTime();\n\nasync function get(path) {\n  const res = await fetch(BASE + path);\n  if (!res.ok) throw new Error('HTTP ' + res.status + ' em ' + path);\n  const j = await res.json();\n  return j.MRData;\n}\n\nasync function load(d2) {\n  const season = String(d2.param('season', 'current') || 'current');\n  d2.setState({ loading: true, error: null });\n  try {\n    const [ds, cs, last, cal] = await Promise.all([\n      get(season + '/driverStandings.json'),\n      get(season + '/constructorStandings.json'),\n      get(season + '/last/results.json'),\n      get(season + '/races.json?limit=100')\n    ]);\n    const dl = (ds.StandingsTable.StandingsLists || [])[0] || {};\n    const cl = (cs.StandingsTable.StandingsLists || [])[0] || {};\n    d2.setState({\n      loading: false,\n      data: {\n        season: dl.season || ds.StandingsTable.season || season,\n        round: Number(dl.round || 0),\n        drivers: dl.DriverStandings || [],\n        teams: cl.ConstructorStandings || [],\n        last: ((last.RaceTable && last.RaceTable.Races) || [])[0] || null,\n        races: (cal.RaceTable && cal.RaceTable.Races) || []\n      }\n    });\n  } catch (e) {\n    d2.setState({ loading: false, error: String((e && e.message) || e) });\n  }\n}\n\nexport default {\n  css: `\n    :host{display:block;width:100%;height:100%}\n    *{box-sizing:border-box}\n    .wrap{width:100%;height:100%;background:#111412;color:#E7ECE8;font-family:Inter,system-ui,-apple-system,sans-serif;padding:24px;display:flex;flex-direction:column;gap:16px;overflow:hidden}\n    .hdr{display:flex;align-items:baseline;justify-content:space-between}\n    .hdr h1{margin:0;font-size:22px;font-weight:700;letter-spacing:-.01em}\n    .hdr h1 span{color:#A3E635}\n    .sub{color:#8A948D;font-size:12px}\n    .grid{flex:1;min-height:0;display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(3,1fr);gap:14px}\n    .card{background:#1C211E;border:1px solid #262C28;border-radius:16px;padding:18px;display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden;position:relative}\n    .lbl{font-size:11px;text-transform:uppercase;letter-spacing:.12em;color:#8A948D;margin-bottom:10px}\n    .big{grid-column:3/5;grid-row:1/3}\n    .c-lead{grid-column:1;grid-row:1}\n    .c-gap{grid-column:2;grid-row:1}\n    .c-next{grid-column:1/3;grid-row:2}\n    .wide{grid-column:3/5}\n    .wide3{grid-column:2/4}\n    .lime{background:#A3E635;color:#111412;border-color:#A3E635}\n    .lime .lbl,.lime .muted{color:#111412;opacity:.65}\n    .mono{font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace}\n    .muted{color:#8A948D}\n    .lst{display:flex;flex-direction:column;flex:1;min-height:0;justify-content:space-between}\n    .row{display:grid;grid-template-columns:26px 1fr 64px;align-items:center;gap:10px;font-size:13px}\n    .row .pos{color:#8A948D;font-weight:600}\n    .row .nm{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .row .nm b{font-weight:600}\n    .row .tm{font-size:11px;color:#8A948D;margin-left:6px}\n    .row .pts{text-align:right;font-weight:600}\n    .bar{height:5px;background:#262C28;border-radius:3px;margin-top:5px;overflow:hidden}\n    .bar i{display:block;height:100%;background:#4B5A4F;border-radius:3px}\n    .row.p1 .pos,.row.p1 .pts{color:#A3E635}\n    .row.p1 .bar i{background:#A3E635}\n    .kpi{font-size:46px;font-weight:700;line-height:1;letter-spacing:-.02em}\n    .kpi small{font-size:15px;font-weight:600;margin-left:4px}\n    .yel{color:#FACC15}\n    .grn{color:#4ADE80}\n    .grow{flex:1}\n    .next{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;flex:1}\n    .next h2{margin:0 0 4px;font-size:22px;font-weight:700}\n    .cd{display:flex;gap:12px}\n    .cd div{text-align:center}\n    .cd b{display:block;font-size:40px;line-height:1;color:#FACC15}\n    .cd span{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:#8A948D}\n    .ring{display:flex;align-items:center;gap:16px;flex:1}\n    .pod{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;align-items:end;flex:1}\n    .step{border-radius:10px;background:#262C28;padding:10px;display:flex;flex-direction:column;justify-content:flex-end;min-width:0}\n    .step.s1{background:#A3E635;color:#111412;height:100%}\n    .step.s2{height:80%}\n    .step.s3{height:62%}\n    .step .p{font-size:22px;font-weight:700}\n    .step .d{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .step .t{font-size:11px;opacity:.7;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n    .trow{display:grid;grid-template-columns:18px 1fr auto;gap:8px;font-size:12px;align-items:center}\n    .trow:first-child{color:#4ADE80;font-weight:600}\n    .skel{background:linear-gradient(90deg,#1C211E,#232925,#1C211E);background-size:200% 100%;animation:sh 1.4s infinite}\n    @keyframes sh{0%{background-position:200% 0}100%{background-position:-200% 0}}\n    .err{margin:auto;text-align:center;max-width:420px}\n    .btn{margin-top:14px;background:#A3E635;color:#111412;border:0;border-radius:999px;padding:10px 18px;font-weight:700;cursor:pointer}\n  `,\n\n  render(d2) {\n    const s = d2.state;\n    const now = s.now || Date.now();\n\n    if (s.error) {\n      return `<div class=\"wrap\"><div class=\"err\"><div class=\"lbl\">Erro ao carregar dados da F1</div><div class=\"muted\">${esc(s.error)}</div><button class=\"btn retry\">Tentar de novo</button></div></div>`;\n    }\n\n    if (!s.data) {\n      return `<div class=\"wrap\"><div class=\"hdr\"><h1>F1 <span>Dashboard</span></h1><div class=\"sub\">carregando…</div></div>\n        <div class=\"grid\">\n          <div class=\"card big skel\"></div><div class=\"card c-lead skel\"></div><div class=\"card c-gap skel\"></div>\n          <div class=\"card c-next skel\"></div><div class=\"card skel\"></div><div class=\"card wide3 skel\"></div><div class=\"card skel\"></div>\n        </div></div>`;\n    }\n\n    const D = s.data;\n    const drivers = D.drivers.slice(0, 10);\n    const maxPts = Math.max(1, ...drivers.map(x => Number(x.points)));\n    const p1 = D.drivers[0], p2 = D.drivers[1];\n    const total = D.races.length || 0;\n    const nextRace = D.races.find(r => raceTs(r) > now) || null;\n\n    const standings = drivers.length ? drivers.map((x, i) => {\n      const team = (x.Constructors && x.Constructors[0] && x.Constructors[0].name) || '';\n      const w = (Number(x.points) / maxPts * 100).toFixed(1);\n      return `<div class=\"row ${i === 0 ? 'p1' : ''}\" data-key=\"${esc(x.Driver.driverId)}\">\n        <div class=\"pos mono\">${esc(x.position)}</div>\n        <div><div class=\"nm\"><b>${esc(x.Driver.givenName)} ${esc(x.Driver.familyName)}</b><span class=\"tm\">${esc(team)}</span></div><div class=\"bar\"><i style=\"width:${w}%\"></i></div></div>\n        <div class=\"pts mono\">${esc(x.points)}</div>\n      </div>`;\n    }).join('') : `<div class=\"muted\">Sem classificação ainda.</div>`;\n\n    const leader = p1 ? `<div class=\"lbl\">Líder do campeonato</div>\n      <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:space-between\">\n        <div style=\"font-size:18px;font-weight:700\">${esc(p1.Driver.givenName)} ${esc(p1.Driver.familyName)}</div>\n        <div><div class=\"kpi mono\">${esc(p1.points)}<small>pts</small></div>\n        <div class=\"muted\" style=\"font-size:12px;margin-top:6px\">${esc(p1.wins)} vitórias · ${esc((p1.Constructors[0] || {}).name || '')}</div></div>\n      </div>` : `<div class=\"lbl\">Líder</div><div class=\"muted\">—</div>`;\n\n    const gapCard = (p1 && p2) ? `<div class=\"lbl\">Vantagem sobre P2</div>\n      <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:space-between\">\n        <div style=\"font-size:14px\" class=\"muted\">vs ${esc(p2.Driver.givenName)} ${esc(p2.Driver.familyName)}</div>\n        <div class=\"kpi mono yel\">+${esc(Number(p1.points) - Number(p2.points))}<small>pts</small></div>\n      </div>` : `<div class=\"lbl\">Vantagem</div><div class=\"muted\">—</div>`;\n\n    let nextCard;\n    if (nextRace) {\n      const ms = Math.max(0, raceTs(nextRace) - now);\n      const dd = Math.floor(ms / 864e5), hh = Math.floor(ms % 864e5 / 36e5), mm = Math.floor(ms % 36e5 / 6e4);\n      const when = new Date(raceTs(nextRace)).toLocaleString('pt-BR', { weekday: 'short', day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });\n      nextCard = `<div class=\"lbl\">Próxima corrida · Round ${esc(nextRace.round)}</div>\n        <div class=\"next\">\n          <div style=\"min-width:0\"><h2>${esc(nextRace.raceName)}</h2>\n            <div class=\"muted\" style=\"font-size:13px\">${esc(nextRace.Circuit.circuitName)} — ${esc(nextRace.Circuit.Location.locality)}, ${esc(nextRace.Circuit.Location.country)}</div>\n            <div style=\"font-size:13px;margin-top:6px\">${esc(when)}</div></div>\n          <div class=\"cd mono\"><div><b>${dd}</b><span>dias</span></div><div><b>${hh}</b><span>horas</span></div><div><b>${mm}</b><span>min</span></div></div>\n        </div>`;\n    } else {\n      nextCard = `<div class=\"lbl\">Próxima corrida</div><div class=\"next\"><h2>Temporada encerrada 🏁</h2></div>`;\n    }\n\n    const pairs = D.teams.map(t => {\n      const cid = t.Constructor.constructorId;\n      const ds = D.drivers.filter(x => (x.Constructors || []).some(c => c.constructorId === cid)).slice(0, 2);\n      return { team: t.Constructor.name, ds };\n    }).filter(p => p.ds.length === 2).slice(0, 5);\n    let duel;\n    if (pairs.length) {\n      const idx = (s.duelIdx || 0) % pairs.length;\n      const p = pairs[idx], a = p.ds[0], b = p.ds[1];\n      const pa = Number(a.points), pb = Number(b.points);\n      const share = (pa + pb) ? (pa / (pa + pb) * 100).toFixed(1) : 50;\n      duel = `<div class=\"lbl\">Duelo interno · ${esc(p.team)}</div>\n        <div class=\"grow\" style=\"display:flex;flex-direction:column;justify-content:center;gap:10px\">\n          <div style=\"display:flex;justify-content:space-between;font-size:13px;font-weight:600\"><span>${esc(a.Driver.familyName)}</span><span class=\"muted\">${esc(b.Driver.familyName)}</span></div>\n          <div style=\"display:flex;height:12px;border-radius:6px;overflow:hidden;background:#262C28\"><div style=\"width:${share}%;background:#A3E635;transition:width .6s ease\"></div><div style=\"flex:1;background:#4B5A4F\"></div></div>\n          <div class=\"mono\" style=\"display:flex;justify-content:space-between;font-size:28px;font-weight:700;line-height:1\"><span>${esc(pa)}</span><span class=\"muted\">${esc(pb)}</span></div>\n          <div class=\"muted\" style=\"display:flex;justify-content:space-between;font-size:11px\"><span>${esc(a.wins)} vitórias</span><span>${esc(b.wins)} vitórias</span></div>\n        </div>\n        <div style=\"display:flex;gap:6px;justify-content:center\">${pairs.map((q, i) => `<button class=\"dot\" data-duel=\"${i}\" title=\"${esc(q.team)}\" style=\"width:8px;height:8px;border-radius:50%;border:0;padding:0;cursor:pointer;background:${i === idx ? '#A3E635' : '#4B5A4F'}\"></button>`).join('')}</div>`;\n    } else {\n      duel = `<div class=\"lbl\">Duelo interno</div><div class=\"muted\">Sem dados ainda.</div>`;\n    }\n\n    let podium;\n    if (D.last && D.last.Results && D.last.Results.length) {\n      const res = D.last.Results.slice(0, 3);\n      const order = [res[1], res[0], res[2]].filter(Boolean);\n      podium = `<div class=\"lbl\">Última corrida · ${esc(D.last.raceName)}</div>\n        <div class=\"pod\">${order.map(r => {\n          const cls = r.position === '1' ? 's1' : r.position === '2' ? 's2' : 's3';\n          const t = (r.Time && r.Time.time) || r.status || '';\n          return `<div class=\"step ${cls}\" data-key=\"${esc(r.Driver.driverId)}\"><div class=\"p mono\">P${esc(r.position)}</div><div class=\"d\">${esc(r.Driver.givenName)} ${esc(r.Driver.familyName)}</div><div class=\"t\">${esc(r.Constructor.name)} · ${esc(t)}</div></div>`;\n        }).join('')}</div>`;\n    } else {\n      podium = `<div class=\"lbl\">Última corrida</div><div class=\"muted\">Nenhuma corrida disputada ainda.</div>`;\n    }\n\n    const teams = `<div class=\"lbl\">Construtores</div>\n      <div class=\"lst\">${D.teams.slice(0, 6).map(t => `<div class=\"trow\" data-key=\"${esc(t.Constructor.constructorId)}\"><span class=\"mono\">${esc(t.position)}</span><span style=\"white-space:nowrap;overflow:hidden;text-overflow:ellipsis\">${esc(t.Constructor.name)}</span><span class=\"mono\">${esc(t.points)}</span></div>`).join('') || '<div class=\"muted\">—</div>'}</div>`;\n\n    return `<div class=\"wrap\" data-ready=\"1\">\n      <div class=\"hdr\"><h1>F1 <span>Dashboard</span> · ${esc(D.season)}</h1><div class=\"sub\">dados: Jolpica F1 API · atualizado ${esc(new Date(now).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }))}</div></div>\n      <div class=\"grid\">\n        <div class=\"card big\"><div class=\"lbl\">Campeonato de pilotos · Top 10</div><div class=\"lst\">${standings}</div></div>\n        <div class=\"card lime c-lead\">${leader}</div>\n        <div class=\"card c-gap\">${gapCard}</div>\n        <div class=\"card c-next\">${nextCard}</div>\n        <div class=\"card\">${duel}</div>\n        <div class=\"card wide3\">${podium}</div>\n        <div class=\"card\">${teams}</div>\n      </div>\n    </div>`;\n  },\n\n  mounted(d2) {\n    d2.state.now = Date.now();\n    load(d2);\n    const t = setInterval(() => { d2.state.now = Date.now(); }, 30000);\n    d2.onUnmount(() => clearInterval(t));\n    d2.on('.retry', 'click', () => load(d2));\n    const rot = setInterval(() => { d2.state.duelIdx = (d2.state.duelIdx || 0) + 1; }, 8000);\n    d2.onUnmount(() => clearInterval(rot));\n    d2.on('[data-duel]', 'click', (e) => { const el = e.target.closest('[data-duel]'); if (el) d2.state.duelIdx = Number(el.dataset.duel); });\n  }\n};"}},"season":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"season"}}}}}}}}}},{"_id":"6ab6e7c6e92b0a71790318bb","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Geo_Quadriculas_Dashboard","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const LIB = { url: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js', global: 'L', css: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' }\nconst C = { fundo: '#f4f1ea', painel: '#ffffff', texto: '#2b2a28', rod: '#c2410c', rio: '#0e7490', dest: '#65a30d', muted: '#8a857c', borda: '#e4ded2' }\nconst NIV = ['#84cc16', '#65a30d', '#3f6212', '#1a2e05']\nconst TILES = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'\nconst MIN_ZOOM_VET = 7\nlet G = null\n\nfunction isRio(p) {\n  if (!p || typeof p !== 'object') return false\n  if (p.waterway) return true\n  if (p.highway) return false\n  for (const k of ['k', 'tipo', 'camada', 'kind', 'layer', 't', 'classe', 'class', 'cat']) {\n    const v = String(p[k] == null ? '' : p[k]).toLowerCase()\n    if (!v) continue\n    if (/rio|river|water|stream|canal|hidro|agua|água/.test(v)) return true\n    if (/rod|road|highway|via|estrada/.test(v)) return false\n  }\n  return false\n}\nfunction toLines(coords) {\n  if (!Array.isArray(coords) || !coords.length) return []\n  if (typeof coords[0][0] === 'number') return [coords.map(c => [c[1], c[0]])]\n  return coords.flatMap(toLines)\n}\nfunction parseObj(o, out) {\n  if (!o || typeof o !== 'object') return\n  if (o.type === 'FeatureCollection' && Array.isArray(o.features)) { o.features.forEach(f => parseObj(f, out)); return }\n  if (Array.isArray(o.features)) { o.features.forEach(f => parseObj(f, out)); return }\n  const g = o.geometry || o.g || (o.coordinates ? o : null)\n  const coords = g ? g.coordinates : (o.c || o.coords)\n  if (!coords) return\n  const p = o.properties || o.p || o\n  const lines = toLines(coords)\n  if (!lines.length) return\n  ;(isRio(p) ? out.rios : out.rods).push(...lines)\n}\nfunction bytesFmt(n) { if (!n) return '0 B'; const u = ['B', 'KB', 'MB', 'GB']; let i = 0; while (n >= 1024 && i < 3) { n /= 1024; i++ } return n.toFixed(i ? 1 : 0) + ' ' + u[i] }\nfunction numFmt(n) { return (n || 0).toLocaleString('pt-BR') }\nfunction heatColor(t) {\n  const a = [253, 230, 138], b = [194, 65, 12]\n  return 'rgb(' + a.map((v, i) => Math.round(v + (b[i] - v) * t)).join(',') + ')'\n}\n\nfunction build(d2, L, mapEl, miniEl) {\n  const g = { mapEl, cells: [], vis: { base: true, rodovias: true, rios: true, grade: true, heat: false }, nivel: '', loaded: 0, downloaded: 0, queue: [], active: 0 }\n  G = g\n  const map = L.map(mapEl, { preferCanvas: true, zoomSnap: 0.5, zoomControl: false }).setView([-15, -52], 4)\n  L.control.zoom({ position: 'topright' }).addTo(map)\n  g.map = map\n  g.L = L\n  g.renderer = L.canvas({ padding: 0.4 })\n  g.base = L.tileLayer(TILES, { attribution: '© OpenStreetMap', maxZoom: 19, className: 'soft' }).addTo(map)\n  g.gridLayer = L.layerGroup().addTo(map)\n  g.heatLayer = L.layerGroup()\n  g.rodLayer = L.layerGroup().addTo(map)\n  g.rioLayer = L.layerGroup().addTo(map)\n\n  const mini = L.map(miniEl, { zoomControl: false, attributionControl: false, dragging: false, scrollWheelZoom: false, doubleClickZoom: false, boxZoom: false, keyboard: false, touchZoom: false }).setView([-15, -52], 2)\n  L.tileLayer(TILES, { className: 'soft' }).addTo(mini)\n  g.mini = mini\n  g.miniRect = L.rectangle(map.getBounds(), { color: C.dest, weight: 1.5, fillOpacity: 0.2, interactive: false }).addTo(mini)\n  mini.on('click', e => map.setView(e.latlng, Math.max(map.getZoom(), 6)))\n\n  map.on('moveend zoomend', () => onMove(d2))\n  const ro = new ResizeObserver(() => { map.invalidateSize(); mini.invalidateSize() })\n  ro.observe(mapEl)\n  d2.onUnmount(() => { try { ro.disconnect(); map.remove(); mini.remove() } catch (e) {} ; if (G === g) G = null })\n  setTimeout(() => { map.invalidateSize(); mini.invalidateSize() }, 50)\n\n  wireUI(d2)\n  renderLayersPanel(d2)\n  loadIndex(d2)\n}\n\nasync function loadIndex(d2) {\n  const g = G\n  toast(d2, 'Carregando índice de quadrículas…')\n  const ds = d2.data('dataset')\n  if (!ds) { toast(d2, 'Parâmetro dataset não configurado'); return }\n  let recs = []\n  try { recs = await ds.loadAll() } catch (e) { d2.log('loadAll falhou', e) }\n  if (!recs || !recs.length) recs = ds.records()\n  for (let i = 0; i < 40 && (!recs || recs.length < 2); i++) {\n    await new Promise(r => setTimeout(r, 500))\n    if (i === 4) { try { ds.refresh() } catch (e) {} }\n    if (i === 10) { try { recs = await ds.loadAll() } catch (e) {} }\n    if (!recs || recs.length < 2) recs = ds.records()\n  }\n  if (g !== G) return\n  if (!recs || !recs.length) { toast(d2, '⚠️ Não consegui ler o índice (0 registros)'); return }\n  const L = g.L\n  g.cells = recs.map(r => {\n    const s = Number(r.celulaGraus) || 1, lon = Number(r.celulaLon), lat = Number(r.celulaLat)\n    return { r, nivel: Number(r.nivel) || 0, bounds: L.latLngBounds([lat, lon], [lat + s, lon + s]), state: null }\n  }).filter(c => !isNaN(c.bounds.getSouth()))\n  g.maxFeat = Math.max(1, ...g.cells.map(c => c.r.feicoes || 0))\n  drawGrid(d2)\n  toast(d2, g.cells.length + ' quadrículas carregadas', 2500)\n  onMove(d2)\n}\n\nfunction visibleCells() { const g = G; return g.cells.filter(c => g.nivel === '' || String(c.nivel) === g.nivel) }\n\nfunction drawGrid(d2) {\n  const g = G, L = g.L\n  g.gridLayer.clearLayers(); g.heatLayer.clearLayers()\n  const lmax = Math.log(g.maxFeat + 1)\n  visibleCells().forEach(c => {\n    const col = NIV[Math.min(3, c.nivel)]\n    const rect = L.rectangle(c.bounds, { renderer: g.renderer, color: col, weight: c.nivel ? 1.2 : 0.8, opacity: 0.75, fillColor: col, fillOpacity: 0.03 })\n    rect.on('click', () => openCell(d2, c))\n    g.gridLayer.addLayer(rect)\n    const t = Math.log((c.r.feicoes || 0) + 1) / lmax\n    g.heatLayer.addLayer(L.rectangle(c.bounds, { renderer: g.renderer, stroke: false, fillColor: heatColor(t), fillOpacity: 0.15 + 0.55 * t, interactive: false }))\n  })\n}\n\nfunction popupHtml(c) {\n  const r = c.r\n  const st = c.state === 'ok' ? '✅ vetores carregados' : c.state === 'loading' ? '⏳ carregando vetores…' : c.state === 'erro' ? '⚠️ ' + (c.err || 'erro ao carregar') : '—'\n  return '<div class=\"pop\"><b>' + (r.quadricula || '') + '</b>'\n    + '<div class=\"pr\"><span>Nível</span><b>' + c.nivel + ' · ' + (r.celulaGraus || '') + '°</b></div>'\n    + '<div class=\"pr\"><span>Feições</span><b>' + numFmt(r.feicoes) + '</b></div>'\n    + '<div class=\"pr\"><span style=\"color:' + C.rod + '\">Rodovias</span><b>' + numFmt(r.rodovias) + '</b></div>'\n    + '<div class=\"pr\"><span style=\"color:' + C.rio + '\">Rios</span><b>' + numFmt(r.rios) + '</b></div>'\n    + '<div class=\"pr\"><span>Tamanho</span><b>' + bytesFmt(r.bytes) + '</b></div>'\n    + '<div class=\"pr\"><span>Range</span><code>' + (r.range || '') + '</code></div>'\n    + '<div class=\"ps\">' + st + '</div></div>'\n}\nfunction openCell(d2, c) {\n  const g = G\n  c.popup = g.L.popup({ maxWidth: 260 }).setLatLng(c.bounds.getCenter()).setContent(popupHtml(c)).openOn(g.map)\n  enqueue(d2, c, true)\n}\n\nfunction enqueue(d2, c, front) {\n  const g = G\n  if (c.state) return\n  c.state = 'queued'\n  front ? g.queue.unshift(c) : g.queue.push(c)\n  pump(d2)\n}\nfunction pump(d2) {\n  const g = G\n  while (g.active < 4 && g.queue.length) { const c = g.queue.shift(); g.active++; loadCell(d2, c).finally(() => { g.active--; pump(d2); renderStats(d2) }) }\n}\nasync function loadCell(d2, c) {\n  const g = G, L = g.L, r = c.r\n  c.state = 'loading'; refreshPopup(c)\n  const url = r.arquivo && r.arquivo.fileUrl\n  if (!url || !r.range) { c.state = 'erro'; c.err = 'sem arquivo/range'; refreshPopup(c); return }\n  try {\n    const res = await fetch(url, { headers: { Range: r.range } })\n    if (res.status !== 206) { try { res.body && res.body.cancel() } catch (e) {} ; throw new Error('servidor não aceitou Range (HTTP ' + res.status + ')') }\n    const txt = await res.text()\n    if (g !== G) return\n    g.downloaded += r.bytes || txt.length\n    const out = { rods: [], rios: [] }\n    txt.split('\\n').forEach(line => { line = line.trim(); if (!line) return; try { parseObj(JSON.parse(line), out) } catch (e) {} })\n    if (out.rods.length) g.rodLayer.addLayer(L.polyline(out.rods, { renderer: g.renderer, color: C.rod, weight: 1.4, opacity: 0.9, interactive: false }))\n    if (out.rios.length) g.rioLayer.addLayer(L.polyline(out.rios, { renderer: g.renderer, color: C.rio, weight: 1.6, opacity: 0.85, interactive: false }))\n    c.state = 'ok'; g.loaded++\n  } catch (e) { c.state = 'erro'; c.err = e.message; toast(d2, '⚠️ ' + e.message, 4000) }\n  refreshPopup(c)\n}\nfunction refreshPopup(c) { if (c.popup && c.popup.isOpen && c.popup.isOpen()) c.popup.setContent(popupHtml(c)) }\n\nfunction onMove(d2) {\n  const g = G\n  if (!g) return\n  const b = g.map.getBounds()\n  g.miniRect.setBounds(b)\n  g.mini.setView(b.getCenter(), Math.max(1, Math.min(5, g.map.getZoom() - 4)), { animate: false })\n  renderStats(d2)\n  if (!g.cells.length) return\n  if (!(g.vis.rodovias || g.vis.rios)) return\n  if (g.map.getZoom() < MIN_ZOOM_VET) { toast(d2, 'Dá zoom (≥ ' + MIN_ZOOM_VET + ') pra carregar rodovias e rios · ou clica numa quadrícula'); return }\n  toast(d2, '')\n  const ctr = b.getCenter()\n  visibleCells().filter(c => !c.state && b.intersects(c.bounds))\n    .sort((a, z) => a.bounds.getCenter().distanceTo(ctr) - z.bounds.getCenter().distanceTo(ctr))\n    .slice(0, 16).forEach(c => enqueue(d2, c))\n}\n\nfunction toast(d2, msg, ms) {\n  const el = d2.$('#toast'); if (!el) return\n  el.textContent = msg; el.style.display = msg ? 'block' : 'none'\n  clearTimeout(el._t); if (msg && ms) el._t = setTimeout(() => { el.style.display = 'none' }, ms)\n}\n\nfunction renderStats(d2) {\n  const g = G, el = d2.$('#stats'); if (!g || !el) return\n  const b = g.map.getBounds()\n  const vis = visibleCells().filter(c => b.intersects(c.bounds))\n  const s = vis.reduce((a, c) => { a.f += c.r.feicoes || 0; a.r += c.r.rodovias || 0; a.w += c.r.rios || 0; a.by += c.r.bytes || 0; return a }, { f: 0, r: 0, w: 0, by: 0 })\n  const carregando = g.active + g.queue.length\n  el.innerHTML =\n    '<div class=\"kpi\"><span>Quadrículas</span><b>' + numFmt(vis.length) + '</b></div>'\n    + '<div class=\"kpi\"><span>Feições</span><b>' + numFmt(s.f) + '</b></div>'\n    + '<div class=\"kpi\"><span><i style=\"background:' + C.rod + '\"></i>Rodovias</span><b>' + numFmt(s.r) + '</b></div>'\n    + '<div class=\"kpi\"><span><i style=\"background:' + C.rio + '\"></i>Rios</span><b>' + numFmt(s.w) + '</b></div>'\n    + '<div class=\"kpi\"><span>Tamanho da área</span><b>' + bytesFmt(s.by) + '</b></div>'\n    + '<div class=\"kpi hi\"><span>Baixado até agora</span><b>' + bytesFmt(g.downloaded) + '</b></div>'\n    + '<div class=\"sub\">' + g.loaded + ' quadrícula(s) com vetores' + (carregando ? ' · ' + carregando + ' na fila' : '') + '</div>'\n}\n\nfunction renderLayersPanel(d2) {\n  const g = G, el = d2.$('#layers'); if (!el) return\n  const items = [\n    ['base', 'Mapa-base', '<i class=\"sw\" style=\"background:linear-gradient(135deg,#eceae4,#d9d6cf)\"></i>'],\n    ['rodovias', 'Rodovias', '<i class=\"ln\" style=\"background:' + C.rod + '\"></i>'],\n    ['rios', 'Rios', '<i class=\"ln\" style=\"background:' + C.rio + '\"></i>'],\n    ['grade', 'Grade das quadrículas', '<i class=\"sw\" style=\"border:2px solid ' + NIV[1] + '\"></i>'],\n    ['heat', 'Heatmap de densidade', '<i class=\"sw\" style=\"background:linear-gradient(90deg,' + heatColor(0) + ',' + heatColor(1) + ')\"></i>']\n  ]\n  el.innerHTML = items.map(([k, t, ic]) => '<label class=\"tg' + (g.vis[k] ? ' on' : '') + '\"><input type=\"checkbox\" data-layer=\"' + k + '\"' + (g.vis[k] ? ' checked' : '') + '>' + ic + '<span>' + t + '</span></label>').join('')\n}\n\nfunction applyVis() {\n  const g = G, m = g.map\n  const set = (lay, on) => { on ? (m.hasLayer(lay) || lay.addTo(m)) : m.removeLayer(lay) }\n  set(g.base, g.vis.base); set(g.heatLayer, g.vis.heat); set(g.gridLayer, g.vis.grade); set(g.rioLayer, g.vis.rios); set(g.rodLayer, g.vis.rodovias)\n  if (g.vis.heat) g.heatLayer.bringToBack && g.heatLayer.eachLayer(l => l.bringToBack())\n}\n\nfunction doSearch(d2, raw) {\n  const g = G, box = d2.$('#qres'); if (!box) return\n  const q = String(raw || '').trim()\n  if (!q) { box.innerHTML = ''; return }\n  const m = q.match(/^(-?\\d+(?:[.,]\\d+)?)\\s*[\\/;\\s]\\s*(-?\\d+(?:[.,]\\d+)?)$/)\n  let hits = []\n  if (m) {\n    const lon = parseFloat(m[1].replace(',', '.')), lat = parseFloat(m[2].replace(',', '.'))\n    const pt = g.L.latLng(lat, lon)\n    hits = g.cells.filter(c => c.bounds.contains(pt) || (c.r.quadricula || '').includes(m[1] + '/' + m[2]))\n    if (!hits.length) { g.map.flyTo(pt, 8); box.innerHTML = '<div class=\"qe\">Sem quadrícula aí — centralizei no ponto</div>'; return }\n  } else {\n    let qq = q.toLowerCase()\n    const qm = qq.match(/^q\\s*0*(\\d+)$/)\n    hits = g.cells.filter(c => {\n      const n = (c.r.quadricula || '').toLowerCase()\n      if (qm) { const nm = n.match(/^q0*(\\d+)/); return nm && nm[1] === qm[1] }\n      return n.includes(qq)\n    })\n  }\n  hits.sort((a, z) => z.nivel - a.nivel)\n  box.innerHTML = hits.length ? hits.slice(0, 6).map(c => '<button class=\"qr\" data-q=\"' + g.cells.indexOf(c) + '\"><b>' + (c.r.quadricula || '').split(' · ')[0] + '</b><span>' + (c.r.quadricula || '').split(' · ').slice(1).join(' · ') + ' · ' + numFmt(c.r.feicoes) + ' feições</span></button>').join('') + (hits.length > 6 ? '<div class=\"qe\">+' + (hits.length - 6) + ' resultados</div>' : '') : '<div class=\"qe\">Nada encontrado</div>'\n  if (hits.length === 1) goCell(d2, hits[0])\n}\nfunction goCell(d2, c) { G.map.flyToBounds(c.bounds.pad(0.3), { duration: 0.8 }); setTimeout(() => openCell(d2, c), 850) }\n\nfunction wireUI(d2) {\n  const g = G\n  d2.on('#layers input', 'change', e => { const k = e.target.dataset.layer; g.vis[k] = e.target.checked; e.target.closest('.tg').classList.toggle('on', e.target.checked); applyVis(); onMove(d2) })\n  d2.on('#q', 'keydown', e => { if (e.key === 'Enter') doSearch(d2, e.target.value) })\n  d2.on('#q', 'input', e => { clearTimeout(g._qt); g._qt = setTimeout(() => doSearch(d2, e.target.value), 250) })\n  d2.on('#qres .qr', 'click', e => { const b = e.target.closest('.qr'); const c = g.cells[Number(b.dataset.q)]; if (c) goCell(d2, c) })\n  d2.on('#niv', 'change', e => { g.nivel = e.target.value; drawGrid(d2); renderStats(d2); onMove(d2) })\n  d2.on('#reset', 'click', () => g.map.flyTo([-15, -52], 4))\n}\n\nfunction init(d2) {\n  const L = d2.lib(LIB)\n  if (!L) return\n  const mapEl = d2.$('#map'), miniEl = d2.$('#mini')\n  if (!mapEl || !miniEl) return\n  if (G && G.mapEl === mapEl && mapEl.querySelector('.leaflet-pane')) return\n  if (G) { try { G.map.remove(); G.mini.remove() } catch (e) {} ; G = null }\n  build(d2, L, mapEl, miniEl)\n}\n\nexport default {\n  css: `\n    :host{display:block;width:100%;height:100%}\n    *{box-sizing:border-box}\n    .app{position:relative;width:100%;height:100%;display:grid;grid-template-columns:minmax(0,1fr) 22%;gap:2.5%;padding:2.2% 2.5%;background:${C.fundo};color:${C.texto};font-family:Inter,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px}\n    .mapwrap{position:relative;border-radius:14px;overflow:hidden;background:${C.painel};box-shadow:0 1px 0 ${C.borda},0 8px 24px rgba(60,50,30,.08);min-height:300px}\n    #map{position:absolute;inset:0;background:#efece5}\n    .tag{position:absolute;left:14px;top:12px;z-index:500;background:${C.painel};padding:6px 10px;border-radius:8px;font-weight:700;letter-spacing:.06em;font-size:11px;box-shadow:0 2px 8px rgba(0,0,0,.08)}\n    #mini{position:absolute;right:12px;bottom:26px;width:170px;height:130px;z-index:600;border-radius:10px;overflow:hidden;border:2px solid ${C.painel};box-shadow:0 4px 14px rgba(0,0,0,.15);cursor:pointer}\n    #toast{display:none;position:absolute;left:50%;top:12px;transform:translateX(-50%);z-index:700;background:${C.texto};color:${C.fundo};padding:7px 12px;border-radius:999px;font-size:12px;white-space:nowrap;max-width:80%;overflow:hidden;text-overflow:ellipsis}\n    .side{display:grid;grid-template-rows:auto minmax(0,1fr);gap:4%;min-height:0}\n    .card{background:${C.painel};border-radius:14px;padding:14px;box-shadow:0 1px 0 ${C.borda},0 8px 24px rgba(60,50,30,.06);min-height:0;overflow:auto}\n    h4{margin:0 0 10px;font-size:11px;letter-spacing:.08em;font-weight:800;color:${C.texto}}\n    h5{margin:14px 0 6px;font-size:10px;letter-spacing:.08em;color:${C.muted};font-weight:700;text-transform:uppercase}\n    input[type=text],select{width:100%;padding:8px 10px;border:1px solid ${C.borda};border-radius:8px;background:${C.fundo};color:${C.texto};font:inherit;font-size:12px;outline:none}\n    input[type=text]:focus,select:focus{border-color:${C.dest}}\n    .row{display:flex;gap:6px;margin-top:6px}\n    #qres{margin-top:6px;display:flex;flex-direction:column;gap:4px}\n    .qr{display:flex;flex-direction:column;align-items:flex-start;text-align:left;border:1px solid ${C.borda};background:${C.painel};border-radius:8px;padding:6px 8px;cursor:pointer;font:inherit;color:${C.texto}}\n    .qr:hover{border-color:${C.dest};background:#f7fbef}\n    .qr span{font-size:11px;color:${C.muted}}\n    .qe{font-size:11px;color:${C.muted};padding:2px}\n    .tg{display:flex;align-items:center;gap:8px;padding:7px 8px;border-radius:8px;cursor:pointer;user-select:none;color:${C.muted}}\n    .tg.on{color:${C.texto};background:#f3f7ea}\n    .tg input{accent-color:${C.dest};margin:0}\n    .ln{display:inline-block;width:16px;height:3px;border-radius:2px}\n    .sw{display:inline-block;width:16px;height:12px;border-radius:3px}\n    .lg{display:flex;align-items:center;gap:6px;font-size:11px;color:${C.muted};margin:3px 0}\n    .lg i{display:inline-block;width:12px;height:12px;border-radius:3px}\n    .grad{height:8px;border-radius:4px;background:linear-gradient(90deg,${heatColor(0)},${heatColor(1)});margin:4px 0 2px}\n    .gl{display:flex;justify-content:space-between;font-size:10px;color:${C.muted}}\n    .kpi{display:flex;justify-content:space-between;align-items:center;padding:5px 0;border-bottom:1px dashed ${C.borda};font-size:12px}\n    .kpi span{color:${C.muted};display:flex;align-items:center;gap:6px}\n    .kpi i{display:inline-block;width:10px;height:3px;border-radius:2px}\n    .kpi.hi b{color:${C.dest}}\n    .sub{font-size:10px;color:${C.muted};margin-top:6px}\n    .btn{border:1px solid ${C.borda};background:${C.fundo};border-radius:8px;padding:6px 10px;font:inherit;font-size:11px;cursor:pointer;color:${C.texto}}\n    .loading{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:${C.fundo};color:${C.muted};z-index:900;font-size:13px}\n    .pop{font-family:Inter,system-ui,sans-serif;font-size:12px;min-width:200px;color:${C.texto}}\n    .pop>b{display:block;font-size:13px;margin-bottom:6px}\n    .pr{display:flex;justify-content:space-between;gap:10px;padding:2px 0}\n    .pr span{color:${C.muted}}\n    .pr code{font-size:10px}\n    .ps{margin-top:6px;font-size:11px;color:${C.muted}}\n    .leaflet-container{font-family:inherit}\n    .soft{filter:grayscale(.75) sepia(.18) brightness(1.06) contrast(.92)}\n  `,\n  render(d2) {\n    const L = d2.lib(LIB)\n    const st = d2.lib.state(LIB)\n    const overlay = L ? '' : '<div class=\"loading\">' + (st === 'error' ? 'Falha ao carregar o mapa: ' + d2.lib.error(LIB) : 'Carregando mapa…') + '</div>'\n    return `<div class=\"app\">\n      <div class=\"mapwrap\">\n        <div id=\"map\"></div>\n        <div class=\"tag\">MAPA</div>\n        <div id=\"toast\"></div>\n        <div id=\"mini\" title=\"Minimapa — clica pra ir\"></div>\n      </div>\n      <div class=\"side\">\n        <div class=\"card\">\n          <h4>FILTRO</h4>\n          <input type=\"text\" id=\"q\" placeholder=\"Busca: Q123 ou -46/-24\" autocomplete=\"off\">\n          <div id=\"qres\"></div>\n          <div class=\"row\">\n            <select id=\"niv\">\n              <option value=\"\">Todos os níveis</option>\n              <option value=\"0\">Nível 0 · 1°</option>\n              <option value=\"1\">Nível 1 · 0,5°</option>\n              <option value=\"2\">Nível 2 · 0,25°</option>\n              <option value=\"3\">Nível 3 · 0,125°</option>\n            </select>\n            <button class=\"btn\" id=\"reset\" title=\"Ver Brasil inteiro\">Brasil</button>\n          </div>\n        </div>\n        <div class=\"card\">\n          <h4>CAMADAS</h4>\n          <div id=\"layers\"></div>\n          <h5>Legenda</h5>\n          <div class=\"lg\"><i style=\"height:3px;background:${C.rod}\"></i>Rodovias</div>\n          <div class=\"lg\"><i style=\"height:3px;background:${C.rio}\"></i>Rios</div>\n          <div class=\"lg\">${NIV.map((c, i) => '<i style=\"border:2px solid ' + c + '\" title=\"nível ' + i + '\"></i>').join('')}Grade · nível 0 → 3</div>\n          <div class=\"grad\"></div><div class=\"gl\"><span>poucas feições</span><span>muitas</span></div>\n          <h5>Área visível</h5>\n          <div id=\"stats\"><div class=\"sub\">—</div></div>\n        </div>\n      </div>\n      ${overlay}\n    </div>`\n  },\n  mounted(d2) { init(d2) },\n  updated(d2) { init(d2) }\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6abee6b456470c27067490ad","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"Hello_World","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .hello { font-family: sans-serif; font-size: 28px; font-weight: 600; padding: 24px; text-align: center; }\n  `,\n  render(d2) {\n    return `<div class=\"hello\">Hello, world! 👋</div>`;\n  }\n};"}}}}}}}},{"_id":"6ace621ef9fc7612597d19e7","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"kStateOfAi02","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"function esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n  });\n}\n\nvar MEDIA = 'https://gui.data2.media/file/';\n\nvar TESE = [\n  ['expand', 'innovate'],\n  ['copy', 'concept']\n];\n\nvar BUILDERS = [\n  {\n    name: 'Adonis Alcici', craft: 'Artista plástico', photo: 'DK-Vc3PUvYcLcW3nsPNVK',\n    body: [\n      'Sempre desenhou carros. Passou a vida inteira, desde a infância, aperfeiçoando a arte até o hiper-realismo.',\n      '!Com o tempo, abandonou o realismo. Descobriu que tinha algo a mais para comunicar, e que o mundo já era real o suficiente diante da liberdade que uma tela em branco traz.',\n      'Hoje pinta velocidade. Fez o ArtCar da TAG Heuer Porsche Cup Brasil e o capacete de Felipe Nasr para as 24 Horas de Le Mans de 2023.'\n    ],\n    link: ['adonisalcici.com', 'https://www.adonisalcici.com/']\n  },\n  {\n    name: 'Sergio Ferreira', craft: 'Seteh7 · upcycling', photo: '5N1ycqRiPy8NK4qrkDyAM', pos: 'center 18%',\n    body: [\n      'Produz moda através do upcycling, e o trabalho dele é puramente expressão artística.',\n      '!Sergio não constrói produtos. Ele fabrica ideias vestíveis.',\n      'Veste artistas como Alice Caymmi e Negra Li. Vende aos sábados na Feira Qualquer Coisa, na Praça Benedito Calixto.'\n    ],\n    link: ['@seteh7', 'https://www.instagram.com/seteh7/']\n  },\n  {\n    name: 'Pedro Guedes', craft: 'Aloeus · alta joalheria', photo: '2mgON-k3nugb6XTvg3W2E', pos: '78% center',\n    body: [\n      'Um criador nato. Designer 3D e produtor musical, já produziu games, vídeos, roupas e móveis.',\n      'Hoje se dedica inteiramente à joalheria na Aloeus, a marca dele de alta joalheria e design.',\n      'Na roda, é quem pode comparar o que muda quando uma ideia sai como game, como música ou como anel.'\n    ],\n    link: ['@aloeus.arts', 'https://www.instagram.com/aloeus.arts/']\n  },\n  {\n    name: 'Alexandre Iervolino', craft: 'ROUE · relógios', photo: 'zTi03wZ5lUaMPSqO2it84', pos: '82% center',\n    body: [\n      'Passou mais de 20 anos no mercado financeiro. Quando o setor em que trabalhava fechou, em 2015, foi à feira de Basel, fez contatos e viajou a Hong Kong para entender como conceber a ROUE.',\n      '!“Um relógio precisa transmitir algo: um design, uma filosofia ou uma personalidade.”',\n      'A ROUE nasceu em 2017 e vende para mais de 60 países. Os relógios vêm dos carros dos anos 60 e 70, dos rádios Braun e do mobiliário escandinavo. O TPS, cronógrafo em homenagem ao Porsche 910, sai em séries de mil peças numeradas.'\n    ],\n    link: ['rouewatch.com', 'https://rouewatch.com/']\n  }\n];\n\nvar PAUTA = [\n  ['01', 'Onde a IA já expande o seu trabalho?',\n    'Variações de uma peça, renderização, versões de cor, mockup. A parte do processo em que cada convidado já deixa a máquina trabalhar, e quantas horas isso devolve.'],\n  ['02', 'Em que ponto ela para de ajudar?',\n    'O momento em que a sugestão do modelo começa a parecer com tudo o que já existe. Como um joalheiro, um artista, quem costura roupa usada e quem desenha relógio percebem isso.'],\n  ['03', 'O cliente sabe a diferença?',\n    'A Migrolino escreveu “receita gerada por IA” na embalagem e virou piada. Uma foto gerada mostra um produto que ninguém fez. O que acontece com a confiança de quem compra.'],\n  ['04', 'Quem fica com o que a IA economizou?',\n    'Se a receita, a lata e o nome saíram de um prompt em dois dias, o cliente quer saber por que o preço é o mesmo.']\n];\n\nvar PROVOCACOES = [\n  'É só temporário?',\n  'Vai melhorar?',\n  'Slop para uns, abundância para outros?',\n  'A abundância, mesmo que com má qualidade, pode ter upsides?'\n];\n\nvar CASOS = [\n  {\n    where: 'Suíça · rede de conveniência', name: 'Migrolino',\n    body: [\n      'Lançou um sanduíche de frango com aviso na embalagem de que a receita foi gerada por IA. Foi o segundo produto da rede feito assim.',\n      'Depois das piadas, a empresa disse que a IA serve para transformar tendências de mercado em receita, que funcionários fazem os ajustes finais e que o aviso existe para mostrar a abordagem inovadora.'\n    ],\n    src: [['20 Minuten', 'https://www.20min.ch/story/migrolino-ki-generiertes-poulet-sandwich-sorgt-fuer-haeme-103324194']]\n  },\n  {\n    where: 'Suíça · 2023 · bebida', name: 'Vivi Nova',\n    body: [\n      'Refrigerante da Vivi Kola vendido na Migros. O ChatGPT escolheu os ingredientes e o Midjourney desenhou a lata. Do conceito ao produto foram dois dias.',\n      'A tabela nutricional ficou com pessoas, porque a IA não dava conta do cálculo. As avaliações saíram divididas.'\n    ],\n    src: [['FoodNavigator', 'https://www.foodnavigator.com/Article/2023/06/09/vivi-nova-ai-designs-beverage-in-switzerland'], ['SIAL', 'https://newsroom.sialparis.com/?p=20334']]\n  },\n  {\n    where: 'EUA · 2026 · restaurantes', name: 'Wonder Create',\n    body: [\n      'Marc Lore, CEO da Wonder, anunciou uma ferramenta que monta um restaurante inteiro a partir de um prompt: nome, marca, cardápio, fotos e preço, por US$ 10 por mês.'\n    ],\n    quote: '“Build me a fast-casual Mexican restaurant for Gen Z, for people that love cycling.”',\n    after: 'É o exemplo que ele repete em várias entrevistas. Numa delas, perguntaram para quem era o produto. Ele respondeu explicando como funciona o prompt.',\n    src: [['Semafor', 'https://www.semafor.com/article/05/05/2026/im-the-ipo-guy-serial-entrepreneur-marc-lore-on-his-plan-to-take-wonder-public']]\n  }\n];\n\nvar FOTOS = [\n  ['fIi16oJRutNWEKc8NyqGI', 'big', 'Painel com Rafa Cunha, Babi Vallim e Aline Sordili'],\n  ['iC0jQ8SJLyo5n2uECSy6u', '', 'Renato Shirakashi e o experimento Mirokami'],\n  ['dHeIpxYwQ2znOqKtFUeUl', '', 'David Lojudice e as sociedades de agentes'],\n  ['0z9NZDU9X29I3UpY5gjKn', '', 'Painel'],\n  ['K8hGOxCBtzdOCMYaUh_nX', '', 'Plateia'],\n  ['P-jsWJo2gXKAJIP_xzeua', 'wide', 'Sala cheia'],\n  ['JHvCplvr08sRKHQx9U1RG', '', 'Speakers e hosts no fim da noite'],\n  ['obksUWINAW0YZzHFh1GWy', '', 'Happy hour']\n];\n\nvar ED01 = [\n  ['Renato Shirakashi', 'O experimento Mirokami: números, erros e aprendizados'],\n  ['David Lojudice', 'O que aprendeu colocando LLMs para conviver'],\n  ['Rafa Cunha', 'Painel sobre gestão de times de IA'],\n  ['Babi Vallim', 'Painel, pela Hera.Build'],\n  ['Aline Sordili', 'Painel, co-fundadora do R7.com']\n];\n\nfunction link(label, href) {\n  return '<a href=\"' + esc(href) + '\" target=\"_blank\" rel=\"noopener\">' + esc(label) + '</a>';\n}\n\nfunction initials(name) {\n  return name.split(' ').map(function (p) { return p.charAt(0); }).join('').slice(0, 2).toUpperCase();\n}\n\nexport default {\n  css: `\n    :host { display:block; width:100%; height:100%; }\n    * { margin:0; padding:0; box-sizing:border-box; }\n    a { color:inherit; -webkit-tap-highlight-color:transparent; }\n    .page {\n      --black:#0a0a0a; --white:#fff; --off:#f7f7f7; --gray:#666; --line:#e0e0e0;\n      position:relative; height:100%; overflow-y:auto; overflow-x:hidden; background:var(--white); color:var(--black);\n      font-family:'Inter', system-ui, sans-serif; -webkit-font-smoothing:antialiased; scroll-behavior:smooth;\n    }\n    .g { font-family:'Space Grotesk', 'Inter', sans-serif; }\n    .serif { font-family:'Source Serif 4', Georgia, serif; }\n\n    nav { position:sticky; top:0; z-index:10; padding:1.2rem 3rem; display:flex; justify-content:space-between; align-items:center;\n      background:rgba(255,255,255,.92); backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px); border-bottom:1px solid var(--line); }\n    .logo { font-weight:700; font-size:1rem; letter-spacing:2px; cursor:pointer; }\n    .links { display:flex; gap:1.8rem; }\n    .links span { color:var(--gray); font-size:.78rem; letter-spacing:1px; text-transform:uppercase; cursor:pointer; transition:color .3s; }\n    .links span:hover { color:var(--black); }\n    .badge { background:var(--black); color:var(--white); padding:.4rem 1rem; font-size:.7rem; font-weight:600; letter-spacing:1px; text-transform:uppercase; }\n\n    .hero { min-height:calc(100vh - 60px); display:grid; grid-template-columns:1fr auto; align-items:end; gap:3rem; padding:4rem 3rem 3rem; border-bottom:2px solid var(--black); }\n    .ed { font-size:.75rem; letter-spacing:3px; text-transform:uppercase; color:var(--gray); margin-bottom:1.2rem; }\n    h1 { font-size:clamp(4rem,12vw,10rem); font-weight:700; line-height:.9; letter-spacing:-4px; }\n    .builders-line { margin-top:1.5rem; font-size:clamp(1rem,2vw,1.3rem); line-height:1.6; }\n    .builders-line .tol { background:var(--black); color:var(--white); padding:.1em .4em; }\n    .entry { margin-top:3rem; border-top:2px solid var(--black); padding-top:1.5rem; max-width:760px; }\n    .entry-head { display:flex; align-items:baseline; gap:1rem; flex-wrap:wrap; }\n    .word { font-size:clamp(2.6rem,6vw,4.5rem); line-height:1; }\n    .pron { font-style:italic; color:var(--gray); font-size:1.2rem; }\n    .cls { font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); }\n    .def { font-size:clamp(1.15rem,2.2vw,1.5rem); line-height:1.5; margin-top:1rem; }\n    .def b { font-family:'Inter', sans-serif; font-size:.9em; margin-right:.4rem; }\n    .src { font-size:.78rem; color:var(--gray); margin-top:.8rem; }\n    .src a { color:var(--gray); }\n    .meta { text-align:right; padding-bottom:.5rem; }\n    .meta div { font-weight:700; font-size:1.1rem; margin-bottom:.3rem; }\n    .meta .muted { color:var(--gray); font-weight:500; }\n    .invite { margin-top:1.2rem; font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); border:1px solid var(--black); display:inline-block; padding:.4rem .8rem; }\n\n    .tese { background:var(--black); color:var(--white); }\n    .tese-in { max-width:1200px; margin:0 auto; padding:6rem 3rem 5rem; }\n    .tese .label { color:#888; }\n    .tg { display:grid; grid-template-columns:1fr 1fr; border-top:1px solid #333; }\n    .tg > div { padding:1.4rem 0; border-bottom:1px solid #333; font-size:clamp(2.4rem,7.5vw,6.5rem); font-weight:700; line-height:1; letter-spacing:-2px; }\n    .tg .hd { font-size:.75rem; letter-spacing:3px; text-transform:uppercase; font-weight:500; padding:1rem 0; color:#888; }\n    .tg .hd.no { color:var(--white); }\n    .tg .can { color:#555; }\n    .tg .cant { color:var(--white); }\n    .tg > div:nth-child(odd) { padding-right:2rem; border-right:3px solid var(--white); }\n    .tg > div:nth-child(even) { padding-left:2rem; }\n    .tese-foot { margin-top:2.5rem; font-size:clamp(1.1rem,2.2vw,1.5rem); line-height:1.5; color:#bbb; max-width:760px; }\n    .tese-foot em { font-style:normal; color:var(--white); }\n\n    section { padding:6rem 3rem; max-width:1200px; margin:0 auto; }\n    hr { border:none; height:1px; background:var(--line); max-width:1200px; margin:0 auto; }\n    .label { font-size:.7rem; letter-spacing:3px; text-transform:uppercase; color:var(--gray); margin-bottom:1.5rem; }\n    h2 { font-size:clamp(2rem,5vw,3.5rem); font-weight:700; line-height:1.1; margin-bottom:2rem; }\n\n    .mani { font-size:clamp(1.2rem,2.5vw,1.8rem); line-height:1.6; color:var(--gray); max-width:860px; }\n    .mani + .mani { margin-top:1.6rem; }\n    .mani em { color:var(--black); font-style:normal; font-weight:600; }\n\n    .bgrid { display:grid; grid-template-columns:repeat(4,1fr); border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .bcard { border-right:2px solid var(--black); border-bottom:2px solid var(--black); display:flex; flex-direction:column; transition:background .3s; }\n    .bcard:hover { background:var(--off); }\n    .bimg { aspect-ratio:4/5; overflow:hidden; background:#111; position:relative; }\n    .bimg img { width:100%; height:100%; object-fit:cover; object-position:center 20%; display:block; filter:grayscale(100%); transition:filter .5s; }\n    .bcard:hover .bimg img { filter:grayscale(0%); }\n    .bph { position:absolute; inset:0; display:flex; flex-direction:column; justify-content:space-between; padding:1.4rem; color:#666; }\n    .bph .ini { font-size:clamp(4rem,9vw,7rem); font-weight:700; color:#222; letter-spacing:-3px; line-height:1; }\n    .bph .soon { font-size:.7rem; letter-spacing:2px; text-transform:uppercase; }\n    .binfo { padding:1.6rem 1.5rem 1.8rem; display:flex; flex-direction:column; flex:1; }\n    .bname { font-size:1.5rem; font-weight:700; }\n    .bcraft { font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); margin:.35rem 0 1rem; }\n    .binfo p { font-size:.92rem; line-height:1.6; color:#333; }\n    .binfo p + p { margin-top:.7rem; }\n    .binfo p.key { font-size:1.05rem; line-height:1.45; color:var(--black); border-left:3px solid var(--black); padding-left:.9rem; margin:1rem 0; }\n    .blink { margin-top:auto; padding-top:1.2rem; font-size:.8rem; }\n    .blink a { color:var(--black); }\n\n    .pauta { border-top:2px solid var(--black); }\n    .pi { display:grid; grid-template-columns:120px 1fr; border-bottom:1px solid var(--line); transition:background .3s; }\n    .pi:last-child { border-bottom:2px solid var(--black); }\n    .pi:hover { background:var(--off); }\n    .pn { font-weight:700; font-size:1.6rem; padding:1.6rem 1.2rem; border-right:2px solid var(--black); }\n    .pb { padding:1.6rem 1.5rem; }\n    .pq { font-weight:700; font-size:clamp(1.15rem,2.2vw,1.5rem); line-height:1.3; }\n    .pd { font-size:.92rem; color:var(--gray); line-height:1.6; margin-top:.5rem; max-width:720px; }\n\n    .prov { margin-top:4rem; background:var(--black); color:var(--white); padding:3rem 2.5rem; }\n    .prov .label { color:#888; margin-bottom:1rem; }\n    .prov ul { list-style:none; }\n    .prov li { font-size:clamp(1.5rem,3.4vw,2.6rem); font-weight:700; line-height:1.15; letter-spacing:-.5px; padding:1rem 0; border-bottom:1px solid #333; }\n    .prov li:last-child { border-bottom:none; }\n    .casos { display:grid; grid-template-columns:repeat(3,1fr); border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .caso { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2rem; display:flex; flex-direction:column; transition:background .3s; }\n    .caso:hover { background:var(--off); }\n    .cw { font-size:.7rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); }\n    .cn { font-weight:700; font-size:1.6rem; margin:.4rem 0 1rem; }\n    .caso p { font-size:.92rem; line-height:1.6; color:#333; }\n    .caso p + p { margin-top:.8rem; }\n    .caso .q { border-left:3px solid var(--black); padding-left:.9rem; font-style:italic; font-size:1rem; color:var(--black); }\n    .cs { margin-top:auto; padding-top:1.4rem; font-size:.75rem; color:var(--gray); }\n    .cs a { color:var(--gray); }\n    .note { margin-top:1.5rem; font-size:.85rem; color:var(--gray); line-height:1.6; max-width:760px; }\n    .note a { color:var(--black); }\n\n    .hosts { display:grid; grid-template-columns:1fr 1fr; border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .host { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2.5rem 2rem; transition:background .3s; }\n    .host:hover { background:var(--off); }\n    .hn { font-size:1.6rem; font-weight:700; }\n    .hc { font-size:.8rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); margin-top:.3rem; }\n\n    .ed01 { background:var(--black); color:var(--white); padding-top:5rem; }\n    .ed01-head { max-width:1200px; margin:0 auto 2rem; padding:0 3rem; }\n    .ed01 .label { color:#999; margin-bottom:.5rem; }\n    .ed01-t { font-size:1.6rem; font-weight:700; }\n    .mosaic { max-width:1200px; margin:0 auto; padding:0 3rem; display:grid; grid-template-columns:repeat(4,1fr); grid-auto-rows:210px; gap:6px; }\n    .ph { position:relative; overflow:hidden; background:#1a1a1a; }\n    .ph.big { grid-column:span 2; grid-row:span 2; }\n    .ph.wide { grid-column:span 2; }\n    .ph img { width:100%; height:100%; object-fit:cover; display:block; filter:grayscale(100%) contrast(1.05); transition:filter .5s, transform .8s; }\n    .ph:hover img { filter:grayscale(0%); transform:scale(1.02); }\n    .credit { max-width:1200px; margin:.9rem auto 0; padding:0 3rem; font-size:.72rem; color:#888; letter-spacing:.5px; }\n    .ed01-in { max-width:1200px; margin:0 auto; padding:4rem 3rem; display:grid; grid-template-columns:240px 1fr; gap:2rem; align-items:start; }\n    .ed01 li { list-style:none; display:grid; grid-template-columns:220px 1fr; gap:1rem; padding:.8rem 0; border-bottom:1px solid #2a2a2a; font-size:.95rem; }\n    .ed01 li:last-child { border-bottom:none; }\n    .ed01 .who { font-weight:700; }\n    .ed01 .what { color:#aaa; }\n\n    .details { display:grid; grid-template-columns:repeat(3,1fr); border-top:2px solid var(--black); border-left:2px solid var(--black); }\n    .dc { border-right:2px solid var(--black); border-bottom:2px solid var(--black); padding:2rem; transition:background .3s; }\n    .dc:hover { background:var(--off); }\n    .dc h3 { font-size:.75rem; letter-spacing:2px; text-transform:uppercase; color:var(--gray); margin-bottom:.8rem; font-weight:700; }\n    .dc p { font-size:1rem; line-height:1.6; }\n    .dc b { font-weight:700; }\n\n    footer { background:var(--black); color:var(--white); padding:3rem; display:flex; justify-content:space-between; align-items:center; gap:1rem; flex-wrap:wrap; border-top:1px solid #2a2a2a; }\n    .fl { font-size:1.2rem; font-weight:700; letter-spacing:2px; }\n    .ft { font-size:.8rem; color:#999; }\n\n    @supports (animation-timeline: view()) {\n      @media (prefers-reduced-motion: no-preference) {\n        .rv { animation: rise linear both; animation-timeline: view(); animation-range: entry 0% entry 35%; }\n        @keyframes rise { from { opacity:0; transform:translateY(30px); } to { opacity:1; transform:none; } }\n      }\n    }\n\n    @media (max-width: 1100px) {\n      .bgrid { grid-template-columns:repeat(2,1fr); }\n    }\n    @media (max-width: 1000px) {\n      .links { gap:1.1rem; }\n      .links span { font-size:.7rem; }\n    }\n    @media (max-width: 900px) {\n      .casos, .details, .bgrid { grid-template-columns:1fr; }\n      .bimg { aspect-ratio:4/3; }\n      .ed01-in { grid-template-columns:1fr; }\n      .mosaic { grid-template-columns:repeat(2,1fr); grid-auto-rows:170px; }\n    }\n    @media (max-width: 768px) {\n      nav { padding:1rem 16px; }\n      .links { display:none; }\n      .hero { grid-template-columns:1fr; padding:3rem 16px 2.5rem; min-height:auto; }\n      h1 { letter-spacing:-2px; }\n      .meta { text-align:left; }\n      .tese-in { padding:4rem 16px 3.5rem; }\n      .tg > div { letter-spacing:-1px; }\n      section { padding:4rem 16px; }\n      .pi { grid-template-columns:56px 1fr; }\n      .pn { font-size:1.1rem; padding:1.4rem .6rem; }\n      .pb { padding:1.4rem 1rem; }\n      .prov { padding:2rem 1.2rem; margin-top:3rem; }\n      .hosts { grid-template-columns:1fr; }\n      .caso, .host, .dc { padding:1.6rem 1.2rem; }\n      .binfo { padding:1.4rem 1.2rem 1.6rem; }\n      .ed01 { padding-top:3.5rem; }\n      .ed01-head, .mosaic, .credit { padding-left:16px; padding-right:16px; }\n      .mosaic { grid-auto-rows:130px; gap:4px; }\n      .ed01-in { padding:3rem 16px; }\n      .ed01 li { grid-template-columns:1fr; gap:.2rem; }\n      footer { padding:2rem 16px; }\n    }\n  `,\n\n  render(d2) {\n    var data = d2.param('dataEvento', 'Terça, 3 de novembro de 2026');\n    var local = d2.param('localEvento', '');\n\n    var h = '<div class=\"page\" data-scroller>';\n\n    h += '<nav><span class=\"logo g\" data-go=\"top\">STATE OF AI</span><div class=\"links\">' +\n      '<span data-go=\"tese\">Manifesto</span><span data-go=\"builders\">Builders</span><span data-go=\"pauta\">Pauta</span>' +\n      '<span data-go=\"casos\">Casos</span><span data-go=\"ed01\">Edição 01</span><span data-go=\"info\">Info</span></div>' +\n      '<span class=\"badge\">Somente convidados</span></nav>';\n\n    h += '<header class=\"hero\" id=\"top\"><div>' +\n      '<div class=\"ed\">Edição 02</div>' +\n      '<h1 class=\"g\">STATE<br>OF AI</h1>' +\n      '<div class=\"builders-line\">Builders allowed.<br><span class=\"tol\">Providers tolerated.</span></div>' +\n      '<div class=\"entry\"><div class=\"entry-head\">' +\n      '<span class=\"word serif\">slop</span><span class=\"pron serif\">/ˈslɑːp/</span><span class=\"cls\">substantivo</span></div>' +\n      '<p class=\"def serif\"><b>1.</b>conteúdo digital de baixa qualidade, geralmente produzido em quantidade por meio de inteligência artificial.</p>' +\n      '<p class=\"src\">Merriam-Webster, palavra do ano de 2025. ' + link('AP', 'https://abc7news.com/amp/post/merriam-websters-2025-word-year-is-slop/18288780/') + '</p>' +\n      '</div></div>' +\n      '<div class=\"meta g\"><div>' + esc(data) + '</div><div>Roda de conversa</div>' +\n      '<div class=\"muted\">' + (local ? esc(local) : 'Local no convite') + '</div>' +\n      '<span class=\"invite\">Evento fechado para convidados</span></div></header>';\n\n    h += '<div class=\"tese\" id=\"tese\"><div class=\"tese-in\"><div class=\"label\">Manifesto</div><div class=\"tg g\">' +\n      '<div class=\"hd\">AI can</div><div class=\"hd no\">AI can’t</div>';\n    TESE.forEach(function (row) {\n      h += '<div class=\"can\">' + esc(row[0]) + '</div><div class=\"cant\">' + esc(row[1]) + '</div>';\n    });\n    h += '</div><p class=\"tese-foot\">Slop é o que sai quando ninguém decide onde a IA para. <em>A edição 02 é sobre quem sabe exatamente onde traçar essa linha.</em></p></div></div>';\n\n    h += '<section id=\"manifesto\" class=\"rv\">' +\n      '<p class=\"mani\">Um modelo de IA aprende com o que já existe. Ele estende um estilo, gera mil variações de uma foto e copia um traço com precisão. Ele não decide que o traço precisa mudar.</p>' +\n      '<p class=\"mani\">O Adonis Alcici passou a vida aperfeiçoando o desenho de carros até o hiper-realismo. Depois abandonou o realismo: descobriu que tinha algo a mais para comunicar, e que o mundo já era real o suficiente diante da liberdade de uma tela em branco. <em>Essa decisão é o conceito</em>, e nenhum modelo toma por você.</p>' +\n      '<p class=\"mani\">A maioria das empresas ainda não achou essa linha. <em>Põe IA na foto do cardápio e deixa gente digitando planilha.</em></p>' +\n      '<p class=\"mani\">A edição 02 é <em>uma roda de conversa</em>, sem palco e sem palestra, com quatro builders que sabem exatamente onde traçar essa linha: um criador que trocou games, música e móveis pela joalheria, um artista que largou o realismo para pintar velocidade, um estilista que fabrica ideias vestíveis e um ex-executivo do mercado financeiro que desenha relógios a partir de carros antigos. Queremos ouvir em que etapa a IA entrou no trabalho deles, em qual foi barrada e o que o cliente percebe quando ela passa do ponto.</p>' +\n      '</section><hr>';\n\n    h += '<section id=\"builders\" class=\"rv\"><div class=\"label\">Builders</div><h2 class=\"g\">Quem sabe onde<br>fica a linha.</h2><div class=\"bgrid\">';\n    BUILDERS.forEach(function (b) {\n      h += '<article class=\"bcard\" data-key=\"' + esc(b.name) + '\"><div class=\"bimg\">';\n      if (b.photo) h += '<img src=\"' + MEDIA + b.photo + '\" alt=\"' + esc(b.name) + '\" loading=\"lazy\" decoding=\"async\"' + (b.pos ? ' style=\"object-position:' + esc(b.pos) + '\"' : '') + '>';\n      else h += '<div class=\"bph\"><span class=\"ini g\">' + esc(initials(b.name)) + '</span><span class=\"soon\">Foto em breve</span></div>';\n      h += '</div><div class=\"binfo\"><div class=\"bname g\">' + esc(b.name) + '</div><div class=\"bcraft\">' + esc(b.craft) + '</div>';\n      b.body.forEach(function (t) { h += t.charAt(0) === '!' ? '<p class=\"key serif\">' + esc(t.slice(1)) + '</p>' : '<p>' + esc(t) + '</p>'; });\n      h += '<div class=\"blink\">' + link(b.link[0], b.link[1]) + '</div></div></article>';\n    });\n    h += '</div></section><hr>';\n\n    h += '<section id=\"pauta\" class=\"rv\"><div class=\"label\">Pauta</div><h2 class=\"g\">Quatro perguntas<br>para a roda.</h2><div class=\"pauta\">';\n    PAUTA.forEach(function (p) {\n      h += '<div class=\"pi\" data-key=\"p' + p[0] + '\"><div class=\"pn g\">' + p[0] + '</div><div class=\"pb\">' +\n        '<div class=\"pq g\">' + esc(p[1]) + '</div><div class=\"pd\">' + esc(p[2]) + '</div></div></div>';\n    });\n    h += '</div><div class=\"prov\"><div class=\"label\">Sobre o slop, algumas provocações</div><ul class=\"g\">' +\n      PROVOCACOES.map(function (q) { return '<li>' + esc(q) + '</li>'; }).join('') + '</ul></div></section><hr>';\n\n    h += '<section id=\"casos\" class=\"rv\"><div class=\"label\">Casos</div><h2 class=\"g\">Três casos de quem<br>passou da linha.</h2><div class=\"casos\">';\n    CASOS.forEach(function (c) {\n      h += '<article class=\"caso\" data-key=\"' + esc(c.name) + '\"><div class=\"cw\">' + esc(c.where) + '</div><div class=\"cn g\">' + esc(c.name) + '</div>';\n      c.body.forEach(function (t) { h += '<p>' + esc(t) + '</p>'; });\n      if (c.quote) h += '<p class=\"q serif\">' + esc(c.quote) + '</p>';\n      if (c.after) h += '<p>' + esc(c.after) + '</p>';\n      h += '<div class=\"cs\">Fonte: ' + c.src.map(function (s) { return link(s[0], s[1]); }).join(' · ') + '</div></article>';\n    });\n    h += '</div></section>';\n\n    h += '<div class=\"ed01\" id=\"ed01\"><div class=\"ed01-head\"><div class=\"label\">Edição 01</div><div class=\"ed01-t g\">A primeira noite.</div></div>' +\n      '<div class=\"mosaic\">' + FOTOS.map(function (f) {\n        return '<figure class=\"ph ' + f[1] + '\" data-key=\"' + f[0] + '\"><img src=\"' + MEDIA + f[0] + '\" alt=\"' + esc(f[2]) + '\" loading=\"lazy\" decoding=\"async\"></figure>';\n      }).join('') + '</div>' +\n      '<p class=\"credit\">Fotos: Alécio Cezar Batista Oliveira</p>' +\n      '<div class=\"ed01-in\"><div><div class=\"label\">Na edição 01</div><div class=\"ed01-t g\">Quem já falou aqui.</div></div><ul>';\n    ED01.forEach(function (e) { h += '<li><span class=\"who g\">' + esc(e[0]) + '</span><span class=\"what\">' + esc(e[1]) + '</span></li>'; });\n    h += '</ul></div></div>';\n\n    h += '<section id=\"info\" class=\"rv\"><div class=\"label\">Informações</div><h2 class=\"g\">Informações.</h2><div class=\"details\">' +\n      '<div class=\"dc\"><h3 class=\"g\">Acesso</h3><p><b>Evento fechado.</b><br>Somente para convidados.<br>Não há venda de ingressos.</p></div>' +\n      '<div class=\"dc\"><h3 class=\"g\">Para quem</h3><p>Gente de produto, marca, tecnologia e dados, sentada na mesma roda com quem vive do próprio ofício.</p></div>' +\n      '<div class=\"dc\"><h3 class=\"g\">Data e local</h3><p><b>' + esc(data) + '.</b><br>' + (local ? esc(local) : 'O endereço vai junto com o convite.') + '</p></div>' +\n      '</div></section>';\n\n    h += '<footer><div class=\"fl g\">STATE OF AI BR</div><div class=\"ft\">Edição 02 · stateofai.com.br</div></footer>';\n    h += '</div>';\n    return h;\n  },\n\n  mounted(d2) {\n    var id = 'stateofai-fonts';\n    if (typeof document !== 'undefined' && document.head && !document.getElementById(id)) {\n      var l = document.createElement('link');\n      l.id = id; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=Source+Serif+4:ital,wght@0,400;1,400&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('[data-go]', 'click', function (e, el) {\n      var target = el.getAttribute('data-go');\n      var scroller = d2.$('[data-scroller]');\n      var node = d2.$('#' + target);\n      if (!scroller || !node) return;\n      scroller.scrollTo({ top: target === 'top' ? 0 : node.offsetTop - 64, behavior: 'smooth' });\n    });\n  }\n};\n"}},"dataEvento":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataEvento"}}}},"localEvento":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"localEvento"}}}}}}}}}},{"_id":"6acfb17cc3e8fc00c090e2b3","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"wd_webview","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":"var API = 'https://gui.data2apis.com/rush/wd-wv-api';\nvar LOGO = 'https://gui.data2.media/file/S5mI9Xg4bjGTs7gkPQVLe';\nvar WA = 'https://wa.me/551140402309';\nvar lastN = -1;\nvar draftTxt = '';\n\nvar STATUS = {\n  entregue: 'Entregue',\n  em_transporte: 'Em transporte',\n  em_separacao: 'Em separação',\n  cancelamento_solicitado: 'Cancelamento solicitado'\n};\nvar MOTIVOS = {\n  troca: ['Tamanho ou modelo errado', 'Não gostei do produto', 'Chegou diferente do anúncio', 'Comprei por engano', 'Outro motivo'],\n  defeito: ['Não liga ou não funciona', 'Chegou quebrado ou amassado', 'Peça faltando', 'Parou de funcionar com o uso', 'Outro problema']\n};\nvar RESOLUCOES = {\n  troca: ['Trocar por outro produto', 'Reembolso', 'Vale-compras'],\n  defeito: ['Troca por um novo', 'Reparo', 'Reembolso']\n};\n\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n  });\n}\nfunction plat() {\n  var ua = (typeof navigator !== 'undefined' && navigator.userAgent) || '';\n  if (/Android/i.test(ua)) return 'android';\n  return 'ios';\n}\nfunction token() {\n  try { return new URLSearchParams(location.search).get('t') || ''; } catch (e) { return ''; }\n}\nfunction brl(n) {\n  try { return 'R$ ' + Number(n || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } catch (e) { return 'R$ ' + n; }\n}\nfunction dia(d) {\n  if (!d) return '';\n  var x = new Date(d);\n  return String(x.getDate()).padStart(2, '0') + '/' + String(x.getMonth() + 1).padStart(2, '0');\n}\nfunction hora(d) {\n  if (!d) return '';\n  var x = new Date(d);\n  return String(x.getHours()).padStart(2, '0') + ':' + String(x.getMinutes()).padStart(2, '0');\n}\nfunction call(op, extra) {\n  var body = Object.assign({ t: token(), op: op }, extra || {});\n  return fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })\n    .then(function (r) { return r.json(); })\n    .then(function (j) { return j && j.data ? j.data : j; });\n}\nfunction compress(file) {\n  return new Promise(function (resolve, reject) {\n    var fr = new FileReader();\n    fr.onload = function () {\n      var img = new Image();\n      img.onload = function () {\n        var max = 1024, w = img.width, h = img.height;\n        if (w > h && w > max) { h = Math.round(h * max / w); w = max; } else if (h > max) { w = Math.round(w * max / h); h = max; }\n        var c = document.createElement('canvas');\n        c.width = w; c.height = h;\n        c.getContext('2d').drawImage(img, 0, 0, w, h);\n        resolve(c.toDataURL('image/jpeg', 0.72));\n      };\n      img.onerror = reject;\n      img.src = fr.result;\n    };\n    fr.onerror = reject;\n    fr.readAsDataURL(file);\n  });\n}\n\nvar CALL_API = 'https://gui.data2apis.com/rush/wd-call-api';\nvar rtc = { pc: null, local: null, remote: null, offer: null, visto: {}, tick: null };\nvar ICO = {\n  phone: '<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6.6 10.8a15.1 15.1 0 0 0 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1A17 17 0 0 1 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1z\"/></svg>',\n  hang: '<svg viewBox=\"0 0 24 24\" style=\"transform:rotate(135deg)\"><path fill=\"currentColor\" d=\"M6.6 10.8a15.1 15.1 0 0 0 6.6 6.6l2.2-2.2c.3-.3.7-.4 1-.2 1.1.4 2.3.6 3.6.6.6 0 1 .4 1 1V20c0 .6-.4 1-1 1A17 17 0 0 1 3 4c0-.6.4-1 1-1h3.5c.6 0 1 .4 1 1 0 1.3.2 2.5.6 3.6.1.3 0 .7-.2 1z\"/></svg>',\n  mic: '<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.9V21h2v-3.1A7 7 0 0 0 19 11z\"/></svg>',\n  micOff: '<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3zm5-3a5 5 0 0 1-10 0H5a7 7 0 0 0 6 6.9V21h2v-3.1A7 7 0 0 0 19 11z\"/><path d=\"M4 4l16 16\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>',\n  cam: '<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M17 10.5V7a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3.5l4 4v-11z\"/></svg>',\n  camOff: '<svg viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M17 10.5V7a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3.5l4 4v-11z\"/><path d=\"M3 3l18 18\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"/></svg>'\n};\nfunction callApi(op, extra) {\n  var body = Object.assign({ t: token(), op: op }, extra || {});\n  return fetch(CALL_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })\n    .then(function (r) { return r.json(); })\n    .then(function (j) { return j && j.data ? j.data : j; });\n}\nfunction waitIce(pc) {\n  return new Promise(function (res) {\n    if (pc.iceGatheringState === 'complete') return res();\n    var t = setTimeout(res, 3500);\n    pc.addEventListener('icegatheringstatechange', function () {\n      if (pc.iceGatheringState === 'complete') { clearTimeout(t); res(); }\n    });\n  });\n}\nfunction stopRtc() {\n  try { if (rtc.pc) rtc.pc.close(); } catch (e) {}\n  try { if (rtc.local) rtc.local.getTracks().forEach(function (t) { t.stop(); }); } catch (e) {}\n  if (rtc.tick) { clearInterval(rtc.tick); rtc.tick = null; }\n  rtc.pc = null; rtc.local = null; rtc.remote = null; rtc.offer = null;\n}\nfunction dur(ms) {\n  var x = Math.max(0, Math.floor(ms / 1000));\n  return Math.floor(x / 60) + ':' + String(x % 60).padStart(2, '0');\n}\nfunction iniciaisN(n) { var p = String(n || '?').trim().split(/\\s+/); return ((p[0] || '?')[0] + (p[1] ? p[1][0] : '')).toUpperCase(); }\nfunction viewCall(s) {\n  var c = s.call;\n  if (!c) return '';\n  var nome = esc(c.agent || 'Atendente');\n  if (c.status === 'tocando') {\n    return '<div class=\"call ring\"><div class=\"call-top\"><div class=\"call-av\">' + esc(iniciaisN(c.agent)) + '</div><div class=\"call-n\">' + nome + '</div><div class=\"call-s\">' + (c.video ? 'Chamada de vídeo' : 'Chamada de voz') + '</div></div>' +\n      '<div class=\"call-btns\"><div class=\"cbw\"><button class=\"cb red\" data-call-decline aria-label=\"Recusar\">' + ICO.hang + '</button><span>Recusar</span></div>' +\n      '<div class=\"cbw\"><button class=\"cb green\" data-call-accept aria-label=\"Atender\">' + (c.video ? ICO.cam : ICO.phone) + '</button><span>Atender</span></div></div></div>';\n  }\n  var sub = c.err ? esc(c.err) : (c.status === 'ativa' ? dur(Date.now() - (c.startedAt || Date.now())) : 'Conectando…');\n  return '<div class=\"call live-call' + (c.video ? ' vid' : '') + '\"><video class=\"rv\" data-rv playsinline autoplay></video>' +\n    (c.video ? '<video class=\"lv' + (c.camOff ? ' off' : '') + '\" data-lv playsinline autoplay muted></video>' : '') +\n    '<div class=\"call-top\">' + (c.video ? '' : '<div class=\"call-av\">' + esc(iniciaisN(c.agent)) + '</div>') + '<div class=\"call-n\">' + nome + '</div><div class=\"call-s\">' + sub + '</div></div>' +\n    '<div class=\"call-btns\"><div class=\"cbw\"><button class=\"cb' + (c.muted ? ' on' : '') + '\" data-call-mute>' + (c.muted ? ICO.micOff : ICO.mic) + '</button><span>' + (c.muted ? 'Sem som' : 'Mudo') + '</span></div>' +\n    (c.video ? '<div class=\"cbw\"><button class=\"cb' + (c.camOff ? ' on' : '') + '\" data-call-cam>' + (c.camOff ? ICO.camOff : ICO.cam) + '</button><span>Câmera</span></div>' : '') +\n    '<div class=\"cbw\"><button class=\"cb red\" data-call-end>' + ICO.hang + '</button><span>Encerrar</span></div></div></div>';\n}\n\nfunction hostSetup() {\n  try {\n    var head = document.head;\n    var vp = head.querySelector('meta[name=\"viewport\"]');\n    if (!vp) { vp = document.createElement('meta'); vp.setAttribute('name', 'viewport'); head.appendChild(vp); }\n    vp.setAttribute('content', 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content');\n    var dark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n    var and = plat() === 'android';\n    var cor = dark ? (and ? '#111318' : '#000000') : (and ? '#f7f9fc' : '#f2f2f7');\n    var tc = head.querySelector('meta[name=\"theme-color\"]');\n    if (!tc) { tc = document.createElement('meta'); tc.setAttribute('name', 'theme-color'); head.appendChild(tc); }\n    tc.setAttribute('content', cor);\n    var cs = head.querySelector('meta[name=\"color-scheme\"]');\n    if (!cs) { cs = document.createElement('meta'); cs.setAttribute('name', 'color-scheme'); head.appendChild(cs); }\n    cs.setAttribute('content', 'light dark');\n    var st = document.getElementById('wd-host-style');\n    if (!st) { st = document.createElement('style'); st.id = 'wd-host-style'; head.appendChild(st); }\n    st.textContent = 'html,body{margin:0;padding:0;height:100%;overflow:hidden;overscroll-behavior:none;-webkit-text-size-adjust:100%;text-size-adjust:100%;touch-action:manipulation;background:' + cor + ';}*{-webkit-tap-highlight-color:transparent;}';\n    document.title = 'Atendimento';\n    ['gesturestart', 'gesturechange'].forEach(function (ev) { document.addEventListener(ev, function (e) { e.preventDefault(); }, { passive: false }); });\n  } catch (e) {}\n}\nfunction fitViewport(host) {\n  try {\n    var vv = window.visualViewport;\n    var h = vv ? vv.height : window.innerHeight;\n    host.style.setProperty('--app-h', Math.round(h) + 'px');\n    if (window.scrollY) window.scrollTo(0, 0);\n  } catch (e) {}\n}\n\nfunction bar(title, sub) {\n  return '<header class=\"bar\"><div class=\"bar-in\"><div class=\"bar-t\">' + esc(title) + '</div>' +\n    (sub ? '<div class=\"bar-s\">' + sub + '</div>' : '') + '</div></header>';\n}\nfunction foot() {\n  return '<footer class=\"foot\"><img class=\"logo\" src=\"' + LOGO + '\" alt=\"Data²\"><span>Demonstração</span></footer>';\n}\nfunction voltar() {\n  return '<a class=\"btn ghost\" href=\"' + WA + '\">Voltar ao WhatsApp</a>';\n}\n\nfunction viewRastreio(s) {\n  var d = s.data;\n  var p = (d.pedidos || []).filter(function (o) { return String(o.number).toUpperCase() === String(d.orderNumber || '').toUpperCase(); })[0];\n  if (!p) return bar('Pedido') + '<main class=\"body\"><div class=\"empty\">Pedido não encontrado.</div>' + voltar() + '</main>';\n  var tl = (p.timeline || []).map(function (e, i, arr) {\n    var atual = e.done && !(arr[i + 1] && arr[i + 1].done);\n    return '<li class=\"tl-i' + (e.done ? ' done' : '') + (atual ? ' now' : '') + '\">' +\n      '<span class=\"dot\"></span><div class=\"tl-c\"><div class=\"tl-l\">' + esc(e.label) + '</div>' +\n      (e.at ? '<div class=\"tl-d\">' + dia(e.at) + ' · ' + hora(e.at) + '</div>' : '') + '</div></li>';\n  }).join('');\n  var itens = (p.items || []).map(function (i) {\n    return '<div class=\"row\"><div class=\"row-m\"><div class=\"row-t\">' + esc(i.name) + '</div><div class=\"row-s\">' + i.qty + ' un.</div></div><div class=\"row-v\">' + brl(i.price) + '</div></div>';\n  }).join('');\n  var entregue = p.status === 'entregue';\n  return bar('Pedido ' + p.number) +\n    '<main class=\"body\">' +\n    '<section class=\"hero card\"><span class=\"pill ' + esc(p.status) + '\">' + esc(STATUS[p.status] || p.status) + '</span>' +\n    '<div class=\"hero-k\">' + (entregue ? 'Entregue em' : 'Previsão de entrega') + '</div>' +\n    '<div class=\"hero-v\">' + dia(p.eta) + '</div>' +\n    '<div class=\"hero-s\">' + esc(p.carrier) + '</div></section>' +\n    '<h3 class=\"sec\">Acompanhamento</h3><section class=\"card pad\"><ol class=\"tl\">' + tl + '</ol></section>' +\n    '<h3 class=\"sec\">Itens</h3><section class=\"card list\">' + itens +\n    '<div class=\"row total\"><div class=\"row-m\"><div class=\"row-t\">Total</div></div><div class=\"row-v\">' + brl(p.total) + '</div></div></section>' +\n    '<h3 class=\"sec\">Entrega</h3><section class=\"card list\"><div class=\"row\"><div class=\"row-m\"><div class=\"row-t\">' + esc(p.address) + '</div><div class=\"row-s\">Endereço de entrega</div></div></div></section>' +\n    voltar() + foot() + '</main>';\n}\n\nfunction viewForm(s) {\n  var d = s.data, k = d.kind, f = s.form;\n  var titulo = k === 'troca' ? 'Troca ou devolução' : 'Produto com defeito';\n  if (s.done) {\n    return bar(titulo) + '<main class=\"body\"><section class=\"card done-card\">' +\n      '<div class=\"check\">✓</div><div class=\"done-t\">' + (k === 'troca' ? 'Solicitação enviada' : 'Defeito registrado') + '</div>' +\n      '<div class=\"done-s\">Protocolo</div><div class=\"done-p\">' + esc(s.done) + '</div>' +\n      '<div class=\"done-x\">A confirmação também chegou na sua conversa do WhatsApp.</div></section>' +\n      voltar() + foot() + '</main>';\n  }\n  var pedidos = (d.pedidos || []).filter(function (o) { return o.status === 'entregue'; });\n  if (!pedidos.length) pedidos = d.pedidos || [];\n  var ped = pedidos.filter(function (o) { return o.number === f.order; })[0];\n  var html = bar(titulo) + '<main class=\"body\">';\n  html += '<p class=\"lead\">' + (d.nome ? esc(d.nome) + ', ' : '') + (k === 'troca' ? 'escolha o pedido e os itens que quer trocar ou devolver.' : 'escolha o item com problema e conte o que aconteceu.') + '</p>';\n  html += '<h3 class=\"sec\">Pedido</h3><section class=\"card list\">' + pedidos.map(function (o) {\n    return '<button class=\"row sel' + (f.order === o.number ? ' on' : '') + '\" data-order=\"' + esc(o.number) + '\">' +\n      '<span class=\"radio\"></span><span class=\"row-m\"><span class=\"row-t\">' + esc(o.number) + '</span><span class=\"row-s\">' + dia(o.placedAt) + ' · ' + brl(o.total) + ' · ' + esc(STATUS[o.status] || o.status) + '</span></span></button>';\n  }).join('') + '</section>';\n  if (ped) {\n    html += '<h3 class=\"sec\">' + (k === 'troca' ? 'Itens' : 'Item com problema') + '</h3><section class=\"card list\">' + (ped.items || []).map(function (i) {\n      var on = !!f.items[i.name];\n      return '<button class=\"row sel' + (on ? ' on' : '') + '\" data-item=\"' + esc(i.name) + '\"><span class=\"checkbox\">' + (on ? '✓' : '') + '</span><span class=\"row-m\"><span class=\"row-t\">' + esc(i.name) + '</span><span class=\"row-s\">' + brl(i.price) + '</span></span></button>';\n    }).join('') + '</section>';\n    html += '<h3 class=\"sec\">Motivo</h3><section class=\"card list\">' + MOTIVOS[k].map(function (m) {\n      return '<button class=\"row sel' + (f.reason === m ? ' on' : '') + '\" data-reason=\"' + esc(m) + '\"><span class=\"radio\"></span><span class=\"row-m\"><span class=\"row-t\">' + esc(m) + '</span></span></button>';\n    }).join('') + '</section>';\n    html += '<h3 class=\"sec\">Detalhes</h3><section class=\"card pad\"><textarea class=\"ta\" data-details rows=\"3\" placeholder=\"' + (k === 'troca' ? 'Opcional' : 'O que aconteceu?') + '\">' + esc(f.details) + '</textarea></section>';\n    html += '<h3 class=\"sec\">Fotos <span class=\"sec-n\">' + s.fotos.length + '/3</span></h3><section class=\"fotos\">' +\n      s.fotos.map(function (u, idx) { return '<div class=\"foto\"><img src=\"' + u + '\" alt=\"\"><button class=\"foto-x\" data-rmfoto=\"' + idx + '\">×</button></div>'; }).join('') +\n      (s.fotos.length < 3 ? '<label class=\"foto add\"><input type=\"file\" accept=\"image/*\" data-file hidden><span>+</span><small>Adicionar</small></label>' : '') + '</section>';\n    html += '<h3 class=\"sec\">Como prefere resolver?</h3><section class=\"card list\">' + RESOLUCOES[k].map(function (r) {\n      return '<button class=\"row sel' + (f.resolution === r ? ' on' : '') + '\" data-res=\"' + esc(r) + '\"><span class=\"radio\"></span><span class=\"row-m\"><span class=\"row-t\">' + esc(r) + '</span></span></button>';\n    }).join('') + '</section>';\n  }\n  var nItens = Object.keys(f.items).filter(function (x) { return f.items[x]; }).length;\n  var pronto = ped && nItens > 0 && f.reason && f.resolution && !s.sending;\n  html += (s.err ? '<div class=\"err\">' + esc(s.err) + '</div>' : '');\n  html += '<div class=\"spacer\"></div>' + foot() + '</main>';\n  html += '<div class=\"dock\"><button class=\"btn primary\" data-submit' + (pronto ? '' : ' disabled') + '>' + (s.sending ? 'Enviando…' : 'Enviar solicitação') + '</button></div>';\n  return html;\n}\n\nfunction viewAtendimento(s) {\n  var d = s.data, a = d.atendimento || {};\n  var sub, fila = '';\n  if (a.status === 'fila') {\n    sub = '<span class=\"st wait\"></span>Na fila · ' + a.posicao + 'º';\n    fila = '<section class=\"card queue\"><div class=\"q-k\">Sua posição na fila</div><div class=\"q-v\">' + a.posicao + 'º</div>' +\n      '<div class=\"q-s\">' + (a.posicao <= 1 ? 'Você é o próximo a ser atendido.' : (a.posicao - 1) + (a.posicao - 1 === 1 ? ' pessoa' : ' pessoas') + ' na sua frente.') + ' Pode escrever enquanto espera.</div></section>';\n  } else if (a.status === 'atendimento') {\n    sub = '<span class=\"st on\"></span>' + esc(a.agentName || 'Atendente') + ' · atendendo';\n  } else {\n    sub = 'Atendimento encerrado';\n  }\n  var msgs = (d.mensagens || []).concat(s.pending || []);\n  var lista = msgs.map(function (m) {\n    if (m.author === 'sistema') return '<div class=\"sys\" data-key=\"' + esc(m._id) + '\">' + esc(m.text) + '</div>';\n    var me = m.author === 'cliente';\n    return '<div class=\"msg ' + (me ? 'me' : 'them') + (m._pending ? ' pend' : '') + '\" data-key=\"' + esc(m._id) + '\">' +\n      (!me ? '<div class=\"who\">' + esc(m.authorName || 'Atendente') + '</div>' : '') +\n      '<div class=\"bub\">' + esc(m.text).replace(/\\n/g, '<br>') + '<span class=\"t\">' + hora(m.createdAt) + '</span></div></div>';\n  }).join('');\n  var fechado = a.status === 'encerrado';\n  return '<div class=\"chat\">' + bar('Atendimento', sub) +\n    '<main class=\"body msgs\" data-msgs>' + fila + lista + (fechado ? '<div class=\"sys\">Este atendimento foi encerrado. Para falar de novo, escreva <b>atendente</b> no WhatsApp.</div>' + voltar() : '') + '</main>' +\n    (fechado ? '' : '<div class=\"composer\"><textarea class=\"in\" data-composer rows=\"1\" placeholder=\"Mensagem\">' + esc(draftTxt) + '</textarea><button class=\"send\" data-send aria-label=\"Enviar\">↑</button></div>') +\n    '</div>';\n}\n\nexport default {\n  css: `\n:host { display: block; height: 100%; }\n.app.live { position: fixed; top: 0; left: 0; right: 0; height: var(--app-h, 100dvh); min-height: 0; overflow: hidden; }\ntextarea, input, .bub, .done-p, .hero-v { -webkit-user-select: text; user-select: text; }\nbutton, a { -webkit-user-select: none; user-select: none; touch-action: manipulation; }\n* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }\n.app { height: 100%; min-height: 100%; display: flex; flex-direction: column; touch-action: manipulation; -webkit-text-size-adjust: 100%; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; background: var(--bg); color: var(--text); font-family: var(--font); -webkit-font-smoothing: antialiased; }\n.ios { --font: -apple-system, BlinkMacSystemFont, \"SF Pro Text\", \"Helvetica Neue\", Arial, sans-serif; --bg: #f2f2f7; --card: #ffffff; --text: #000000; --muted: #6c6c70; --sep: rgba(60,60,67,.18); --accent: #007aff; --onaccent: #ffffff; --fill: #e9e9eb; --me: #007aff; --onme: #ffffff; --radius: 10px; --btnr: 12px; --bar: rgba(249,249,249,.86); }\n.android { --font: Roboto, \"Google Sans\", \"Segoe UI\", system-ui, sans-serif; --bg: #f7f9fc; --card: #ffffff; --text: #1a1c1e; --muted: #43474e; --sep: transparent; --accent: #0b57d0; --onaccent: #ffffff; --fill: #e7ebf2; --me: #d3e3fd; --onme: #041e49; --radius: 16px; --btnr: 999px; --bar: #f7f9fc; }\n@media (prefers-color-scheme: dark) {\n  .ios { --bg: #000000; --card: #1c1c1e; --text: #ffffff; --muted: #8e8e93; --sep: rgba(84,84,88,.55); --accent: #0a84ff; --fill: #2c2c2e; --me: #0a84ff; --bar: rgba(22,22,24,.86); }\n  .android { --bg: #111318; --card: #1d2024; --text: #e2e2e6; --muted: #c3c6cf; --accent: #a8c7fa; --onaccent: #062e6f; --fill: #282a2f; --me: #0842a0; --onme: #d3e3fd; --bar: #111318; }\n  .logo { filter: none !important; }\n}\n.bar { position: relative; flex: none; z-index: 5; background: var(--bar); padding-top: env(safe-area-inset-top); }\n.ios .bar { -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px); border-bottom: .5px solid var(--sep); }\n.bar-in { min-height: 44px; display: flex; flex-direction: column; justify-content: center; padding: 6px 16px; }\n.ios .bar-in { align-items: center; text-align: center; }\n.ios .bar-t { font-size: 17px; font-weight: 600; letter-spacing: -.41px; }\n.android .bar-in { min-height: 64px; padding: 8px 20px; }\n.android .bar-t { font-size: 22px; font-weight: 400; }\n.bar-s { font-size: 12px; color: var(--muted); margin-top: 1px; display: flex; align-items: center; gap: 6px; }\n.ios .bar-s { justify-content: center; }\n.st { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }\n.st.on { background: #34c759; }\n.st.wait { background: #ff9f0a; }\n.body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior-y: contain; -webkit-overflow-scrolling: touch; padding: 8px 16px calc(24px + env(safe-area-inset-bottom)); }\n.lead { font-size: 15px; color: var(--muted); margin: 12px 4px 4px; line-height: 1.4; }\n.sec { font-size: 13px; font-weight: 400; color: var(--muted); margin: 22px 16px 7px; display: flex; justify-content: space-between; }\n.ios .sec { text-transform: uppercase; letter-spacing: -.08px; }\n.android .sec { font-size: 14px; font-weight: 500; color: var(--accent); margin: 24px 4px 8px; }\n.sec-n { text-transform: none; }\n.card { background: var(--card); border-radius: var(--radius); overflow: hidden; }\n.android .card { background: var(--card); box-shadow: 0 1px 2px rgba(0,0,0,.06); }\n.pad { padding: 14px 16px; }\n.list .row + .row { border-top: .5px solid var(--sep); }\n.ios .list .row + .row { margin-left: 16px; padding-left: 0; }\n.row { width: 100%; display: flex; align-items: center; gap: 12px; padding: 12px 16px; min-height: 48px; background: none; border: 0; color: inherit; font: inherit; text-align: left; }\n.android .row { min-height: 56px; }\nbutton.row { cursor: pointer; }\nbutton.row:active { background: var(--fill); }\n.row-m { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n.row-t { font-size: 16px; line-height: 1.3; }\n.row-s { font-size: 13px; color: var(--muted); }\n.row-v { font-size: 15px; color: var(--muted); white-space: nowrap; }\n.total .row-t, .total .row-v { font-weight: 600; color: var(--text); }\n.radio { width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--muted); flex: none; position: relative; opacity: .7; }\n.on .radio { border-color: var(--accent); opacity: 1; }\n.on .radio::after { content: \"\"; position: absolute; inset: 3px; border-radius: 50%; background: var(--accent); }\n.checkbox { width: 22px; height: 22px; border-radius: 6px; border: 2px solid var(--muted); flex: none; display: grid; place-items: center; font-size: 14px; font-weight: 700; color: var(--onaccent); opacity: .7; }\n.ios .checkbox { border-radius: 50%; }\n.on .checkbox { background: var(--accent); border-color: var(--accent); opacity: 1; }\n.ta { width: 100%; border: 0; background: transparent; color: var(--text); font: inherit; font-size: 16px; resize: none; outline: none; min-height: 64px; }\n.hero { padding: 20px 18px; margin-top: 12px; }\n.pill { display: inline-block; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px; background: var(--fill); color: var(--text); }\n.pill.entregue { background: rgba(52,199,89,.16); color: #1f8a3d; }\n.pill.em_transporte { background: rgba(0,122,255,.14); color: var(--accent); }\n.pill.cancelamento_solicitado { background: rgba(255,59,48,.14); color: #d70015; }\n.hero-k { margin-top: 16px; font-size: 13px; color: var(--muted); }\n.hero-v { font-size: 34px; font-weight: 700; letter-spacing: -.5px; margin-top: 2px; }\n.android .hero-v { font-weight: 400; font-size: 36px; }\n.hero-s { font-size: 14px; color: var(--muted); margin-top: 4px; }\n.tl { list-style: none; margin: 0; padding: 0; }\n.tl-i { position: relative; display: flex; gap: 14px; padding-bottom: 18px; }\n.tl-i:last-child { padding-bottom: 0; }\n.tl-i::before { content: \"\"; position: absolute; left: 6px; top: 16px; bottom: -2px; width: 2px; background: var(--fill); }\n.tl-i:last-child::before { display: none; }\n.tl-i.done::before { background: var(--accent); }\n.tl-i.now::before { background: var(--fill); }\n.dot { width: 14px; height: 14px; border-radius: 50%; background: var(--card); border: 2px solid var(--fill); flex: none; margin-top: 2px; position: relative; z-index: 1; }\n.done .dot { background: var(--accent); border-color: var(--accent); }\n.now .dot { box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent); }\n.tl-l { font-size: 15px; }\n.tl-i:not(.done) .tl-l { color: var(--muted); }\n.now .tl-l { font-weight: 600; }\n.tl-d { font-size: 12px; color: var(--muted); margin-top: 2px; }\n.fotos { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }\n.foto { position: relative; aspect-ratio: 1; border-radius: var(--radius); overflow: hidden; background: var(--card); }\n.foto img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.foto-x { position: absolute; top: 6px; right: 6px; width: 26px; height: 26px; border-radius: 50%; border: 0; background: rgba(0,0,0,.55); color: #fff; font-size: 18px; line-height: 1; }\n.foto.add { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; color: var(--accent); border: 1.5px dashed color-mix(in srgb, var(--accent) 45%, transparent); background: transparent; cursor: pointer; }\n.foto.add span { font-size: 28px; line-height: 1; }\n.foto.add small { font-size: 12px; }\n.btn { display: flex; align-items: center; justify-content: center; width: 100%; border: 0; font: inherit; text-decoration: none; cursor: pointer; }\n.ios .btn { height: 50px; border-radius: var(--btnr); font-size: 17px; font-weight: 600; }\n.android .btn { height: 48px; border-radius: var(--btnr); font-size: 14px; font-weight: 500; letter-spacing: .1px; }\n.btn.primary { background: var(--accent); color: var(--onaccent); }\n.btn.primary:disabled { opacity: .38; }\n.btn:active { opacity: .7; }\n.btn.ghost { background: transparent; color: var(--accent); margin-top: 18px; }\n.android .btn.ghost { border: 1px solid color-mix(in srgb, var(--muted) 45%, transparent); }\n.dock { position: relative; flex: none; padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); background: var(--bar); }\n.ios .dock { -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); border-top: .5px solid var(--sep); }\n.spacer { height: 8px; }\n.err { margin: 16px 4px 0; color: #d70015; font-size: 14px; }\n.empty { text-align: center; color: var(--muted); padding: 60px 0 10px; }\n.done-card { margin-top: 24px; padding: 28px 20px; text-align: center; }\n.check { width: 56px; height: 56px; margin: 0 auto 14px; border-radius: 50%; background: #34c759; color: #fff; font-size: 30px; display: grid; place-items: center; }\n.done-t { font-size: 20px; font-weight: 600; }\n.done-s { margin-top: 18px; font-size: 13px; color: var(--muted); }\n.done-p { font-size: 26px; font-weight: 700; letter-spacing: .5px; margin-top: 2px; font-variant-numeric: tabular-nums; }\n.done-x { margin-top: 14px; font-size: 14px; color: var(--muted); line-height: 1.4; }\n.foot { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 28px 0 8px; color: var(--muted); font-size: 12px; opacity: .8; }\n.logo { height: 13px; width: auto; filter: invert(1); }\n.chat { flex: 1; min-height: 0; display: flex; flex-direction: column; }\n.msgs { display: flex; flex-direction: column; gap: 6px; padding-top: 12px; padding-bottom: 12px; }\n.queue { padding: 20px 18px; text-align: center; margin-bottom: 10px; }\n.q-k { font-size: 13px; color: var(--muted); }\n.q-v { font-size: 48px; font-weight: 700; line-height: 1.1; margin: 4px 0; font-variant-numeric: tabular-nums; }\n.android .q-v { font-weight: 400; }\n.q-s { font-size: 14px; color: var(--muted); line-height: 1.4; }\n.sys { align-self: center; text-align: center; font-size: 12px; color: var(--muted); background: var(--fill); padding: 5px 12px; border-radius: 999px; margin: 6px 0; max-width: 90%; }\n.msg { display: flex; flex-direction: column; max-width: 80%; }\n.msg.me { align-self: flex-end; align-items: flex-end; }\n.msg.them { align-self: flex-start; }\n.who { font-size: 12px; color: var(--muted); margin: 0 0 2px 12px; }\n.bub { padding: 8px 12px; font-size: 16px; line-height: 1.35; border-radius: 18px; word-wrap: break-word; }\n.android .bub { border-radius: 20px; }\n.me .bub { background: var(--me); color: var(--onme); }\n.ios .me .bub { border-bottom-right-radius: 6px; }\n.android .me .bub { border-top-right-radius: 6px; }\n.them .bub { background: var(--card); color: var(--text); }\n.ios .them .bub { background: var(--fill); border-bottom-left-radius: 6px; }\n.android .them .bub { border-top-left-radius: 6px; }\n.t { font-size: 11px; opacity: .6; margin-left: 8px; float: right; margin-top: 6px; }\n.pend { opacity: .6; }\n.composer { flex: none; display: flex; align-items: flex-end; gap: 8px; padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); background: var(--bar); }\n.ios .composer { border-top: .5px solid var(--sep); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); }\n.in { flex: 1; resize: none; font: inherit; font-size: 16px; line-height: 1.3; color: var(--text); background: var(--card); border: .5px solid var(--sep); border-radius: 18px; padding: 8px 14px; outline: none; max-height: 120px; }\n.android .in { background: var(--fill); border: 0; border-radius: 24px; padding: 12px 18px; }\n.send { width: 36px; height: 36px; border-radius: 50%; border: 0; background: var(--accent); color: var(--onaccent); font-size: 20px; font-weight: 700; flex: none; cursor: pointer; }\n.android .send { width: 48px; height: 48px; border-radius: 16px; }\n.send:active { opacity: .7; }\n.load { flex: 1; display: grid; place-items: center; color: var(--muted); font-size: 15px; }\n.erro { padding: 80px 24px 24px; text-align: center; }\n.erro-t { font-size: 20px; font-weight: 600; margin-bottom: 8px; }\n.erro-s { color: var(--muted); font-size: 15px; line-height: 1.4; }\n\n.app { position: relative; }\n.call { position: absolute; inset: 0; z-index: 50; display: flex; flex-direction: column; justify-content: space-between; background: #1c1c1e; color: #fff; padding: calc(28px + env(safe-area-inset-top)) 24px calc(36px + env(safe-area-inset-bottom)); overflow: hidden; -webkit-user-select: none; user-select: none; }\n.android .call { background: #1b1b1f; }\n.call-top { text-align: center; position: relative; z-index: 2; }\n.call-av { width: 104px; height: 104px; border-radius: 50%; background: #3a3a3c; margin: 48px auto 18px; display: grid; place-items: center; font-size: 38px; font-weight: 600; }\n.call-n { font-size: 28px; font-weight: 600; letter-spacing: -.3px; }\n.android .call-n { font-weight: 400; }\n.call-s { font-size: 16px; opacity: .72; margin-top: 6px; font-variant-numeric: tabular-nums; }\n.call-btns { display: flex; justify-content: space-evenly; align-items: flex-start; position: relative; z-index: 2; }\n.cbw { display: flex; flex-direction: column; align-items: center; gap: 8px; font-size: 13px; opacity: .95; }\n.cb { width: 72px; height: 72px; border-radius: 50%; border: 0; background: rgba(255,255,255,.2); color: #fff; display: grid; place-items: center; cursor: pointer; -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); }\n.android .cb { border-radius: 24px; }\n.cb svg { width: 30px; height: 30px; }\n.cb.red { background: #ff3b30; }\n.cb.green { background: #34c759; }\n.cb.on { background: #fff; color: #1c1c1e; }\n.cb:active { opacity: .75; }\n.rv { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; background: #000; }\n.live-call:not(.vid) .rv { display: none; }\n.vid .call-top { text-align: left; margin-top: 0; }\n.vid .call-n { font-size: 20px; text-shadow: 0 1px 6px rgba(0,0,0,.55); }\n.vid .call-s { text-shadow: 0 1px 6px rgba(0,0,0,.55); }\n.lv { position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px; width: 104px; height: 148px; object-fit: cover; border-radius: 14px; z-index: 3; transform: scaleX(-1); background: #2c2c2e; box-shadow: 0 2px 12px rgba(0,0,0,.35); }\n.lv.off { opacity: 0; }\n.ring .call-av { animation: none; }\n`,\n\n  render: function (d2) {\n    var s = d2.state;\n    var cls = 'app ' + (s.plat || plat()) + ((d2.context && d2.context.isBuilder) ? '' : ' live');\n    if (!s.data && !s.erro) return '<div class=\"' + cls + '\"><div class=\"load\">Carregando…</div></div>';\n    if (s.erro) {\n      var msg = s.erro === 'expirado' ? 'Este link expirou. Peça um novo na conversa do WhatsApp.' : 'Não encontramos este link. Abra a tela de novo pelo botão na conversa do WhatsApp.';\n      return '<div class=\"' + cls + '\"><div class=\"erro\"><div class=\"erro-t\">Link indisponível</div><div class=\"erro-s\">' + msg + '</div>' + voltar() + '</div>' + foot() + '</div>';\n    }\n    var k = s.data.kind;\n    var inner = k === 'rastreio' ? viewRastreio(s) : (k === 'atendimento' ? viewAtendimento(s) : viewForm(s));\n    return '<div class=\"' + cls + '\">' + inner + (k === 'atendimento' ? viewCall(s) : '') + '</div>';\n  },\n\n  mounted: function (d2, root) {\n    var timer = null;\n    var host = (root && root.host) || root;\n    var builder = d2.context && d2.context.isBuilder;\n    if (!builder) {\n      hostSetup();\n      fitViewport(host);\n      var onVV = function () { fitViewport(host); };\n      if (window.visualViewport) { window.visualViewport.addEventListener('resize', onVV); window.visualViewport.addEventListener('scroll', onVV); }\n      window.addEventListener('orientationchange', onVV);\n      d2.onUnmount(function () {\n        if (window.visualViewport) { window.visualViewport.removeEventListener('resize', onVV); window.visualViewport.removeEventListener('scroll', onVV); }\n        window.removeEventListener('orientationchange', onVV);\n      });\n    }\n    d2.setState({ plat: plat(), form: { order: null, items: {}, reason: null, resolution: null, details: '' }, fotos: [], pending: [], sending: false, done: null, err: null });\n\n    function aplicar(r) {\n      if (!r || r.ok === false) { d2.setState({ erro: (r && r.error) || 'token' }); return; }\n      var patch = { data: r };\n      if (r.kind === 'atendimento') {\n        var ids = {};\n        (r.mensagens || []).forEach(function (m) { ids[m.text + '|' + m.author] = true; });\n        patch.pending = (d2.state.pending || []).filter(function (p) { return !ids[p.text + '|cliente']; });\n      }\n      d2.setState(patch);\n    }\n    function encerrarLocal() {\n      stopRtc();\n      d2.setState({ call: null });\n    }\n    function pollCall() {\n      callApi('poll').then(function (r) {\n        var c = r && r.chamada, cur = d2.state.call;\n        if (c && c.status === 'chamando' && !cur && !rtc.visto[c._id]) {\n          rtc.visto[c._id] = true;\n          rtc.offer = c.offer;\n          d2.setState({ call: { id: c._id, status: 'tocando', video: !!c.video, agent: c.agentName } });\n          try { if (navigator.vibrate) navigator.vibrate([500, 300, 500, 300, 500]); } catch (e) {}\n          return;\n        }\n        if (cur && (!c || c._id !== cur.id || /encerrada|perdida|recusada/.test(c.status))) encerrarLocal();\n      }).catch(function () {});\n    }\n    function sync() {\n      if (typeof document !== 'undefined' && document.hidden) return;\n      call('sync').then(aplicar).catch(function () {});\n      pollCall();\n    }\n    call('init').then(function (r) {\n      aplicar(r);\n      if (r && r.kind === 'atendimento') {\n        timer = setInterval(sync, 2500);\n        pollCall();\n        if (typeof document !== 'undefined') document.addEventListener('visibilitychange', sync);\n      }\n    }).catch(function () { d2.setState({ erro: 'token' }); });\n    d2.onUnmount(function () {\n      stopRtc();\n      if (timer) clearInterval(timer);\n      if (typeof document !== 'undefined') document.removeEventListener('visibilitychange', sync);\n    });\n\n    d2.on('[data-order]', 'click', function (e, el) {\n      d2.setState({ form: Object.assign({}, d2.state.form, { order: el.getAttribute('data-order'), items: {} }) });\n    });\n    d2.on('[data-item]', 'click', function (e, el) {\n      var f = d2.state.form, items = Object.assign({}, f.items), n = el.getAttribute('data-item');\n      items[n] = !items[n];\n      d2.setState({ form: Object.assign({}, f, { items: items }) });\n    });\n    d2.on('[data-reason]', 'click', function (e, el) {\n      d2.setState({ form: Object.assign({}, d2.state.form, { reason: el.getAttribute('data-reason') }) });\n    });\n    d2.on('[data-res]', 'click', function (e, el) {\n      d2.setState({ form: Object.assign({}, d2.state.form, { resolution: el.getAttribute('data-res') }) });\n    });\n    d2.on('[data-details]', 'input', function (e, el) {\n      d2.state.form.details = el.value;\n    });\n    d2.on('[data-file]', 'change', function (e, el) {\n      var file = el.files && el.files[0];\n      if (!file) return;\n      compress(file).then(function (u) {\n        d2.setState({ fotos: d2.state.fotos.concat([u]).slice(0, 3) });\n      }).catch(function () { d2.setState({ err: 'Não consegui ler essa foto. Tente outra.' }); });\n      el.value = '';\n    });\n    d2.on('[data-rmfoto]', 'click', function (e, el) {\n      var i = Number(el.getAttribute('data-rmfoto'));\n      d2.setState({ fotos: d2.state.fotos.filter(function (x, idx) { return idx !== i; }) });\n    });\n    d2.on('[data-submit]', 'click', function () {\n      var f = d2.state.form;\n      var ta = d2.$('[data-details]');\n      var itens = Object.keys(f.items).filter(function (x) { return f.items[x]; });\n      d2.setState({ sending: true, err: null });\n      call('submit', { orderNumber: f.order, items: itens, reason: f.reason, details: ta ? ta.value : f.details, resolution: f.resolution, fotos: d2.state.fotos })\n        .then(function (r) {\n          if (r && r.protocolo) d2.setState({ sending: false, done: r.protocolo });\n          else d2.setState({ sending: false, err: 'Não foi possível enviar agora. Tente de novo.' });\n        })\n        .catch(function () { d2.setState({ sending: false, err: 'Sem conexão. Tente de novo.' }); });\n    });\n\n    function enviar() {\n      var ta = d2.$('[data-composer]');\n      if (!ta) return;\n      var txt = ta.value.trim();\n      if (!txt) return;\n      ta.value = '';\n      draftTxt = '';\n      ta.style.height = '';\n      ta.focus();\n      var p = { _id: 'p' + Date.now(), author: 'cliente', text: txt, createdAt: new Date().toISOString(), _pending: true };\n      d2.setState({ pending: (d2.state.pending || []).concat([p]) });\n      call('send', { text: txt }).then(aplicar).catch(function () {});\n    }\n\n    d2.on('[data-call-accept]', 'click', function () {\n      var c = d2.state.call;\n      if (!c) return;\n      try { if (navigator.vibrate) navigator.vibrate(0); } catch (e) {}\n      d2.setState({ call: Object.assign({}, c, { status: 'conectando' }) });\n      callApi('ice').then(function (r) {\n        return navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true }, video: c.video ? { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 } } : false }).then(function (stream) {\n          rtc.local = stream;\n          rtc.remote = new MediaStream();\n          var pc = new RTCPeerConnection({ iceServers: (r && r.iceServers) || [] });\n          rtc.pc = pc;\n          stream.getTracks().forEach(function (t) { pc.addTrack(t, stream); });\n          pc.ontrack = function (ev) {\n            (ev.streams[0] ? ev.streams[0].getTracks() : [ev.track]).forEach(function (t) { if (rtc.remote.getTracks().indexOf(t) < 0) rtc.remote.addTrack(t); });\n            d2.setState({ _t: Date.now() });\n          };\n          pc.onconnectionstatechange = function () {\n            var cc = d2.state.call;\n            if (!cc) return;\n            if (pc.connectionState === 'connected' && cc.status !== 'ativa') {\n              d2.setState({ call: Object.assign({}, cc, { status: 'ativa', startedAt: Date.now(), err: null }) });\n              if (!rtc.tick) rtc.tick = setInterval(function () { d2.setState({ _t: Date.now() }); }, 1000);\n            } else if (pc.connectionState === 'failed') {\n              d2.setState({ call: Object.assign({}, cc, { err: 'Falha na conexão' }) });\n            }\n          };\n          d2.setState({ _t: Date.now() });\n          return pc.setRemoteDescription(JSON.parse(rtc.offer))\n            .then(function () { return pc.createAnswer(); })\n            .then(function (a) { return pc.setLocalDescription(a); })\n            .then(function () { return waitIce(pc); })\n            .then(function () { return callApi('answer', { callId: c.id, answer: JSON.stringify(pc.localDescription) }); });\n        });\n      }).catch(function () {\n        var cc = d2.state.call;\n        if (cc) d2.setState({ call: Object.assign({}, cc, { err: 'Sem acesso ao ' + (c.video ? 'microfone ou à câmera' : 'microfone') }) });\n      });\n    });\n    d2.on('[data-call-decline]', 'click', function () {\n      var c = d2.state.call;\n      try { if (navigator.vibrate) navigator.vibrate(0); } catch (e) {}\n      if (c) callApi('end', { callId: c.id }).catch(function () {});\n      encerrarLocal();\n    });\n    d2.on('[data-call-end]', 'click', function () {\n      var c = d2.state.call;\n      if (c) callApi('end', { callId: c.id }).catch(function () {});\n      encerrarLocal();\n    });\n    d2.on('[data-call-mute]', 'click', function () {\n      var c = d2.state.call;\n      if (!c || !rtc.local) return;\n      var m = !c.muted;\n      rtc.local.getAudioTracks().forEach(function (t) { t.enabled = !m; });\n      d2.setState({ call: Object.assign({}, c, { muted: m }) });\n    });\n    d2.on('[data-call-cam]', 'click', function () {\n      var c = d2.state.call;\n      if (!c || !rtc.local) return;\n      var off = !c.camOff;\n      rtc.local.getVideoTracks().forEach(function (t) { t.enabled = !off; });\n      d2.setState({ call: Object.assign({}, c, { camOff: off }) });\n    });\n    d2.on('[data-send]', 'click', enviar);\n    d2.on('[data-send]', 'pointerdown', function (e) { e.preventDefault(); });\n    d2.on('[data-composer]', 'focusin', function () {\n      setTimeout(function () { var b = d2.$('[data-msgs]'); if (b) b.scrollTop = b.scrollHeight; }, 250);\n    });\n    d2.on('[data-composer]', 'keydown', function (e) {\n      if (e.key === 'Enter' && !e.shiftKey && d2.state.plat !== 'ios' && d2.state.plat !== 'android') { e.preventDefault(); enviar(); }\n    });\n    d2.on('[data-composer]', 'input', function (e, el) {\n      draftTxt = el.value;\n      el.style.height = 'auto';\n      el.style.height = Math.min(el.scrollHeight, 120) + 'px';\n    });\n  },\n\n  updated: function (d2, root) {\n    var rv = d2.$('[data-rv]');\n    if (rv && rtc.remote && rv.srcObject !== rtc.remote) { rv.srcObject = rtc.remote; try { rv.play().catch(function () {}); } catch (e) {} }\n    var lv = d2.$('[data-lv]');\n    if (lv && rtc.local && lv.srcObject !== rtc.local) { lv.srcObject = rtc.local; try { lv.play().catch(function () {}); } catch (e) {} }\n    var comp = d2.$('[data-composer]');\n    if (comp) {\n      if (comp.value !== draftTxt && !(root && root.activeElement === comp)) comp.value = draftTxt;\n      if (draftTxt) { comp.style.height = 'auto'; comp.style.height = Math.min(comp.scrollHeight, 120) + 'px'; }\n    }\n    var box = d2.$('[data-msgs]');\n    if (!box) return;\n    var n = ((d2.state.data && d2.state.data.mensagens) || []).length + (d2.state.pending || []).length;\n    if (n !== lastN) {\n      lastN = n;\n      box.scrollTop = box.scrollHeight;\n    }\n  }\n};"}}}}}}}},{"_id":"6acfb30586ba87b276bc2a6f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"wd_console","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":"var API = 'https://gui.data2apis.com/rush/wd-agent-api';\nvar LOGO = 'https://gui.data2.media/file/S5mI9Xg4bjGTs7gkPQVLe';\nvar lastN = -1;\nvar lastSel = null;\nvar drafts = {};\n\nvar STATUS = { entregue: 'Entregue', em_transporte: 'Em transporte', em_separacao: 'Em separação', cancelamento_solicitado: 'Cancelamento solicitado' };\nvar KIND = { cancelamento: 'Cancelamento', troca: 'Troca/devolução', defeito: 'Defeito', generica: 'Ocorrência', avaliacao: 'Avaliação' };\n\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n  });\n}\nfunction lsGet(k) { try { return localStorage.getItem(k) || ''; } catch (e) { return ''; } }\nfunction lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) {} }\nfunction hora(d) { if (!d) return ''; var x = new Date(d); return String(x.getHours()).padStart(2, '0') + ':' + String(x.getMinutes()).padStart(2, '0'); }\nfunction dia(d) { if (!d) return ''; var x = new Date(d); return String(x.getDate()).padStart(2, '0') + '/' + String(x.getMonth() + 1).padStart(2, '0'); }\nfunction espera(d) {\n  if (!d) return '';\n  var m = Math.max(0, Math.floor((Date.now() - new Date(d).getTime()) / 60000));\n  if (m < 1) return 'agora';\n  if (m < 60) return m + ' min';\n  return Math.floor(m / 60) + ' h ' + (m % 60) + ' min';\n}\nfunction fone(p) {\n  var s = String(p || '');\n  if (s.length === 13) return '+' + s.slice(0, 2) + ' ' + s.slice(2, 4) + ' ' + s.slice(4, 9) + '-' + s.slice(9);\n  return '+' + s;\n}\nfunction brl(n) { try { return 'R$ ' + Number(n || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }); } catch (e) { return 'R$ ' + n; } }\nfunction iniciais(n) { var p = String(n || '?').trim().split(/\\s+/); return ((p[0] || '?')[0] + (p[1] ? p[1][0] : '')).toUpperCase(); }\n\nfunction call(op, extra) {\n  var body = Object.assign({ key: lsGet('wd_key'), agentName: lsGet('wd_agent'), op: op }, extra || {});\n  return fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })\n    .then(function (r) { return r.json(); })\n    .then(function (j) { return j && j.data ? j.data : j; });\n}\n\nvar CALL_API = 'https://gui.data2apis.com/rush/wd-call-api';\nvar rtc = { pc: null, local: null, remote: null, id: null, poll: null, tick: null, answered: false };\nfunction callApi(op, extra) {\n  var body = Object.assign({ key: lsGet('wd_key'), agentName: lsGet('wd_agent'), op: op }, extra || {});\n  return fetch(CALL_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })\n    .then(function (r) { return r.json(); })\n    .then(function (j) { return j && j.data ? j.data : j; });\n}\nfunction waitIce(pc) {\n  return new Promise(function (res) {\n    if (pc.iceGatheringState === 'complete') return res();\n    var t = setTimeout(res, 3500);\n    pc.addEventListener('icegatheringstatechange', function () {\n      if (pc.iceGatheringState === 'complete') { clearTimeout(t); res(); }\n    });\n  });\n}\nfunction stopRtc() {\n  try { if (rtc.pc) rtc.pc.close(); } catch (e) {}\n  try { if (rtc.local) rtc.local.getTracks().forEach(function (t) { t.stop(); }); } catch (e) {}\n  if (rtc.poll) { clearInterval(rtc.poll); rtc.poll = null; }\n  if (rtc.tick) { clearInterval(rtc.tick); rtc.tick = null; }\n  rtc.pc = null; rtc.local = null; rtc.remote = null; rtc.id = null; rtc.answered = false;\n}\nfunction dur(ms) { var x = Math.max(0, Math.floor(ms / 1000)); return Math.floor(x / 60) + ':' + String(x % 60).padStart(2, '0'); }\nfunction viewCallPanel(s) {\n  var c = s.call;\n  if (!c) return '';\n  var txt = c.err ? esc(c.err) : (c.status === 'preparando' ? 'Preparando…' : (c.status === 'chamando' ? 'Chamando o cliente…' : (c.status === 'ativa' ? 'Em chamada · ' + dur(Date.now() - (c.startedAt || Date.now())) : 'Conectando…')));\n  return '<div class=\"callp' + (c.video ? ' vid' : '') + '\">' +\n    '<video class=\"rv\" data-rv playsinline autoplay></video>' +\n    (c.video ? '<video class=\"lv' + (c.camOff ? ' off' : '') + '\" data-lv playsinline autoplay muted></video>' : '<div class=\"callp-voz\"><div class=\"callp-av\">' + esc(iniciais(s.atendimento && s.atendimento.name)) + '</div></div>') +\n    '<div class=\"callp-bar\"><span class=\"callp-s\">' + (c.video ? 'Vídeo' : 'Voz') + ' · ' + txt + '</span><span class=\"callp-b\">' +\n    '<button class=\"btn ghost sm\" data-c-mute>' + (c.muted ? 'Ativar som' : 'Mudo') + '</button>' +\n    (c.video ? '<button class=\"btn ghost sm\" data-c-cam>' + (c.camOff ? 'Ligar câmera' : 'Desligar câmera') + '</button>' : '') +\n    '<button class=\"btn red sm\" data-c-end>Encerrar</button></span></div></div>';\n}\n\nfunction viewLogin(s) {\n  return '<div class=\"login\"><div class=\"login-card\">' +\n    '<img class=\"logo lg\" src=\"' + LOGO + '\" alt=\"Data²\">' +\n    '<h1>Console de atendimento</h1><p>Demonstração do WhatsApp integrado à Data².</p>' +\n    '<label>Seu nome<input data-l-name value=\"' + esc(lsGet('wd_agent')) + '\" placeholder=\"Como o cliente vai te ver\"></label>' +\n    '<label>Chave de acesso<input data-l-key type=\"password\" value=\"' + esc(lsGet('wd_key')) + '\"></label>' +\n    (s.loginErr ? '<div class=\"err\">' + esc(s.loginErr) + '</div>' : '') +\n    '<button class=\"btn\" data-login>Entrar</button></div></div>';\n}\n\nfunction item(h, sel) {\n  var badge = h.unreadAgent > 0 && h.status !== 'encerrado' ? '<span class=\"badge\">' + h.unreadAgent + '</span>' : '';\n  var meta = h.status === 'fila' ? h.posicao + 'º · espera ' + espera(h.enteredAt) : (h.status === 'atendimento' ? esc(h.agentName || '') : (h.rating ? 'Nota ' + h.rating : 'Encerrado ' + hora(h.closedAt)));\n  return '<button class=\"qi' + (sel ? ' sel' : '') + '\" data-open=\"' + esc(h._id) + '\">' +\n    '<span class=\"av\">' + esc(iniciais(h.name)) + (h.status !== 'encerrado' ? '<i class=\"pres' + (h.online ? ' on' : '') + '\"></i>' : '') + '</span>' +\n    '<span class=\"qi-m\"><span class=\"qi-r\"><b>' + esc(h.name || fone(h.phone)) + '</b><small>' + meta + '</small></span>' +\n    '<span class=\"qi-r\"><span class=\"qi-l\">' + esc(h.lastMessageText || '') + '</span>' + badge + '</span></span></button>';\n}\n\nfunction viewFila(s) {\n  var q = s.fila || [];\n  var fila = q.filter(function (h) { return h.status === 'fila'; });\n  var at = q.filter(function (h) { return h.status === 'atendimento'; });\n  var enc = s.encerrados || [];\n  function grupo(t, arr, vazio) {\n    return '<div class=\"grp\"><div class=\"grp-t\">' + t + '<span>' + arr.length + '</span></div>' +\n      (arr.length ? arr.map(function (h) { return item(h, h._id === s.sel); }).join('') : '<div class=\"vazio\">' + vazio + '</div>') + '</div>';\n  }\n  return '<aside class=\"col fila\">' +\n    '<div class=\"col-h\"><img class=\"logo\" src=\"' + LOGO + '\" alt=\"Data²\"><span class=\"who-am-i\">' + esc(lsGet('wd_agent')) + ' · <a data-logout>sair</a></span></div>' +\n    '<div class=\"col-b\">' + grupo('Na fila', fila, 'Ninguém esperando.') + grupo('Em atendimento', at, 'Nenhuma conversa aberta.') + grupo('Encerrados', enc, 'Nada por aqui ainda.') + '</div></aside>';\n}\n\nfunction viewConversa(s) {\n  var a = s.atendimento;\n  if (!a) return '<section class=\"col conv\"><div class=\"placeholder\"><div class=\"ph-t\">Selecione uma conversa</div><div class=\"ph-s\">Quem pede atendente no WhatsApp entra na fila à esquerda.</div></div></section>';\n  var pres = a.status === 'encerrado' ? '' : (a.online ? '<span class=\"tag on\">Cliente na tela agora</span>' : '<span class=\"tag off\">Cliente fora da tela · a próxima resposta gera aviso no WhatsApp</span>');\n  var acao = a.status === 'fila' ? '<button class=\"btn\" data-accept>Assumir atendimento</button>' :\n    (a.status === 'atendimento' ? (s.call ? '' : '<button class=\"btn ghost\" data-c-start=\"0\">Ligar</button><button class=\"btn ghost\" data-c-start=\"1\">Vídeo</button>') + '<button class=\"btn ghost\" data-close>Encerrar atendimento</button>' : '<span class=\"tag\">Encerrado' + (a.rating ? ' · nota ' + a.rating : '') + '</span>');\n  var msgs = (s.mensagens || []).concat(s.pending || []).map(function (m) {\n    if (m.author === 'sistema') return '<div class=\"sys\" data-key=\"' + esc(m._id) + '\">' + esc(m.text) + ' · ' + hora(m.createdAt) + '</div>';\n    var lado = m.author === 'cliente' ? 'in' : 'out';\n    var rot = m.author === 'cliente' ? (m.channel === 'webview' ? 'Cliente · tela' : 'Cliente · WhatsApp') : (m.author === 'bot' ? 'Bot' : esc(m.authorName || 'Atendente'));\n    return '<div class=\"m ' + lado + ' ' + esc(m.author) + (m._pending ? ' pend' : '') + '\" data-key=\"' + esc(m._id) + '\"><div class=\"m-who\">' + rot + ' · ' + hora(m.createdAt) + '</div><div class=\"m-b\">' + esc(m.text).replace(/\\n/g, '<br>') + '</div></div>';\n  }).join('');\n  var composer = a.status === 'atendimento' ?\n    '<div class=\"composer\"><textarea data-text rows=\"2\" placeholder=\"Escreva para ' + esc((a.name || '').split(' ')[0]) + '… (Enter envia, Shift+Enter quebra linha)\">' + esc(drafts[s.sel] || '') + '</textarea><button class=\"btn\" data-send>Enviar</button></div>' :\n    (a.status === 'fila' ? '<div class=\"composer muted\">Assuma o atendimento para responder.</div>' : '');\n  return '<section class=\"col conv\">' +\n    '<div class=\"conv-h\"><div><div class=\"conv-n\">' + esc(a.name || fone(a.phone)) + '</div><div class=\"conv-s\">' + fone(a.phone) + ' · ' + esc(a.reason || '') + '</div></div><div class=\"conv-a\">' + pres + acao + '</div></div>' +\n    viewCallPanel(s) + '<div class=\"conv-b\" data-msgs>' + msgs + '</div>' + composer + '</section>';\n}\n\nfunction viewContexto(s) {\n  var a = s.atendimento;\n  if (!a) return '<aside class=\"col ctx\"></aside>';\n  var ped = (s.pedidos || []).map(function (o) {\n    return '<div class=\"ci\"><div class=\"ci-r\"><b>' + esc(o.number) + '</b><span class=\"st ' + esc(o.status) + '\">' + esc(STATUS[o.status] || o.status) + '</span></div>' +\n      '<div class=\"ci-s\">' + dia(o.placedAt) + ' · ' + brl(o.total) + ' · ' + (o.items || []).map(function (i) { return esc(i.name); }).join(', ') + '</div></div>';\n  }).join('') || '<div class=\"vazio\">Sem pedidos.</div>';\n  var oc = (s.ocorrencias || []).map(function (o) {\n    return '<div class=\"ci\"><div class=\"ci-r\"><b>' + esc(o.protocol) + '</b><span class=\"st\">' + esc(KIND[o.kind] || o.kind) + '</span></div>' +\n      '<div class=\"ci-s\">' + dia(o.createdAt) + (o.orderNumber ? ' · ' + esc(o.orderNumber) : '') + ' · ' + esc(o.reason || '') + ' · via ' + esc(o.channel === 'flow' ? 'Flow' : 'tela') + '</div></div>';\n  }).join('') || '<div class=\"vazio\">Nenhum protocolo.</div>';\n  return '<aside class=\"col ctx\"><div class=\"col-h\"><b>Contexto</b></div><div class=\"col-b\">' +\n    '<div class=\"grp-t\">Pedidos</div>' + ped + '<div class=\"grp-t\">Protocolos</div>' + oc +\n    (a.ratingComment ? '<div class=\"grp-t\">Comentário da avaliação</div><div class=\"ci\">' + esc(a.ratingComment) + '</div>' : '') +\n    '</div></aside>';\n}\n\nexport default {\n  css: `\n:host { display: block; height: 100%; }\n* { box-sizing: border-box; }\n.app { --bg: #f4f5f7; --panel: #ffffff; --text: #16181d; --muted: #6b7280; --line: #e6e8ec; --accent: #2457f5; --accent-soft: #e8eefe; --ok: #16a34a; --warn: #d97706; --in: #ffffff; --bot: #eef0f3; height: 100%; min-height: 100vh; background: var(--bg); color: var(--text); font: 14px/1.45 Inter, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif; }\n@media (prefers-color-scheme: dark) {\n  .app { --bg: #0e1013; --panel: #16191e; --text: #e8eaee; --muted: #8b93a1; --line: #252a31; --accent: #7a9cff; --accent-soft: #1b2440; --in: #1d2128; --bot: #1a1e24; }\n  .logo { filter: none !important; }\n}\n.logo { height: 16px; width: auto; filter: invert(1); display: block; }\n.logo.lg { height: 26px; margin-bottom: 22px; }\n.shell { display: grid; grid-template-columns: 320px 1fr 300px; height: 100vh; }\n.col { display: flex; flex-direction: column; min-height: 0; background: var(--panel); }\n.fila { border-right: 1px solid var(--line); }\n.ctx { border-left: 1px solid var(--line); }\n.conv { background: var(--bg); }\n.col-h { height: 56px; flex: none; display: flex; align-items: center; justify-content: space-between; padding: 0 16px; border-bottom: 1px solid var(--line); }\n.who-am-i { font-size: 12px; color: var(--muted); }\n.who-am-i a { color: var(--accent); cursor: pointer; }\n.col-b { flex: 1; overflow-y: auto; padding: 8px 0 16px; }\n.ctx .col-b { padding: 8px 16px 16px; }\n.grp { margin-bottom: 6px; }\n.grp-t { display: flex; justify-content: space-between; font-size: 11px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); padding: 14px 16px 6px; }\n.ctx .grp-t { padding: 16px 0 8px; }\n.vazio { font-size: 13px; color: var(--muted); padding: 4px 16px 8px; }\n.ctx .vazio { padding: 0 0 8px; }\n.qi { width: 100%; display: flex; gap: 12px; align-items: center; padding: 10px 16px; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; border-left: 3px solid transparent; }\n.qi:hover { background: var(--bg); }\n.qi.sel { background: var(--accent-soft); border-left-color: var(--accent); }\n.av { position: relative; width: 36px; height: 36px; border-radius: 50%; background: var(--bot); display: grid; place-items: center; font-size: 13px; font-weight: 600; flex: none; }\n.pres { position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px; border-radius: 50%; background: #9ca3af; border: 2px solid var(--panel); }\n.pres.on { background: var(--ok); }\n.qi-m { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }\n.qi-r { display: flex; justify-content: space-between; align-items: center; gap: 8px; }\n.qi-r b { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.qi-r small { font-size: 12px; color: var(--muted); white-space: nowrap; }\n.qi-l { font-size: 13px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.badge { background: var(--accent); color: #fff; font-size: 11px; font-weight: 600; border-radius: 999px; padding: 1px 7px; flex: none; }\n.placeholder { margin: auto; text-align: center; color: var(--muted); }\n.ph-t { font-size: 16px; color: var(--text); font-weight: 600; margin-bottom: 4px; }\n.conv-h { height: 64px; flex: none; display: flex; align-items: center; justify-content: space-between; padding: 0 20px; background: var(--panel); border-bottom: 1px solid var(--line); gap: 16px; }\n.conv-n { font-size: 16px; font-weight: 600; }\n.conv-s { font-size: 12px; color: var(--muted); }\n.conv-a { display: flex; align-items: center; gap: 10px; }\n.tag { font-size: 12px; padding: 4px 10px; border-radius: 999px; background: var(--bot); color: var(--muted); white-space: nowrap; }\n.tag.on { background: rgba(22,163,74,.12); color: var(--ok); }\n.tag.off { background: rgba(217,119,6,.12); color: var(--warn); }\n.conv-b { flex: 1; overflow-y: auto; padding: 20px 24px; display: flex; flex-direction: column; gap: 10px; }\n.m { max-width: 64%; display: flex; flex-direction: column; }\n.m.in { align-self: flex-start; }\n.m.out { align-self: flex-end; align-items: flex-end; }\n.m-who { font-size: 11px; color: var(--muted); margin: 0 4px 3px; }\n.m-b { padding: 9px 13px; border-radius: 12px; background: var(--in); border: 1px solid var(--line); white-space: normal; word-wrap: break-word; }\n.m.bot .m-b { background: var(--bot); color: var(--muted); border-color: transparent; }\n.m.atendente .m-b { background: var(--accent); color: #fff; border-color: transparent; }\n.pend { opacity: .55; }\n.sys { align-self: center; font-size: 12px; color: var(--muted); padding: 2px 10px; }\n.composer { flex: none; display: flex; gap: 10px; align-items: flex-end; padding: 12px 16px; background: var(--panel); border-top: 1px solid var(--line); }\n.composer.muted { color: var(--muted); font-size: 13px; justify-content: center; padding: 18px; }\n.composer textarea { flex: 1; resize: none; font: inherit; color: var(--text); background: var(--bg); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; outline: none; }\n.composer textarea:focus { border-color: var(--accent); }\n.btn { height: 36px; padding: 0 16px; border-radius: 8px; border: 0; background: var(--accent); color: #fff; font: inherit; font-weight: 600; cursor: pointer; white-space: nowrap; }\n.btn.ghost { background: transparent; color: var(--text); border: 1px solid var(--line); }\n.btn:active { opacity: .8; }\n.ci { padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; margin-bottom: 8px; background: var(--panel); }\n.ci-r { display: flex; justify-content: space-between; align-items: center; gap: 8px; }\n.ci-s { font-size: 12px; color: var(--muted); margin-top: 3px; }\n.st { font-size: 11px; padding: 2px 8px; border-radius: 999px; background: var(--bot); color: var(--muted); white-space: nowrap; }\n.st.entregue { color: var(--ok); }\n.st.em_transporte { color: var(--accent); }\n.st.cancelamento_solicitado { color: #dc2626; }\n.login { min-height: 100vh; display: grid; place-items: center; background: var(--bg); padding: 24px; }\n.login-card { width: 360px; background: var(--panel); border: 1px solid var(--line); border-radius: 14px; padding: 32px; }\n.login-card h1 { font-size: 20px; margin: 0 0 4px; }\n.login-card p { color: var(--muted); margin: 0 0 22px; font-size: 13px; }\n.login-card label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 14px; }\n.login-card input { display: block; width: 100%; margin-top: 6px; height: 40px; border-radius: 8px; border: 1px solid var(--line); background: var(--bg); color: var(--text); padding: 0 12px; font: inherit; outline: none; }\n.login-card input:focus { border-color: var(--accent); }\n.login-card .btn { width: 100%; height: 42px; margin-top: 6px; }\n.err { color: #dc2626; font-size: 13px; margin: -4px 0 10px; }\n\n.btn.sm { height: 30px; padding: 0 12px; font-size: 13px; }\n.btn.red { background: #dc2626; color: #fff; }\n.callp { position: relative; flex: none; height: 46vh; min-height: 260px; background: #0b0c0e; color: #fff; overflow: hidden; border-bottom: 1px solid var(--line); }\n.callp .rv { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; }\n.callp:not(.vid) .rv { display: none; }\n.callp .lv { position: absolute; top: 14px; right: 14px; width: 180px; height: 120px; object-fit: cover; border-radius: 10px; transform: scaleX(-1); background: #222; box-shadow: 0 2px 10px rgba(0,0,0,.4); }\n.callp .lv.off { opacity: 0; }\n.callp-voz { position: absolute; inset: 0 0 52px; display: grid; place-items: center; }\n.callp-av { width: 96px; height: 96px; border-radius: 50%; background: #2a2d33; display: grid; place-items: center; font-size: 34px; font-weight: 600; }\n.callp-bar { position: absolute; left: 0; right: 0; bottom: 0; display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 14px; background: linear-gradient(transparent, rgba(0,0,0,.65)); }\n.callp-s { font-size: 13px; font-variant-numeric: tabular-nums; }\n.callp-b { display: flex; gap: 8px; }\n.callp .btn.ghost { color: #fff; border-color: rgba(255,255,255,.35); }\n`,\n\n  render: function (d2) {\n    var s = d2.state;\n    if (!s.logado) return '<div class=\"app\">' + viewLogin(s) + '</div>';\n    return '<div class=\"app\"><div class=\"shell\">' + viewFila(s) + viewConversa(s) + viewContexto(s) + '</div></div>';\n  },\n\n  mounted: function (d2, root) {\n    var timer = null;\n    d2.setState({ logado: false, fila: [], encerrados: [], sel: null, atendimento: null, mensagens: [], pedidos: [], ocorrencias: [], pending: [] });\n\n    function aplicarConversa(r) {\n      if (!r || r.ok === false) return;\n      var patch = { atendimento: r.atendimento, mensagens: r.mensagens || [] };\n      if (r.pedidos && r.pedidos.length) patch.pedidos = r.pedidos;\n      if (r.ocorrencias && r.ocorrencias.length) patch.ocorrencias = r.ocorrencias;\n      var vistos = {};\n      (r.mensagens || []).forEach(function (m) { vistos[m.text + '|' + m.author] = true; });\n      patch.pending = (d2.state.pending || []).filter(function (p) { return !vistos[p.text + '|atendente']; });\n      d2.setState(patch);\n    }\n    function tick() {\n      call('queue').then(function (r) {\n        if (!r || r.ok === false) { d2.setState({ logado: false, loginErr: 'Chave inválida.' }); return; }\n        d2.setState({ fila: r.fila || [], encerrados: r.encerrados || [] });\n      }).catch(function () {});\n      if (d2.state.sel) call('messages', { handoffId: d2.state.sel }).then(aplicarConversa).catch(function () {});\n    }\n    function iniciar() {\n      d2.setState({ logado: true, loginErr: null });\n      tick();\n      if (timer) clearInterval(timer);\n      timer = setInterval(tick, 3000);\n    }\n    if (lsGet('wd_key') && lsGet('wd_agent')) iniciar();\n    d2.onUnmount(function () { if (timer) clearInterval(timer); stopRtc(); });\n\n    d2.on('[data-login]', 'click', function () {\n      var n = d2.$('[data-l-name]'), k = d2.$('[data-l-key]');\n      if (!n.value.trim() || !k.value.trim()) { d2.setState({ loginErr: 'Preencha nome e chave.' }); return; }\n      lsSet('wd_agent', n.value.trim());\n      lsSet('wd_key', k.value.trim());\n      call('queue').then(function (r) {\n        if (r && r.ok) iniciar(); else d2.setState({ loginErr: 'Chave inválida.' });\n      }).catch(function () { d2.setState({ loginErr: 'Sem conexão.' }); });\n    });\n    d2.on('[data-logout]', 'click', function () {\n      lsSet('wd_key', '');\n      if (timer) clearInterval(timer);\n      d2.setState({ logado: false, sel: null, atendimento: null });\n    });\n    d2.on('[data-open]', 'click', function (e, el) {\n      var id = el.getAttribute('data-open');\n      d2.setState({ sel: id, atendimento: null, mensagens: [], pedidos: [], ocorrencias: [], pending: [] });\n      call('open', { handoffId: id }).then(function (r) {\n        if (r && r.ok) d2.setState({ pedidos: r.pedidos || [], ocorrencias: r.ocorrencias || [] });\n        aplicarConversa(r);\n      }).catch(function () {});\n    });\n    d2.on('[data-accept]', 'click', function () {\n      call('accept', { handoffId: d2.state.sel }).then(function (r) { aplicarConversa(r); tick(); }).catch(function () {});\n    });\n    d2.on('[data-close]', 'click', function () {\n      call('close', { handoffId: d2.state.sel }).then(function (r) { aplicarConversa(r); tick(); }).catch(function () {});\n    });\n    function enviar() {\n      var ta = d2.$('[data-text]');\n      if (!ta) return;\n      var txt = ta.value.trim();\n      if (!txt) return;\n      ta.value = '';\n      drafts[d2.state.sel] = '';\n      var p = { _id: 'p' + Date.now(), author: 'atendente', authorName: lsGet('wd_agent'), text: txt, createdAt: new Date().toISOString(), _pending: true };\n      d2.setState({ pending: (d2.state.pending || []).concat([p]) });\n      call('send', { handoffId: d2.state.sel, text: txt }).then(aplicarConversa).catch(function () {});\n    }\n\n    function fimChamada(msg) {\n      stopRtc();\n      d2.setState({ call: msg ? { status: 'fim', err: msg } : null });\n      if (msg) setTimeout(function () { if (d2.state.call && d2.state.call.status === 'fim') d2.setState({ call: null }); }, 3500);\n    }\n    d2.on('[data-c-start]', 'click', function (e, el) {\n      var video = el.getAttribute('data-c-start') === '1';\n      var hid = d2.state.sel;\n      if (!hid || d2.state.call) return;\n      d2.setState({ call: { status: 'preparando', video: video } });\n      callApi('ice', { handoffId: hid }).then(function (r) {\n        return navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: true, noiseSuppression: true }, video: video ? { width: { ideal: 1280 }, height: { ideal: 720 } } : false }).then(function (stream) {\n          rtc.local = stream;\n          rtc.remote = new MediaStream();\n          var pc = new RTCPeerConnection({ iceServers: (r && r.iceServers) || [] });\n          rtc.pc = pc;\n          stream.getTracks().forEach(function (t) { pc.addTrack(t, stream); });\n          if (!video) pc.addTransceiver('video', { direction: 'recvonly' });\n          pc.ontrack = function (ev) {\n            (ev.streams[0] ? ev.streams[0].getTracks() : [ev.track]).forEach(function (t) { if (rtc.remote.getTracks().indexOf(t) < 0) rtc.remote.addTrack(t); });\n            d2.setState({ _t: Date.now() });\n          };\n          pc.onconnectionstatechange = function () {\n            var cc = d2.state.call;\n            if (!cc) return;\n            if (pc.connectionState === 'connected' && cc.status !== 'ativa') {\n              d2.setState({ call: Object.assign({}, cc, { status: 'ativa', startedAt: Date.now(), err: null }) });\n              if (!rtc.tick) rtc.tick = setInterval(function () { d2.setState({ _t: Date.now() }); }, 1000);\n            } else if (pc.connectionState === 'failed') {\n              d2.setState({ call: Object.assign({}, cc, { err: 'Falha na conexão' }) });\n            }\n          };\n          d2.setState({ _t: Date.now() });\n          return pc.createOffer()\n            .then(function (o) { return pc.setLocalDescription(o); })\n            .then(function () { return waitIce(pc); })\n            .then(function () { return callApi('start', { handoffId: hid, video: video, offer: JSON.stringify(pc.localDescription) }); })\n            .then(function (res) {\n              var ch = res && res.chamada;\n              if (!ch) { fimChamada('Não foi possível iniciar a chamada'); return; }\n              rtc.id = ch._id;\n              d2.setState({ call: Object.assign({}, d2.state.call, { status: 'chamando', id: ch._id }) });\n              rtc.poll = setInterval(function () {\n                callApi('poll', { handoffId: hid, callId: rtc.id }).then(function (p) {\n                  var x = p && p.chamada;\n                  if (!x || !rtc.pc) return;\n                  if (x.answer && !rtc.answered) {\n                    rtc.answered = true;\n                    rtc.pc.setRemoteDescription(JSON.parse(x.answer)).catch(function () { fimChamada('Erro ao conectar'); });\n                    d2.setState({ call: Object.assign({}, d2.state.call, { status: d2.state.call.status === 'ativa' ? 'ativa' : 'conectando' }) });\n                  }\n                  if (x.status === 'recusada') fimChamada('O cliente recusou a chamada');\n                  else if (x.status === 'perdida') fimChamada('O cliente não atendeu');\n                  else if (x.status === 'encerrada') fimChamada('Chamada encerrada');\n                }).catch(function () {});\n              }, 1500);\n            });\n        });\n      }).catch(function () { fimChamada('Sem acesso ao microfone ou à câmera deste computador'); });\n    });\n    d2.on('[data-c-end]', 'click', function () {\n      if (rtc.id) callApi('end', { handoffId: d2.state.sel, callId: rtc.id }).catch(function () {});\n      fimChamada(null);\n    });\n    d2.on('[data-c-mute]', 'click', function () {\n      var c = d2.state.call;\n      if (!c || !rtc.local) return;\n      var m = !c.muted;\n      rtc.local.getAudioTracks().forEach(function (t) { t.enabled = !m; });\n      d2.setState({ call: Object.assign({}, c, { muted: m }) });\n    });\n    d2.on('[data-c-cam]', 'click', function () {\n      var c = d2.state.call;\n      if (!c || !rtc.local) return;\n      var off = !c.camOff;\n      rtc.local.getVideoTracks().forEach(function (t) { t.enabled = !off; });\n      d2.setState({ call: Object.assign({}, c, { camOff: off }) });\n    });\n    d2.on('[data-send]', 'click', enviar);\n    d2.on('[data-text]', 'input', function (e, el) { drafts[d2.state.sel] = el.value; });\n    d2.on('[data-text]', 'keydown', function (e) {\n      if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); enviar(); }\n    });\n  },\n\n  updated: function (d2, root) {\n    var rv = d2.$('[data-rv]');\n    if (rv && rtc.remote && rv.srcObject !== rtc.remote) { rv.srcObject = rtc.remote; try { rv.play().catch(function () {}); } catch (e) {} }\n    var lv = d2.$('[data-lv]');\n    if (lv && rtc.local && lv.srcObject !== rtc.local) { lv.srcObject = rtc.local; try { lv.play().catch(function () {}); } catch (e) {} }\n    var tx = d2.$('[data-text]');\n    var dr = drafts[d2.state.sel] || '';\n    if (tx && tx.value !== dr && !(root && root.activeElement === tx)) tx.value = dr;\n    var box = d2.$('[data-msgs]');\n    if (!box) return;\n    var n = (d2.state.mensagens || []).length + (d2.state.pending || []).length;\n    if (n !== lastN || d2.state.sel !== lastSel) {\n      lastN = n;\n      lastSel = d2.state.sel;\n      box.scrollTop = box.scrollHeight;\n    }\n  }\n};"}}}}}}}},{"_id":"6ad9654f920e26afae5cb7f5","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-sidebar","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: `.sb{height:100%;box-sizing:border-box;background:#0f172a;color:#cbd5e1;padding:24px 16px;font-family:Inter,system-ui,sans-serif;display:flex;flex-direction:column;gap:28px}\n  .logo{display:flex;align-items:center;gap:10px;color:#fff;font-weight:700;font-size:18px;padding:0 8px}\n  .dot{width:28px;height:28px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#22d3ee)}\n  .nav{display:flex;flex-direction:column;gap:4px}\n  .it{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:10px;font-size:14px;cursor:pointer;transition:background .15s}\n  .it:hover{background:#1e293b}\n  .it.on{background:#1e293b;color:#fff}\n  .it .g{width:20px;text-align:center}\n  .foot{margin-top:auto;display:flex;align-items:center;gap:10px;padding:12px;border-radius:12px;background:#1e293b}\n  .av{width:34px;height:34px;border-radius:50%;background:#6366f1;color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:13px}\n  .nm{color:#fff;font-size:13px;font-weight:600} .rl{font-size:11px;color:#94a3b8}`,\n  render(d2){\n    const items = [['▦','Overview',1],['$','Revenue'],['🛒','Orders'],['👥','Customers'],['📦','Products'],['⚙','Settings']]\n    return `<div class=\"sb\">\n      <div class=\"logo\"><div class=\"dot\"></div>Pulse</div>\n      <div class=\"nav\">${items.map(i => `<div class=\"it ${i[2] ? 'on' : ''}\" data-key=\"${i[1]}\"><span class=\"g\">${i[0]}</span>${i[1]}</div>`).join('')}</div>\n      <div class=\"foot\"><div class=\"av\">AM</div><div><div class=\"nm\">Ana Martins</div><div class=\"rl\">Admin</div></div></div>\n    </div>`\n  },\n}"}}}}}}}},{"_id":"6ad9655019fcc8af09c54b87","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-kpi","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: `.kpi{height:100%;box-sizing:border-box;background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:20px;display:flex;flex-direction:column;justify-content:space-between;font-family:Inter,system-ui,sans-serif}\n  .top{display:flex;justify-content:space-between;align-items:center}\n  .l{font-size:13px;color:#6b7280;font-weight:500}\n  .ic{width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px}\n  .v{font-size:30px;font-weight:700;color:#111827;letter-spacing:-.02em}\n  .d{font-size:12px;font-weight:600;display:flex;align-items:center;gap:6px}\n  .up{color:#059669} .dn{color:#dc2626} .sub{color:#9ca3af;font-weight:400}`,\n  render(d2){\n    const v = Number(d2.param('value', 0)) || 0\n    const dl = Number(d2.param('delta', 0)) || 0\n    const tone = d2.param('tone', '#6366f1')\n    const shown = d2.param('money', false) ? d2.fmt.currency(v, 'USD') : d2.fmt.number(v)\n    const label = String(d2.param('label', '')).replace(/</g, '&lt;')\n    return `<div class=\"kpi\">\n      <div class=\"top\"><span class=\"l\">${label}</span><span class=\"ic\" style=\"background:${tone}22;color:${tone}\">${d2.param('glyph', '●')}</span></div>\n      <div class=\"v\">${shown}</div>\n      <div class=\"d ${dl >= 0 ? 'up' : 'dn'}\">${dl >= 0 ? '▲' : '▼'} ${Math.abs(dl)}%<span class=\"sub\">vs last month</span></div>\n    </div>`\n  },\n}"}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}},"delta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"delta"}}}},"glyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"glyph"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}},"money":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"money"}}}}}}}}}},{"_id":"6ad9655e920e26afae5cb832","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-chart","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: `.card{height:100%;box-sizing:border-box;background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:20px 24px;display:flex;flex-direction:column;font-family:Inter,system-ui,sans-serif}\n  .hd{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px}\n  .t{font-size:16px;font-weight:600;color:#111827} .s{font-size:12px;color:#6b7280;margin-top:2px}\n  .tot{font-size:22px;font-weight:700;color:#111827;text-align:right} .tg{font-size:12px;color:#059669;font-weight:600;text-align:right}\n  .pl{flex:1;min-height:0} svg{width:100%;height:100%;display:block}\n  .ax{font-size:11px;fill:#9ca3af} .gl{stroke:#f1f5f9;stroke-width:1}`,\n  render(d2){\n    const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']\n    const vals = [42,48,45,56,61,58,67,72,69,78,85,94]\n    const W = 800, H = 260, pad = 28, max = 100\n    const x = i => pad + i * (W - pad * 2) / (vals.length - 1)\n    const y = v => H - 24 - (v / max) * (H - 44)\n    const line = vals.map((v, i) => (i ? 'L' : 'M') + x(i).toFixed(1) + ' ' + y(v).toFixed(1)).join(' ')\n    const area = line + ` L${x(vals.length - 1)} ${H - 24} L${x(0)} ${H - 24} Z`\n    const grid = [0, 25, 50, 75, 100].map(g => `<line class=\"gl\" x1=\"${pad}\" x2=\"${W - pad}\" y1=\"${y(g)}\" y2=\"${y(g)}\"/><text class=\"ax\" x=\"0\" y=\"${y(g) + 4}\">${g}k</text>`).join('')\n    const lbls = months.map((m, i) => `<text class=\"ax\" x=\"${x(i)}\" y=\"${H - 6}\" text-anchor=\"middle\">${m}</text>`).join('')\n    const pts = vals.map((v, i) => `<circle cx=\"${x(i)}\" cy=\"${y(v)}\" r=\"3.5\" fill=\"#fff\" stroke=\"#6366f1\" stroke-width=\"2\"><title>${months[i]}: $${v}k</title></circle>`).join('')\n    return `<div class=\"card\">\n      <div class=\"hd\"><div><div class=\"t\">Revenue</div><div class=\"s\">Last 12 months</div></div>\n      <div><div class=\"tot\">$${vals.reduce((a, b) => a + b, 0)}k</div><div class=\"tg\">▲ 18.4%</div></div></div>\n      <div class=\"pl\"><svg viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"none\">\n        <defs><linearGradient id=\"ag\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"#6366f1\" stop-opacity=\".25\"/><stop offset=\"1\" stop-color=\"#6366f1\" stop-opacity=\"0\"/></linearGradient></defs>\n        ${grid}<path d=\"${area}\" fill=\"url(#ag)\"/><path d=\"${line}\" fill=\"none\" stroke=\"#6366f1\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/>${pts}${lbls}\n      </svg></div>\n    </div>`\n  },\n}"}}}}}}}},{"_id":"6ad9655e19fcc8af09c54baa","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-orders","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: `.card{height:100%;box-sizing:border-box;background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:20px 24px;font-family:Inter,system-ui,sans-serif;overflow:hidden}\n  .t{font-size:16px;font-weight:600;color:#111827;margin-bottom:12px}\n  table{width:100%;border-collapse:collapse;font-size:13px}\n  th{text-align:left;color:#6b7280;font-weight:500;padding:8px 0;border-bottom:1px solid #f1f5f9}\n  td{padding:11px 0;border-bottom:1px solid #f8fafc;color:#111827}\n  td.m{color:#6b7280} td.a{text-align:right;font-weight:600} th.a{text-align:right}\n  .b{display:inline-block;padding:3px 10px;border-radius:999px;font-size:11px;font-weight:600}\n  .paid{background:#d1fae5;color:#047857} .pend{background:#fef3c7;color:#b45309} .fail{background:#fee2e2;color:#b91c1c}`,\n  render(d2){\n    const rows = [\n      ['#1042','Marina Costa','May 12','paid','Paid',1240],\n      ['#1041','Lucas Ferreira','May 12','pend','Pending',860],\n      ['#1040','Julia Rocha','May 11','paid','Paid',2310],\n      ['#1039','Pedro Alves','May 11','fail','Failed',430],\n      ['#1038','Carla Nunes','May 10','paid','Paid',1575],\n      ['#1037','Rafael Lima','May 10','paid','Paid',990],\n    ]\n    return `<div class=\"card\"><div class=\"t\">Recent orders</div>\n      <table><thead><tr><th>Order</th><th>Customer</th><th>Date</th><th>Status</th><th class=\"a\">Amount</th></tr></thead>\n      <tbody>${rows.map(r => `<tr data-key=\"${r[0]}\"><td class=\"m\">${r[0]}</td><td>${r[1]}</td><td class=\"m\">${r[2]}</td><td><span class=\"b ${r[3]}\">${r[4]}</span></td><td class=\"a\">${d2.fmt.currency(r[5], 'USD')}</td></tr>`).join('')}</tbody></table></div>`\n  },\n}"}}}}}}}},{"_id":"6ad966201d8aedc7cced83f1","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dashboard-app","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: `.app{display:grid;grid-template-columns:240px 1fr;height:100%;width:100%;background:#f4f5f9;font-family:Inter,system-ui,sans-serif;color:#111827;box-sizing:border-box}\n  .sb{background:#0f172a;color:#cbd5e1;padding:24px 16px;display:flex;flex-direction:column;gap:28px}\n  .logo{display:flex;align-items:center;gap:10px;color:#fff;font-weight:700;font-size:18px;padding:0 8px}\n  .dot{width:28px;height:28px;border-radius:8px;background:linear-gradient(135deg,#6366f1,#22d3ee)}\n  .nav{display:flex;flex-direction:column;gap:4px}\n  .it{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:10px;font-size:14px;cursor:pointer;transition:background .15s}\n  .it:hover{background:#1e293b} .it.on{background:#1e293b;color:#fff} .it .g{width:20px;text-align:center}\n  .user{margin-top:auto;display:flex;align-items:center;gap:10px;padding:12px;border-radius:12px;background:#1e293b}\n  .av{width:34px;height:34px;border-radius:50%;background:#6366f1;color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:13px}\n  .nm{color:#fff;font-size:13px;font-weight:600} .rl{font-size:11px;color:#94a3b8}\n  .main{padding:28px 32px;display:grid;grid-template-rows:auto 130px 1fr 1fr;gap:20px;min-height:0;min-width:0;box-sizing:border-box}\n  .hd{display:flex;justify-content:space-between;align-items:center}\n  .h1{font-size:28px;font-weight:700;letter-spacing:-.02em} .sub{font-size:13px;color:#6b7280;margin-top:2px}\n  .rng{display:flex;gap:4px;background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:4px}\n  .rng span{padding:6px 14px;font-size:12px;font-weight:600;border-radius:7px;color:#6b7280;cursor:pointer}\n  .rng span.on{background:#111827;color:#fff}\n  .kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}\n  .card{background:#fff;border:1px solid #e5e7eb;border-radius:16px;padding:20px;box-sizing:border-box;min-height:0;min-width:0;overflow:hidden}\n  .kpi{display:flex;flex-direction:column;justify-content:space-between}\n  .top{display:flex;justify-content:space-between;align-items:center}\n  .l{font-size:13px;color:#6b7280;font-weight:500}\n  .ic{width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:18px}\n  .v{font-size:30px;font-weight:700;letter-spacing:-.02em}\n  .d{font-size:12px;font-weight:600;display:flex;gap:6px} .up{color:#059669} .dn{color:#dc2626} .d i{font-style:normal;color:#9ca3af;font-weight:400}\n  .chart{display:flex;flex-direction:column}\n  .ch{display:flex;justify-content:space-between;margin-bottom:10px}\n  .t{font-size:16px;font-weight:600} .tot{font-size:22px;font-weight:700;text-align:right} .tg{font-size:12px;color:#059669;font-weight:600;text-align:right}\n  .pl{flex:1;min-height:0} svg{width:100%;height:100%;display:block}\n  .ax{font-size:11px;fill:#9ca3af} .gl{stroke:#f1f5f9;stroke-width:1}\n  table{width:100%;border-collapse:collapse;font-size:13px;margin-top:8px}\n  th{text-align:left;color:#6b7280;font-weight:500;padding:8px 0;border-bottom:1px solid #f1f5f9}\n  td{padding:10px 0;border-bottom:1px solid #f8fafc} td.m{color:#6b7280} td.a,th.a{text-align:right} td.a{font-weight:600}\n  .b{display:inline-block;padding:3px 10px;border-radius:999px;font-size:11px;font-weight:600}\n  .paid{background:#d1fae5;color:#047857} .pend{background:#fef3c7;color:#b45309} .fail{background:#fee2e2;color:#b91c1c}`,\n  render(d2){\n    const nav = [['▦','Overview'],['$','Revenue'],['🛒','Orders'],['👥','Customers'],['📦','Products'],['⚙','Settings']]\n    const active = d2.state.nav || 'Overview'\n    const range = d2.state.range || '12M'\n    const all = [42,48,45,56,61,58,67,72,69,78,85,94]\n    const vals = range === '12M' ? all : range === '6M' ? all.slice(6) : all.slice(9)\n    const mAll = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']\n    const months = mAll.slice(12 - vals.length)\n    const kpis = [\n      ['Total revenue', d2.fmt.currency(94200, 'USD'), 18.4, '$', '#6366f1'],\n      ['Orders', d2.fmt.number(1284), 9.2, '🛒', '#0ea5e9'],\n      ['New customers', d2.fmt.number(342), 4.1, '👥', '#10b981'],\n      ['Refunds', d2.fmt.number(27), -6.3, '↩', '#f59e0b'],\n    ]\n    const W = 800, H = 240, pad = 28\n    const x = i => pad + i * (W - pad * 2) / (vals.length - 1)\n    const y = v => H - 24 - (v / 100) * (H - 44)\n    const line = vals.map((v, i) => (i ? 'L' : 'M') + x(i).toFixed(1) + ' ' + y(v).toFixed(1)).join(' ')\n    const area = line + ` L${x(vals.length - 1)} ${H - 24} L${x(0)} ${H - 24} Z`\n    const grid = [0, 25, 50, 75, 100].map(g => `<line class=\"gl\" x1=\"${pad}\" x2=\"${W - pad}\" y1=\"${y(g)}\" y2=\"${y(g)}\"/><text class=\"ax\" x=\"0\" y=\"${y(g) + 4}\">${g}k</text>`).join('')\n    const lbls = months.map((m, i) => `<text class=\"ax\" x=\"${x(i)}\" y=\"${H - 6}\" text-anchor=\"middle\">${m}</text>`).join('')\n    const pts = vals.map((v, i) => `<circle cx=\"${x(i)}\" cy=\"${y(v)}\" r=\"3.5\" fill=\"#fff\" stroke=\"#6366f1\" stroke-width=\"2\"><title>${months[i]}: $${v}k</title></circle>`).join('')\n    const rows = [\n      ['#1042','Marina Costa','May 12','paid','Paid',1240],['#1041','Lucas Ferreira','May 12','pend','Pending',860],\n      ['#1040','Julia Rocha','May 11','paid','Paid',2310],['#1039','Pedro Alves','May 11','fail','Failed',430],\n      ['#1038','Carla Nunes','May 10','paid','Paid',1575],\n    ]\n    return `<div class=\"app\">\n      <aside class=\"sb\">\n        <div class=\"logo\"><div class=\"dot\"></div>Pulse</div>\n        <div class=\"nav\">${nav.map(n => `<div class=\"it ${n[1] === active ? 'on' : ''}\" data-nav=\"${n[1]}\" data-key=\"${n[1]}\"><span class=\"g\">${n[0]}</span>${n[1]}</div>`).join('')}</div>\n        <div class=\"user\"><div class=\"av\">AM</div><div><div class=\"nm\">Ana Martins</div><div class=\"rl\">Admin</div></div></div>\n      </aside>\n      <main class=\"main\">\n        <div class=\"hd\"><div><div class=\"h1\">${active}</div><div class=\"sub\">Store performance</div></div>\n          <div class=\"rng\">${['3M','6M','12M'].map(r => `<span class=\"${r === range ? 'on' : ''}\" data-range=\"${r}\" data-key=\"${r}\">${r}</span>`).join('')}</div></div>\n        <div class=\"kpis\">${kpis.map(k => `<div class=\"card kpi\" data-key=\"${k[0]}\">\n          <div class=\"top\"><span class=\"l\">${k[0]}</span><span class=\"ic\" style=\"background:${k[4]}22;color:${k[4]}\">${k[3]}</span></div>\n          <div class=\"v\">${k[1]}</div>\n          <div class=\"d ${k[2] >= 0 ? 'up' : 'dn'}\">${k[2] >= 0 ? '▲' : '▼'} ${Math.abs(k[2])}%<i>vs last period</i></div></div>`).join('')}</div>\n        <div class=\"card chart\">\n          <div class=\"ch\"><div class=\"t\">Revenue</div><div><div class=\"tot\">$${vals.reduce((a, b) => a + b, 0)}k</div><div class=\"tg\">▲ 18.4%</div></div></div>\n          <div class=\"pl\"><svg viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"none\">\n            <defs><linearGradient id=\"ag\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"#6366f1\" stop-opacity=\".25\"/><stop offset=\"1\" stop-color=\"#6366f1\" stop-opacity=\"0\"/></linearGradient></defs>\n            ${grid}<path d=\"${area}\" fill=\"url(#ag)\"/><path d=\"${line}\" fill=\"none\" stroke=\"#6366f1\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/>${pts}${lbls}</svg></div>\n        </div>\n        <div class=\"card\"><div class=\"t\">Recent orders</div>\n          <table><thead><tr><th>Order</th><th>Customer</th><th>Date</th><th>Status</th><th class=\"a\">Amount</th></tr></thead>\n          <tbody>${rows.map(r => `<tr data-key=\"${r[0]}\"><td class=\"m\">${r[0]}</td><td>${r[1]}</td><td class=\"m\">${r[2]}</td><td><span class=\"b ${r[3]}\">${r[4]}</span></td><td class=\"a\">${d2.fmt.currency(r[5], 'USD')}</td></tr>`).join('')}</tbody></table></div>\n      </main>\n    </div>`\n  },\n  mounted(d2){\n    d2.on('[data-nav]', 'click', (e, el) => { d2.state.nav = el.getAttribute('data-nav') })\n    d2.on('[data-range]', 'click', (e, el) => { d2.state.range = el.getAttribute('data-range') })\n  },\n}"}}}}}}}},{"_id":"6ad968ffa6663f5d5a814966","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"test-dashboard","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block}\n    .db{box-sizing:border-box;width:100%;height:100%;padding:32px 36px;background:radial-gradient(900px 420px at 85% -10%,rgba(79,140,255,.14),transparent 60%),radial-gradient(700px 380px at -5% 105%,rgba(167,139,250,.09),transparent 60%),#0c0f15;color:#e8ebf2;font-family:Inter,system-ui,sans-serif;display:flex;flex-direction:column;gap:20px;overflow:auto}\n    .top{display:flex;align-items:center;justify-content:space-between;padding-bottom:18px;border-bottom:1px solid #1d2331}\n    .brand{display:flex;align-items:center;gap:14px}\n    .logo{width:42px;height:42px;border-radius:12px;background:linear-gradient(135deg,#4f8cff,#a78bfa);display:flex;align-items:center;justify-content:center;font-size:20px;box-shadow:0 6px 20px rgba(79,140,255,.35)}\n    .live{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;color:#3ed47e;background:rgba(62,212,126,.1);border:1px solid rgba(62,212,126,.25);padding:3px 9px;border-radius:99px;margin-left:12px;vertical-align:middle}\n    .live i{width:6px;height:6px;border-radius:50%;background:#3ed47e;box-shadow:0 0 8px #3ed47e}\n    .right{display:flex;align-items:center;gap:14px}\n    .date{font-size:12px;color:#8a93a6}\n    .ttl{font-size:24px;font-weight:700;letter-spacing:-.02em}\n    .sub{font-size:12px;color:#8a93a6;margin-top:2px}\n    .tabs{display:flex;gap:4px;background:#141925;border:1px solid #232a38;padding:4px;border-radius:11px}\n    .tab{padding:7px 16px;border-radius:8px;font-size:12px;font-weight:600;color:#8a93a6;cursor:pointer;user-select:none;transition:all .15s}\n    .tab:hover{color:#e8ebf2}\n    .tab.on{background:linear-gradient(135deg,#4f8cff,#3b6fe0);color:#fff;box-shadow:0 4px 14px rgba(79,140,255,.4)}\n    .kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}\n    .kpis{grid-auto-rows:108px}\n    .row{display:grid;grid-template-columns:2fr 1fr;gap:16px}\n    .r1{grid-auto-rows:240px}\n    .r2{grid-auto-rows:300px}\n    .db>*{flex-shrink:0}\n  `,\n  // Composes 5 child code components, each a \"string\" param holding a \"gui@<ref>\" typeId:\n  // kpiCard=gui@dash-kpi-card, revenueChart=gui@dash-revenue-chart, goalCard=gui@dash-goal-card,\n  // ordersTable=gui@dash-orders-table, channelsCard=gui@dash-channels-card.\n  render(d2) {\n    const range = d2.state.range || '7d';\n    const D = {\n      '7d': { rev: [12,18,14,22,19,27,31], users: '2.481', ord: '312', conv: '3,8%', revT: 'R$ 48,2k', d: ['+12%','+8%','-3%','+0,4pp'], goal: ['R$ 60k', 80] },\n      '30d': { rev: [20,24,19,28,33,30,38,35,42,40,47,52], users: '9.870', ord: '1.204', conv: '4,1%', revT: 'R$ 191k', d: ['+21%','+15%','+6%','+0,9pp'], goal: ['R$ 230k', 83] }\n    }[range];\n    const kpis = [['Receita', D.revT, D.d[0]], ['Usuários', D.users, D.d[1]], ['Pedidos', D.ord, D.d[2]], ['Conversão', D.conv, D.d[3]]];\n    const kpiHtml = kpis.map(k => d2.render(d2.param('kpiCard'), { label: k[0], value: k[1], delta: k[2], tone: k[0] === 'Conversão' ? 'violet' : 'blue', spark: D.rev.map((v, i) => v + (k[0].length * (i % 3)) % 7) }, { key: k[0] })).join('');\n    const chans = [['Orgânico', 72], ['Direto', 54], ['Social', 38], ['Email', 25], ['Ads', 17]];\n    const orders = [['#1042','Ana Souza','R$ 320','Pago','ok'],['#1041','Bruno Lima','R$ 89','Pendente','wait'],['#1040','Carla Dias','R$ 540','Pago','ok'],['#1039','Diego Reis','R$ 120','Falhou','fail'],['#1038','Elisa Prado','R$ 76','Pago','ok']];\n    return `<div class=\"db\">\n      <div class=\"top\"><div class=\"brand\"><div class=\"logo\">📈</div><div><div class=\"ttl\">Visão geral de vendas<span class=\"live\"><i></i>Ao vivo</span></div><div class=\"sub\">Dados fictícios · teste de code component</div></div></div>\n        <div class=\"right\"><div class=\"date\">${range === '7d' ? 'Últimos 7 dias' : 'Últimos 30 dias'}</div><div class=\"tabs\"><div class=\"tab ${range === '7d' ? 'on' : ''}\" data-range=\"7d\">7 dias</div><div class=\"tab ${range === '30d' ? 'on' : ''}\" data-range=\"30d\">30 dias</div></div></div></div>\n      <div class=\"kpis\">${kpiHtml}</div>\n      <div class=\"row r1\">\n        ${d2.render(d2.param('revenueChart'), { heading: 'Receita', points: D.rev }, { key: 'rev' })}\n        ${d2.render(d2.param('goalCard'), { heading: 'Meta do período', value: D.revT, target: D.goal[0], pct: D.goal[1] }, { key: 'goal' })}\n      </div>\n      <div class=\"row r2\">\n        ${d2.render(d2.param('ordersTable'), { heading: 'Pedidos recentes', orders: orders }, { key: 'orders' })}\n        ${d2.render(d2.param('channelsCard'), { heading: 'Canais de aquisição', channels: chans }, { key: 'chans' })}\n      </div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('.tab', 'click', (e) => {\n      const el = e.target.closest('[data-range]');\n      if (el) d2.state.range = el.dataset.range;\n    });\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"kpiCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiCard"}}}},"revenueChart":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"revenueChart"}}}},"goalCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"goalCard"}}}},"ordersTable":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ordersTable"}}}},"channelsCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"channelsCard"}}}}}}}}}},{"_id":"6ad9772b61da96aa2786770d","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-kpi-card","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}\n    .kpi{position:relative;overflow:hidden;transition:transform .15s,border-color .15s;box-sizing:border-box;height:100%;background:linear-gradient(160deg,#1a2030,#141926);border:1px solid #262e42;border-top:2px solid #4f8cff;border-radius:16px;padding:18px 20px;box-shadow:0 8px 24px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.04);font-family:Inter,system-ui,sans-serif;color:#e8ebf2}\n    .kpi.alt{background:linear-gradient(160deg,#2a1f4a,#1c1633);border-color:#4a3a7a;border-top:2px solid #a78bfa}\n    .kpi.alt .kl,.kpi.alt .kd.up{color:#c4b5fd}\n    .kpi:hover{transform:translateY(-2px);border-color:#3a4562}\n    .sp{position:absolute;right:14px;bottom:14px;width:70px;height:28px;opacity:.9}\n    .kl{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:#8a93a6}\n    .kv{font-size:28px;font-weight:700;margin-top:8px;font-variant-numeric:tabular-nums;letter-spacing:-.02em}\n    .kd{font-size:12px;margin-top:6px;font-weight:600}\n    .up{color:#3ed47e}.dn{color:#f87171}\n  `,\n  render(d2) {\n    const p = d2.params;\n    const d = String(p.delta || '');\n    const s = Array.isArray(p.spark) ? p.spark : [];\n    const mx = Math.max.apply(null, s), mn = Math.min.apply(null, s), rg = (mx - mn) || 1;\n    const col = p.tone === 'violet' ? '#a78bfa' : '#4f8cff';\n    const spark = s.length > 1 ? `<svg class=\"sp\" viewBox=\"0 0 70 28\" preserveAspectRatio=\"none\"><polyline points=\"${s.map((v, i) => Math.round(i * 70 / (s.length - 1)) + ',' + Math.round(26 - ((v - mn) / rg) * 24)).join(' ')}\" fill=\"none\" stroke=\"${col}\" stroke-width=\"2\" vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/></svg>` : '';\n    return `<div class=\"kpi${p.tone === 'violet' ? ' alt' : ''}\"><div class=\"kl\">${p.label || ''}</div><div class=\"kv\">${p.value || ''}</div><div class=\"kd ${d[0] === '-' ? 'dn' : 'up'}\">${d[0] === '-' ? '▼' : '▲'} ${d.replace(/^[+-]/, '')}</div>${spark}</div>`;\n  }\n}"}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}},"delta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"delta"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}},"spark":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"spark"}}}}}}}}}},{"_id":"6ad9772c219ed57f77d2c298","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-revenue-chart","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}\n    .card{box-sizing:border-box;height:100%;background:#161b27;border:1px solid #262e42;border-radius:16px;padding:20px 22px;box-shadow:0 8px 24px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.04);display:flex;flex-direction:column;font-family:Inter,system-ui,sans-serif;color:#e8ebf2}\n    .ch{font-size:13px;font-weight:600;margin-bottom:12px}\n    svg{width:100%;flex:1;min-height:0}\n    .ax{display:flex;justify-content:space-between;font-size:10px;color:#6b7488;margin-top:6px}\n    .tot{font-size:11px;color:#8a93a6;font-weight:500;margin-left:8px}\n    .ch{display:flex;align-items:baseline}\n  `,\n  render(d2) {\n    const pts = d2.param('points', [1, 2, 3]);\n    const max = Math.max.apply(null, pts) || 1;\n    const w = 600, h = 200;\n    const xy = pts.map((v, i) => [Math.round(i * (w / Math.max(pts.length - 1, 1))), Math.round(h - (v / max) * (h - 20) - 6)]);\n    const line = xy.map(p => p.join(',')).join(' ');\n    const area = '0,' + h + ' ' + line + ' ' + w + ',' + h;\n    return `<div class=\"card\"><div class=\"ch\">${d2.param('heading', 'Receita')}<span class=\"tot\">últimos ${pts.length} pontos</span></div>\n      <svg viewBox=\"0 0 ${w} ${h}\" preserveAspectRatio=\"none\"><defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\"><stop offset=\"0\" stop-color=\"#4f8cff\" stop-opacity=\".35\"/><stop offset=\"1\" stop-color=\"#4f8cff\" stop-opacity=\"0\"/></linearGradient></defs>\n      ${[0.25, 0.5, 0.75].map(f => `<line x1=\"0\" x2=\"${w}\" y1=\"${Math.round(h * f)}\" y2=\"${Math.round(h * f)}\" stroke=\"#232a38\" stroke-width=\"1\" stroke-dasharray=\"4 4\" vector-effect=\"non-scaling-stroke\"/>`).join('')}<polygon points=\"${area}\" fill=\"url(#g)\"/><polyline points=\"${line}\" fill=\"none\" stroke=\"#4f8cff\" stroke-width=\"3\" vector-effect=\"non-scaling-stroke\" stroke-linejoin=\"round\"/></svg><div class=\"ax\"><span>início</span><span>pico: ${max}k</span><span>hoje</span></div></div>`;\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"points":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"points"}}}}}}}}}},{"_id":"6ad9772da25cf380d0fdcd8c","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-goal-card","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}\n    .card{box-sizing:border-box;height:100%;background:#161b27;border:1px solid #262e42;border-radius:16px;padding:20px 22px;box-shadow:0 8px 24px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.04);display:flex;flex-direction:column;font-family:Inter,system-ui,sans-serif;color:#e8ebf2}\n    .ch{font-size:13px;font-weight:600;margin-bottom:12px}\n    .kv{font-size:28px;font-weight:700}\n    .sub{font-size:12px;color:#8a93a6;margin-top:2px}\n    .bt{height:10px;background:#232a38;border-radius:5px;overflow:hidden;margin-top:18px}\n    .bf{height:100%;background:linear-gradient(90deg,#3b6fe0,#4f8cff);border-radius:5px;transition:width .5s ease}\n    .kv{font-variant-numeric:tabular-nums;letter-spacing:-.02em}\n    .kd{font-size:12px;margin-top:10px;font-weight:600;color:#3ed47e}\n  `,\n  render(d2) {\n    const p = d2.params;\n    return `<div class=\"card\"><div class=\"ch\">${p.heading || 'Meta do período'}</div><div class=\"kv\">${p.value || ''}</div><div class=\"sub\">de ${p.target || ''}</div><div class=\"bt\"><div class=\"bf\" style=\"width:${p.pct || 0}%\"></div></div><div class=\"kd\">${p.pct || 0}% atingido · faltam ${100 - (p.pct || 0)}%</div></div>`;\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}},"target":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"target"}}}},"pct":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pct"}}}}}}}}}},{"_id":"6ad9772dd912ae0a94229130","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-orders-table","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}\n    .card{box-sizing:border-box;height:100%;background:#161b27;border:1px solid #262e42;border-radius:16px;padding:20px 22px;box-shadow:0 8px 24px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.04);display:flex;flex-direction:column;overflow:auto;font-family:Inter,system-ui,sans-serif;color:#e8ebf2}\n    .ch{font-size:13px;font-weight:600;margin-bottom:12px}\n    table{width:100%;border-collapse:collapse;font-size:12px}\n    th{text-align:left;color:#6b7488;font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:.08em;padding:8px 0;border-bottom:1px solid #262e42}\n    td{padding:10px 0;border-bottom:1px solid #1f2636;color:#d5d9e4}\n    tbody tr{transition:background .12s}\n    tbody tr:hover{background:#1b2232}\n    td:nth-child(3),th:nth-child(3){text-align:right;font-variant-numeric:tabular-nums;padding-right:16px}\n    td:first-child{color:#8a93a6}\n    .pill{display:inline-block;padding:3px 9px;border-radius:99px;font-size:11px;font-weight:600}\n    .ok{background:rgba(62,212,126,.15);color:#3ed47e}.wait{background:rgba(251,191,36,.15);color:#fbbf24}.fail{background:rgba(248,113,113,.15);color:#f87171}\n  `,\n  render(d2) {\n    const rows = d2.param('orders', []).map(o => `<tr data-key=\"${o[0]}\"><td>${o[0]}</td><td>${o[1]}</td><td>${o[2]}</td><td><span class=\"pill ${o[4]}\">${o[3]}</span></td></tr>`).join('');\n    return `<div class=\"card\"><div class=\"ch\">${d2.param('heading', 'Pedidos recentes')}</div><table><thead><tr><th>Pedido</th><th>Cliente</th><th>Valor</th><th>Status</th></tr></thead><tbody>${rows}</tbody></table></div>`;\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"orders":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orders"}}}}}}}}}},{"_id":"6ad9772da6663f5d5a8163c8","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"dash-channels-card","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}\n    .card{box-sizing:border-box;height:100%;background:#161b27;border:1px solid #262e42;border-radius:16px;padding:20px 22px;box-shadow:0 8px 24px rgba(0,0,0,.28),inset 0 1px 0 rgba(255,255,255,.04);display:flex;flex-direction:column;overflow:auto;font-family:Inter,system-ui,sans-serif;color:#e8ebf2}\n    .ch{font-size:13px;font-weight:600;margin-bottom:12px}\n    .bar{display:flex;align-items:center;gap:10px;margin:9px 0;font-size:12px}\n    .bar{padding:2px 6px;margin:5px -6px;border-radius:8px;transition:background .12s}\n    .bar:hover{background:#1b2232}\n    .bn{width:80px;color:#c5cbd9}\n    .bt{flex:1;height:8px;background:#232a38;border-radius:4px;overflow:hidden}\n    .bf{height:100%;background:linear-gradient(90deg,#3b6fe0,#4f8cff);border-radius:4px;transition:width .4s ease}\n    .bv{font-variant-numeric:tabular-nums;width:44px;text-align:right;color:#8a93a6}\n  `,\n  render(d2) {\n    const bars = d2.param('channels', []).map(c => `<div class=\"bar\" data-key=\"${c[0]}\"><div class=\"bn\">${c[0]}</div><div class=\"bt\"><div class=\"bf\" style=\"width:${c[1]}%\"></div></div><div class=\"bv\">${c[1]}%</div></div>`).join('');\n    return `<div class=\"card\"><div class=\"ch\">${d2.param('heading', 'Canais de aquisição')}</div>${bars}</div>`;\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"channels":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"channels"}}}}}}}}}},{"_id":"6adaa4381b8feac035e657df","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"expenses-dashboard-code","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; }\n    .ex { box-sizing:border-box; width:100%; height:100%; padding:24px; background:#f1f5f9; font-family:Inter,system-ui,sans-serif; color:#0f172a; display:grid; grid-template-rows:auto auto 1fr; gap:20px; overflow:auto; }\n    .ex * { box-sizing:border-box; }\n    .hd { display:flex; justify-content:space-between; align-items:flex-end; }\n    .hd h1 { margin:0; font-size:26px; font-weight:700; }\n    .hd span { font-size:12px; color:#64748b; font-weight:700; }\n    .kpis { display:grid; grid-template-columns:repeat(4,1fr); gap:16px; }\n    .kpi { position:relative; background:#fff; border:1px solid rgba(15,23,42,.08); border-radius:12px; padding:18px 20px 18px 24px; overflow:hidden; min-height:130px; }\n    .kpi i { position:absolute; left:0; top:0; bottom:0; width:4px; }\n    .kpi .l { font-size:12px; color:#64748b; }\n    .kpi .v { font-size:30px; font-weight:700; margin:8px 0 6px; }\n    .kpi .m { font-size:11px; color:#64748b; }\n    .kpi .m.up { color:#16a34a; font-weight:700; }\n    .bar { height:8px; background:#e2e8f0; border-radius:4px; margin:6px 0; overflow:hidden; }\n    .bar b { display:block; height:100%; background:#22c55e; border-radius:4px; }\n    .pill { display:inline-block; padding:4px 12px; border-radius:11px; background:#ede9fe; color:#6d28d9; font-size:10px; font-weight:700; }\n    .main { display:grid; grid-template-columns:1fr 280px; gap:16px; min-height:0; }\n    .card { background:#fff; border:1px solid rgba(15,23,42,.08); border-radius:16px; padding:24px; min-height:0; }\n    .card h2 { margin:0; font-size:20px; font-weight:700; }\n    .card p { margin:4px 0 16px; font-size:12px; color:#64748b; }\n    .top { display:flex; justify-content:space-between; align-items:flex-start; }\n    .tabs { display:flex; gap:6px; }\n    .tab { padding:6px 12px; border-radius:8px; font-size:12px; font-weight:700; color:#64748b; background:#f1f5f9; cursor:pointer; }\n    .tab.on { background:#0f172a; color:#fff; }\n    table { width:100%; border-collapse:collapse; }\n    th { text-align:left; font-size:11px; color:#64748b; padding:8px 0; border-bottom:1px solid #e2e8f0; }\n    th.r, td.r { text-align:right; }\n    td { padding:14px 0; font-size:14px; border-bottom:1px solid #f1f5f9; }\n    td.mer { font-weight:700; }\n    td.dt { color:#64748b; font-size:13px; }\n    .chart { display:flex; align-items:flex-end; gap:10px; height:150px; margin-top:12px; }\n    .chart div { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:flex-end; height:100%; gap:6px; font-size:10px; color:#64748b; }\n    .chart em { display:block; width:100%; background:#3b82f6; border-radius:6px 6px 0 0; }\n    .tot { margin-top:16px; font-size:12px; color:#64748b; }\n    .tot b { display:block; font-size:22px; color:#0f172a; }\n  `,\n  render(d2) {\n    const all = [\n      { m: 'Whole Foods Market', c: 'Food', d: 'Jan 28', a: 86.4 },\n      { m: 'Uber', c: 'Transport', d: 'Jan 27', a: 24.1 },\n      { m: 'Netflix', c: 'Subscriptions', d: 'Jan 25', a: 15.99 },\n      { m: 'Blue Bottle Coffee', c: 'Food', d: 'Jan 24', a: 12.5 },\n      { m: 'Shell', c: 'Transport', d: 'Jan 22', a: 58.0 },\n    ];\n    const flt = d2.state.filter || 'All';\n    const rows = all.filter(r => flt === 'All' || r.c === flt);\n    const tabs = ['All', 'Food', 'Transport', 'Subscriptions'];\n    const bars = [['Aug', 62], ['Sep', 48], ['Oct', 71], ['Nov', 55], ['Dec', 80], ['Jan', 90]];\n    return `<div class=\"ex\">\n      <div class=\"hd\"><h1>Expenses</h1><span>Jan 2026</span></div>\n      <div class=\"kpis\">\n        <div class=\"kpi\"><i style=\"background:#ef4444\"></i><div class=\"l\">Total spent</div><div class=\"v\">$2,240</div><div class=\"m up\">+12.4% vs Dec</div></div>\n        <div class=\"kpi\"><i style=\"background:#3b82f6\"></i><div class=\"l\">Budget remaining</div><div class=\"v\">$1,860</div><div class=\"bar\"><b style=\"width:65%\"></b></div><div class=\"m\">65% of monthly budget</div></div>\n        <div class=\"kpi\"><i style=\"background:#f59e0b\"></i><div class=\"l\">Avg daily spend</div><div class=\"v\">$74.6</div><div class=\"m\">+6.2% vs last month</div></div>\n        <div class=\"kpi\"><i style=\"background:#8b5cf6\"></i><div class=\"l\">Top category</div><div class=\"v\" style=\"font-size:22px\">Food &amp; Dining</div><div class=\"m\" style=\"margin-bottom:8px\">47% of total spend</div><span class=\"pill\">Food</span></div>\n      </div>\n      <div class=\"main\">\n        <div class=\"card\">\n          <div class=\"top\"><div><h2>Recent expenses</h2><p>Last 30 days activity</p></div>\n            <div class=\"tabs\">${tabs.map(t => `<div class=\"tab ${t === flt ? 'on' : ''}\" data-tab=\"${t}\">${t}</div>`).join('')}</div></div>\n          <table><thead><tr><th>Merchant</th><th>Category</th><th>Date</th><th class=\"r\">Amount</th></tr></thead>\n          <tbody>${rows.map(r => `<tr data-key=\"${r.m}\"><td class=\"mer\">${r.m}</td><td><span class=\"pill\">${r.c}</span></td><td class=\"dt\">${r.d}</td><td class=\"r\">$${r.a.toFixed(2)}</td></tr>`).join('')}</tbody></table>\n        </div>\n        <div class=\"card\">\n          <h2>Cashflow</h2><p>Spend per month</p>\n          <div class=\"chart\">${bars.map(b => `<div><em style=\"height:${b[1]}%\"></em>${b[0]}</div>`).join('')}</div>\n          <div class=\"tot\">Six-month total<b>$11,420</b></div>\n        </div>\n      </div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('[data-tab]', 'click', (e, el) => { d2.state.filter = el.getAttribute('data-tab'); });\n  }\n}"}}}}}}}},{"_id":"6adb8e55d690531d8dc32e71","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"tanquinhos-game","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{width:100%;height:100%;display:flex;flex-direction:column;background:#1b2a49;font-family:'Trebuchet MS',Verdana,sans-serif;user-select:none}\n  .cv{display:block;width:100%;aspect-ratio:2/1;height:auto}\n  .ctl{flex:1;display:flex;align-items:center;justify-content:center;gap:28px;padding:10px 20px;background:linear-gradient(#26365e,#1b2a49)}\n  .grp{display:flex;align-items:center;gap:8px;background:#ffffff14;border-radius:22px;padding:8px 14px}\n  .lbl{color:#cfe0ff;font-size:18px;font-weight:700;min-width:70px;text-align:center}\n  .b{width:64px;height:64px;border-radius:32px;border:0;background:#ffd36b;color:#3b2a00;font-size:28px;font-weight:900;box-shadow:0 5px 0 #c9993a;cursor:pointer}\n  .b:active{transform:translateY(4px);box-shadow:0 1px 0 #c9993a}\n  .fire{width:150px;height:76px;border-radius:38px;border:0;background:#ff5c7a;color:#fff;font-size:30px;font-weight:900;box-shadow:0 6px 0 #b83552;cursor:pointer}\n  .fire:active{transform:translateY(5px);box-shadow:0 1px 0 #b83552}\n  .rst{width:64px;height:64px;border-radius:32px;border:0;background:#7ad7ff;color:#08334a;font-size:26px;font-weight:900;box-shadow:0 5px 0 #3d9cc4;cursor:pointer}\n  `,\n  render(d2) {\n    return `<div class=\"wrap\">\n      <canvas class=\"cv\" data-d2-static width=\"2560\" height=\"1280\"></canvas>\n      <div class=\"ctl\">\n        <div class=\"grp\"><button class=\"b\" data-hold=\"left\">◀</button><div class=\"lbl\">Mover</div><button class=\"b\" data-hold=\"right\">▶</button></div>\n        <div class=\"grp\"><button class=\"b\" data-hold=\"angDown\">−</button><div class=\"lbl\">Ângulo</div><button class=\"b\" data-hold=\"angUp\">+</button></div>\n        <div class=\"grp\"><button class=\"b\" data-hold=\"pwDown\">−</button><div class=\"lbl\">Força</div><button class=\"b\" data-hold=\"pwUp\">+</button></div>\n        <button class=\"fire\" data-fire=\"1\">FOGO!</button>\n        <button class=\"rst\" data-restart=\"1\">↻</button>\n      </div>\n    </div>`\n  },\n  mounted(d2, root) {\n    const W = 1280, H = 640\n    const cv = d2.$('.cv'), ctx = cv.getContext('2d')\n    const G = 600, EXP_R = 52\n    const rnd = (a, b) => a + Math.random() * (b - a)\n    let top, tanks, cur, state, wind, proj, parts, texts, clouds, winner, settleT, lastT, clock\n    const held = {}\n\n    function genTerrain() {\n      top = new Float32Array(W + 1)\n      const p1 = rnd(0, 6), p2 = rnd(0, 6), p3 = rnd(0, 6)\n      for (let x = 0; x <= W; x++) top[x] = 430 + Math.sin(x * 0.006 + p1) * 62 + Math.sin(x * 0.015 + p2) * 28 + Math.sin(x * 0.034 + p3) * 7\n    }\n    function newWind() { wind = Math.round(rnd(-5, 5)) }\n    function reset() {\n      genTerrain()\n      tanks = [\n        { name: 'Jogador 1', x: 170, hp: 100, dir: 1, angle: 45, power: 55, fuel: 160, color: '#ff7a9c', color2: '#ffc1d3' },\n        { name: 'Jogador 2', x: W - 170, hp: 100, dir: -1, angle: 45, power: 55, fuel: 160, color: '#5cc8ff', color2: '#b8e8ff' }\n      ]\n      cur = 0; state = 'aim'; proj = null; parts = []; texts = []; winner = null; settleT = 0\n      newWind()\n    }\n    clouds = [{ x: 150, y: 70, s: 1, v: 6 }, { x: 600, y: 120, s: 1.3, v: 4 }, { x: 1000, y: 60, s: 0.9, v: 8 }]\n    clock = 0\n    reset()\n\n    const gy = (x) => top[Math.max(0, Math.min(W, Math.round(x)))]\n    const slope = (x) => Math.atan2(gy(x + 10) - gy(x - 10), 20)\n    const aimVec = (t) => ({ x: t.dir * Math.cos(t.angle * Math.PI / 180), y: -Math.sin(t.angle * Math.PI / 180) })\n    const center = (t) => ({ x: t.x, y: gy(t.x) - 20 })\n\n    function fire() {\n      if (state !== 'aim') return\n      const t = tanks[cur], v = aimVec(t)\n      const px = t.x + v.x * 38, py = gy(t.x) - 34 + v.y * 38\n      const sp = t.power * 7.4\n      proj = { x: px, y: py, vx: v.x * sp, vy: v.y * sp, age: 0 }\n      state = 'flight'\n      for (let i = 0; i < 8; i++) parts.push({ x: px, y: py, vx: v.x * rnd(40, 160) + rnd(-40, 40), vy: v.y * rnd(40, 160) + rnd(-40, 40), life: 0.5, max: 0.5, r: rnd(3, 7), c: '#fff5d6' })\n    }\n\n    function explode(x, y) {\n      for (let xi = Math.floor(x - EXP_R); xi <= Math.ceil(x + EXP_R); xi++) {\n        if (xi < 0 || xi > W) continue\n        const dx = xi - x, s = Math.sqrt(Math.max(0, EXP_R * EXP_R - dx * dx))\n        if (y - s <= top[xi] && top[xi] <= y + s) top[xi] = Math.min(H, y + s)\n      }\n      tanks.forEach((t) => {\n        const c = center(t), d = Math.hypot(c.x - x, c.y - y)\n        if (d < EXP_R + 26) {\n          const dmg = Math.max(6, Math.round(48 * (1 - d / (EXP_R + 26))))\n          t.hp = Math.max(0, t.hp - dmg)\n          texts.push({ x: t.x, y: gy(t.x) - 70, t: '-' + dmg, life: 1.2, max: 1.2 })\n        }\n      })\n      const cols = ['#ffd36b', '#ff9b45', '#ff5c4a', '#fff2b0']\n      for (let i = 0; i < 38; i++) {\n        const a = rnd(0, Math.PI * 2), sp = rnd(60, 380)\n        parts.push({ x, y, vx: Math.cos(a) * sp, vy: Math.sin(a) * sp - 60, life: rnd(0.4, 0.9), max: 0.9, r: rnd(4, 11), c: cols[(Math.random() * cols.length) | 0] })\n      }\n      for (let i = 0; i < 12; i++) parts.push({ x: x + rnd(-20, 20), y: y + rnd(-10, 10), vx: rnd(-30, 30), vy: rnd(-90, -30), life: rnd(0.8, 1.4), max: 1.4, r: rnd(10, 20), c: '#ffffff55', smoke: 1 })\n      parts.push({ ring: 1, x, y, life: 0.4, max: 0.4, r: EXP_R })\n      proj = null; state = 'settle'; settleT = 1.3\n    }\n\n    function nextTurn() {\n      const dead = tanks.findIndex((t) => t.hp <= 0)\n      if (dead >= 0) { winner = tanks[1 - dead].name; state = 'over'; return }\n      cur = 1 - cur; tanks[cur].fuel = 160; newWind(); state = 'aim'\n    }\n\n    function step(dt) {\n      clock += dt\n      clouds.forEach((c) => { c.x += c.v * dt; if (c.x > W + 120) c.x = -140 })\n      const t = tanks[cur]\n      if (state === 'aim') {\n        if (held.left && t.fuel > 0) { t.x = Math.max(34, t.x - 80 * dt); t.fuel -= 80 * dt; t.dir = -1 }\n        if (held.right && t.fuel > 0) { t.x = Math.min(W - 34, t.x + 80 * dt); t.fuel -= 80 * dt; t.dir = 1 }\n        if (held.angUp) t.angle = Math.min(88, t.angle + 40 * dt)\n        if (held.angDown) t.angle = Math.max(4, t.angle - 40 * dt)\n        if (held.pwUp) t.power = Math.min(100, t.power + 30 * dt)\n        if (held.pwDown) t.power = Math.max(10, t.power - 30 * dt)\n      }\n      if (state === 'flight' && proj) {\n        const n = 4, h = dt / n\n        for (let i = 0; i < n && proj; i++) {\n          proj.vx += wind * 12 * h; proj.vy += G * h\n          proj.x += proj.vx * h; proj.y += proj.vy * h; proj.age += h\n          if (i === 0) parts.push({ x: proj.x, y: proj.y, vx: 0, vy: 0, life: 0.5, max: 0.5, r: 4, c: '#ffffff99' })\n          if (proj.x < -40 || proj.x > W + 40 || proj.y > H + 60) { proj = null; state = 'settle'; settleT = 0.8; break }\n          if (proj.y >= 0 && proj.x >= 0 && proj.x <= W && proj.y >= top[Math.round(proj.x)]) { explode(proj.x, proj.y); break }\n          for (let k = 0; k < 2; k++) {\n            if (k === cur && proj.age < 0.35) continue\n            const c = center(tanks[k])\n            if (Math.hypot(c.x - proj.x, c.y - proj.y) < 26) { explode(proj.x, proj.y); break }\n          }\n        }\n      }\n      if (state === 'settle') { settleT -= dt; if (settleT <= 0) nextTurn() }\n      parts = parts.filter((p) => { p.life -= dt; if (!p.ring) { p.x += p.vx * dt; p.y += p.vy * dt; if (!p.smoke) p.vy += 400 * dt } return p.life > 0 })\n      texts = texts.filter((q) => { q.life -= dt; q.y -= 30 * dt; return q.life > 0 })\n    }\n\n    function rr(x, y, w, h, r) { ctx.beginPath(); ctx.roundRect ? ctx.roundRect(x, y, w, h, r) : ctx.rect(x, y, w, h) }\n    const OUT = '#232840'\n\n    function drawSky() {\n      const g = ctx.createLinearGradient(0, 0, 0, H)\n      g.addColorStop(0, '#7ccbff'); g.addColorStop(0.7, '#d9f1ff'); g.addColorStop(1, '#fff0cf')\n      ctx.fillStyle = g; ctx.fillRect(0, 0, W, H)\n      ctx.fillStyle = '#fff6b8'; ctx.beginPath(); ctx.arc(1120, 110, 52, 0, 7); ctx.fill()\n      ctx.fillStyle = '#fff6b855'; ctx.beginPath(); ctx.arc(1120, 110, 78, 0, 7); ctx.fill()\n      ctx.fillStyle = '#b6e6c0'; ctx.beginPath(); ctx.moveTo(0, H)\n      for (let x = 0; x <= W; x += 20) ctx.lineTo(x, 380 + Math.sin(x * 0.005 + 1) * 50 + Math.sin(x * 0.013) * 20)\n      ctx.lineTo(W, H); ctx.fill()\n      clouds.forEach((c) => {\n        ctx.fillStyle = '#ffffffdd'\n        ;[[0, 0, 28], [26, -8, 34], [58, 0, 26], [30, 8, 30]].forEach((b) => { ctx.beginPath(); ctx.arc(c.x + b[0] * c.s, c.y + b[1] * c.s, b[2] * c.s, 0, 7); ctx.fill() })\n      })\n    }\n\n    function drawTerrain() {\n      ctx.beginPath(); ctx.moveTo(0, H)\n      for (let x = 0; x <= W; x += 2) ctx.lineTo(x, top[x])\n      ctx.lineTo(W, H); ctx.closePath()\n      const g = ctx.createLinearGradient(0, 330, 0, H)\n      g.addColorStop(0, '#b5834f'); g.addColorStop(1, '#6e4a2c')\n      ctx.fillStyle = g; ctx.fill()\n      ctx.beginPath(); for (let x = 0; x <= W; x += 2) { x ? ctx.lineTo(x, top[x]) : ctx.moveTo(x, top[x]) }\n      ctx.lineJoin = 'round'; ctx.strokeStyle = '#59c86a'; ctx.lineWidth = 14; ctx.stroke()\n      ctx.strokeStyle = '#8be38f'; ctx.lineWidth = 6; ctx.translate(0, -3); ctx.stroke(); ctx.translate(0, 3)\n    }\n\n    function drawTank(t, active) {\n      const y0 = gy(t.x), sl = slope(t.x) * 0.8\n      const v = aimVec(t), ba = Math.atan2(v.y, v.x)\n      ctx.save(); ctx.translate(t.x, y0); ctx.rotate(sl)\n      ctx.lineWidth = 2.5; ctx.strokeStyle = OUT; ctx.lineJoin = 'round'\n      ctx.fillStyle = '#3a4060'; rr(-27, -15, 54, 16, 8); ctx.fill(); ctx.stroke()\n      ;[-18, 0, 18].forEach((dx) => {\n        ctx.fillStyle = '#58608a'; ctx.beginPath(); ctx.arc(dx, -7, 7, 0, 7); ctx.fill(); ctx.stroke()\n        ctx.fillStyle = '#d6dcff'; ctx.beginPath(); ctx.arc(dx, -7, 2.5, 0, 7); ctx.fill()\n      })\n      ctx.save(); ctx.translate(0, -34); ctx.rotate(ba - sl)\n      ctx.fillStyle = '#ffd36b'; rr(0, -5, 36, 10, 5); ctx.fill(); ctx.stroke()\n      ctx.fillStyle = '#e8a93d'; rr(30, -7, 9, 14, 4); ctx.fill(); ctx.stroke()\n      ctx.restore()\n      ctx.fillStyle = t.color2; ctx.beginPath(); ctx.arc(0, -30, 16, Math.PI, 0); ctx.closePath(); ctx.fill(); ctx.stroke()\n      ctx.fillStyle = '#ffffff88'; ctx.beginPath(); ctx.ellipse(-6, -38, 5, 3, -0.5, 0, 7); ctx.fill()\n      ctx.fillStyle = t.color; rr(-24, -31, 48, 22, 10); ctx.fill(); ctx.stroke()\n      ctx.fillStyle = '#ffffff55'; rr(-18, -29, 36, 5, 3); ctx.fill()\n      ;[-8, 8].forEach((ex) => {\n        ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(ex, -21, 5, 0, 7); ctx.fill(); ctx.lineWidth = 1.5; ctx.stroke()\n        ctx.fillStyle = OUT; ctx.beginPath(); ctx.arc(ex + t.dir * 1.6, -20.5, 2.4, 0, 7); ctx.fill()\n      })\n      ctx.fillStyle = '#ff6f8d66'; ctx.beginPath(); ctx.arc(-15, -14, 3.5, 0, 7); ctx.arc(15, -14, 3.5, 0, 7); ctx.fill()\n      ctx.lineWidth = 2; ctx.beginPath()\n      if (t.hp < 35) ctx.arc(0, -10.5, 3.5, Math.PI * 1.1, Math.PI * 1.9); else ctx.arc(0, -15, 3.5, Math.PI * 0.1, Math.PI * 0.9)\n      ctx.stroke()\n      ctx.restore()\n      const bx = t.x, by = y0 - 66, bw = 60\n      ctx.fillStyle = OUT; rr(bx - bw / 2 - 2, by - 2, bw + 4, 12, 6); ctx.fill()\n      ctx.fillStyle = t.hp > 50 ? '#6fe08a' : t.hp > 25 ? '#ffd23f' : '#ff5c4a'\n      if (t.hp > 0) { rr(bx - bw / 2, by, bw * t.hp / 100, 8, 4); ctx.fill() }\n      if (active) {\n        const bounce = Math.sin(clock * 6) * 4\n        ctx.fillStyle = '#ffd36b'; ctx.beginPath()\n        ctx.moveTo(bx - 9, by - 26 + bounce); ctx.lineTo(bx + 9, by - 26 + bounce); ctx.lineTo(bx, by - 12 + bounce); ctx.closePath(); ctx.fill()\n        ctx.lineWidth = 2; ctx.strokeStyle = OUT; ctx.stroke()\n      }\n    }\n\n    function drawHUD() {\n      const t = tanks[cur]\n      ctx.fillStyle = '#ffffffcc'; rr(14, 14, 300, 92, 18); ctx.fill()\n      ctx.fillStyle = t.color; rr(14, 14, 300, 28, 14); ctx.fill()\n      ctx.fillStyle = '#fff'; ctx.font = '800 17px Trebuchet MS, sans-serif'; ctx.textAlign = 'left'\n      ctx.fillText('Vez de ' + t.name, 28, 34)\n      ctx.fillStyle = OUT; ctx.font = '700 16px Trebuchet MS, sans-serif'\n      ctx.fillText('Ângulo ' + Math.round(t.angle) + '°', 28, 64)\n      ctx.fillText('Força ' + Math.round(t.power), 160, 64)\n      ctx.fillStyle = '#d9deef'; rr(28, 78, 120, 10, 5); ctx.fill()\n      ctx.fillStyle = '#ffb347'; rr(28, 78, 120 * t.power / 100, 10, 5); ctx.fill()\n      ctx.fillStyle = '#d9deef'; rr(160, 78, 120, 10, 5); ctx.fill()\n      ctx.fillStyle = '#6fd6ff'; rr(160, 78, 120 * Math.max(0, t.fuel) / 160, 10, 5); ctx.fill()\n      ctx.fillStyle = '#ffffffcc'; rr(W / 2 - 80, 14, 160, 54, 18); ctx.fill()\n      ctx.fillStyle = OUT; ctx.textAlign = 'center'; ctx.font = '700 14px Trebuchet MS, sans-serif'\n      ctx.fillText('VENTO', W / 2, 33)\n      const dir = wind === 0 ? 0 : Math.sign(wind), len = Math.abs(wind) * 11\n      ctx.strokeStyle = '#3d9cc4'; ctx.lineWidth = 6; ctx.lineCap = 'round'\n      if (dir === 0) { ctx.fillStyle = '#3d9cc4'; ctx.fillText('calmo', W / 2, 56) } else {\n        ctx.beginPath(); ctx.moveTo(W / 2 - dir * len / 2, 50); ctx.lineTo(W / 2 + dir * len / 2, 50); ctx.stroke()\n        ctx.fillStyle = '#3d9cc4'; ctx.beginPath()\n        ctx.moveTo(W / 2 + dir * (len / 2 + 9), 50); ctx.lineTo(W / 2 + dir * (len / 2 - 2), 43); ctx.lineTo(W / 2 + dir * (len / 2 - 2), 57); ctx.fill()\n      }\n    }\n\n    function drawFX() {\n      parts.forEach((p) => {\n        const a = Math.max(0, p.life / p.max)\n        if (p.ring) {\n          ctx.strokeStyle = 'rgba(255,255,255,' + a + ')'; ctx.lineWidth = 6 * a\n          ctx.beginPath(); ctx.arc(p.x, p.y, p.r * (1.2 - a * 0.9) + 10, 0, 7); ctx.stroke(); return\n        }\n        ctx.globalAlpha = a; ctx.fillStyle = p.c\n        ctx.beginPath(); ctx.arc(p.x, p.y, p.r * (p.smoke ? 1.4 - a * 0.4 : a + 0.2), 0, 7); ctx.fill()\n      })\n      ctx.globalAlpha = 1\n      if (proj) {\n        ctx.fillStyle = '#ff9b45'; ctx.strokeStyle = OUT; ctx.lineWidth = 2.5\n        ctx.beginPath(); ctx.arc(proj.x, proj.y, 7, 0, 7); ctx.fill(); ctx.stroke()\n        ctx.fillStyle = '#fff3c4'; ctx.beginPath(); ctx.arc(proj.x - 2, proj.y - 2, 2.5, 0, 7); ctx.fill()\n      }\n      texts.forEach((q) => {\n        ctx.globalAlpha = Math.min(1, q.life / q.max * 1.5)\n        ctx.font = '900 26px Trebuchet MS, sans-serif'; ctx.textAlign = 'center'\n        ctx.lineWidth = 5; ctx.strokeStyle = OUT; ctx.strokeText(q.t, q.x, q.y)\n        ctx.fillStyle = '#ff5c4a'; ctx.fillText(q.t, q.x, q.y)\n      })\n      ctx.globalAlpha = 1\n    }\n\n    function draw() {\n      ctx.setTransform(2, 0, 0, 2, 0, 0)\n      drawSky(); drawTerrain()\n      tanks.forEach((t, i) => drawTank(t, i === cur && state === 'aim'))\n      drawFX(); drawHUD()\n      if (state === 'over') {\n        ctx.fillStyle = '#10182ecc'; ctx.fillRect(0, 0, W, H)\n        ctx.textAlign = 'center'; ctx.lineWidth = 8; ctx.strokeStyle = OUT\n        ctx.font = '900 64px Trebuchet MS, sans-serif'\n        ctx.strokeText(winner + ' venceu!', W / 2, H / 2 - 10)\n        ctx.fillStyle = '#ffd36b'; ctx.fillText(winner + ' venceu!', W / 2, H / 2 - 10)\n        ctx.fillStyle = '#fff'; ctx.font = '700 24px Trebuchet MS, sans-serif'\n        ctx.fillText('Toque no ↻ para jogar de novo', W / 2, H / 2 + 40)\n      }\n    }\n\n    d2.on('[data-hold]', 'pointerdown', (e, el) => { const k = (el || e.target.closest('[data-hold]')).getAttribute('data-hold'); held[k] = true })\n    const release = () => { for (const k in held) held[k] = false }\n    window.addEventListener('pointerup', release); window.addEventListener('pointercancel', release)\n    d2.onUnmount(() => { window.removeEventListener('pointerup', release); window.removeEventListener('pointercancel', release) })\n    d2.on('[data-fire]', 'click', () => fire())\n    d2.on('[data-restart]', 'click', () => reset())\n\n    const keyMap = { ArrowLeft: 'left', ArrowRight: 'right', ArrowUp: 'angUp', ArrowDown: 'angDown', a: 'pwDown', d: 'pwUp' }\n    const kd = (e) => { if (e.code === 'Space') { e.preventDefault(); fire(); return } const k = keyMap[e.key]; if (k) { held[k] = true; e.preventDefault() } }\n    const ku = (e) => { const k = keyMap[e.key]; if (k) held[k] = false }\n    window.addEventListener('keydown', kd); window.addEventListener('keyup', ku)\n    d2.onUnmount(() => { window.removeEventListener('keydown', kd); window.removeEventListener('keyup', ku) })\n\n    lastT = performance.now()\n    d2.raf(() => {\n      const now = performance.now()\n      const dt = Math.min(0.033, (now - lastT) / 1000); lastT = now\n      step(dt); draw()\n    })\n  }\n}"}}}}}}}},{"_id":"6ae498a08b9edfaf770d22be","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-vitrine","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 ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\nconst money = (n) => (Number(n) || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })\nconst KEY = 'lojaboa.cart'\nconst loadLocal = () => { try { return JSON.parse(localStorage.getItem(KEY) || '{}') } catch { return {} } }\nconst saveLocal = (o) => { try { localStorage.setItem(KEY, JSON.stringify(o)) } catch {} }\nconst items = (st) => Object.values(st.cart || {})\nconst subtotal = (st) => items(st).reduce((s, it) => s + it.preco * it.qtd, 0)\nconst qty = (st) => items(st).reduce((s, it) => s + it.qtd, 0)\nconst unwrap = (r) => (r && typeof r === 'object' && 'data' in r && !('ok' in r) ? r.data : r)\nconst tile = (p, big) => `<div class=\"tile${big ? ' big' : ''}\" style=\"--c:${esc(p.cor || '#2f6b4f')}\">${p.imagem ? `<img src=\"${esc(p.imagem)}\" alt=\"\">` : `<span class=\"rotulo\"><i>Loja Boa</i><b>${esc(p.nome)}</b><em>${esc(p.categoria || '')}</em></span>`}</div>`\n\nfunction card(p, st) {\n  const inCart = st.cart?.[p._id]?.qtd || 0\n  const out = !(Number(p.estoque) > 0)\n  return `<article class=\"card${out ? ' out' : ''}\" data-key=\"${esc(p._id)}\">\n    ${tile(p)}\n    <div class=\"cbody\">\n      <div class=\"cat\">${esc(p.categoria || '')}</div>\n      <h3>${esc(p.nome)}</h3>\n      <p>${esc(p.descricao || '')}</p>\n      <div class=\"crow\"><span class=\"price\">${money(p.preco)}</span>\n        ${out ? '<span class=\"soldout\">esgotado</span>' : inCart ? `<span class=\"stepper\"><button class=\"dec\" data-id=\"${esc(p._id)}\">−</button><b>${inCart}</b><button class=\"inc\" data-id=\"${esc(p._id)}\">+</button></span>` : `<button class=\"add\" data-id=\"${esc(p._id)}\">Adicionar</button>`}\n      </div>\n    </div>\n  </article>`\n}\n\nfunction drawer(d2, st) {\n  const list = items(st)\n  return `<div class=\"veil\" data-close=\"1\"></div><aside class=\"drawer\">\n    <header><h2>Sua sacola</h2><button class=\"x\" data-close=\"1\" aria-label=\"fechar\">×</button></header>\n    ${list.length ? `<div class=\"lines\">${list.map((it) => `<div class=\"line\" data-key=\"${esc(it.id)}\">${tile({ ...it, nome: it.nome })}<div class=\"lmeta\"><b>${esc(it.nome)}</b><span>${money(it.preco)}</span></div><span class=\"stepper\"><button class=\"dec\" data-id=\"${esc(it.id)}\">−</button><b>${it.qtd}</b><button class=\"inc\" data-id=\"${esc(it.id)}\">+</button></span><span class=\"ltotal\">${money(it.preco * it.qtd)}</span></div>`).join('')}</div>\n    <footer><div class=\"sum\"><span>Subtotal</span><b>${money(subtotal(st))}</b></div><div class=\"hint\">${subtotal(st) >= 200 ? 'Frete grátis no padrão ✓' : `Faltam ${money(200 - subtotal(st))} para o frete grátis`}</div><button class=\"primary go-checkout\">Fechar compra</button></footer>` : `<div class=\"empty\"><b>Sua sacola está vazia.</b><span>Escolha algo na vitrine.</span></div>`}\n  </aside>`\n}\n\nfunction checkout(d2, st) {\n  const f = st.form || {}\n  const fr = st.frete\n  const list = items(st)\n  const sub = subtotal(st)\n  const chosen = fr?.opcoes?.find((o) => o.servico === st.servico) || fr?.opcoes?.[0]\n  const total = sub + (chosen ? Number(chosen.valor) : 0)\n  const inp = (name, label, extra = '') => `<label class=\"f\"><span>${label}</span><input name=\"${name}\" value=\"${esc(f[name] || '')}\" ${extra}></label>`\n  return `<div class=\"veil\"></div><section class=\"checkout\">\n    <header><button class=\"back\">← voltar à loja</button><h2>Checkout</h2></header>\n    <div class=\"cols\">\n      <form class=\"form\" autocomplete=\"on\">\n        <h4>1 · Seus dados</h4>\n        <div class=\"row2\">${inp('nome', 'Nome')}${inp('email', 'E-mail', 'type=\"email\"')}</div>\n        ${inp('telefone', 'WhatsApp', 'placeholder=\"11 99999-9999\"')}\n        <h4>2 · Entrega</h4>\n        <div class=\"row2\">${inp('cep', 'CEP', 'placeholder=\"01311-000\" inputmode=\"numeric\"')}<span class=\"f\"><span>&nbsp;</span><button type=\"button\" class=\"ghost cotar\" ${st.busy ? 'disabled' : ''}>${st.busy === 'cotar' ? 'Cotando…' : 'Calcular frete'}</button></span></div>\n        ${fr ? `<div class=\"opts\">${fr.opcoes.map((o) => `<label class=\"opt${(chosen?.servico === o.servico) ? ' on' : ''}\"><input type=\"radio\" name=\"servico\" value=\"${esc(o.servico)}\" ${(chosen?.servico === o.servico) ? 'checked' : ''}><b>${esc(o.servico)}</b><span>${o.prazo} dia${o.prazo > 1 ? 's' : ''} úteis · ${fr.regiao}</span><em>${Number(o.valor) === 0 ? 'grátis' : money(o.valor)}</em></label>`).join('')}</div>` : '<div class=\"muted\">Informe o CEP e calcule o frete.</div>'}\n        ${inp('endereco', 'Endereço e número')}\n        <div class=\"row2\">${inp('cidade', 'Cidade')}${inp('uf', 'UF', 'maxlength=\"2\"')}</div>\n        <h4>3 · Pagamento</h4>\n        <div class=\"row2\">${inp('cartao', 'Número do cartão', 'placeholder=\"4111 1111 1111 1111\" inputmode=\"numeric\"')}${inp('titular', 'Nome no cartão')}</div>\n        <div class=\"muted\">Ambiente de teste: um cartão com final <b>0000</b> é recusado pelo gateway.</div>\n        ${st.error ? `<div class=\"err\">${esc(st.error)}</div>` : ''}\n      </form>\n      <aside class=\"summary\">\n        <h4>Resumo</h4>\n        ${list.map((it) => `<div class=\"sl\" data-key=\"${esc(it.id)}\"><span>${it.qtd} × ${esc(it.nome)}</span><b>${money(it.preco * it.qtd)}</b></div>`).join('')}\n        <div class=\"sl\"><span>Frete${chosen ? ` · ${esc(chosen.servico)}` : ''}</span><b>${chosen ? (Number(chosen.valor) === 0 ? 'grátis' : money(chosen.valor)) : '—'}</b></div>\n        <div class=\"sl tot\"><span>Total</span><b>${money(total)}</b></div>\n        <button class=\"primary pagar\" ${st.busy ? 'disabled' : ''}>${st.busy === 'pagar' ? 'Processando pagamento…' : `Pagar ${money(total)}`}</button>\n        <div class=\"muted small\">Pagamento via gateway, confirmação por webhook. Separação e entrega acompanhadas em “Meus pedidos”.</div>\n      </aside>\n    </div>\n  </section>`\n}\n\nfunction done(d2, st) {\n  const r = st.done || {}\n  const ok = r.status === 'pago'\n  return `<div class=\"veil\"></div><section class=\"done ${ok ? 'ok' : 'bad'}\">\n    <div class=\"badge\">${ok ? '✓' : '!'}</div>\n    <h2>${ok ? 'Pedido confirmado' : 'Pagamento recusado'}</h2>\n    <p class=\"num\">Pedido <b>${esc(r.pedido || '')}</b> · ${money(r.total)}</p>\n    <p>${ok ? 'Seu pagamento foi aprovado. O pedido já entrou na fila de separação do estoque.' : `${esc(r.motivo || 'O gateway recusou o pagamento.')} Nada foi cobrado; a sacola continua aqui.`}</p>\n    <div class=\"actions\">${ok ? `<a class=\"primary\" href=\"/pedidos?email=${encodeURIComponent(st.form?.email || '')}\">Acompanhar em Meus pedidos</a>` : '<button class=\"primary retry\">Tentar outro cartão</button>'}<button class=\"ghost back\">Voltar à loja</button></div>\n  </section>`\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .shop{--bg:#f6f1e8;--ink:#1e2a22;--mut:#6b7a70;--line:#e3dccd;--acc:#2f6b4f;--acc2:#d9a441;--card:#fffdf8;--serif:\"Iowan Old Style\",\"Palatino Linotype\",Palatino,Georgia,serif;--sans:-apple-system,\"Inter\",system-ui,sans-serif;\n    font:15px/1.5 var(--sans);color:var(--ink);background:var(--bg);min-height:100%;height:100%;overflow:auto;position:relative}\n  .top{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:22px;padding:14px 32px;background:rgba(246,241,232,.92);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}\n  .brand{font:600 24px var(--serif);letter-spacing:.02em}\n  .brand i{font-style:normal;color:var(--acc)}\n  .top nav{display:flex;gap:16px;margin-left:12px;color:var(--mut);font-size:14px}\n  .top nav a{cursor:pointer}\n  .top nav a.on{color:var(--ink);font-weight:600}\n  .cartBtn{margin-left:auto;background:var(--ink);color:#fff;border:0;border-radius:999px;padding:9px 16px;font:600 14px var(--sans);cursor:pointer}\n  .cartBtn b{background:var(--acc2);color:var(--ink);border-radius:999px;padding:1px 8px;margin-left:8px}\n  .hero{display:grid;grid-template-columns:1.2fr 1fr;gap:28px;align-items:center;padding:44px 32px 28px}\n  .hero h1{font:600 clamp(26px,6.5vw,46px)/1.05 var(--serif);margin:0 0 12px;max-width:560px}\n  .hero p{color:var(--mut);max-width:480px;margin:0 0 18px}\n  .hero .tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}\n  .tile{aspect-ratio:1;border-radius:14px;background:linear-gradient(160deg,color-mix(in srgb,var(--c) 70%,#fff) 0%,var(--c) 100%);display:grid;place-items:center;overflow:hidden;position:relative;flex:none}\n  .tile img{width:100%;height:100%;object-fit:cover}\n  .tile .rotulo{background:rgba(255,253,248,.92);border:1px solid rgba(0,0,0,.08);border-radius:6px;padding:10px 12px;width:72%;text-align:center;display:grid;gap:2px;font-family:var(--serif)}\n  .tile .rotulo i{font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:var(--acc);font-style:normal}\n  .tile .rotulo b{font-size:13px;line-height:1.15;font-weight:600}\n  .tile .rotulo em{font-size:10px;color:var(--mut);font-style:normal}\n  .tile.big .rotulo{width:60%}\n  .chips{display:flex;gap:8px;padding:6px 32px 18px;flex-wrap:wrap}\n  .chip{border:1px solid var(--line);background:var(--card);border-radius:999px;padding:6px 14px;cursor:pointer;font-size:13px}\n  .chip.on{background:var(--ink);color:#fff;border-color:var(--ink)}\n  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:18px;padding:0 32px 48px}\n  .card{background:var(--card);border:1px solid var(--line);border-radius:16px;overflow:hidden;display:flex;flex-direction:column;transition:transform .15s,box-shadow .15s}\n  .card:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(30,42,34,.08)}\n  .card .tile{border-radius:0;aspect-ratio:1.1}\n  .cbody{padding:14px 16px 16px;display:grid;gap:4px}\n  .cat{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--acc)}\n  .card h3{font:600 18px/1.2 var(--serif);margin:0}\n  .card p{margin:0;color:var(--mut);font-size:13px;min-height:38px}\n  .crow{display:flex;align-items:center;justify-content:space-between;margin-top:8px}\n  .price{font:600 17px var(--sans)}\n  .add,.primary{background:var(--acc);color:#fff;border:0;border-radius:10px;padding:9px 14px;font:600 14px var(--sans);cursor:pointer}\n  .add:hover,.primary:hover{filter:brightness(1.08)}\n  .primary[disabled]{opacity:.6;cursor:wait}\n  .ghost{background:transparent;border:1px solid var(--line);border-radius:10px;padding:9px 14px;font:600 14px var(--sans);cursor:pointer;color:var(--ink)}\n  .soldout{font-size:12px;color:#a0584b;font-weight:600;letter-spacing:.06em;text-transform:uppercase}\n  .card.out .tile{filter:grayscale(.7) opacity(.7)}\n  .stepper{display:inline-flex;align-items:center;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#fff}\n  .stepper button{border:0;background:transparent;width:30px;height:32px;cursor:pointer;font-size:16px}\n  .stepper b{min-width:22px;text-align:center}\n  .foot{padding:28px 32px;border-top:1px solid var(--line);color:var(--mut);font-size:13px;display:flex;gap:24px}\n  .veil{position:absolute;inset:0;background:rgba(30,42,34,.35);z-index:8}\n  .drawer{position:absolute;top:0;right:0;bottom:0;width:min(420px,100%);background:var(--card);z-index:9;display:flex;flex-direction:column;box-shadow:-20px 0 50px rgba(0,0,0,.15)}\n  .drawer header{display:flex;align-items:center;justify-content:space-between;padding:18px 20px;border-bottom:1px solid var(--line)}\n  .drawer h2,.checkout h2,.done h2{font:600 24px var(--serif);margin:0}\n  .x{border:0;background:transparent;font-size:26px;cursor:pointer;line-height:1}\n  .lines{flex:1;overflow:auto;padding:8px 20px}\n  .line{display:grid;grid-template-columns:56px 1fr auto auto;gap:12px;align-items:center;padding:12px 0;border-bottom:1px solid var(--line)}\n  .line .tile{width:56px;border-radius:10px}\n  .line .tile .rotulo{display:none}\n  .lmeta{display:grid}\n  .lmeta span{color:var(--mut);font-size:13px}\n  .ltotal{font-weight:600;min-width:72px;text-align:right}\n  .drawer footer{padding:16px 20px 20px;border-top:1px solid var(--line);display:grid;gap:10px}\n  .sum{display:flex;justify-content:space-between;font-size:17px}\n  .hint{font-size:12px;color:var(--acc)}\n  .empty{flex:1;display:grid;place-content:center;text-align:center;gap:4px;color:var(--mut)}\n  .checkout{position:absolute;inset:24px;z-index:9;background:var(--card);border-radius:18px;overflow:auto;box-shadow:0 30px 80px rgba(0,0,0,.25)}\n  .checkout header{display:flex;align-items:center;gap:18px;padding:18px 26px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--card);z-index:1}\n  .back{border:0;background:transparent;cursor:pointer;color:var(--mut);font:14px var(--sans)}\n  .cols{display:grid;grid-template-columns:1.4fr 1fr;gap:28px;padding:22px 26px 30px}\n  .form h4,.summary h4{font:600 13px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--acc);margin:18px 0 8px}\n  .form h4:first-child{margin-top:0}\n  .f{display:grid;gap:4px;font-size:12px;color:var(--mut)}\n  .f input{border:1px solid var(--line);border-radius:10px;padding:10px 12px;font:15px var(--sans);color:var(--ink);background:#fff;width:100%}\n  .f input:focus{outline:2px solid color-mix(in srgb,var(--acc) 35%,#fff);border-color:var(--acc)}\n  .row2{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:10px}\n  .form .f{margin-bottom:10px}\n  .row2 .f{margin-bottom:0}\n  .opts{display:grid;gap:8px;margin-bottom:12px}\n  .opt{display:grid;grid-template-columns:auto 1fr auto;gap:10px;align-items:center;border:1px solid var(--line);border-radius:10px;padding:10px 12px;cursor:pointer}\n  .opt.on{border-color:var(--acc);background:color-mix(in srgb,var(--acc) 6%,#fff)}\n  .opt b{grid-column:2}\n  .opt span{grid-column:2;color:var(--mut);font-size:12px}\n  .opt em{grid-column:3;grid-row:1/3;font-style:normal;font-weight:600}\n  .opt input{grid-row:1/3}\n  .muted{color:var(--mut);font-size:13px}\n  .small{font-size:12px;margin-top:10px}\n  .err{background:#fbe9e7;color:#8a2f22;border-radius:10px;padding:10px 12px;margin-top:10px;font-size:14px}\n  .summary{background:var(--bg);border-radius:14px;padding:18px;align-self:start;position:sticky;top:80px}\n  .sl{display:flex;justify-content:space-between;gap:12px;padding:6px 0;border-bottom:1px dashed var(--line);font-size:14px}\n  .sl.tot{border:0;font-size:18px;margin:6px 0 14px}\n  .summary .primary{width:100%;padding:13px}\n  .done{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:9;background:var(--card);border-radius:20px;padding:36px 40px;width:min(520px,92%);text-align:center;box-shadow:0 30px 80px rgba(0,0,0,.25)}\n  .done .badge{width:64px;height:64px;border-radius:50%;display:grid;place-items:center;margin:0 auto 14px;font:600 30px var(--serif);color:#fff;background:var(--acc)}\n  .done.bad .badge{background:#a0584b}\n  .done .num{font-size:15px;color:var(--mut)}\n  .done .actions{display:flex;gap:10px;justify-content:center;margin-top:18px}\n  .done a.primary{text-decoration:none;display:inline-block}\n  @media (max-width:860px){.hero{grid-template-columns:1fr}.cols{grid-template-columns:1fr}.checkout{inset:8px}.top{padding:12px 16px}.grid,.chips,.hero{padding-left:16px;padding-right:16px}}\n  @media (max-width:520px){.top{gap:12px;padding:10px 14px}.top nav{display:none}.brand{font-size:20px}.cartBtn{padding:7px 12px;font-size:13px}.hero{padding:22px 14px 12px;gap:16px}.hero p{font-size:14px;margin-bottom:12px}.hero .tiles{gap:8px}.tile .rotulo{width:86%;padding:6px 5px}.tile .rotulo b{font-size:10.5px}.tile .rotulo i{font-size:7.5px}.tile .rotulo em{font-size:9px}.chips{padding:4px 14px 12px}.grid{grid-template-columns:repeat(2,1fr);gap:10px;padding:0 14px 32px}.card .tile{aspect-ratio:1}.cbody{padding:10px 12px 12px}.card h3{font-size:15px}.card p{font-size:12px;min-height:0}.price{font-size:15px}.add{padding:7px 10px;font-size:13px}.stepper button{width:26px;height:28px}.foot{flex-direction:column;gap:6px;padding:18px 14px}.row2{grid-template-columns:1fr}.done{padding:26px 20px}.drawer{width:100%}}\n  `,\n  render(d2) {\n    const st = d2.state\n    const ds = d2.data('produtos')\n    const all = ds ? ds.records() : []\n    const cats = [...new Set(all.map((p) => p.categoria).filter(Boolean))]\n    const cat = st.cat || ''\n    const list = cat ? all.filter((p) => p.categoria === cat) : all\n    const feats = all.filter((p) => p.destaque).slice(0, 3)\n    const n = qty(st)\n    return `<div class=\"shop\">\n      <header class=\"top\"><div class=\"brand\">Loja <i>Boa</i></div>\n        <nav>${['', ...cats].map((c) => `<a class=\"chipnav${c === cat ? ' on' : ''}\" data-cat=\"${esc(c)}\">${c || 'Tudo'}</a>`).join('')}</nav>\n        <button class=\"cartBtn\">Sacola${n ? `<b>${n}</b>` : ''}</button></header>\n      <section class=\"hero\"><div><h1>Banho, corpo e casa, feitos como antigamente.</h1><p>Fórmulas vegetais, perfumes de botica e embalagens de papel. Frete grátis a partir de ${money(200)}.</p><button class=\"ghost ver\">Ver a vitrine ↓</button></div>\n        <div class=\"tiles\">${feats.map((p) => tile(p, true)).join('')}</div></section>\n      <div class=\"chips\">${['', ...cats].map((c) => `<button class=\"chip${c === cat ? ' on' : ''}\" data-cat=\"${esc(c)}\">${c || 'Tudo'}</button>`).join('')}</div>\n      ${ds && ds.loading ? '<div class=\"muted\" style=\"padding:0 32px 40px\">Carregando a vitrine…</div>' : `<div class=\"grid\">${list.map((p) => card(p, st)).join('')}</div>`}\n      <footer class=\"foot\"><span>Loja Boa · demonstração</span><span>Pedidos, separação, entrega e NPS rodam de verdade no data2.</span></footer>\n      ${st.cartOpen && st.view !== 'checkout' ? drawer(d2, st) : ''}\n      ${st.view === 'checkout' ? checkout(d2, st) : ''}\n      ${st.view === 'done' ? done(d2, st) : ''}\n    </div>`\n  },\n  mounted(d2) {\n    const st = d2.state\n    const local = loadLocal()\n    d2.setState({ cart: local.items || {}, cartId: local.cartId || null, form: local.form || {}, view: 'loja', cartOpen: false })\n    const persist = () => saveLocal({ items: st.cart, cartId: st.cartId, form: st.form })\n    const byId = (id) => (d2.data('produtos')?.records() || []).find((p) => String(p._id) === String(id))\n    let syncTimer = null\n    const sync = () => {\n      clearTimeout(syncTimer)\n      syncTimer = setTimeout(async () => {\n        const ds = d2.data('carrinhos'); if (!ds) return\n        const list = items(st)\n        const rec = { cliente: st.form?.email || '', nome: st.form?.nome || '', itens: list.map((it) => ({ produto: it.id, nome: it.nome, preco: it.preco, qtd: it.qtd })), total: subtotal(st), status: 'aberto', atualizado_em: new Date().toISOString() }\n        try {\n          if (st.cartId) await ds.update({ _id: st.cartId, ...rec })\n          else if (list.length) { const r = unwrap(await ds.create(rec)); st.cartId = r?._id || r?.data?._id || st.cartId; persist() }\n        } catch (e) { d2.log('cart sync', e) }\n      }, 500)\n    }\n    const setQty = (id, delta) => {\n      const p = byId(id); if (!p) return\n      const cart = { ...(st.cart || {}) }\n      const cur = cart[id]?.qtd || 0\n      const next = Math.max(0, Math.min(Number(p.estoque) || 0, cur + delta))\n      if (next === 0) delete cart[id]\n      else cart[id] = { id, nome: p.nome, preco: Number(p.preco), cor: p.cor, imagem: p.imagem, categoria: p.categoria, qtd: next }\n      st.cart = cart; persist(); sync()\n    }\n    d2.on('.add, .inc', 'click', (e, el) => setQty(el.dataset.id, +1))\n    d2.on('.dec', 'click', (e, el) => setQty(el.dataset.id, -1))\n    d2.on('.chip, .chipnav', 'click', (e, el) => { st.cat = el.dataset.cat || '' })\n    d2.on('.ver', 'click', () => { d2.$('.grid')?.scrollIntoView({ behavior: 'smooth' }) })\n    d2.on('.cartBtn', 'click', () => { st.cartOpen = !st.cartOpen })\n    d2.on('[data-close]', 'click', () => { st.cartOpen = false })\n    d2.on('.go-checkout', 'click', () => { d2.setState({ view: 'checkout', cartOpen: false, error: null }) })\n    d2.on('.back', 'click', () => { d2.setState({ view: 'loja', error: null }) })\n    d2.on('.retry', 'click', () => { d2.setState({ view: 'checkout', error: null }) })\n    d2.on('.form input', 'input', (e, el) => {\n      if (el.name === 'servico') return\n      st.form = { ...(st.form || {}), [el.name]: el.value }; persist()\n    })\n    d2.on('.form input', 'change', (e, el) => { if (el.name === 'email' || el.name === 'nome') sync() })\n    d2.on('input[name=servico]', 'change', (e, el) => { st.servico = el.value })\n    d2.on('.cotar', 'click', async () => {\n      const cep = (st.form?.cep || '').replace(/\\D/g, '')\n      if (cep.length < 8) { st.error = 'Informe um CEP com 8 dígitos.'; return }\n      d2.setState({ busy: 'cotar', error: null })\n      try {\n        const r = unwrap(await d2.rush(d2.params.checkout).invoke({ acao: 'cotar', cep, subtotal: subtotal(st) }))\n        if (!r?.ok) throw new Error(r?.error || 'não foi possível cotar o frete')\n        d2.setState({ frete: r, servico: r.opcoes?.[0]?.servico || null, busy: null })\n      } catch (e) { d2.setState({ busy: null, error: String(e.message || e) }) }\n    })\n    d2.on('.pagar', 'click', async () => {\n      const f = st.form || {}\n      const need = ['nome', 'email', 'cep', 'endereco', 'cidade', 'uf', 'cartao']\n      const missing = need.filter((k) => !String(f[k] || '').trim())\n      if (missing.length) { st.error = `Preencha: ${missing.join(', ')}.`; return }\n      if (!st.frete) { st.error = 'Calcule o frete antes de pagar.'; return }\n      const chosen = st.frete.opcoes.find((o) => o.servico === st.servico) || st.frete.opcoes[0]\n      const digits = String(f.cartao).replace(/\\D/g, '')\n      d2.setState({ busy: 'pagar', error: null })\n      try {\n        const r = unwrap(await d2.rush(d2.params.checkout).invoke({\n          acao: 'fechar',\n          cliente: { nome: f.nome, email: f.email, telefone: f.telefone || '' },\n          endereco: { cep: f.cep, endereco: f.endereco, cidade: f.cidade, uf: String(f.uf || '').toUpperCase() },\n          itens: items(st).map((it) => ({ produto: it.id, nome: it.nome, preco: it.preco, qtd: it.qtd })),\n          frete: { servico: chosen.servico, valor: Number(chosen.valor), prazo: chosen.prazo },\n          cartao: { final: digits.slice(-4), nome: f.titular || f.nome },\n          carrinhoId: st.cartId || null,\n        }))\n        if (!r?.ok) throw new Error(r?.error || 'o checkout falhou')\n        const paid = r.status === 'pago'\n        d2.setState({ done: r, view: 'done', busy: null, frete: paid ? null : st.frete })\n        if (paid) { st.cart = {}; st.cartId = null; persist() }\n      } catch (e) { d2.setState({ busy: null, error: String(e.message || e) }) }\n    })\n  },\n  tests: [\n    { name: 'renders the storefront with products', records: [{ _id: 'p1', nome: 'Sabonete', preco: 18.9, estoque: 3, categoria: 'Banho', destaque: true }], expect: { contains: 'Sabonete' } },\n  ],\n}\n"}},"produtos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"produtos"}}}},"carrinhos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"carrinhos"}}}},"checkout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkout"}}}}}}}}}},{"_id":"6ae498a0c2040be6c69fd8e0","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-pedidos","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 ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\nconst money = (n) => (Number(n) || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })\nconst when = (d) => { if (!d) return ''; const t = new Date(d); return isNaN(t) ? '' : t.toLocaleString('pt-BR', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' }) }\nconst unwrap = (r) => (r && typeof r === 'object' && 'data' in r && !('ok' in r) ? r.data : r)\nconst STEPS = [['pago', 'Pago'], ['separado', 'Separado'], ['enviado', 'Enviado'], ['em_rota', 'Em rota'], ['entregue', 'Entregue']]\nconst RANK = { aguardando_pagamento: 0, pago: 1, separado: 2, enviado: 3, em_rota: 4, entregue: 5, recusado: -1, cancelado: -2 }\nconst EV = { em_rota: 'Saiu para entrega', entregue: 'Entregue', coletado: 'Coletado pela transportadora', postado: 'Postado' }\nconst emailFromUrl = () => { try { return new URLSearchParams(location.search).get('email') || '' } catch { return '' } }\nconst emailLocal = () => { try { return JSON.parse(localStorage.getItem('lojaboa.cart') || '{}').form?.email || '' } catch { return '' } }\n\nfunction timeline(p) {\n  const r = RANK[p.status] ?? 0\n  if (r < 0) return `<div class=\"tl bad\"><span class=\"dot on\"></span><b>${p.status === 'recusado' ? 'Pagamento recusado' : 'Cancelado'}</b></div>`\n  return `<div class=\"tl\">${STEPS.map(([k, label], i) => { const done = (RANK[k] ?? 0) <= r; const cur = p.status === k; return `<div class=\"st${done ? ' on' : ''}${cur ? ' cur' : ''}\"><span class=\"dot\"></span><b>${label}</b><small>${done ? when(p[`${k}_em`] || (k === 'em_rota' ? p.rastreio?.atualizado_em : null)) : ''}</small></div>` }).join('')}</div>`\n}\n\nfunction nps(p, st, already) {\n  if (p.status !== 'entregue') return ''\n  if (already) return `<div class=\"npsdone\">Obrigado pela avaliação · nota <b>${already.nota}</b>${already.comentario ? ` · “${esc(already.comentario)}”` : ''}</div>`\n  const sel = st.nps?.[p.numero]\n  const nota = sel?.nota\n  return `<div class=\"nps\"><div class=\"q\">${esc(p.nps?.mensagem || 'De 0 a 10, o quanto você recomendaria a Loja Boa?')}</div>\n    <div class=\"scale\">${Array.from({ length: 11 }, (_, i) => `<button class=\"n${nota === i ? ' on' : ''}\" data-ped=\"${esc(p.numero)}\" data-nota=\"${i}\">${i}</button>`).join('')}</div>\n    ${nota != null ? `<div class=\"npsrow\"><input class=\"npsc\" data-ped=\"${esc(p.numero)}\" placeholder=\"Quer contar o porquê? (opcional)\" value=\"${esc(sel?.comentario || '')}\"><button class=\"primary npsgo\" data-ped=\"${esc(p.numero)}\" ${st.busy === p.numero ? 'disabled' : ''}>${st.busy === p.numero ? 'Enviando…' : 'Enviar'}</button></div>` : ''}\n  </div>`\n}\n\nfunction order(p, st, avals) {\n  const already = avals.find((a) => a.pedido === p.numero)\n  const ev = (p.rastreio?.eventos || []).slice().reverse()\n  return `<article class=\"order\" data-key=\"${esc(p._id)}\">\n    <header><div><div class=\"num\">Pedido ${esc(p.numero)}</div><div class=\"dt\">${when(p.criado_em)} · ${esc(p.cliente?.nome || '')}</div></div><div class=\"tot\">${money(p.total)}</div></header>\n    ${timeline(p)}\n    <div class=\"cols\">\n      <div><h5>Itens</h5>${(p.itens || []).map((it, i) => `<div class=\"it\" data-key=\"${i}\"><span>${it.qtd} × ${esc(it.nome)}</span><b>${money(it.preco * it.qtd)}</b></div>`).join('')}\n        <div class=\"it\"><span>Frete</span><b>${Number(p.frete) === 0 ? 'grátis' : money(p.frete)}</b></div></div>\n      <div><h5>Entrega</h5><div class=\"addr\">${esc(p.endereco?.endereco || '')}<br>${esc(p.endereco?.cidade || '')} · ${esc(p.endereco?.uf || '')} · CEP ${esc(p.endereco?.cep || '')}</div>\n        ${p.rastreio?.codigo ? `<h5>Rastreio</h5><div class=\"code\">${esc(p.rastreio.codigo)} <small>${esc(p.rastreio.transportadora || '')}</small></div>${ev.length ? `<ul class=\"evs\">${ev.map((e, i) => `<li data-key=\"${i}\"><b>${esc(EV[e.tipo] || e.tipo)}</b><small>${when(e.em)}${e.motorista ? ` · ${esc(e.motorista)}` : ''}</small></li>`).join('')}</ul>` : '<div class=\"muted\">Etiqueta gerada, aguardando coleta.</div>'}` : (p.status === 'pago' ? '<div class=\"muted\">Na fila de separação do estoque.</div>' : p.status === 'separado' ? '<div class=\"muted\">Separado, aguardando a transportadora.</div>' : '')}\n      </div>\n    </div>\n    ${nps(p, st, already)}\n  </article>`\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .wrap{--bg:#f6f1e8;--ink:#1e2a22;--mut:#6b7a70;--line:#e3dccd;--acc:#2f6b4f;--acc2:#d9a441;--card:#fffdf8;--serif:\"Iowan Old Style\",\"Palatino Linotype\",Palatino,Georgia,serif;--sans:-apple-system,\"Inter\",system-ui,sans-serif;\n    font:15px/1.5 var(--sans);color:var(--ink);background:var(--bg);height:100%;overflow:auto}\n  .top{display:flex;align-items:center;gap:22px;padding:14px 32px;border-bottom:1px solid var(--line);position:sticky;top:0;background:rgba(246,241,232,.92);backdrop-filter:blur(8px);z-index:2}\n  .brand{font:600 24px var(--serif)} .brand i{font-style:normal;color:var(--acc)}\n  .top a{color:var(--mut);text-decoration:none;font-size:14px} .top a.on{color:var(--ink);font-weight:600}\n  .who{margin-left:auto;display:flex;gap:8px;align-items:center}\n  .who input{border:1px solid var(--line);border-radius:10px;padding:8px 12px;font:14px var(--sans);min-width:240px;background:#fff}\n  .ghost{background:transparent;border:1px solid var(--line);border-radius:10px;padding:8px 12px;font:600 13px var(--sans);cursor:pointer;color:var(--ink)}\n  .primary{background:var(--acc);color:#fff;border:0;border-radius:10px;padding:9px 14px;font:600 14px var(--sans);cursor:pointer}\n  .primary[disabled]{opacity:.6}\n  .body{padding:28px 32px 48px;max-width:980px;margin:0 auto}\n  h1{font:600 34px var(--serif);margin:0 0 4px}\n  .sub{color:var(--mut);margin-bottom:22px}\n  .order{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:20px 22px;margin-bottom:16px}\n  .order header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:14px}\n  .num{font:600 20px var(--serif)} .dt{color:var(--mut);font-size:13px} .tot{font:600 20px var(--sans)}\n  .tl{display:grid;grid-template-columns:repeat(5,1fr);gap:0;margin:6px 0 18px;position:relative}\n  .tl:before{content:\"\";position:absolute;left:8%;right:8%;top:7px;height:2px;background:var(--line)}\n  .st{text-align:center;position:relative;color:var(--mut);font-size:12px}\n  .st .dot{display:block;width:16px;height:16px;border-radius:50%;background:#fff;border:2px solid var(--line);margin:0 auto 6px;position:relative}\n  .st.on{color:var(--ink)} .st.on .dot{background:var(--acc);border-color:var(--acc)}\n  .st.cur .dot{box-shadow:0 0 0 5px color-mix(in srgb,var(--acc) 25%,#fff)}\n  .st b{display:block} .st small{display:block;color:var(--mut);font-size:11px;min-height:14px}\n  .tl.bad{display:flex;align-items:center;gap:8px;color:#a0584b} .tl.bad:before{display:none} .tl.bad .dot{width:12px;height:12px;border-radius:50%;background:#a0584b;display:inline-block}\n  .cols{display:grid;grid-template-columns:1fr 1fr;gap:24px}\n  h5{font:600 11px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--acc);margin:0 0 6px}\n  .it{display:flex;justify-content:space-between;font-size:14px;padding:4px 0;border-bottom:1px dashed var(--line)}\n  .addr{font-size:14px;line-height:1.5;margin-bottom:12px}\n  .code{font:600 16px ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.04em} .code small{font:12px var(--sans);color:var(--mut);margin-left:6px}\n  .evs{list-style:none;padding:0;margin:8px 0 0} .evs li{padding:4px 0 4px 14px;border-left:2px solid var(--line);font-size:13px} .evs li:first-child{border-color:var(--acc)} .evs small{display:block;color:var(--mut)}\n  .muted{color:var(--mut);font-size:13px}\n  .nps{margin-top:18px;border-top:1px solid var(--line);padding-top:14px}\n  .q{font:600 16px var(--serif);margin-bottom:8px}\n  .scale{display:flex;gap:6px;flex-wrap:wrap}\n  .n{width:36px;height:36px;border-radius:10px;border:1px solid var(--line);background:#fff;cursor:pointer;font:600 14px var(--sans)}\n  .n.on{background:var(--acc);color:#fff;border-color:var(--acc)}\n  .npsrow{display:flex;gap:8px;margin-top:10px} .npsc{flex:1;border:1px solid var(--line);border-radius:10px;padding:9px 12px;font:14px var(--sans)}\n  .npsdone{margin-top:14px;color:var(--acc);font-size:14px}\n  .empty{background:var(--card);border:1px dashed var(--line);border-radius:16px;padding:40px;text-align:center;color:var(--mut)}\n  .empty a{color:var(--acc)}\n  @media (max-width:760px){.cols{grid-template-columns:1fr}.top{flex-wrap:wrap;padding:12px 16px}.who{margin-left:0;width:100%}.who input{flex:1;min-width:0}.body{padding:20px 16px}}\n  `,\n  render(d2) {\n    const st = d2.state\n    const ds = d2.data('pedidos')\n    const av = d2.data('avaliacoes')\n    const email = (st.email ?? '').trim().toLowerCase()\n    const all = ds ? ds.records() : []\n    const mine = email ? all.filter((p) => String(p.cliente?.email || '').toLowerCase() === email) : []\n    mine.sort((a, b) => new Date(b.criado_em || 0) - new Date(a.criado_em || 0))\n    const avals = av ? av.records() : []\n    return `<div class=\"wrap\">\n      <header class=\"top\"><div class=\"brand\">Loja <i>Boa</i></div><a href=\"/\">Vitrine</a><a class=\"on\">Meus pedidos</a>\n        <div class=\"who\"><input class=\"email\" type=\"email\" placeholder=\"seu e-mail\" value=\"${esc(st.email ?? '')}\"><button class=\"ghost refresh\" title=\"atualizar\">↻</button></div></header>\n      <div class=\"body\"><h1>Meus pedidos</h1><div class=\"sub\">${email ? `${mine.length} pedido${mine.length === 1 ? '' : 's'} de ${esc(email)} · atualiza sozinho` : 'Informe o e-mail usado na compra.'}</div>\n        ${ds && ds.loading ? '<div class=\"muted\">Carregando…</div>' : (email && !mine.length ? `<div class=\"empty\">Nenhum pedido ainda para este e-mail. <a href=\"/\">Ir à vitrine</a></div>` : mine.map((p) => order(p, st, avals)).join(''))}\n      </div></div>`\n  },\n  mounted(d2) {\n    const st = d2.state\n    d2.setState({ email: emailFromUrl() || emailLocal() || '', nps: {} })\n    const ds = d2.data('pedidos')\n    if (ds && ds.loadAll) ds.loadAll().catch(() => {})\n    const av = d2.data('avaliacoes')\n    if (av && av.loadAll) av.loadAll().catch(() => {})\n    const tick = setInterval(() => { try { d2.data('pedidos')?.refresh() } catch {} }, 10000)\n    d2.onUnmount(() => clearInterval(tick))\n    d2.on('.email', 'change', (e, el) => { st.email = el.value })\n    d2.on('.refresh', 'click', () => { d2.data('pedidos')?.refresh(); d2.data('avaliacoes')?.refresh() })\n    d2.on('.n', 'click', (e, el) => { const nps = { ...(st.nps || {}) }; nps[el.dataset.ped] = { ...(nps[el.dataset.ped] || {}), nota: Number(el.dataset.nota) }; st.nps = nps })\n    d2.on('.npsc', 'input', (e, el) => { const nps = { ...(st.nps || {}) }; nps[el.dataset.ped] = { ...(nps[el.dataset.ped] || {}), comentario: el.value }; st.nps = nps })\n    d2.on('.npsgo', 'click', async (e, el) => {\n      const num = el.dataset.ped\n      const sel = st.nps?.[num]; if (!sel || sel.nota == null) return\n      const p = (d2.data('pedidos')?.records() || []).find((x) => x.numero === num)\n      st.busy = num\n      try {\n        await d2.data('avaliacoes').create({ pedido: num, cliente: st.email, nota: sel.nota, comentario: sel.comentario || '', criado_em: new Date().toISOString() })\n        d2.data('avaliacoes')?.refresh()\n      } catch (err) { d2.log('nps', err) } finally { st.busy = null }\n    })\n  },\n  tests: [\n    { name: 'renders the header', expect: { contains: 'Meus pedidos' } },\n  ],\n}\n"}},"pedidos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pedidos"}}}},"avaliacoes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"avaliacoes"}}}}}}}}}},{"_id":"6ae498a0c0028ad81045f6fe","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-separacao","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 ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\nconst money = (n) => (Number(n) || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })\nconst unwrap = (r) => (r && typeof r === 'object' && 'data' in r && !('ok' in r) ? r.data : r)\nconst mins = (d) => { if (!d) return 0; const t = new Date(d).getTime(); return isNaN(t) ? 0 : Math.max(0, Math.round((Date.now() - t) / 60000)) }\nconst ago = (d) => { const m = mins(d); return m < 1 ? 'agora' : m < 60 ? `${m} min` : m < 1440 ? `${Math.floor(m / 60)} h ${m % 60} min` : `${Math.floor(m / 1440)} d` }\nconst hm = (d) => { if (!d) return ''; const t = new Date(d); return isNaN(t) ? '' : t.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) }\nconst OPS = ['Caio', 'Dani']\nconst today = (d) => d && new Date(d).toDateString() === new Date().toDateString()\n\nfunction pick(p, st) {\n  const done = st.picked?.[p.numero] || {}\n  const items = p.itens || []\n  const all = items.length && items.every((it, i) => done[i])\n  const busy = st.busy === p.numero\n  return `<article class=\"job\" data-key=\"${esc(p._id)}\">\n    <header><div><b>${esc(p.numero)}</b><span class=\"who\">${esc(p.cliente?.nome || '')}</span></div><span class=\"wait${mins(p.pago_em) > 30 ? ' late' : ''}\">esperando ${ago(p.pago_em)}</span></header>\n    <ul class=\"pl\">${items.map((it, i) => `<li data-key=\"${i}\"><label><input type=\"checkbox\" class=\"pk\" data-ped=\"${esc(p.numero)}\" data-i=\"${i}\" ${done[i] ? 'checked' : ''}><span class=\"q\">${it.qtd}×</span><span class=\"${done[i] ? 'ok' : ''}\">${esc(it.nome)}</span></label></li>`).join('')}</ul>\n    <div class=\"addr\">${esc(p.endereco?.cidade || '')} · ${esc(p.endereco?.uf || '')} · ${esc(p.frete === 0 ? 'frete grátis' : money(p.frete))}</div>\n    <button class=\"primary sep\" data-ped=\"${esc(p.numero)}\" ${busy ? 'disabled' : ''}>${busy ? 'Gravando…' : all ? 'Separado ✓' : `Separado (${items.filter((_, i) => done[i]).length}/${items.length})`}</button>\n  </article>`\n}\n\nfunction ready(p, st) {\n  const busy = st.busy === p.numero\n  return `<article class=\"job ready\" data-key=\"${esc(p._id)}\">\n    <header><div><b>${esc(p.numero)}</b><span class=\"who\">${esc(p.cliente?.nome || '')}</span></div><span class=\"wait\">separado ${hm(p.separado_em)} · ${esc(p.operador || '')}</span></header>\n    <div class=\"addr\">${esc(p.endereco?.endereco || '')}<br>${esc(p.endereco?.cidade || '')} · ${esc(p.endereco?.uf || '')} · CEP ${esc(p.endereco?.cep || '')}</div>\n    <button class=\"primary ship\" data-ped=\"${esc(p.numero)}\" ${busy ? 'disabled' : ''}>${busy ? 'Gerando etiqueta…' : 'Despachar → transportadora'}</button>\n  </article>`\n}\n\nfunction shipped(p) {\n  return `<article class=\"job done\" data-key=\"${esc(p._id)}\"><header><div><b>${esc(p.numero)}</b><span class=\"who\">${esc(p.cliente?.nome || '')}</span></div><span class=\"tag ${esc(p.status)}\">${esc({ enviado: 'enviado', em_rota: 'em rota', entregue: 'entregue' }[p.status] || p.status)}</span></header>\n    <div class=\"addr\"><span class=\"code\">${esc(p.rastreio?.codigo || '')}</span> ${esc(p.rastreio?.transportadora || '')} · ${hm(p.enviado_em)}</div></article>`\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .wrap{--bg:#f1f3f0;--ink:#1e2a22;--mut:#6b7a70;--line:#dfe3dc;--acc:#2f6b4f;--warn:#b7791f;--card:#fff;--sans:-apple-system,\"Inter\",system-ui,sans-serif;\n    font:14px/1.45 var(--sans);color:var(--ink);background:var(--bg);height:100%;overflow:auto;display:flex;flex-direction:column}\n  .top{display:flex;align-items:center;gap:18px;padding:12px 24px;background:#fff;border-bottom:1px solid var(--line);position:sticky;top:0;z-index:2}\n  .ttl{font-weight:700;font-size:16px} .ttl small{color:var(--mut);font-weight:500;margin-left:8px}\n  .ops{margin-left:auto;display:flex;gap:6px;align-items:center;color:var(--mut);font-size:13px}\n  .op{border:1px solid var(--line);background:#fff;border-radius:999px;padding:5px 12px;cursor:pointer;font:600 13px var(--sans)}\n  .op.on{background:var(--ink);color:#fff;border-color:var(--ink)}\n  .kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding:16px 24px 0}\n  .kpi{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px 14px}\n  .kpi b{display:block;font-size:26px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums} .kpi span{color:var(--mut);font-size:12px}\n  .kpi.warn b{color:var(--warn)}\n  .cols{display:grid;grid-template-columns:1.1fr 1fr .9fr;gap:16px;padding:16px 24px 32px;flex:1;align-items:start}\n  .col h4{font:600 11px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin:0 0 10px;display:flex;justify-content:space-between}\n  .col h4 b{color:var(--ink)}\n  .job{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;margin-bottom:10px;box-shadow:0 1px 2px rgba(0,0,0,.03)}\n  .job header{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}\n  .job header b{font-size:16px} .who{color:var(--mut);margin-left:8px;font-size:13px}\n  .wait{font-size:12px;color:var(--mut);white-space:nowrap} .wait.late{color:var(--warn);font-weight:600}\n  .pl{list-style:none;margin:0 0 10px;padding:0}\n  .pl li{padding:5px 0;border-bottom:1px dashed var(--line)} .pl label{display:flex;gap:10px;align-items:center;cursor:pointer}\n  .pl .q{color:var(--mut);min-width:28px;font-variant-numeric:tabular-nums} .pl .ok{text-decoration:line-through;color:var(--mut)}\n  .addr{font-size:13px;color:var(--mut);margin-bottom:10px}\n  .primary{width:100%;background:var(--acc);color:#fff;border:0;border-radius:10px;padding:10px 14px;font:600 14px var(--sans);cursor:pointer}\n  .primary[disabled]{opacity:.6}\n  .job.ready .primary{background:#1e2a22}\n  .job.done{padding:10px 14px} .job.done header{margin:0 0 4px}\n  .tag{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;background:#e8efe9;color:var(--acc)}\n  .tag.entregue{background:#e3f1e6} .tag.em_rota{background:#fff4df;color:var(--warn)}\n  .code{font:600 13px ui-monospace,SFMono-Regular,Menlo,monospace}\n  .empty{border:1px dashed var(--line);border-radius:14px;padding:26px;text-align:center;color:var(--mut);font-size:13px}\n  .toast{position:sticky;bottom:12px;margin:0 24px 12px;background:var(--ink);color:#fff;border-radius:12px;padding:10px 14px;font-size:13px}\n  @media (max-width:980px){.cols{grid-template-columns:1fr}.kpis{grid-template-columns:repeat(2,1fr)}}\n  `,\n  render(d2) {\n    const st = d2.state\n    const ds = d2.data('pedidos')\n    const all = ds ? ds.records() : []\n    const fila = all.filter((p) => p.status === 'pago').sort((a, b) => new Date(a.pago_em || 0) - new Date(b.pago_em || 0))\n    const prontos = all.filter((p) => p.status === 'separado').sort((a, b) => new Date(a.separado_em || 0) - new Date(b.separado_em || 0))\n    const saiu = all.filter((p) => ['enviado', 'em_rota', 'entregue'].includes(p.status)).sort((a, b) => new Date(b.enviado_em || 0) - new Date(a.enviado_em || 0)).slice(0, 12)\n    const hoje = all.filter((p) => today(p.separado_em)).length\n    const espera = fila.length ? Math.round(fila.reduce((s, p) => s + mins(p.pago_em), 0) / fila.length) : 0\n    const op = st.op || OPS[0]\n    return `<div class=\"wrap\">\n      <header class=\"top\"><span class=\"ttl\">Separação<small>Estoque · Loja Boa</small></span>\n        <div class=\"ops\">operador ${OPS.map((o) => `<button class=\"op${o === op ? ' on' : ''}\" data-op=\"${o}\">${o}</button>`).join('')}</div></header>\n      <div class=\"kpis\"><div class=\"kpi${espera > 30 ? ' warn' : ''}\"><b>${fila.length}</b><span>na fila de separação</span></div><div class=\"kpi${espera > 30 ? ' warn' : ''}\"><b>${espera} min</b><span>espera média na fila</span></div><div class=\"kpi\"><b>${prontos.length}</b><span>prontos para despacho</span></div><div class=\"kpi\"><b>${hoje}</b><span>separados hoje</span></div></div>\n      <div class=\"cols\">\n        <div class=\"col\"><h4>Fila de separação <b>${fila.length}</b></h4>${ds && ds.loading ? '<div class=\"empty\">Carregando…</div>' : fila.length ? fila.map((p) => pick(p, st)).join('') : '<div class=\"empty\">Nada para separar. Quando um pagamento for aprovado, o pedido aparece aqui.</div>'}</div>\n        <div class=\"col\"><h4>Prontos para despacho <b>${prontos.length}</b></h4>${prontos.length ? prontos.map((p) => ready(p, st)).join('') : '<div class=\"empty\">Nenhum pedido separado aguardando etiqueta.</div>'}</div>\n        <div class=\"col\"><h4>Despachados <b>${saiu.length}</b></h4>${saiu.length ? saiu.map(shipped).join('') : '<div class=\"empty\">Nenhum despacho ainda.</div>'}</div>\n      </div>\n      ${st.toast ? `<div class=\"toast\">${esc(st.toast)}</div>` : ''}\n    </div>`\n  },\n  mounted(d2) {\n    const st = d2.state\n    let op = OPS[0]; try { op = localStorage.getItem('lojaboa.op') || OPS[0] } catch {}\n    d2.setState({ op, picked: {} })\n    const ds = d2.data('pedidos')\n    if (ds && ds.loadAll) ds.loadAll().catch(() => {})\n    const tick = setInterval(() => { try { d2.data('pedidos')?.refresh() } catch {} }, 8000)\n    d2.onUnmount(() => clearInterval(tick))\n    const toast = (t) => { st.toast = t; setTimeout(() => { if (st.toast === t) st.toast = null }, 4000) }\n    d2.on('.op', 'click', (e, el) => { st.op = el.dataset.op; try { localStorage.setItem('lojaboa.op', el.dataset.op) } catch {} })\n    d2.on('.pk', 'change', (e, el) => { const picked = { ...(st.picked || {}) }; picked[el.dataset.ped] = { ...(picked[el.dataset.ped] || {}), [el.dataset.i]: el.checked }; st.picked = picked })\n    d2.on('.sep', 'click', async (e, el) => {\n      const num = el.dataset.ped\n      st.busy = num\n      try {\n        const r = unwrap(await d2.rush(d2.params.separar).invoke({ pedido: num, operador: st.op }))\n        if (!r?.ok) throw new Error(r?.error || 'não foi possível separar')\n        toast(`${num} separado por ${st.op}.`)\n        d2.data('pedidos')?.refresh()\n      } catch (err) { toast(`Erro: ${err.message || err}`) } finally { st.busy = null }\n    })\n    d2.on('.ship', 'click', async (e, el) => {\n      const num = el.dataset.ped\n      st.busy = num\n      try {\n        const r = unwrap(await d2.rush(d2.params.enviar).invoke({ pedido: num, operador: st.op }))\n        if (!r?.ok) throw new Error(r?.error || 'não foi possível despachar')\n        toast(`${num} despachado · ${r.transportadora || ''} ${r.codigo || ''}`)\n        d2.data('pedidos')?.refresh()\n      } catch (err) { toast(`Erro: ${err.message || err}`) } finally { st.busy = null }\n    })\n  },\n  tests: [\n    { name: 'renders the queue header', expect: { contains: 'Fila de separação' } },\n  ],\n}\n"}},"pedidos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pedidos"}}}},"separar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"separar"}}}},"enviar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"enviar"}}}}}}}}}},{"_id":"6ae498a1a85331b36e153d34","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-entrega","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 ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\nconst unwrap = (r) => (r && typeof r === 'object' && 'data' in r && !('ok' in r) ? r.data : r)\nconst hm = (d) => { if (!d) return ''; const t = new Date(d); return isNaN(t) ? '' : t.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) }\nconst today = (d) => d && new Date(d).toDateString() === new Date().toDateString()\n\nfunction stop(p, st, kind) {\n  const busy = st.busy === p.numero\n  return `<article class=\"stop ${kind}\" data-key=\"${esc(p._id)}\">\n    <div class=\"l\"><span class=\"code\">${esc(p.rastreio?.codigo || '')}</span><span class=\"car\">${esc(p.rastreio?.transportadora || '')}</span></div>\n    <div class=\"m\"><b>${esc(p.cliente?.nome || '')}</b><span>${esc(p.endereco?.endereco || '')}</span><span>${esc(p.endereco?.cidade || '')} · ${esc(p.endereco?.uf || '')} · CEP ${esc(p.endereco?.cep || '')}</span><small>${(p.itens || []).reduce((s, it) => s + Number(it.qtd || 0), 0)} volume(s) · pedido ${esc(p.numero)}${p.rastreio?.ultimo === 'em_rota' ? ` · saiu ${hm((p.rastreio.eventos || []).slice(-1)[0]?.em)}` : ''}</small></div>\n    <div class=\"r\">${kind === 'coleta' ? `<button class=\"go out\" data-ped=\"${esc(p.numero)}\" data-cod=\"${esc(p.rastreio?.codigo || '')}\" ${busy ? 'disabled' : ''}>${busy ? '…' : 'Saiu para entrega'}</button>` : `<button class=\"go done\" data-ped=\"${esc(p.numero)}\" data-cod=\"${esc(p.rastreio?.codigo || '')}\" ${busy ? 'disabled' : ''}>${busy ? '…' : 'Entregue ✓'}</button><button class=\"ghost fail\" data-ped=\"${esc(p.numero)}\" data-cod=\"${esc(p.rastreio?.codigo || '')}\" ${busy ? 'disabled' : ''}>Ausente</button>`}</div>\n  </article>`\n}\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .wrap{--bg:#15201a;--ink:#f1f3f0;--mut:#9fb0a6;--line:#2a3a31;--acc:#5bbf8a;--warn:#e0a23a;--card:#1d2b24;--sans:-apple-system,\"Inter\",system-ui,sans-serif;\n    font:15px/1.45 var(--sans);color:var(--ink);background:var(--bg);height:100%;overflow:auto}\n  .top{display:flex;align-items:center;gap:14px;padding:14px 20px;border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:2}\n  .ttl{font-weight:700;font-size:17px} .ttl small{color:var(--mut);font-weight:500;margin-left:8px}\n  .me{margin-left:auto;display:flex;gap:8px;align-items:center;color:var(--mut);font-size:13px}\n  .me input{background:var(--card);border:1px solid var(--line);color:var(--ink);border-radius:10px;padding:8px 12px;font:14px var(--sans);width:150px}\n  .stats{display:flex;gap:10px;padding:14px 20px 0;flex-wrap:wrap}\n  .st{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:10px 14px;min-width:120px}\n  .st b{display:block;font-size:24px;font-weight:700;font-variant-numeric:tabular-nums} .st span{color:var(--mut);font-size:12px}\n  h4{font:600 11px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin:18px 20px 8px}\n  .stop{display:grid;grid-template-columns:120px 1fr auto;gap:14px;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px 14px;margin:0 20px 10px}\n  .stop.rota{border-color:#3d5a4a}\n  .l{display:grid;gap:2px} .code{font:600 14px ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.04em} .car{color:var(--mut);font-size:12px}\n  .m{display:grid;gap:1px;font-size:14px} .m span{color:var(--mut);font-size:13px} .m small{color:var(--mut);font-size:12px;margin-top:3px}\n  .r{display:flex;gap:8px}\n  .go{border:0;border-radius:10px;padding:10px 14px;font:600 14px var(--sans);cursor:pointer;color:#0f1a14;background:var(--acc);white-space:nowrap}\n  .go.out{background:var(--warn)}\n  .go[disabled],.ghost[disabled]{opacity:.5}\n  .ghost{background:transparent;border:1px solid var(--line);color:var(--ink);border-radius:10px;padding:10px 12px;font:600 13px var(--sans);cursor:pointer}\n  .empty{margin:0 20px 10px;border:1px dashed var(--line);border-radius:14px;padding:22px;text-align:center;color:var(--mut);font-size:13px}\n  .log{margin:0 20px 24px;padding:0;list-style:none}\n  .log li{display:flex;gap:10px;padding:6px 0;border-bottom:1px solid var(--line);font-size:13px;color:var(--mut)}\n  .log li b{color:var(--ink)} .log .code{font-size:12px}\n  .toast{position:sticky;bottom:12px;margin:0 20px 12px;background:var(--acc);color:#0f1a14;border-radius:12px;padding:10px 14px;font-size:13px;font-weight:600}\n  @media (max-width:720px){.stop{grid-template-columns:1fr;gap:8px}.r{justify-content:flex-start}}\n  `,\n  render(d2) {\n    const st = d2.state\n    const ds = d2.data('pedidos')\n    const all = ds ? ds.records() : []\n    const coleta = all.filter((p) => p.status === 'enviado').sort((a, b) => new Date(a.enviado_em || 0) - new Date(b.enviado_em || 0))\n    const rota = all.filter((p) => p.status === 'em_rota')\n    const feitas = all.filter((p) => p.status === 'entregue' && today(p.entregue_em)).sort((a, b) => new Date(b.entregue_em || 0) - new Date(a.entregue_em || 0))\n    return `<div class=\"wrap\">\n      <header class=\"top\"><span class=\"ttl\">Entrega<small>rota do dia · Loja Boa</small></span><div class=\"me\">motorista <input class=\"moto\" value=\"${esc(st.moto || '')}\" placeholder=\"seu nome\"></div></header>\n      <div class=\"stats\"><div class=\"st\"><b>${coleta.length}</b><span>para coletar</span></div><div class=\"st\"><b>${rota.length}</b><span>em rota agora</span></div><div class=\"st\"><b>${feitas.length}</b><span>entregues hoje</span></div></div>\n      <h4>Em rota · ${rota.length}</h4>\n      ${rota.length ? rota.map((p) => stop(p, st, 'rota')).join('') : '<div class=\"empty\">Nenhuma entrega em rota. Colete abaixo.</div>'}\n      <h4>Para coletar na expedição · ${coleta.length}</h4>\n      ${ds && ds.loading ? '<div class=\"empty\">Carregando…</div>' : coleta.length ? coleta.map((p) => stop(p, st, 'coleta')).join('') : '<div class=\"empty\">Nada despachado ainda. Quando o estoque gerar a etiqueta, o volume aparece aqui.</div>'}\n      <h4>Entregues hoje · ${feitas.length}</h4>\n      ${feitas.length ? `<ul class=\"log\">${feitas.map((p) => `<li data-key=\"${esc(p._id)}\"><span class=\"code\">${esc(p.rastreio?.codigo || '')}</span><b>${esc(p.cliente?.nome || '')}</b><span>${esc(p.endereco?.cidade || '')}</span><span>· ${hm(p.entregue_em)} · ${esc(p.motorista || '')}</span></li>`).join('')}</ul>` : '<div class=\"empty\">Nenhuma entrega concluída hoje.</div>'}\n      ${st.toast ? `<div class=\"toast\">${esc(st.toast)}</div>` : ''}\n    </div>`\n  },\n  mounted(d2) {\n    const st = d2.state\n    let moto = 'Motorista'; try { moto = localStorage.getItem('lojaboa.moto') || moto } catch {}\n    d2.setState({ moto })\n    const ds = d2.data('pedidos')\n    if (ds && ds.loadAll) ds.loadAll().catch(() => {})\n    const tick = setInterval(() => { try { d2.data('pedidos')?.refresh() } catch {} }, 8000)\n    d2.onUnmount(() => clearInterval(tick))\n    const toast = (t) => { st.toast = t; setTimeout(() => { if (st.toast === t) st.toast = null }, 4000) }\n    d2.on('.moto', 'change', (e, el) => { st.moto = el.value; try { localStorage.setItem('lojaboa.moto', el.value) } catch {} })\n    const fire = async (el, evento, obs) => {\n      const num = el.dataset.ped\n      st.busy = num\n      try {\n        const r = unwrap(await d2.rush(d2.params.rastreio).invoke({ codigo: el.dataset.cod, evento, motorista: st.moto, obs }))\n        if (!r?.ok) throw new Error(r?.error || 'falhou')\n        toast(evento === 'entregue' ? `${num} entregue. Pesquisa NPS disparada.` : evento === 'em_rota' ? `${num} saiu para entrega.` : `${num}: ${obs}`)\n        d2.data('pedidos')?.refresh()\n      } catch (err) { toast(`Erro: ${err.message || err}`) } finally { st.busy = null }\n    }\n    d2.on('.go.out', 'click', (e, el) => fire(el, 'em_rota'))\n    d2.on('.go.done', 'click', (e, el) => fire(el, 'entregue'))\n    d2.on('.fail', 'click', (e, el) => fire(el, 'em_rota', 'destinatário ausente, nova tentativa amanhã'))\n  },\n  tests: [\n    { name: 'renders the route header', expect: { contains: 'Em rota' } },\n  ],\n}\n"}},"pedidos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pedidos"}}}},"rastreio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rastreio"}}}}}}}}}},{"_id":"6ae498a1c2040be6c69fd8ef","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-cockpit","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 ?? '').replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c])\nconst money = (n) => (Number(n) || 0).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL', maximumFractionDigits: 0 })\nconst today = (d) => d && new Date(d).toDateString() === new Date().toDateString()\nconst mins = (d) => { if (!d) return 0; const t = new Date(d).getTime(); return isNaN(t) ? 0 : Math.max(0, Math.round((Date.now() - t) / 60000)) }\nconst hm = (d) => { if (!d) return ''; const t = new Date(d); return isNaN(t) ? '' : t.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) }\nconst LABEL = { aguardando_pagamento: 'aguardando pagamento', pago: 'pago', separado: 'separado', enviado: 'enviado', em_rota: 'em rota', entregue: 'entregue', recusado: 'recusado' }\nconst lastTouch = (p) => Math.max(...['criado_em', 'pago_em', 'separado_em', 'enviado_em', 'entregue_em'].map((k) => new Date(p[k] || 0).getTime()), new Date(p.rastreio?.atualizado_em || 0).getTime())\n\nconst kpi = (label, value, sub, tone = '') => `<div class=\"kpi ${tone}\"><span>${label}</span><b>${value}</b><small>${sub || ''}</small></div>`\n\nexport default {\n  css: `\n  :host{display:block;height:100%}\n  *{box-sizing:border-box}\n  .wrap{--bg:#f6f1e8;--ink:#1e2a22;--mut:#6b7a70;--line:#e3dccd;--acc:#2f6b4f;--warn:#b7791f;--bad:#a0584b;--card:#fffdf8;--serif:\"Iowan Old Style\",\"Palatino Linotype\",Palatino,Georgia,serif;--sans:-apple-system,\"Inter\",system-ui,sans-serif;\n    font:14px/1.45 var(--sans);color:var(--ink);background:var(--bg);height:100%;overflow:auto;padding:22px 26px 30px}\n  header{display:flex;align-items:baseline;gap:14px;margin-bottom:16px}\n  h1{font:600 28px var(--serif);margin:0} .sub{color:var(--mut)}\n  .live{margin-left:auto;font-size:12px;color:var(--acc);display:flex;align-items:center;gap:6px}\n  .live i{width:8px;height:8px;border-radius:50%;background:var(--acc);display:inline-block;animation:p 1.6s infinite}\n  @keyframes p{0%,100%{opacity:1}50%{opacity:.3}}\n  .kpis{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px}\n  .kpi{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px 14px;display:grid;gap:2px}\n  .kpi span{color:var(--mut);font-size:12px} .kpi b{font-size:28px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums;line-height:1.1} .kpi small{color:var(--mut);font-size:11px;min-height:14px}\n  .kpi.main{background:var(--ink);color:#fff;border-color:var(--ink)} .kpi.main span,.kpi.main small{color:#b9c6bd}\n  .kpi.warn b{color:var(--warn)} .kpi.bad b{color:var(--bad)}\n  .row{display:grid;grid-template-columns:1.2fr 1fr;gap:14px}\n  .panel{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}\n  h4{font:600 11px var(--sans);letter-spacing:.14em;text-transform:uppercase;color:var(--mut);margin:0 0 10px}\n  .funnel{display:grid;gap:8px}\n  .fr{display:grid;grid-template-columns:150px 1fr 40px;gap:10px;align-items:center;font-size:13px}\n  .bar{height:14px;border-radius:7px;background:#ece6d8;overflow:hidden} .bar i{display:block;height:100%;background:var(--acc);border-radius:7px}\n  .fr.bad .bar i{background:var(--bad)} .fr.warn .bar i{background:var(--warn)}\n  .fr b{text-align:right;font-variant-numeric:tabular-nums}\n  .feed{list-style:none;margin:0;padding:0;max-height:360px;overflow:auto}\n  .feed li{display:grid;grid-template-columns:48px 1fr auto;gap:10px;padding:7px 0;border-bottom:1px dashed var(--line);font-size:13px;align-items:center}\n  .feed time{color:var(--mut);font-variant-numeric:tabular-nums;font-size:12px}\n  .tag{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;padding:3px 8px;border-radius:999px;background:#e8efe9;color:var(--acc)}\n  .tag.recusado{background:#fbe9e7;color:var(--bad)} .tag.aguardando_pagamento{background:#f1ede4;color:var(--mut)} .tag.em_rota{background:#fff4df;color:var(--warn)}\n  .nps{display:flex;gap:16px;align-items:baseline} .nps b{font-size:34px;font-weight:700} .nps span{color:var(--mut);font-size:12px}\n  .mini{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}\n  @media (max-width:1100px){.kpis{grid-template-columns:repeat(3,1fr)}.row{grid-template-columns:1fr}}\n  `,\n  render(d2) {\n    const ped = d2.data('pedidos')?.records() || []\n    const carts = d2.data('carrinhos')?.records() || []\n    const av = d2.data('avaliacoes')?.records() || []\n    const hoje = ped.filter((p) => today(p.criado_em))\n    const pagosHoje = ped.filter((p) => p.pago_em && today(p.pago_em))\n    const receita = pagosHoje.reduce((s, p) => s + Number(p.total || 0), 0)\n    const recusados = hoje.filter((p) => p.status === 'recusado').length\n    const pctRec = hoje.length ? Math.round((recusados / hoje.length) * 100) : 0\n    const fila = ped.filter((p) => p.status === 'pago')\n    const espera = fila.length ? Math.round(fila.reduce((s, p) => s + mins(p.pago_em), 0) / fila.length) : 0\n    const rota = ped.filter((p) => ['enviado', 'em_rota'].includes(p.status)).length\n    const entreguesHoje = ped.filter((p) => p.status === 'entregue' && today(p.entregue_em)).length\n    const abertos = carts.filter((c) => c.status === 'aberto').length\n    const lembrados = carts.filter((c) => c.status === 'lembrado').length\n    const convertidos = carts.filter((c) => c.status === 'convertido').length\n    const notas = av.map((a) => Number(a.nota)).filter((n) => !isNaN(n))\n    const prom = notas.filter((n) => n >= 9).length, det = notas.filter((n) => n <= 6).length\n    const npsScore = notas.length ? Math.round(((prom - det) / notas.length) * 100) : null\n    const steps = [['aguardando_pagamento', 'Aguardando pagamento'], ['pago', 'Pago (na separação)'], ['separado', 'Separado'], ['enviado', 'Enviado'], ['em_rota', 'Em rota'], ['entregue', 'Entregue'], ['recusado', 'Recusado']]\n    const counts = Object.fromEntries(steps.map(([k]) => [k, ped.filter((p) => p.status === k).length]))\n    const max = Math.max(1, ...Object.values(counts))\n    const feed = ped.slice().sort((a, b) => lastTouch(b) - lastTouch(a)).slice(0, 14)\n    return `<div class=\"wrap\">\n      <header><h1>Cockpit</h1><span class=\"sub\">Loja Boa · operação de hoje</span><span class=\"live\"><i></i>ao vivo</span></header>\n      <div class=\"kpis\">\n        ${kpi('Pedidos pagos hoje', pagosHoje.length, money(receita) + ' em receita', 'main')}\n        ${kpi('Na separação', fila.length, espera ? `espera média ${espera} min` : 'fila vazia', espera > 30 ? 'warn' : '')}\n        ${kpi('Em rota', rota, `${entreguesHoje} entregue${entreguesHoje === 1 ? '' : 's'} hoje`)}\n        ${kpi('Pagamentos recusados', `${pctRec}%`, `${recusados} de ${hoje.length} hoje`, pctRec > 15 ? 'bad' : '')}\n        ${kpi('Carrinhos abertos', abertos, `${lembrados} lembrado${lembrados === 1 ? '' : 's'} · ${convertidos} convertido${convertidos === 1 ? '' : 's'}`)}\n        ${kpi('NPS', npsScore == null ? '—' : npsScore, notas.length ? `${notas.length} resposta${notas.length === 1 ? '' : 's'}` : 'sem respostas ainda')}\n      </div>\n      <div class=\"row\">\n        <div class=\"panel\"><h4>Pedidos por estado</h4><div class=\"funnel\">${steps.map(([k, label]) => `<div class=\"fr${k === 'recusado' ? ' bad' : k === 'pago' && espera > 30 ? ' warn' : ''}\" data-key=\"${k}\"><span>${label}</span><div class=\"bar\"><i style=\"width:${Math.round((counts[k] / max) * 100)}%\"></i></div><b>${counts[k]}</b></div>`).join('')}</div>\n          <div class=\"mini\"><div><h4 style=\"margin-top:14px\">Carrinhos</h4><div class=\"funnel\">${[['aberto', 'Abertos', abertos], ['lembrado', 'Lembrados (WhatsApp)', lembrados], ['convertido', 'Convertidos', convertidos]].map(([k, l, v]) => `<div class=\"fr\" data-key=\"${k}\"><span>${l}</span><div class=\"bar\"><i style=\"width:${Math.round((v / Math.max(1, abertos, lembrados, convertidos)) * 100)}%\"></i></div><b>${v}</b></div>`).join('')}</div></div>\n          <div><h4 style=\"margin-top:14px\">NPS</h4><div class=\"nps\"><b>${npsScore == null ? '—' : npsScore}</b><span>${prom} promotores · ${det} detratores</span></div></div></div>\n        </div>\n        <div class=\"panel\"><h4>Últimos movimentos</h4><ul class=\"feed\">${feed.map((p) => `<li data-key=\"${esc(p._id)}\"><time>${hm(lastTouch(p))}</time><span><b>${esc(p.numero)}</b> · ${esc(p.cliente?.nome || '')} · ${money(p.total)}</span><span class=\"tag ${esc(p.status)}\">${esc(LABEL[p.status] || p.status)}</span></li>`).join('') || '<li>Nenhum pedido ainda.</li>'}</ul></div>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    for (const n of ['pedidos', 'carrinhos', 'avaliacoes']) { const ds = d2.data(n); if (ds && ds.loadAll) ds.loadAll().catch(() => {}) }\n    const tick = setInterval(() => { for (const n of ['pedidos', 'carrinhos', 'avaliacoes']) { try { d2.data(n)?.refresh() } catch {} } }, 10000)\n    d2.onUnmount(() => clearInterval(tick))\n  },\n  tests: [\n    { name: 'renders the cockpit', expect: { contains: 'Cockpit' } },\n  ],\n}\n"}},"pedidos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pedidos"}}}},"carrinhos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"carrinhos"}}}},"avaliacoes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"avaliacoes"}}}}}}}}}},{"_id":"6ae4d6d701f7c0437efe186f","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"mapa_preco_m2","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const LIB = { name: 'leaflet@1.9.4', css: 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css' }\nconst DAY = 86400000\nconst BANDS = [\n  { max: 9000, c: '#3b82c4', l: 'até 9 mil' }, { max: 12000, c: '#38b2a3', l: '9–12 mil' },\n  { max: 15000, c: '#9acb5a', l: '12–15 mil' }, { max: 18000, c: '#f2b134', l: '15–18 mil' },\n  { max: Infinity, c: '#e0452f', l: 'acima de 18 mil' }\n]\nconst BAIRROS = [\n  { id: 'pin', nome: 'Pinheiros', lat: -23.567, lng: -46.692, base: 14800, ruas: [['Rua dos Pinheiros', -23.566, -46.683, -23.568, -46.696], ['Rua Teodoro Sampaio', -23.5635, -46.676, -23.566, -46.689]] },\n  { id: 'vm', nome: 'Vila Madalena', lat: -23.556, lng: -46.691, base: 13200, ruas: [['Rua Harmonia', -23.554, -46.690, -23.559, -46.693], ['Rua Aspicuelta', -23.556, -46.688, -23.558, -46.695]] },\n  { id: 'itb', nome: 'Itaim Bibi', lat: -23.584, lng: -46.679, base: 19500, ruas: [['Rua Joaquim Floriano', -23.581, -46.674, -23.585, -46.680], ['Rua Tabapuã', -23.583, -46.675, -23.586, -46.679], ['Av. Brig. Faria Lima', -23.579, -46.689, -23.590, -46.686]] },\n  { id: 'moe', nome: 'Moema', lat: -23.601, lng: -46.666, base: 16900, ruas: [['Av. Ibirapuera', -23.596, -46.664, -23.611, -46.666], ['Alameda dos Jurupis', -23.600, -46.665, -23.604, -46.669]] },\n  { id: 'vmar', nome: 'Vila Mariana', lat: -23.589, lng: -46.635, base: 12600, ruas: [['Rua Domingos de Morais', -23.588, -46.640, -23.593, -46.635], ['Rua Vergueiro', -23.576, -46.637, -23.587, -46.635]] },\n  { id: 'perd', nome: 'Perdizes', lat: -23.536, lng: -46.677, base: 11400, ruas: [['Rua Cardoso de Almeida', -23.535, -46.682, -23.538, -46.670], ['Rua Monte Alegre', -23.537, -46.680, -23.540, -46.672]] },\n  { id: 'ipi', nome: 'Ipiranga', lat: -23.590, lng: -46.610, base: 8900, ruas: [['Rua do Manifesto', -23.588, -46.608, -23.593, -46.612], ['Av. Nazaré', -23.587, -46.608, -23.594, -46.606]] },\n  { id: 'sau', nome: 'Saúde', lat: -23.618, lng: -46.637, base: 10300, ruas: [['Av. Jabaquara', -23.610, -46.640, -23.625, -46.637], ['Rua Domingos de Morais (Saúde)', -23.615, -46.638, -23.620, -46.636]] }\n]\nconst bandOf = v => BANDS.find(b => v <= b.max)\nconst brl = n => 'R$ ' + Math.round(n).toLocaleString('pt-BR')\nconst esc = s => String(s == null ? '' : s).replace(/[&<>\"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[c]))\nconst fdate = t => new Date(t).toLocaleDateString('pt-BR')\nconst meters = (a, b, c, d) => Math.hypot((d - b) * Math.cos((a + c) * Math.PI / 360) * 111320, (c - a) * 110540)\nconst med = arr => { if (!arr.length) return 0; const a = arr.slice().sort((x, y) => x - y), m = Math.floor(a.length / 2); return a.length % 2 ? a[m] : (a[m - 1] + a[m]) / 2 }\nconst MOCK = (() => {\n  let seed = 7\n  const rnd = () => { seed = (seed * 1664525 + 1013904223) % 4294967296; return seed / 4294967296 }\n  const now = Date.now(), out = []\n  const TIPOS = ['Apartamento', 'Apartamento', 'Apartamento', 'Casa', 'Terreno']\n  BAIRROS.forEach(b => {\n    for (let i = 0; i < 22; i++) {\n      const r = b.ruas[Math.floor(rnd() * b.ruas.length)], t = rnd(), tipo = TIPOS[Math.floor(rnd() * TIPOS.length)]\n      const area = tipo === 'Terreno' ? 200 + Math.round(rnd() * 400) : 35 + Math.round(rnd() * 140)\n      const m2 = b.base * (0.8 + rnd() * 0.45) * (tipo === 'Terreno' ? 0.7 : 1)\n      const dias = Math.floor(Math.pow(rnd(), 1.4) * 95) + 1\n      out.push({\n        id: b.id + '-' + i, bairroId: b.id, bairro: b.nome, rua: r[0], numero: 30 + Math.floor(rnd() * 1700),\n        unidade: tipo === 'Apartamento' ? 'Apto ' + (2 + Math.floor(rnd() * 20)) + '0' + (1 + Math.floor(rnd() * 4)) : '',\n        tipo, area, m2, valor: m2 * area, dias,\n        lat: r[1] + (r[3] - r[1]) * t + (rnd() - 0.5) * 0.0006, lng: r[2] + (r[4] - r[2]) * t + (rnd() - 0.5) * 0.0006,\n        data: now - dias * DAY, registro: now - Math.max(1, dias - 3 - Math.floor(rnd() * 7)) * DAY\n      })\n    }\n  })\n  return out\n})()\nfunction compute(d2) {\n  const given = d2.param('sales', [])\n  const all = Array.isArray(given) && given.length ? given : MOCK\n  const dias = d2.state.dias || 90\n  const sales = all.filter(s => s.dias <= dias)\n  const stat = {}\n  BAIRROS.forEach(b => { const a = sales.filter(s => s.bairroId === b.id); stat[b.id] = { n: a.length, med: med(a.map(s => s.m2)) || b.base, last: a.length ? Math.min(...a.map(s => s.dias)) : null } })\n  const c = d2.state.click\n  const near = c ? sales.filter(s => meters(c.lat, c.lng, s.lat, s.lng) <= 300) : []\n  return { all, sales, dias, stat, near, c }\n}\nexport default {\n  css: `\n  :host{display:block}\n  .wrap{display:grid;grid-template-columns:1fr 330px;grid-template-rows:auto 1fr;gap:12px;height:100%;padding:16px;box-sizing:border-box;background:#0f141b;color:#e8edf3;font-family:Inter,system-ui,sans-serif}\n  .top{grid-column:1/3;display:flex;align-items:center;gap:12px;flex-wrap:wrap}\n  .ttl{font-size:18px;font-weight:700;margin-right:auto}\n  .ttl small{display:block;font-size:12px;font-weight:400;color:#8b98a9;margin-top:2px}\n  .fresh{display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:8px;font-size:12px;background:#1a222d}\n  .fresh i{width:9px;height:9px;border-radius:50%;display:inline-block}\n  .seg{display:flex;background:#1a222d;border-radius:8px;padding:3px;gap:2px}\n  .seg button{all:unset;cursor:pointer;padding:6px 12px;border-radius:6px;font-size:12px;color:#9fb0c3}\n  .seg button[data-on]{background:#2f6fed;color:#fff}\n  .mapbox{position:relative;border-radius:12px;overflow:hidden;min-height:420px;background:#141b24}\n  .lmap{position:absolute;inset:0}\n  .load{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#8b98a9;z-index:5;background:#141b24}\n  .side{background:#1a222d;border-radius:12px;padding:16px;overflow:auto;display:flex;flex-direction:column;gap:12px}\n  .side h3{margin:0;font-size:15px}\n  .muted{color:#8b98a9;font-size:12px}\n  .kpis{display:grid;grid-template-columns:1fr 1fr;gap:8px}\n  .kpi{background:#0f141b;border-radius:8px;padding:10px}\n  .kpi b{display:block;font-size:15px;margin-top:2px}\n  .via{background:linear-gradient(135deg,#1d3b78,#2f6fed);border-radius:10px;padding:12px}\n  .via b{font-size:20px;display:block;margin:4px 0}\n  .card{background:#0f141b;border-radius:8px;padding:10px;font-size:13px;line-height:1.55}\n  .bl{display:flex;flex-direction:column;gap:4px}\n  .bn{display:flex;justify-content:space-between;gap:8px;padding:6px 8px;border-radius:6px;background:#0f141b;font-size:12px;cursor:pointer}\n  .bn:hover{background:#243040}\n  .lg{display:flex;flex-wrap:wrap;gap:8px;font-size:11px}\n  .lg i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:4px;vertical-align:-1px}\n  .leaflet-container{font-family:inherit}\n  `,\n  render(d2) {\n    const lib = d2.lib(LIB)\n    const { all, sales, dias, stat, near, c } = compute(d2)\n    const mode = d2.state.mode || 'heat'\n    const sale = sales.find(s => s.id === d2.state.sale)\n    const lastAge = all.length ? Math.min(...all.map(s => s.dias)) : null\n    const fc = lastAge == null ? '#8b98a9' : lastAge <= 30 ? '#38b26b' : lastAge <= 60 ? '#f2b134' : '#e0452f'\n    const fl = lastAge == null ? 'sem dados' : lastAge <= 30 ? 'dados atuais' : lastAge <= 60 ? 'atenção: dados com mais de 30 dias' : 'dados defasados'\n    let side = ''\n    if (sale) {\n      side = '<div class=\"card\"><b>' + esc(sale.rua) + ', ' + sale.numero + '</b>' + (sale.unidade ? ' · ' + esc(sale.unidade) : '') + '<br>' + esc(sale.tipo) + ' · ' + sale.area + ' m²<br>' + brl(sale.valor) + ' · <b>' + brl(sale.m2) + '/m²</b><br><span class=\"muted\">Transação em ' + fdate(sale.data) + ' (há ' + sale.dias + ' dias)<br>Registrada no ITBI em ' + fdate(sale.registro) + '</span></div>'\n    } else if (c) {\n      const ms = near.map(s => s.m2), ruas = {}\n      near.forEach(s => { (ruas[s.rua] = ruas[s.rua] || []).push(s.m2) })\n      const top = Object.keys(ruas).sort((a, b) => ruas[b].length - ruas[a].length).slice(0, 3)\n      const m = med(ms), recent = near.length ? Math.min(...near.map(s => s.dias)) : null\n      side = near.length\n        ? '<h3>Raio de 300 m</h3><div class=\"kpis\"><div class=\"kpi\"><span class=\"muted\">Mediana</span><b>' + brl(m) + '/m²</b></div><div class=\"kpi\"><span class=\"muted\">Vendas</span><b>' + near.length + '</b></div><div class=\"kpi\"><span class=\"muted\">Mais recente</span><b style=\"font-size:13px\">' + (recent === 0 ? 'hoje' : 'há ' + recent + ' d') + '</b></div><div class=\"kpi\"><span class=\"muted\">Mais antiga</span><b style=\"font-size:13px\">há ' + Math.max(...near.map(s => s.dias)) + ' d</b></div></div>'\n          + '<div class=\"card\">' + top.map(r => esc(r) + ' · ' + ruas[r].length + ' vendas · ' + brl(med(ruas[r])) + '/m²').join('<br>') + '</div>'\n          + '<div class=\"via\"><span class=\"muted\" style=\"color:#cfe0ff\">Viabilidade · preço de venda sugerido</span><b>' + brl(m * 70) + '</b><span style=\"font-size:12px;color:#cfe0ff\">unidade de 70 m² a ' + brl(m) + '/m² neste raio</span></div>'\n        : '<div class=\"card\">Nenhuma venda em 300 m nos últimos ' + dias + ' dias. Amplie o período ou clique em outro ponto.</div>'\n    } else side = '<div class=\"card\">Clique em qualquer rua do mapa para ver o preço do m² num raio de 300 m, com a data das transações. Clique num ponto para ver a venda.</div>'\n    return '<div class=\"wrap\">'\n      + '<div class=\"top\"><div class=\"ttl\">' + esc(d2.param('titulo', 'Preço real do m² · ITBI')) + '<small>' + sales.length + ' vendas nos últimos ' + dias + ' dias · dados de exemplo</small></div>'\n      + '<div class=\"fresh\"><i style=\"background:' + fc + '\"></i><span>Venda mais recente: ' + (lastAge == null ? '—' : 'há ' + lastAge + ' dias') + ' · ' + fl + '</span></div>'\n      + '<div class=\"seg\">' + [['heat', 'Calor'], ['points', 'Vendas']].map(m => '<button data-mode=\"' + m[0] + '\" ' + (mode === m[0] ? 'data-on' : '') + '>' + m[1] + '</button>').join('') + '</div>'\n      + '<div class=\"seg\">' + [[30, 'Último mês'], [60, '60 dias'], [90, '90 dias']].map(p => '<button data-dias=\"' + p[0] + '\" ' + (dias === p[0] ? 'data-on' : '') + '>' + p[1] + '</button>').join('') + '</div></div>'\n      + '<div class=\"mapbox\"><div class=\"lmap\" data-d2-static></div>' + (lib ? '' : '<div class=\"load\">' + (d2.lib.state(LIB) === 'error' ? 'Falha ao carregar o mapa: ' + esc(d2.lib.error(LIB)) : 'Carregando mapa…') + '</div>') + '</div>'\n      + '<div class=\"side\">' + side\n      + '<h3>Bairros · mediana do período</h3><div class=\"bl\">' + BAIRROS.map(b => '<div class=\"bn\" data-fly=\"' + b.id + '\"><span>' + esc(b.nome) + '</span><span><b>' + brl(stat[b.id].med) + '</b> · ' + stat[b.id].n + ' vendas' + (stat[b.id].last != null ? ' · há ' + stat[b.id].last + ' d' : '') + '</span></div>').join('') + '</div>'\n      + '<div class=\"lg\">' + BANDS.map(b => '<span><i style=\"background:' + b.c + '\"></i>' + b.l + '</span>').join('') + '</div>'\n      + '<div class=\"muted\">Base: ITBI da prefeitura. Mapa © OpenStreetMap.</div></div></div>'\n  },\n  mounted(d2) {\n    d2.on('[data-mode]', 'click', (e, el) => { d2.state.mode = el.dataset.mode })\n    d2.on('[data-dias]', 'click', (e, el) => { d2.setState({ dias: Number(el.dataset.dias), sale: null }) })\n    d2.on('[data-fly]', 'click', (e, el) => {\n      const b = BAIRROS.find(x => x.id === el.dataset.fly), m = d2.$('.lmap')\n      if (m && m.__map && b) { m.__map.flyTo([b.lat, b.lng], 15, { duration: 0.8 }); d2.setState({ click: { lat: b.lat, lng: b.lng }, sale: null }) }\n    })\n  },\n  updated(d2, root) {\n    const lib = d2.lib(LIB)\n    const el = root.querySelector('.lmap')\n    if (!lib || !el) return\n    const L = lib.map ? lib : lib.default\n    if (!L || !L.map) return\n    if (!el.__map) {\n      const map = L.map(el, { zoomControl: true }).setView([-23.585, -46.665], 12)\n      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map)\n      el.__map = map\n      el.__layer = L.layerGroup().addTo(map)\n      map.on('click', e => d2.setState({ click: { lat: e.latlng.lat, lng: e.latlng.lng }, sale: null }))\n      setTimeout(() => map.invalidateSize(), 50)\n    }\n    const { all, sales, dias, near, c } = compute(d2)\n    const mode = d2.state.mode || 'heat'\n    const sig = [mode, dias, d2.state.sale, c && c.lat, c && c.lng, all.length].join('|')\n    if (el.__sig === sig) return\n    el.__sig = sig\n    el.__map.invalidateSize()\n    const g = el.__layer\n    g.clearLayers()\n    if (mode === 'heat') {\n      const cell = 0.004, grid = {}\n      sales.forEach(s => { const k = Math.floor(s.lat / cell) + ':' + Math.floor(s.lng / cell); (grid[k] = grid[k] || []).push(s) })\n      Object.keys(grid).forEach(k => {\n        const [i, j] = k.split(':').map(Number), a = grid[k], v = a.reduce((x, s) => x + s.m2, 0) / a.length\n        const r = L.rectangle([[i * cell, j * cell], [(i + 1) * cell, (j + 1) * cell]], { stroke: false, fillColor: bandOf(v).c, fillOpacity: 0.55 })\n        r.bindTooltip(brl(v) + '/m² · ' + a.length + ' vendas · mais recente há ' + Math.min(...a.map(s => s.dias)) + ' d')\n        r.addTo(g)\n      })\n    }\n    const zoom = el.__map.getZoom()\n    sales.forEach(s => {\n      const on = d2.state.sale === s.id\n      if (mode !== 'points' && !on) return\n      const m = L.circleMarker([s.lat, s.lng], { radius: on ? 10 : 7, color: '#fff', weight: on ? 3 : 1.2, fillColor: bandOf(s.m2).c, fillOpacity: 0.95 })\n      m.bindTooltip((s.unidade ? s.unidade + ' · ' : '') + esc(s.rua) + ', ' + s.numero + ' · ' + brl(s.m2) + '/m² · ' + fdate(s.data), { direction: 'top' })\n      m.on('click', ev => { L.DomEvent.stopPropagation(ev); d2.setState({ sale: s.id, click: null }) })\n      m.addTo(g)\n    })\n    if (c) L.circle([c.lat, c.lng], { radius: 300, color: '#2f6fed', weight: 2, fillColor: '#2f6fed', fillOpacity: 0.12, interactive: false }).addTo(g)\n  }\n}"}},"titulo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"titulo"}}}},"sales":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sales"}}}}}}}}}},{"_id":"6ae4d9eb8b9edfaf770dce59","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"geosampa-explorer","package":"gui","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"const BASE = 'http://wfs.geosampa.prefeitura.sp.gov.br/geoserver/ows'\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\nconst PRESETS = [\n  { t: 'Av. Paulista 1000', layer: 'geoportal:lote_cidadao', cql: \"nm_logradouro_completo LIKE 'AV PAULISTA%' AND cd_numero_porta = '1000'\" },\n  { t: 'Residenciais acima de 500 m² de terreno', layer: 'geoportal:lote_cidadao', cql: \"dc_tipo_uso_imovel = 'Residencial' AND qt_area_terreno > 500\" },\n  { t: 'Setor fiscal 009', layer: 'geoportal:lote_cidadao', cql: \"cd_setor_fiscal = '009'\" },\n  { t: 'Lotes por rua (Rua Augusta)', layer: 'geoportal:lote_cidadao', cql: \"nm_logradouro_completo LIKE 'R AUGUSTA%'\" }\n]\nasync function call(d2, url) {\n  let r = await d2.rush(d2.params.api).invoke({ url })\n  for (let i = 0; i < 3; i++) { if (r && r.status === undefined && r.data !== undefined) r = r.data }\n  let body = r && r.data\n  if (typeof body === 'string' && /^\\s*[\\[{]/.test(body)) { try { body = JSON.parse(body) } catch (e) {} }\n  return { status: r && r.status, body }\n}\nfunction wfs(params) {\n  const q = Object.keys(params).filter((k) => params[k] !== '' && params[k] != null).map((k) => k + '=' + encodeURIComponent(params[k]))\n  return BASE + '?service=WFS&version=2.0.0&' + q.join('&')\n}\nexport default {\n  css: `\n    :host{display:block;height:100%}\n    .w{font:14px/1.45 system-ui,sans-serif;color:#1c2333;background:#f5f6fa;height:100%;display:grid;grid-template-columns:300px 1fr;overflow:hidden}\n    .side{background:#fff;border-right:1px solid #e3e6ef;padding:14px;overflow:auto;display:flex;flex-direction:column;gap:10px}\n    .main{padding:14px;overflow:auto;display:flex;flex-direction:column;gap:12px;min-width:0}\n    h1{font-size:16px;margin:0}\n    h2{font-size:12px;text-transform:uppercase;letter-spacing:.06em;color:#626a80;margin:6px 0 0}\n    input,select{width:100%;box-sizing:border-box;padding:7px 9px;border:1px solid #d5d9e6;border-radius:6px;font:inherit}\n    button{padding:7px 10px;border:0;border-radius:6px;background:#2f4be0;color:#fff;font:inherit;cursor:pointer}\n    button.g{background:#eef0f8;color:#1c2333;text-align:left}\n    button:disabled{opacity:.5}\n    .item{padding:5px 8px;border-radius:6px;cursor:pointer;font-size:12px;word-break:break-all}\n    .item:hover,.item.on{background:#e7ebff}\n    .side>*{flex:none}\n    .list{max-height:220px;min-height:60px;overflow:auto;border:1px solid #e3e6ef;border-radius:6px}\n    .kpis{display:flex;gap:10px;flex-wrap:wrap}\n    .k{background:#fff;border:1px solid #e3e6ef;border-radius:8px;padding:8px 12px;min-width:110px}\n    .k b{display:block;font-size:18px}\n    .k span{font-size:11px;color:#626a80}\n    table{border-collapse:collapse;width:100%;background:#fff;font-size:12px}\n    th,td{border:1px solid #e3e6ef;padding:5px 8px;text-align:left;white-space:nowrap}\n    th{background:#eef0f8;position:sticky;top:0}\n    .box{background:#fff;border:1px solid #e3e6ef;border-radius:8px;overflow:auto;max-height:46vh}\n    code{display:block;background:#10162a;color:#b9c4ff;padding:8px 10px;border-radius:6px;font-size:11px;word-break:break-all;white-space:pre-wrap}\n    .err{color:#b3261e}\n    .tag{display:inline-block;background:#eef0f8;border-radius:4px;padding:1px 6px;margin:1px;font-size:11px;cursor:pointer}\n    .tag.on{background:#2f4be0;color:#fff}\n  `,\n  render(d2) {\n    const s = d2.state\n    const layers = (s.layers || []).filter((l) => !s.lf || (l.n + ' ' + l.t).toLowerCase().includes(s.lf.toLowerCase())).slice(0, 80)\n    const fields = s.fields || []\n    const rows = s.rows || []\n    const cols = []\n    rows.forEach((r) => Object.keys(r).forEach((k) => { if (!cols.includes(k)) cols.push(k) }))\n    const stats = cols.map((c) => {\n      const v = rows.map((r) => r[c]).filter((x) => typeof x === 'number')\n      if (v.length < 2) return ''\n      const avg = v.reduce((a, b) => a + b, 0) / v.length\n      return `<div class=\"k\"><span>${esc(c)} (min / média / máx)</span><b>${d2.fmt.number(Math.min(...v))} / ${d2.fmt.number(avg, { maximumFractionDigits: 1 })} / ${d2.fmt.number(Math.max(...v))}</b></div>`\n    }).join('')\n    return `<div class=\"w\">\n      <div class=\"side\">\n        <h1>GeoSampa Explorer</h1>\n        <button data-act=\"layers\" ${s.busy ? 'disabled' : ''}>1. Listar camadas da Prefeitura</button>\n        <input data-f=\"lf\" placeholder=\"filtrar camadas (ex: lote, quadra, zoneamento)\" value=\"${esc(s.lf || '')}\">\n        <div class=\"list\">${layers.map((l) => `<div class=\"item ${l.n === s.layer ? 'on' : ''}\" data-key=\"${esc(l.n)}\" data-layer=\"${esc(l.n)}\">${esc(l.t || l.n)}<br><small>${esc(l.n)}</small></div>`).join('') || '<div class=\"item\">Nenhuma camada carregada</div>'}</div>\n        <h2>Camada</h2>\n        <input data-f=\"layer\" value=\"${esc(s.layer || '')}\">\n        <h2>Filtro CQL</h2>\n        <input data-f=\"cql\" placeholder=\"qt_area_terreno > 500\" value=\"${esc(s.cql || '')}\">\n        <h2>Quantidade</h2>\n        <input data-f=\"count\" type=\"number\" value=\"${esc(s.count || 20)}\">\n        <h2>Campos ${fields.length ? '(' + fields.length + ')' : ''}</h2>\n        <div>${fields.map((f) => `<span class=\"tag ${s.sel && s.sel[f.name] ? 'on' : ''}\" data-key=\"f-${esc(f.name)}\" data-field=\"${esc(f.name)}\" title=\"${esc(f.localType)}\">${esc(f.name)}</span>`).join('') || '<small>Escolha uma camada e carregue os campos</small>'}</div>\n        <button class=\"g\" data-act=\"fields\" ${s.busy ? 'disabled' : ''}>2. Carregar campos da camada</button>\n        <button data-act=\"sample\" ${s.busy ? 'disabled' : ''}>3. Buscar amostra</button>\n        <h2>Atalhos</h2>\n        ${PRESETS.map((p, i) => `<button class=\"g\" data-key=\"p${i}\" data-preset=\"${i}\">${esc(p.t)}</button>`).join('')}\n      </div>\n      <div class=\"main\">\n        ${s.busy ? '<div>Consultando…</div>' : ''}\n        ${s.err ? `<div class=\"err\">${esc(s.err)}</div>` : ''}\n        <div class=\"kpis\">\n          ${s.total != null ? `<div class=\"k\"><span>Registros que batem com o filtro</span><b>${d2.fmt.number(s.total)}</b></div>` : ''}\n          ${s.rows ? `<div class=\"k\"><span>Linhas na amostra</span><b>${rows.length}</b></div>` : ''}\n          ${s.ms ? `<div class=\"k\"><span>Tempo da consulta</span><b>${s.ms} ms</b></div>` : ''}\n          ${stats}\n        </div>\n        ${s.url ? `<code>${esc(s.url)}</code>` : ''}\n        ${rows.length ? `<div class=\"box\"><table><tr>${cols.map((c) => `<th>${esc(c)}</th>`).join('')}</tr>${rows.slice(0, 100).map((r, i) => `<tr data-key=\"r${i}\">${cols.map((c) => `<td>${esc(r[c])}</td>`).join('')}</tr>`).join('')}</table></div>` : ''}\n        ${s.raw ? `<h2>Resposta bruta</h2><code>${esc(s.raw)}</code>` : ''}\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    if (!d2.state.layer) d2.state.layer = 'geoportal:lote_cidadao'\n    const run = async (fn) => {\n      d2.state.busy = true; d2.state.err = ''\n      const t0 = Date.now()\n      try { await fn() } catch (e) { d2.state.err = String((e && e.message) || e) }\n      d2.state.ms = Date.now() - t0; d2.state.busy = false\n    }\n    d2.on('[data-f]', 'input', (e) => { const t = e.target; d2.state[t.getAttribute('data-f')] = t.value })\n    d2.on('[data-layer]', 'click', (e) => { const el = e.target.closest('[data-layer]'); d2.state.layer = el.getAttribute('data-layer'); d2.state.fields = []; d2.state.sel = {} })\n    d2.on('[data-field]', 'click', (e) => { const el = e.target.closest('[data-field]'); const n = el.getAttribute('data-field'); d2.state.sel = Object.assign({}, d2.state.sel, { [n]: !(d2.state.sel && d2.state.sel[n]) }) })\n    d2.on('[data-preset]', 'click', (e) => { const el = e.target.closest('[data-preset]'); const p = PRESETS[Number(el.getAttribute('data-preset'))]; d2.state.layer = p.layer; d2.state.cql = p.cql; d2.state.fields = []; d2.state.sel = {} })\n    d2.on('[data-act]', 'click', (e) => {\n      const el = e.target.closest('[data-act]')\n      const act = el.getAttribute('data-act')\n      if (act === 'layers') run(async () => {\n        const url = wfs({ request: 'GetCapabilities', sections: 'FeatureTypeList' })\n        const r = await call(d2, url)\n        const xml = typeof r.body === 'string' ? r.body : JSON.stringify(r.body)\n        const out = []\n        const re = /<(?:\\w+:)?FeatureType(?:\\s[^>]*)?>([\\s\\S]*?)<\\/(?:\\w+:)?FeatureType>/g\n        let m\n        while ((m = re.exec(xml))) {\n          const n = (m[1].match(/<(?:\\w+:)?Name>([^<]*)</) || [])[1]\n          const t = (m[1].match(/<(?:\\w+:)?Title>([^<]*)</) || [])[1]\n          if (n) out.push({ n, t: t || n })\n        }\n        d2.state.layers = out; d2.state.url = url\n        if (!out.length) d2.state.err = 'Nenhuma camada no retorno (status ' + r.status + ')'\n      })\n      if (act === 'fields') run(async () => { await loadFields(d2) })\n      if (act === 'sample') run(async () => {\n        if (!(d2.state.fields || []).length) await loadFields(d2)\n        const sel = Object.keys(d2.state.sel || {}).filter((k) => d2.state.sel[k])\n        const geo = (d2.state.fields || []).filter((f) => /gml:/i.test(f.type || ''))\n        const names = sel.length ? sel : (d2.state.fields || []).filter((f) => !geo.includes(f)).map((f) => f.name)\n        const url = wfs({ request: 'GetFeature', typeNames: d2.state.layer, outputFormat: 'application/json', count: d2.state.count || 20, propertyName: names.join(','), cql_filter: d2.state.cql })\n        const r = await call(d2, url)\n        d2.state.url = url\n        const b = r.body\n        if (b && b.features) {\n          d2.state.rows = b.features.map((f) => f.properties || {}); d2.state.total = b.numberMatched != null ? b.numberMatched : b.totalFeatures; d2.state.raw = ''\n        } else { d2.state.rows = []; d2.state.total = null; d2.state.raw = typeof b === 'string' ? b.slice(0, 1500) : JSON.stringify(b).slice(0, 1500) }\n      })\n    })\n    async function loadFields(d2) {\n      const url = wfs({ request: 'DescribeFeatureType', typeNames: d2.state.layer, outputFormat: 'application/json' })\n      const r = await call(d2, url)\n      const ft = r.body && r.body.featureTypes && r.body.featureTypes[0]\n      d2.state.url = url\n      if (ft) d2.state.fields = ft.properties.map((p) => ({ name: p.name, type: p.type, localType: p.localType }))\n      else d2.state.err = 'Campos não encontrados para a camada ' + d2.state.layer\n    }\n  }\n}"}},"api":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"api"}}}}}}}}}},{"_id":"6ae4e467223137412595e8a1","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"mesa-analista","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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\nconst CASOS = [\n  { id: 'c1', lote: 'R Augusta, 1200', setor: '009 · quadra 048 · lote 0138', area: 3487, uso: 'Não residencial', rs: 14200, prov: 'Incorporadora A', sinais: [{ t: 'Unificação de lote', d: '2026-03-12', f: 'Diário Oficial' }, { t: 'Alvará de demolição', d: '2026-06-02', f: 'Diário Oficial' }, { t: 'Estande de vendas no site', d: '2026-09-20', f: 'Site do concorrente' }], prev: 'Lançamento previsto entre jan e abr de 2027' },\n  { id: 'c2', lote: 'Av Arraias do Araguaia, 95', setor: '148 · quadra 063 · lote 0024', area: 1180, uso: 'Residencial', rs: 9800, prov: 'Incorporadora B', sinais: [{ t: 'Alvará de execução', d: '2026-08-14', f: 'Aprovação de alvarás' }, { t: 'EIV protocolado', d: '2026-09-30', f: 'Dados abertos' }], prev: 'Obra deve começar em 2 a 3 meses' }\n]\nexport default {\n  css: `\n    :host{display:block;height:100%}\n    .w{font:14px/1.45 system-ui,sans-serif;color:#1c2333;background:#f5f6fa;height:100%;display:grid;grid-template-columns:260px 1fr;overflow:hidden}\n    .q{background:#fff;border-right:1px solid #e3e6ef;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:8px}\n    .q h1{font-size:15px;margin:0 0 4px}\n    .c{border:1px solid #e3e6ef;border-radius:8px;padding:8px 10px;cursor:pointer}\n    .c.on{border-color:#2f4be0;background:#eef1ff}\n    .c small{display:block;color:#626a80}\n    .d{padding:16px;overflow:auto;display:flex;flex-direction:column;gap:12px}\n    .k{display:flex;gap:10px;flex-wrap:wrap}\n    .k div{background:#fff;border:1px solid #e3e6ef;border-radius:8px;padding:8px 12px;min-width:120px}\n    .k b{display:block;font-size:17px}\n    .k span{font-size:11px;color:#626a80}\n    .s{background:#fff;border:1px solid #e3e6ef;border-radius:8px;padding:10px 12px}\n    .s li{margin:6px 0;list-style:none;padding-left:14px;border-left:3px solid #2f4be0}\n    .s small{color:#626a80}\n    .b{display:flex;gap:8px}\n    button{padding:9px 14px;border:0;border-radius:6px;color:#fff;font:inherit;cursor:pointer}\n    .a{background:#12a26b}.m{background:#2f4be0}.x{background:#626a80}\n    .ok{background:#e6f6ee;border:1px solid #12a26b;border-radius:8px;padding:10px 12px}\n  `,\n  render(d2) {\n    const s = d2.state\n    const sel = s.sel || CASOS[0].id\n    const dec = s.dec || {}\n    const c = CASOS.find((x) => x.id === sel)\n    return `<div class=\"w\">\n      <div class=\"q\"><h1>${esc(d2.param('titulo', 'Fila do analista'))}</h1>\n        ${CASOS.map((x) => `<div class=\"c ${x.id === sel ? 'on' : ''}\" data-key=\"${x.id}\" data-caso=\"${x.id}\"><b>${esc(x.lote)}</b><small>${esc(x.prov)} · ${x.sinais.length} sinais</small>${dec[x.id] ? `<small>Decisão: ${esc(dec[x.id])}</small>` : ''}</div>`).join('')}\n      </div>\n      <div class=\"d\">\n        <div class=\"k\">\n          <div><span>Terreno</span><b>${d2.fmt.number(c.area)} m²</b></div>\n          <div><span>Uso</span><b>${esc(c.uso)}</b></div>\n          <div><span>R$/m² na região</span><b>${d2.fmt.number(c.rs)}</b></div>\n          <div><span>Concorrente provável</span><b>${esc(c.prov)}</b></div>\n        </div>\n        <div class=\"s\"><b>Sinais no lote</b><ul style=\"padding:0;margin:6px 0 0\">${c.sinais.map((x, i) => `<li data-key=\"s${i}\">${esc(x.t)}<br><small>${esc(x.d)} · ${esc(x.f)}</small></li>`).join('')}</ul></div>\n        <div class=\"s\"><b>Leitura</b><br>${esc(c.prev)}<br><small>${esc(c.setor)}</small></div>\n        ${dec[c.id] ? `<div class=\"ok\">Decisão registrada: <b>${esc(dec[c.id])}</b></div>` : `<div class=\"b\"><button class=\"a\" data-dec=\"Agir\">Agir: avisar o time de terrenos</button><button class=\"m\" data-dec=\"Acompanhar\">Acompanhar</button><button class=\"x\" data-dec=\"Descartar\">Descartar</button></div>`}\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('[data-caso]', 'click', (e) => { d2.state.sel = e.target.closest('[data-caso]').getAttribute('data-caso') })\n    d2.on('[data-dec]', 'click', (e) => {\n      const v = e.target.closest('[data-dec]').getAttribute('data-dec')\n      d2.state.dec = Object.assign({}, d2.state.dec, { [d2.state.sel || CASOS[0].id]: v })\n    })\n  }\n}"}},"titulo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"titulo"}}}}}}}}}},{"_id":"6ae7e286ba063940cc480bbf","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-pedidos-ui","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":"// Loja Boa · Meus pedidos — the same screen as loja-pedidos, written over the kit:\n// `ui: true` adopts Porcelain into the shadow root; d2.html escapes; d2.ui.* are the parts.\nconst STEPS = [['pago', 'Pago'], ['separado', 'Separado'], ['enviado', 'Enviado'], ['em_rota', 'Em rota'], ['entregue', 'Entregue']]\nconst RANK = { aguardando_pagamento: 0, pago: 1, separado: 2, enviado: 3, em_rota: 4, entregue: 5, recusado: -1, cancelado: -2 }\nconst EV = { em_rota: 'Saiu para entrega', entregue: 'Entregue', coletado: 'Coletado pela transportadora', postado: 'Postado' }\nconst emailFromUrl = () => { try { return new URLSearchParams(location.search).get('email') || '' } catch { return '' } }\nconst emailLocal = () => { try { return JSON.parse(localStorage.getItem('lojaboa.cart') || '{}').form?.email || '' } catch { return '' } }\n\nexport default {\n  ui: { accent: '#2f6b4f', radius: 'lg', tokens: { bg: '#f6f1e8', surface: '#fffdf8', line: '#e3dccd' } },\n  css: `\n  :host{display:block;height:100%;--serif:\"Iowan Old Style\",\"Palatino Linotype\",Palatino,Georgia,serif}\n  .wrap{height:100%;overflow:auto;background:var(--bg)}\n  .top{position:sticky;top:0;z-index:2;display:flex;align-items:center;gap:18px;padding:12px 28px;background:var(--glass);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}\n  .brand{font:600 22px var(--serif)} .brand i{font-style:normal;color:var(--accent)}\n  .top a{color:var(--muted);text-decoration:none;font-size:14px} .top a.on{color:var(--text);font-weight:600}\n  .who{margin-left:auto;display:flex;gap:8px;align-items:center} .who .in{min-width:240px}\n  .body{padding:26px 28px 44px;max-width:920px;margin:0 auto}\n  h1{font:600 32px/1.1 var(--serif);margin:0 0 4px}\n  .order{margin-bottom:14px}\n  .order .num{font:600 18px var(--serif)} .order .ttl{font-family:var(--serif)}\n  .scale{display:flex;gap:6px;flex-wrap:wrap}\n  `,\n  render(d2) {\n    const { html, ui, fmt } = d2\n    const st = d2.state\n    const ds = d2.data('pedidos')\n    const av = d2.data('avaliacoes')\n    const email = (st.email ?? '').trim().toLowerCase()\n    const all = ds ? ds.records() : []\n    const mine = email ? all.filter((p) => String(p.cliente?.email || '').toLowerCase() === email) : []\n    mine.sort((a, b) => new Date(b.criado_em || 0) - new Date(a.criado_em || 0))\n    const avals = av ? av.records() : []\n    const when = (d) => (d ? fmt.date(d, { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' }) : '')\n\n    const timeline = (p) => {\n      const r = RANK[p.status] ?? 0\n      if (r < 0) return ui.notice({ kind: 'bad', title: p.status === 'recusado' ? 'Pagamento recusado' : 'Cancelado', text: 'Nada foi cobrado. Tente outro cartão na vitrine.' })\n      return ui.steps(STEPS.map(([k, label]) => ({ label, text: when(p[`${k}_em`]) })), Math.max(0, r - 1) + (r >= 5 ? 1 : 0))\n    }\n    const nps = (p, already) => {\n      if (p.status !== 'entregue') return ''\n      if (already) return ui.notice({ kind: 'ok', title: `Obrigado pela avaliação · nota ${already.nota}`, text: already.comentario ? `“${already.comentario}”` : '' })\n      const sel = st.nps?.[p.numero]\n      return html`<div style=\"margin-top:16px;padding-top:14px;border-top:1px solid var(--line)\">\n        <div class=\"ttl\" style=\"margin-bottom:8px\">${p.nps?.mensagem || 'De 0 a 10, o quanto você recomendaria a Loja Boa?'}</div>\n        <div class=\"scale\">${Array.from({ length: 11 }, (_, i) => ui.chip(String(i), { on: sel?.nota === i, value: i, cls: 'n', attrs: { 'data-ped': p.numero, 'data-nota': i } }))}</div>\n        ${sel?.nota != null ? html`<div class=\"row nw\" style=\"margin-top:10px\">${ui.input({ name: 'npsc', placeholder: 'Quer contar o porquê? (opcional)', value: sel?.comentario || '', attrs: { 'data-ped': p.numero, class: 'npsc' } })}${ui.btn('Enviar', { kind: 'pri', busy: st.busy === p.numero, cls: 'npsgo', attrs: { 'data-ped': p.numero } })}</div>` : ''}\n      </div>`\n    }\n    const order = (p) => {\n      const already = avals.find((a) => a.pedido === p.numero)\n      const ev = (p.rastreio?.eventos || []).slice().reverse()\n      return ui.card({\n        cls: 'order',\n        pad: true,\n        attrs: { 'data-key': p._id },\n        body: html`<div class=\"row nw\" style=\"align-items:flex-start;margin-bottom:12px\"><div><div class=\"num\">Pedido ${p.numero}</div><div class=\"muted\" style=\"font-size:13px\">${when(p.criado_em)} · ${p.cliente?.nome || ''}</div></div><span class=\"sp\"></span><b style=\"font-size:18px\" class=\"tnum\">${fmt.currency(p.total, 'BRL')}</b></div>\n          ${timeline(p)}\n          <div class=\"grid g2\" style=\"margin-top:18px\">\n            <div><h3 class=\"sub\" style=\"margin:0 0 8px\">Itens</h3>${ui.kv([...(p.itens || []).map((it) => [`${it.qtd} × ${it.nome}`, fmt.currency(it.preco * it.qtd, 'BRL')]), ['Frete', Number(p.frete) === 0 ? 'grátis' : fmt.currency(p.frete, 'BRL')]], { cols: '1fr auto' })}</div>\n            <div><h3 class=\"sub\" style=\"margin:0 0 8px\">Entrega</h3><div style=\"font-size:13.5px;line-height:1.5\">${p.endereco?.endereco || ''}<br>${p.endereco?.cidade || ''} · ${p.endereco?.uf || ''} · CEP ${p.endereco?.cep || ''}</div>\n              ${p.rastreio?.codigo ? html`<div class=\"row\" style=\"margin-top:10px\"><span class=\"mono\">${p.rastreio.codigo}</span>${ui.pill(p.rastreio.transportadora || 'transportadora', '', { out: true })}</div>${ev.length ? html`<div style=\"margin-top:10px\">${ui.timeline(ev.map((e, i) => ({ title: EV[e.tipo] || e.tipo, text: e.motorista || '', time: when(e.em), kind: i === 0 ? 'acc' : '' })))}</div>` : html`<p class=\"muted\" style=\"font-size:13px;margin:8px 0 0\">Etiqueta gerada, aguardando coleta.</p>`}` : p.status === 'pago' ? html`<p class=\"muted\" style=\"font-size:13px;margin:8px 0 0\">Na fila de separação do estoque.</p>` : p.status === 'separado' ? html`<p class=\"muted\" style=\"font-size:13px;margin:8px 0 0\">Separado, aguardando a transportadora.</p>` : ''}\n            </div>\n          </div>\n          ${nps(p, already)}`,\n      })\n    }\n\n    return html`<div class=\"wrap\">\n      <header class=\"top\"><div class=\"brand\">Loja <i>Boa</i></div><a href=\"/\">Vitrine</a><a class=\"on\">Meus pedidos</a>\n        <div class=\"who\">${ui.input({ name: 'email', type: 'email', placeholder: 'seu e-mail', value: st.email ?? '', icon: 'mail', attrs: { class: 'email' } })}${ui.btn('', { kind: 'ghost', icon: 'clock', cls: 'refresh', attrs: { 'data-tip': 'Atualizar agora|a lista atualiza sozinha a cada 10 s' } })}</div></header>\n      <div class=\"body\"><h1>Meus pedidos</h1><p class=\"muted\" style=\"margin:0 0 20px\">${email ? `${mine.length} pedido${mine.length === 1 ? '' : 's'} de ${email} · atualiza sozinho` : 'Informe o e-mail usado na compra.'}</p>\n        ${ds && ds.loading ? ui.skeleton({ lines: 3, h: 120, gap: 14 }) : email && !mine.length ? ui.empty({ icon: 'box', title: 'Nenhum pedido ainda', text: 'Nada para este e-mail por enquanto.', action: ui.btn('Ir à vitrine', { kind: 'soft', attrs: { onclick: \"location.href='/'\" } }) }) : mine.map(order)}\n      </div></div>`\n  },\n  mounted(d2) {\n    const st = d2.state\n    d2.setState({ email: emailFromUrl() || emailLocal() || '', nps: {} })\n    const ds = d2.data('pedidos')\n    if (ds && ds.loadAll) ds.loadAll().catch(() => {})\n    const av = d2.data('avaliacoes')\n    if (av && av.loadAll) av.loadAll().catch(() => {})\n    const tick = setInterval(() => { try { d2.data('pedidos')?.refresh() } catch {} }, 10000)\n    d2.onUnmount(() => clearInterval(tick))\n    d2.on('.email', 'change', (e, el) => { st.email = el.value })\n    d2.on('.refresh', 'click', () => { d2.data('pedidos')?.refresh(); d2.data('avaliacoes')?.refresh(); d2.ui.toast('Atualizado', { kind: 'ok' }) })\n    d2.on('.n', 'click', (e, el) => { const nps = { ...(st.nps || {}) }; nps[el.dataset.ped] = { ...(nps[el.dataset.ped] || {}), nota: Number(el.dataset.nota) }; st.nps = nps })\n    d2.on('.npsc', 'input', (e, el) => { const nps = { ...(st.nps || {}) }; nps[el.dataset.ped] = { ...(nps[el.dataset.ped] || {}), comentario: el.value }; st.nps = nps })\n    d2.on('.npsgo', 'click', async (e, el) => {\n      const num = el.dataset.ped\n      const sel = st.nps?.[num]; if (!sel || sel.nota == null) return\n      if (!(await d2.ui.confirm({ title: `Enviar nota ${sel.nota}?`, body: 'Sua avaliação ajuda a Loja Boa a melhorar.', ok: 'Enviar' }))) return\n      const p = (d2.data('pedidos')?.records() || []).find((x) => x.numero === num)\n      st.busy = num\n      try {\n        await d2.data('avaliacoes').create({ pedido: num, cliente: st.email, nota: sel.nota, comentario: sel.comentario || '', criado_em: new Date().toISOString() })\n        if (p) await d2.data('pedidos').update({ _id: p._id, nps: { ...(p.nps || {}), nota: sel.nota, comentario: sel.comentario || '', respondido_em: new Date().toISOString() } })\n        d2.ui.toast({ title: 'Avaliação enviada', text: 'Obrigado!', kind: 'ok' })\n      } catch (err) { d2.log('nps', err); d2.ui.toast({ title: 'Não foi possível enviar', text: String(err?.message || err), kind: 'bad' }) } finally { st.busy = null }\n    })\n  },\n}\n"}},"pedidos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pedidos"}}}},"avaliacoes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"avaliacoes"}}}}}}}}}},{"_id":"6ae7faa2ba063940cc482530","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-vitrine-terra","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; }\n    .wrap { --verde:#2f6b4f; --ambar:#d9a441; --papel:#f6f1e8; --tinta:#1e2a22; --argila:#a0584b; --linho:#e3dccd;\n      width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--papel); color:var(--tinta);\n      font-family:'DM Sans', system-ui, sans-serif; display:flex; align-items:center; justify-content:center; }\n    .card { width:100%; max-width:360px; background:#fffdf8; border:1px solid var(--linho); border-radius:20px 20px 20px 4px;\n      overflow:hidden; box-shadow:0 14px 32px -16px rgba(30,42,34,.35); }\n    .photo { position:relative; height:180px; background:linear-gradient(135deg,var(--verde) 0%, #3f8062 55%, var(--ambar) 140%); }\n    .photo::after { content:''; position:absolute; inset:auto 24px -28px auto; width:84px; height:84px; border-radius:50%;\n      background:var(--argila); opacity:.9; }\n    .badge { position:absolute; top:16px; left:16px; padding:4px 10px; border-radius:999px; background:var(--papel);\n      color:var(--verde); font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; }\n    .body { padding:28px 24px 24px; }\n    h3 { margin:0 0 6px; font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:26px; line-height:1.15; color:var(--tinta); }\n    p { margin:0 0 20px; font-size:14px; line-height:1.5; color:#4a5a50; }\n    .row { display:flex; align-items:center; justify-content:space-between; gap:12px; }\n    .price { font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:22px; color:var(--verde); }\n    button { border:0; cursor:pointer; padding:12px 20px; border-radius:12px 12px 12px 3px; background:var(--verde); color:var(--papel);\n      font:600 14px 'DM Sans', system-ui, sans-serif; transition:transform .15s ease, background .15s ease; }\n    button:hover { background:#255a42; transform:translateY(-1px); }\n    button:active { transform:translateY(0); background:var(--ambar); color:var(--tinta); }\n  `,\n  render(d2) {\n    const name = d2.param('productName') || 'Sabonete de argila';\n    const tag = d2.param('tagline') || 'Feito em lote pequeno, curado em linho.';\n    const price = d2.param('price') || 'R$ 38,00';\n    const cta = d2.param('cta') || 'Adicionar à sacola';\n    const badge = d2.param('badge') || 'Novo';\n    return `<div class=\"wrap\"><div class=\"card\">\n      <div class=\"photo\"><span class=\"badge\">${badge}</span></div>\n      <div class=\"body\">\n        <h3>${name}</h3>\n        <p>${tag}</p>\n        <div class=\"row\"><span class=\"price\">${price}</span><button>${cta}</button></div>\n      </div>\n    </div></div>`;\n  }\n}"}},"productName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productName"}}}},"tagline":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tagline"}}}},"price":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"price"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}}}}}}}},{"_id":"6ae804b0abeaa9eb78ab43a4","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"loja-admin-argila","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; }\n    .wrap { --areia:#dbd3c9; --papel:#efeae3; --pedra:#bbb2a8; --taupe:#a79787; --terracota:#9a6765; --ardosia:#798595; --salvia:#69745c; --tinta:#2b2622;\n      width:100%; height:100%; box-sizing:border-box; padding:20px; background:var(--areia); color:var(--tinta);\n      font-family:'Manrope', system-ui, sans-serif; display:flex; flex-direction:column; gap:14px; }\n    .top { display:flex; align-items:baseline; justify-content:space-between; }\n    h2 { margin:0; font-family:'Instrument Serif', Georgia, serif; font-weight:400; font-size:30px; letter-spacing:-.01em; }\n    .tag { font-size:11px; font-weight:700; letter-spacing:.08em; text-transform:uppercase; color:var(--taupe); }\n    .grid { display:grid; grid-template-columns:1fr 1fr; gap:12px; }\n    .kpi { background:var(--papel); border:1px solid var(--pedra); border-radius:14px; padding:14px 16px; }\n    .kpi small { display:block; font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d6358; margin-bottom:6px; }\n    .kpi b { font-family:'Instrument Serif', Georgia, serif; font-weight:400; font-size:34px; line-height:1; }\n    .delta { display:inline-block; margin-left:8px; padding:2px 8px; border-radius:999px; background:var(--salvia); color:var(--papel); font-size:12px; font-weight:700; vertical-align:middle; }\n    .chart { flex:1; min-height:0; background:var(--papel); border:1px solid var(--pedra); border-radius:14px; padding:14px 16px; display:flex; flex-direction:column; }\n    .chart small { font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:#6d6358; }\n    svg { flex:1; width:100%; min-height:0; margin-top:8px; }\n    .line { fill:none; stroke:var(--terracota); stroke-width:2.5; stroke-linecap:round; stroke-linejoin:round; }\n    .area { fill:var(--terracota); opacity:.14; }\n    .dot { fill:var(--ardosia); }\n  `,\n  render(d2) {\n    const heading = d2.param('heading') || 'Painel da loja';\n    const revenue = d2.param('revenue') || 'R$ 48.230';\n    const orders = d2.param('orders') || '312';\n    const delta = d2.param('delta') || '+8,4%';\n    const s = d2.param('series');\n    const series = Array.isArray(s) && s.length > 1 ? s : [12, 18, 14, 22, 19, 27, 31, 28, 36];\n    const max = Math.max.apply(null, series), min = Math.min.apply(null, series);\n    const span = (max - min) || 1;\n    const pts = series.map(function (v, i) {\n      return [(i / (series.length - 1)) * 300, 70 - ((v - min) / span) * 60];\n    });\n    const path = pts.map(function (p, i) { return (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1); }).join(' ');\n    const last = pts[pts.length - 1];\n    return `<div class=\"wrap\">\n      <div class=\"top\"><h2>${heading}</h2><span class=\"tag\">últimos 9 dias</span></div>\n      <div class=\"grid\">\n        <div class=\"kpi\"><small>Receita</small><b>${revenue}</b><span class=\"delta\">${delta}</span></div>\n        <div class=\"kpi\"><small>Pedidos</small><b>${orders}</b></div>\n      </div>\n      <div class=\"chart\"><small>Vendas por dia</small>\n        <svg viewBox=\"0 0 300 80\" preserveAspectRatio=\"none\">\n          <path class=\"area\" d=\"${path} L300 80 L0 80 Z\"></path>\n          <path class=\"line\" d=\"${path}\"></path>\n          <circle class=\"dot\" cx=\"${last[0]}\" cy=\"${last[1].toFixed(1)}\" r=\"4\"></circle>\n        </svg>\n      </div>\n    </div>`;\n  }\n}"}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"revenue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"revenue"}}}},"orders":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orders"}}}},"delta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"delta"}}}},"series":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"series"}}}}}}}}}},{"_id":"6ae804b1922cc639b2bbccdc","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"admin-painel-areia","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; }\n    .wrap { --areia:#dbd3c9; --papel:#efebe4; --pedra:#bbb2a8; --taupe:#a79787; --rosa:#9a6765; --ardosia:#798595; --salvia:#69745c; --tinta:#2e2b27;\n      width:100%; height:100%; box-sizing:border-box; padding:20px; background:var(--areia); color:var(--tinta);\n      font-family:'Manrope', system-ui, sans-serif; display:flex; }\n    .panel { flex:1; background:var(--papel); border-radius:18px; padding:22px; display:flex; flex-direction:column; gap:18px;\n      box-shadow:0 1px 0 rgba(255,255,255,.6) inset, 0 18px 36px -22px rgba(46,43,39,.45); }\n    .head { display:flex; align-items:center; justify-content:space-between; }\n    h3 { margin:0; font-family:'Newsreader', Georgia, serif; font-weight:500; font-size:24px; letter-spacing:-.01em; }\n    .pill { padding:4px 12px; border-radius:999px; background:var(--salvia); color:var(--papel); font-size:12px; font-weight:600; }\n    .kpis { display:grid; grid-template-columns:1fr 1fr; gap:12px; }\n    .kpi { background:var(--areia); border-radius:14px; padding:14px 16px; }\n    .kpi small { display:block; font-size:12px; font-weight:600; color:#6d655c; letter-spacing:.03em; text-transform:uppercase; }\n    .kpi b { display:block; margin:6px 0 2px; font-family:'Newsreader', Georgia, serif; font-weight:500; font-size:32px; line-height:1; }\n    .kpi span { font-size:13px; font-weight:600; color:var(--salvia); }\n    .kpi.alt span { color:var(--rosa); }\n    .chart { flex:1; min-height:90px; display:flex; align-items:flex-end; gap:10px; padding:12px 4px 0; border-top:1px solid var(--pedra); }\n    .bar { flex:1; border-radius:8px 8px 3px 3px; background:var(--pedra); transition:background .15s ease; }\n    .bar:hover { background:var(--ardosia); }\n    .bar.on { background:var(--salvia); }\n  `,\n  render(d2) {\n    const title = d2.param('panelTitle') || 'Visão geral';\n    const l1 = d2.param('kpiOneLabel') || 'Pedidos';\n    const v1 = d2.param('kpiOneValue') || '1.284';\n    const d1 = d2.param('kpiOneDelta') || '+12% na semana';\n    const l2 = d2.param('kpiTwoLabel') || 'Devoluções';\n    const v2 = d2.param('kpiTwoValue') || '37';\n    const d2v = d2.param('kpiTwoDelta') || '+3 hoje';\n    const hs = [42, 58, 35, 70, 52, 88, 64];\n    const bars = hs.map((h, i) => '<div class=\"bar' + (i === 5 ? ' on' : '') + '\" data-key=\"b' + i + '\" style=\"height:' + h + '%\"></div>').join('');\n    return `<div class=\"wrap\"><div class=\"panel\">\n      <div class=\"head\"><h3>${title}</h3><span class=\"pill\">Ao vivo</span></div>\n      <div class=\"kpis\">\n        <div class=\"kpi\"><small>${l1}</small><b>${v1}</b><span>${d1}</span></div>\n        <div class=\"kpi alt\"><small>${l2}</small><b>${v2}</b><span>${d2v}</span></div>\n      </div>\n      <div class=\"chart\">${bars}</div>\n    </div></div>`;\n  }\n}"}},"panelTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panelTitle"}}}},"kpiOneLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiOneLabel"}}}},"kpiOneValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiOneValue"}}}},"kpiOneDelta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiOneDelta"}}}},"kpiTwoLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiTwoLabel"}}}},"kpiTwoValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiTwoValue"}}}},"kpiTwoDelta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpiTwoDelta"}}}}}}}}}},{"_id":"6ae807e2abeaa9eb78ab46ce","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"landing-loja","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":"// Loja Boa · landing page — a whole marketing page on the Porcelain kit, every colour, radius and\n// typeface read from the kit's tokens, so a palette or a type dropped on it in a design board\n// re-dresses the page live. `produtos` (optional) fills the shelf with the system's real products.\nconst PLACEHOLDER = [\n  { nome: 'Barra de argila e alecrim', preco: 24, tag: 'novo' },\n  { nome: 'Sabão de leite de aveia', preco: 22 },\n  { nome: 'Caixa das irmãs · 4 barras', preco: 79, tag: 'mais vendido' },\n  { nome: 'Saboneteira de barro', preco: 48 },\n]\nconst NOISE = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.18'/%3E%3C/svg%3E\")`\n\nexport default {\n  ui: { accent: '#2f6b4f', radius: 'lg', tokens: { bg: '#f6f1e8', surface: '#fffdf8', line: '#e3dccd', text: '#1e2a22' }, font: '\"Fraunces\", \"Iowan Old Style\", Georgia, serif' },\n  css: `\n  :host{display:block;height:100%}\n  .wrap{height:100%;overflow:auto;background:var(--bg);color:var(--text);container-type:inline-size;scroll-behavior:smooth}\n  .pg{max-width:1120px;margin:0 auto;padding:0 clamp(18px,4cqi,48px)}\n  .nav{position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:22px;height:64px;background:color-mix(in srgb,var(--bg) 82%,transparent);backdrop-filter:blur(10px);border-bottom:1px solid color-mix(in srgb,var(--line) 70%,transparent)}\n  .brand{font:600 22px/1 var(--font);letter-spacing:-.01em} .brand i{font-style:italic;color:var(--accent)}\n  .nav a{color:var(--muted);text-decoration:none;font-size:14px;transition:color .15s} .nav a:hover{color:var(--text)}\n  .nav .sp{flex:1}\n  .hg{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(24px,5cqi,64px);align-items:center;padding:clamp(44px,8cqi,96px) 0 clamp(36px,6cqi,72px)}\n  @container (max-width:820px){.hg{grid-template-columns:1fr}.vis{min-height:420px}}\n  .eyebrow{display:inline-flex;align-items:center;gap:8px;margin-bottom:18px}\n  h1{font:500 clamp(40px,6.4cqi,76px)/1.02 var(--font);letter-spacing:-.035em;margin:0 0 18px;text-wrap:balance}\n  h1 i{font-style:italic;color:var(--accent)}\n  .lead{font-size:clamp(16px,1.6cqi,19px);line-height:1.5;color:var(--muted);max-width:52ch;margin:0 0 26px}\n  .cta{display:flex;gap:10px;flex-wrap:wrap;align-items:center}\n  .proof{display:flex;align-items:center;gap:10px;margin-top:22px;color:var(--muted);font-size:13px}\n  .proof .starz{color:var(--accent);letter-spacing:1px}\n  .vis{position:relative;min-height:520px}\n  .pic{position:relative;overflow:hidden;border-radius:var(--r-2xl);background:\n      radial-gradient(120% 90% at 20% 10%,color-mix(in oklab,var(--accent) 38%,var(--surface)),transparent 60%),\n      radial-gradient(90% 80% at 85% 95%,color-mix(in oklab,var(--accent) 55%,#000 8%),transparent 55%),\n      color-mix(in oklab,var(--accent) 18%,var(--surface))}\n  .pic::after{content:\"\";position:absolute;inset:0;background:${NOISE};mix-blend-mode:multiply;pointer-events:none}\n  .pic .soap{position:absolute;left:12%;right:12%;top:38%;height:22%;border-radius:12px;background:linear-gradient(180deg,color-mix(in oklab,var(--accent) 12%,#fff),color-mix(in oklab,var(--accent) 30%,#fff 0%));box-shadow:0 30px 50px -24px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.6);transform:rotate(-6deg)}\n  .pic .soap::before{content:\"LOJA BOA\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font:600 11px/1 var(--font);letter-spacing:.32em;color:color-mix(in oklab,var(--accent) 70%,#000)}\n  .main{position:absolute;inset:0 8% 10% 0;height:auto}\n  .float{position:absolute;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-xl);box-shadow:var(--sh-3);padding:14px 16px;animation:drift 7s ease-in-out infinite}\n  .f1{right:-4px;bottom:8%;width:min(250px,70%)} .f2{left:-8px;top:6%;width:min(220px,64%);animation-delay:-3.5s}\n  @keyframes drift{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}\n  .float .kp{display:flex;align-items:baseline;gap:8px} .float .kp b{font:600 26px/1 var(--font)} .float small{color:var(--muted)}\n  .press{display:flex;align-items:center;gap:clamp(18px,4cqi,44px);flex-wrap:wrap;padding:22px 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line);color:var(--faint);font-size:12px}\n  .press b{font:600 15px/1 var(--font);color:var(--muted);letter-spacing:.02em}\n  section{padding:clamp(40px,7cqi,84px) 0}\n  .kicker{font-size:12px;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);margin-bottom:10px}\n  h2{font:500 clamp(28px,3.6cqi,44px)/1.1 var(--font);letter-spacing:-.025em;margin:0 0 14px;text-wrap:balance}\n  .sub{color:var(--muted);max-width:56ch;margin:0 0 30px;line-height:1.55}\n  .feat .card{height:100%} .feat .fic{width:40px;height:40px;border-radius:12px;display:grid;place-items:center;background:color-mix(in oklab,var(--accent) 14%,var(--surface));color:var(--accent);margin-bottom:14px}\n  .feat h3{font:600 18px/1.2 var(--font);margin:0 0 6px} .feat p{margin:0;color:var(--muted);line-height:1.5;font-size:14px}\n  .band{margin:0 calc(-1 * clamp(18px,4cqi,48px));padding:clamp(48px,8cqi,96px) clamp(18px,4cqi,48px);background:var(--accent);color:var(--on-accent);text-align:center}\n  .band q{display:block;font:400 clamp(28px,4.6cqi,56px)/1.15 var(--font);letter-spacing:-.02em;font-style:italic;max-width:22ch;margin:0 auto 14px;quotes:\"“\" \"”\"}\n  .band small{opacity:.8;letter-spacing:.14em;text-transform:uppercase;font-size:12px}\n  .shelf{display:grid;grid-template-columns:repeat(4,1fr);gap:18px} @container (max-width:900px){.shelf{grid-template-columns:repeat(2,1fr)}}\n  .prod{display:flex;flex-direction:column;gap:10px;cursor:pointer} .prod .pic{aspect-ratio:4/5;transition:transform .35s cubic-bezier(.23,1,.32,1)} .prod:hover .pic{transform:translateY(-4px) rotate(-.6deg)}\n  .prod .pic .soap{top:46%;height:16%}\n  .prod .pname{font:500 16px/1.25 var(--font)} .prod .prow{display:flex;align-items:center;gap:8px;color:var(--muted);font-size:14px}\n  .prod .prow b{color:var(--text);font-variant-numeric:tabular-nums}\n  .voices{display:grid;grid-template-columns:repeat(3,1fr);gap:18px} @container (max-width:820px){.voices{grid-template-columns:1fr}}\n  .vc p{font:400 17px/1.45 var(--font);margin:0 0 16px;font-style:italic} .vc .by{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--muted)}\n  .tiers{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;align-items:stretch} @container (max-width:820px){.tiers{grid-template-columns:1fr}}\n  .tier{display:flex;flex-direction:column;gap:14px;padding:26px;border:1px solid var(--line);border-radius:var(--r-2xl);background:var(--surface)}\n  .tier.hi{border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--accent) 16%,transparent),var(--sh-2);position:relative}\n  .tier.hi .tiertag{position:absolute;top:-12px;left:22px}\n  .tier .price{font:500 42px/1 var(--font);letter-spacing:-.03em} .tier .price small{font:400 14px var(--font);color:var(--muted);letter-spacing:0}\n  .tier ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px;font-size:14px;color:var(--muted)} .tier li{display:flex;gap:8px;align-items:center} .tier li svg{color:var(--accent)}\n  .tier .btn{margin-top:auto}\n  footer{padding:48px 0 36px;border-top:1px solid var(--line);display:grid;grid-template-columns:1.2fr 1fr;gap:32px;align-items:start;color:var(--muted);font-size:13px} @container (max-width:720px){footer{grid-template-columns:1fr}}\n  footer .nl{display:flex;gap:8px;max-width:420px} footer .nl .in{flex:1}\n  footer .cols{display:grid;grid-template-columns:repeat(3,1fr);gap:16px} footer .cols b{display:block;color:var(--text);margin-bottom:8px;font-weight:600} footer .cols a{display:block;color:var(--muted);text-decoration:none;margin:4px 0}\n  .fine{grid-column:1/-1;display:flex;justify-content:space-between;color:var(--faint);padding-top:18px;border-top:1px solid var(--line)}\n  `,\n  render(d2) {\n    const { html, ui, fmt } = d2\n    const st = d2.state\n    const ds = d2.data('produtos')\n    const live = ds ? ds.records() : []\n    const items = (live.length ? live.slice(0, 4) : PLACEHOLDER).map((p, i) => ({ nome: p.nome || p.name || p.titulo || `Produto ${i + 1}`, preco: Number(p.preco ?? p.price ?? 0) || PLACEHOLDER[i % 4].preco, tag: p.tag || (live.length ? (i === 0 ? 'novo' : '') : PLACEHOLDER[i].tag) }))\n    const hue = (i) => `filter:hue-rotate(${[0, -14, 18, -28][i % 4]}deg)`\n    const tick = (t) => html`<li>${d2.icon('check', 'sm')}${t}</li>`\n\n    return html`<div class=\"wrap\">\n      <nav class=\"nav pg\"><div class=\"brand\">Loja <i>Boa</i></div><a>Vitrine</a><a>Assinatura</a><a>Jornal</a><a>Sobre</a><span class=\"sp\"></span>${ui.btn('Entrar', { kind: 'ghost', size: 'sm' })}${ui.btn('Comprar', { kind: 'pri', size: 'sm', icon: 'box', cls: 'go' })}</nav>\n      <div class=\"pg\">\n        <header class=\"hg\">\n          <div>\n            <div class=\"eyebrow\">${ui.pill('Coleção Terra e papel', 'ok', { dot: true, out: true })}<span class=\"muted\" style=\"font-size:13px\">nova, 12 barras</span></div>\n            <h1>Sabão de barra, feito <i>devagar.</i></h1>\n            <p class=\"lead\">Óleos prensados a frio, barro do vale e seis semanas de cura. Embalado em papel, sem um grama de plástico, na sua porta em 48 h.</p>\n            <div class=\"cta\">${ui.btn('Ver a vitrine', { kind: 'pri', size: 'lg', iconRight: 'arrow', cls: 'go' })}${ui.btn('Montar minha caixa', { kind: 'soft', size: 'lg', cls: 'box' })}</div>\n            <div class=\"proof\"><span class=\"starz\">★★★★★</span><span><b>4,9</b> · 1.284 avaliações</span><span>·</span><span>frete grátis acima de R$ 120</span></div>\n          </div>\n          <div class=\"vis\">\n            <div class=\"pic main\"><div class=\"soap\"></div></div>\n            <div class=\"float f1\"><div class=\"kp\"><b>${st.hoje ?? 128}</b><small>pedidos hoje</small>${ui.pill('+12%', 'ok', { solid: true })}</div>${ui.kpi({ label: 'últimos 14 dias', value: '', spark: [8, 11, 9, 14, 13, 17, 16, 19, 18, 22, 21, 25, 24, 28] })}</div>\n            <div class=\"float f2\">${ui.timeline([{ title: 'Entregue', text: 'Pedido 1042 · Belo Horizonte', time: '14:20', kind: 'acc' }, { title: 'Saiu para entrega', text: 'Pedido 1041', time: '09:05' }])}</div>\n          </div>\n        </header>\n        <div class=\"press\"><span>Como visto em</span><b>Folha</b><b>Casa Vogue</b><b>Vida Simples</b><b>Época</b><b>GQ Brasil</b></div>\n        <section class=\"feat\">\n          <div class=\"kicker\">Por que a Loja Boa</div>\n          <h2>Três coisas que a gente não abre mão.</h2>\n          <p class=\"sub\">Receita da avó, matéria-prima de quem a gente conhece pelo nome e uma logística que entrega no dia seguinte sem gritar.</p>\n          <div class=\"grid g3\">\n            ${[['sun', 'Prensado a frio', 'Azeite, coco e babaçu prensados a frio, sem aquecer: a barra guarda a glicerina e a pele agradece.'], ['box', 'Embalado em papel', 'Papel kraft, cera de abelha e barbante. O carrinho inteiro vai sem plástico, até o lacre.'], ['truck', 'Em 48 h na sua porta', 'Separação no mesmo dia e transportadora própria no Sudeste. O rastreio chega no WhatsApp.']].map(([ic, t, p]) => ui.card({ pad: true, body: html`<div class=\"fic\">${d2.icon(ic)}</div><h3>${t}</h3><p>${p}</p>` }))}\n          </div>\n        </section>\n        <div class=\"band\"><q>Um sabão que parece pão: quente, honesto, feito por gente.</q><small>Casa Vogue · edição de maio</small></div>\n        <section>\n          <div class=\"row\" style=\"align-items:flex-end;margin-bottom:26px\"><div><div class=\"kicker\">A vitrine</div><h2 style=\"margin:0\">${live.length ? 'Direto da prateleira.' : 'Os mais pedidos.'}</h2></div><span class=\"sp\"></span>${ui.btn('Ver tudo', { kind: 'link', iconRight: 'arrow' })}</div>\n          <div class=\"shelf\">${items.map((p, i) => html`<div class=\"prod\"><div class=\"pic\" style=\"${hue(i)}\"><div class=\"soap\"></div></div><div class=\"pname\">${p.nome}</div><div class=\"prow\"><b>${fmt.currency(p.preco, 'BRL')}</b>${p.tag ? ui.pill(p.tag, p.tag === 'novo' ? 'ok' : 'warn', { out: true }) : ''}</div></div>`)}</div>\n        </section>\n        <section>\n          <div class=\"kicker\">Quem usa</div>\n          <h2>Cartas da prateleira.</h2>\n          <div class=\"voices\" style=\"margin-top:26px\">\n            ${[['Ana Beatriz', 'Comprei por causa da embalagem e fiquei pelo cheiro. A de alecrim dura dois meses.', 'Campinas'], ['Rafael T.', 'Pedi às 10h, chegou no dia seguinte antes do almoço, com um bilhete à mão. Virei assinante.', 'São Paulo'], ['Dona Lúcia', 'Minha neta me deu a caixa das irmãs. A de aveia é a mesma que eu fazia em casa.', 'Tiradentes']].map(([n, t, c]) => ui.card({ pad: true, cls: 'vc', body: html`<p>“${t}”</p><div class=\"by\">${ui.avatar(n, { size: 'sm' })}<span><b>${n}</b> · ${c}</span></div>` }))}\n          </div>\n        </section>\n        <section>\n          <div class=\"kicker\">Assinatura</div>\n          <h2>Nunca mais acabar o sabão.</h2>\n          <p class=\"sub\">Uma caixa com as barras da estação, no seu ritmo. Pausa quando quiser, sem multa, sem telefonema.</p>\n          <div class=\"tiers\">\n            ${[['Mensal', 49, ['3 barras da estação', 'Frete grátis', 'Pausa quando quiser'], false], ['Trimestral', 129, ['9 barras + saboneteira', 'Frete grátis', 'Uma barra surpresa', 'Jornal impresso'], true], ['Anual', 449, ['36 barras, 12 entregas', 'Caixa das irmãs no Natal', 'Visita ao galpão'], false]].map(([n, v, feats, hi]) => html`<div class=\"tier${hi ? ' hi' : ''}\">${hi ? html`<span class=\"tiertag\">${ui.pill('mais escolhido', 'ok', { solid: true })}</span>` : ''}<div><div class=\"muted\" style=\"font-size:13px\">${n}</div><div class=\"price\">${fmt.currency(v, 'BRL')}<small> / ${n === 'Mensal' ? 'mês' : n === 'Anual' ? 'ano' : 'trimestre'}</small></div></div><ul>${feats.map(tick)}</ul>${ui.btn(hi ? 'Assinar agora' : 'Escolher', { kind: hi ? 'pri' : 'soft', block: true, cls: 'btn sub', attrs: { 'data-tier': n } })}</div>`)}\n          </div>\n        </section>\n        <footer>\n          <div><div class=\"brand\" style=\"margin-bottom:10px\">Loja <i>Boa</i></div><p style=\"margin:0 0 14px;max-width:40ch;line-height:1.5\">Uma carta por mês sobre o ofício, a casa e o que a gente anda cozinhando. Sem spam, prometido.</p><div class=\"nl\">${ui.input({ name: 'nl', type: 'email', placeholder: 'seu e-mail', icon: 'mail', attrs: { class: 'in' } })}${ui.btn('Assinar', { kind: 'pri', cls: 'nlgo' })}</div></div>\n          <div class=\"cols\"><div><b>Loja</b><a>Vitrine</a><a>Assinatura</a><a>Presentes</a></div><div><b>Casa</b><a>Sobre nós</a><a>O galpão</a><a>Jornal</a></div><div><b>Ajuda</b><a>Entregas</a><a>Trocas</a><a>WhatsApp</a></div></div>\n          <div class=\"fine\"><span>© 2026 Loja Boa · Tiradentes, MG</span><span>feito devagar</span></div>\n        </footer>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    const ds = d2.data('produtos')\n    if (ds && ds.loadAll) ds.loadAll().catch(() => {})\n    d2.on('.go', 'click', () => d2.ui.toast({ title: 'A vitrine', text: 'Abriria a loja aqui.', kind: 'ok' }))\n    d2.on('.box', 'click', () => d2.ui.sheet({ title: 'Montar minha caixa', body: '<p class=\"muted\">Escolha três barras. A caixa fecha com barbante e vai com um bilhete.</p>', side: 'right', width: 360 }))\n    d2.on('.sub', 'click', async (e, el) => { if (await d2.ui.confirm({ title: `Assinar o plano ${el.dataset.tier}?`, body: 'Você pode pausar quando quiser.', ok: 'Assinar' })) d2.ui.toast('Bem-vindo à prateleira', { kind: 'ok' }) })\n    d2.on('.nlgo', 'click', () => d2.ui.toast('Primeira carta no mês que vem', { kind: 'ok' }))\n  },\n}\n"}},"produtos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"produtos"}}}}}}}}}},{"_id":"6ae80812800882f440a1cab8","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"editorial-jornal","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":"// Jornal da Loja · an editorial spread on the Porcelain kit: masthead, cover story, drop cap, two\n// columns, a pull quote, the aside, a photo essay. Typeface, ink and paper come from the kit's\n// tokens, so a type sample or a palette dropped on it in a design board re-sets the page live.\nconst NOISE = `url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.2'/%3E%3C/svg%3E\")`\nconst BODY = [\n  'A sala de cura não tem janelas. Tem prateleiras de pinho até o teto, um termômetro de mercúrio e um silêncio que cheira a alecrim. É aqui que as barras ficam seis semanas, viradas uma a uma a cada domingo, até perderem a água e ganharem a dureza de uma pedra de rio.',\n  '“Minha avó dizia que sabão bom é sabão paciente”, conta Marta, a mais velha das três irmãs, enquanto risca a lateral de uma barra com a unha. “Se marca, não está pronto.” A receita está num caderno de capa dura, escrito a lápis, e nunca saiu da família. O que saiu foi a escala: de doze barras por mês, em 2021, para quatro mil.',\n  'O salto veio sem fábrica. Veio com um galpão alugado, uma prensa de azeite de segunda mão e uma decisão que parecia teimosia: nada de plástico, em nenhum ponto, nem no lacre da caixa. “Perdemos uma rede de farmácias por causa disso”, lembra Júlia. “Ganhamos todo o resto.”',\n  'Hoje a loja vende para o país inteiro, entrega em dois dias no Sudeste e tem uma lista de espera para visitar o galpão aos sábados. Na porta, uma placa escrita à mão avisa: “Aqui tudo demora um pouco. É de propósito.”',\n]\n\nexport default {\n  ui: { accent: '#a0584b', radius: 'sm', tokens: { bg: '#f6f1e8', surface: '#fffdf8', line: '#dcd3c3', text: '#1b1a17' }, font: '\"Fraunces\", \"Iowan Old Style\", Georgia, serif' },\n  css: `\n  :host{display:block;height:100%}\n  .wrap{height:100%;overflow:auto;background:var(--bg);color:var(--text);container-type:inline-size;font-family:var(--font)}\n  .page{max-width:1080px;margin:0 auto;padding:clamp(20px,4cqi,44px) clamp(18px,4cqi,48px) 64px}\n  .mast{display:flex;align-items:baseline;gap:18px;padding-bottom:14px;border-bottom:3px double var(--text);flex-wrap:wrap}\n  .mast .mname{font:600 clamp(26px,3.4cqi,38px)/1 var(--font);letter-spacing:-.02em} .mast .mname i{font-style:italic;color:var(--accent)}\n  .mast .iss{color:var(--muted);font-size:13px;letter-spacing:.12em;text-transform:uppercase}\n  .mast .secs{margin-left:auto;display:flex;gap:18px;font-size:13px;color:var(--muted)} .mast .secs b{color:var(--text);font-weight:600;border-bottom:2px solid var(--accent)}\n  .cover{padding:clamp(28px,5cqi,56px) 0 22px}\n  .kick{font-size:12px;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);margin-bottom:14px;display:flex;gap:10px;align-items:center} .kick::after{content:\"\";flex:1;max-width:80px;height:1px;background:var(--accent)}\n  h1{font:400 clamp(44px,8.4cqi,104px)/.98 var(--font);letter-spacing:-.04em;margin:0 0 20px;text-wrap:balance;max-width:14ch} h1 i{font-style:italic;color:var(--accent)}\n  .deck{font:400 clamp(18px,2.1cqi,24px)/1.35 var(--font);color:var(--muted);max-width:40ch;margin:0 0 20px;font-style:italic}\n  .by{display:flex;gap:14px;align-items:center;font-size:13px;color:var(--muted);flex-wrap:wrap} .by b{color:var(--text);font-weight:600} .by .dot{width:3px;height:3px;border-radius:50%;background:var(--faint)}\n  .pic{position:relative;overflow:hidden;background:\n      radial-gradient(110% 80% at 15% 15%,color-mix(in oklab,var(--accent) 34%,var(--surface)),transparent 60%),\n      radial-gradient(80% 70% at 90% 90%,color-mix(in oklab,var(--accent) 60%,#000 12%),transparent 55%),\n      color-mix(in oklab,var(--accent) 16%,var(--surface))}\n  .pic::after{content:\"\";position:absolute;inset:0;background:${NOISE};mix-blend-mode:multiply;pointer-events:none}\n  .pic .shelf{position:absolute;left:8%;right:8%;top:30%;height:6px;background:color-mix(in oklab,var(--accent) 55%,#000);box-shadow:0 60px 0 color-mix(in oklab,var(--accent) 55%,#000),0 120px 0 color-mix(in oklab,var(--accent) 55%,#000);opacity:.5}\n  .pic .soap{position:absolute;width:12%;height:7%;border-radius:3px;background:color-mix(in oklab,var(--accent) 10%,#fff);box-shadow:0 10px 18px -8px rgba(0,0,0,.5);opacity:.95}\n  .heroimg{aspect-ratio:21/9;margin-top:34px} .heroimg .soap:nth-child(2){left:12%;top:22%} .heroimg .soap:nth-child(3){left:28%;top:22%;width:10%} .heroimg .soap:nth-child(4){left:60%;top:22%} .heroimg .soap:nth-child(5){left:20%;top:52%} .heroimg .soap:nth-child(6){left:44%;top:52%;width:14%} .heroimg .soap:nth-child(7){left:72%;top:52%} .heroimg .soap:nth-child(8){left:34%;top:82%;width:9%}\n  figcaption{font-size:12.5px;color:var(--muted);padding:10px 0 0;display:flex;gap:8px} figcaption b{color:var(--accent);font-weight:600}\n  .spread{display:grid;grid-template-columns:1fr 280px;gap:clamp(28px,5cqi,64px);padding-top:clamp(28px,4cqi,48px);border-top:1px solid var(--line);margin-top:38px}\n  @container (max-width:820px){.spread{grid-template-columns:1fr}.cols{columns:1!important}}\n  .cols{columns:2;column-gap:36px;font-size:16.5px;line-height:1.62;text-align:justify;hyphens:auto} @container (max-width:980px){.cols{font-size:15.5px}}\n  .cols p{margin:0 0 1em;break-inside:avoid-column}\n  .cols p:first-of-type::first-letter{float:left;font:400 84px/.78 var(--font);padding:8px 10px 0 0;color:var(--accent)}\n  .pull{column-span:all;margin:12px 0 28px;padding:22px 0;border-top:1px solid var(--text);border-bottom:1px solid var(--text);text-align:left}\n  .pull q{font:400 clamp(26px,3.4cqi,40px)/1.15 var(--font);font-style:italic;letter-spacing:-.02em;quotes:\"“\" \"”\";display:block;max-width:26ch}\n  .pull small{display:block;margin-top:10px;color:var(--muted);font-size:12px;letter-spacing:.14em;text-transform:uppercase}\n  .end::after{content:\"\";display:inline-block;width:9px;height:9px;background:var(--accent);margin-left:8px;vertical-align:baseline}\n  aside{display:flex;flex-direction:column;gap:22px;font-size:14px}\n  aside h4{font:600 12px/1 var(--font);letter-spacing:.2em;text-transform:uppercase;color:var(--muted);margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--line)}\n  .toc{list-style:none;margin:0;padding:0;counter-reset:n} .toc li{counter-increment:n;display:grid;grid-template-columns:28px 1fr;gap:10px;padding:9px 0;border-bottom:1px dotted var(--line);align-items:baseline}\n  .toc li::before{content:counter(n,decimal-leading-zero);font-size:12px;color:var(--accent);font-variant-numeric:tabular-nums} .toc li b{display:block;font-weight:600} .toc li span{color:var(--muted);font-size:13px}\n  .nums{display:grid;grid-template-columns:1fr 1fr;gap:10px} .nums div{padding:12px;background:var(--surface);border:1px solid var(--line)} .nums b{display:block;font:400 30px/1 var(--font);letter-spacing:-.03em;color:var(--accent)} .nums span{font-size:12px;color:var(--muted)}\n  .essay{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:16px;margin-top:clamp(36px,5cqi,64px)} @container (max-width:820px){.essay{grid-template-columns:1fr 1fr}}\n  .essay figure{margin:0} .essay .pic{aspect-ratio:4/3} .essay figure:first-child .pic{aspect-ratio:auto;height:100%;min-height:260px}\n  .next{margin-top:clamp(36px,5cqi,64px);padding-top:22px;border-top:3px double var(--text);display:flex;gap:18px;align-items:center;justify-content:space-between;flex-wrap:wrap}\n  .next .lab{font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:var(--muted)} .next .nxt{font:400 clamp(20px,2.6cqi,30px)/1.15 var(--font);letter-spacing:-.02em;max-width:30ch}\n  `,\n  render(d2) {\n    const { html, ui } = d2\n    const bars = html`<div class=\"shelf\"></div><div class=\"soap\"></div><div class=\"soap\"></div><div class=\"soap\"></div><div class=\"soap\"></div><div class=\"soap\"></div><div class=\"soap\"></div><div class=\"soap\"></div>`\n    return html`<div class=\"wrap\"><div class=\"page\">\n      <header class=\"mast\"><div class=\"mname\">Jornal da <i>Loja</i></div><div class=\"iss\">Nº 07 · Outono 2026</div><div class=\"secs\"><b>Ofício</b><span>Casa</span><span>Receitas</span><span>Cartas</span></div></header>\n      <article>\n        <div class=\"cover\">\n          <div class=\"kick\">Ofício · Tiradentes, MG</div>\n          <h1>O retorno do sabão <i>de barra</i></h1>\n          <p class=\"deck\">Em um galpão sem janelas, três irmãs recuperam a receita da avó, recusam o plástico e vendem para o país inteiro — devagar, de propósito.</p>\n          <div class=\"by\">${ui.avatar('Marina Reis', { size: 'sm' })}<span>por <b>Marina Reis</b></span><span class=\"dot\"></span><span>fotos <b>Teo Lacerda</b></span><span class=\"dot\"></span><span>8 min de leitura</span></div>\n          <figure style=\"margin:0\"><div class=\"pic heroimg\">${bars}</div><figcaption><b>Acima</b> A sala de cura: seis semanas até a barra perder a água e ganhar a dureza de uma pedra de rio.</figcaption></figure>\n        </div>\n        <div class=\"spread\">\n          <div class=\"cols\">\n            <p>${BODY[0]}</p>\n            <p>${BODY[1]}</p>\n            <div class=\"pull\"><q>Perdemos uma rede de farmácias por causa do plástico. Ganhamos todo o resto.</q><small>Júlia, a irmã do meio</small></div>\n            <p>${BODY[2]}</p>\n            <p class=\"end\">${BODY[3]}</p>\n          </div>\n          <aside>\n            <div><h4>Nesta edição</h4><ol class=\"toc\"><li><b>O retorno do sabão de barra</b><span>A capa · Ofício</span></li><li><b>Uma casa que cheira a cedro</b><span>Casa</span></li><li><b>Pão de fermentação longa, em três dias</b><span>Receitas</span></li><li><b>Cartas: a saboneteira que virou herança</b><span>Leitores</span></li></ol></div>\n            <div><h4>Em números</h4><div class=\"nums\"><div><b>6</b><span>semanas de cura</span></div><div><b>0</b><span>gramas de plástico</span></div><div><b>48h</b><span>até a sua porta</span></div><div><b>4 mil</b><span>barras por mês</span></div></div></div>\n            <div><h4>Para visitar</h4><p style=\"margin:0;line-height:1.5;color:var(--muted)\">Sábados, 10h, com lista de espera. Rua do Galpão, 12 — Tiradentes. Leve um pote: a sobra do dia vai de presente.</p>${ui.btn('Entrar na lista', { kind: 'soft', size: 'sm', iconRight: 'arrow', cls: 'visit' })}</div>\n          </aside>\n        </div>\n        <div class=\"essay\">\n          <figure><div class=\"pic\">${bars}</div><figcaption><b>1</b> A prensa de azeite, comprada de segunda mão em 2021.</figcaption></figure>\n          <figure><div class=\"pic\">${bars}</div><figcaption><b>2</b> O caderno de capa dura.</figcaption></figure>\n          <figure><div class=\"pic\">${bars}</div><figcaption><b>3</b> Barbante, cera e papel: o lacre.</figcaption></figure>\n        </div>\n        <div class=\"next\"><div><div class=\"lab\">A seguir · Casa</div><div class=\"nxt\">Uma casa que cheira a cedro: a reforma que começou pela saboneteira</div></div>${ui.btn('Continuar lendo', { kind: 'ghost', iconRight: 'arrow' })}</div>\n      </article>\n    </div></div>`\n  },\n  mounted(d2) {\n    d2.on('.visit', 'click', () => d2.ui.toast({ title: 'Lista de espera', text: 'A gente avisa quando abrir vaga no sábado.', kind: 'ok' }))\n  },\n}\n"}}}}}}}},{"_id":"6ae873033dc1619bc8d01d65","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"pedido-cobre-nevoa","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  ui: {\n    theme: 'light',\n    accent: '#894617',\n    radius: 'xl',\n    font: '\"Public Sans\", system-ui, sans-serif',\n    tokens: { bg: '#a8b9b6', surface: '#f2f2f2', line: '#dad4bb', text: '#26302f' }\n  },\n  css: `\n    :host { display:block; }\n    .pcn-wrap { width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; align-items:center; justify-content:center; }\n    .pcn-card { width:100%; max-width:380px; background:var(--surface); border-radius:var(--r-xl); overflow:hidden;\n      box-shadow:0 18px 36px -20px color-mix(in srgb, var(--text) 55%, transparent); }\n    .pcn-top { background:color-mix(in srgb, var(--text) 78%, var(--bg)); color:var(--surface); padding:18px 22px;\n      display:flex; align-items:center; justify-content:space-between; }\n    .pcn-code { font-weight:700; font-size:20px; letter-spacing:-.01em; }\n    .pcn-status { padding:4px 12px; border-radius:999px; background:var(--line); color:var(--accent); font-size:12px; font-weight:700; letter-spacing:.03em; }\n    .pcn-body { padding:22px; display:flex; flex-direction:column; gap:16px; }\n    .pcn-item { display:flex; gap:14px; align-items:center; }\n    .pcn-thumb { flex:none; width:56px; height:56px; border-radius:var(--r-md); background:linear-gradient(140deg,var(--accent),var(--line)); }\n    .pcn-item h4 { margin:0 0 3px; font-weight:600; font-size:18px; }\n    .pcn-item p { margin:0; font-size:13px; color:var(--muted); }\n    .pcn-track { display:flex; align-items:center; gap:6px; }\n    .pcn-step { flex:1; height:6px; border-radius:3px; background:var(--line); }\n    .pcn-step.pcn-on { background:var(--accent); }\n    .pcn-row { display:flex; align-items:center; justify-content:space-between; padding-top:4px; border-top:1px solid var(--line); }\n    .pcn-total { font-weight:700; font-size:22px; padding-top:14px; }\n    .pcn-row .btn { margin-top:14px; }\n  `,\n  render(d2) {\n    const code = d2.param('orderCode') || 'Pedido #4821';\n    const status = d2.param('statusLabel') || 'A caminho';\n    const item = d2.param('itemName') || 'Jarra de cerâmica';\n    const meta = d2.param('itemMeta') || '2 unidades · entrega em 2 dias';\n    const total = d2.param('total') || 'R$ 214,00';\n    const cta = d2.param('cta') || 'Acompanhar';\n    const steps = [1, 1, 1, 0].map((on, i) => '<span class=\"pcn-step' + (on ? ' pcn-on' : '') + '\" data-key=\"s' + i + '\"></span>').join('');\n    return `<div class=\"pcn-wrap\"><div class=\"pcn-card\">\n      <div class=\"pcn-top\"><span class=\"pcn-code\">${code}</span><span class=\"pcn-status\">${status}</span></div>\n      <div class=\"pcn-body\">\n        <div class=\"pcn-item\"><div class=\"pcn-thumb\"></div><div><h4>${item}</h4><p>${meta}</p></div></div>\n        <div class=\"pcn-track\">${steps}</div>\n        <div class=\"pcn-row\"><span class=\"pcn-total\">${total}</span><button class=\"btn pri\">${cta}</button></div>\n      </div>\n    </div></div>`;\n  }\n}"}},"orderCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderCode"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"itemName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"itemName"}}}},"itemMeta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"itemMeta"}}}},"total":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"total"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}}}}}}}},{"_id":"6ae87335abf64c835dca036a","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"estoque-grafite","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  ui: {\n    theme: 'dark',\n    accent: '#ededec',\n    radius: 'lg',\n    font: '\"Space Grotesk\", system-ui, sans-serif',\n    tokens: { bg: '#050505', surface: '#272729', line: '#474748', text: '#ededec' }\n  },\n  css: `\n    :host { display:block; }\n    .egr-wrap { width:100%; height:100%; box-sizing:border-box; padding:20px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; }\n    .egr-wrap .card { flex:1; display:flex; flex-direction:column; gap:16px; min-height:0; padding:20px; background:var(--surface);\n      border:1px solid var(--line); border-radius:var(--r-lg); }\n    .egr-head { display:flex; align-items:flex-end; justify-content:space-between; gap:12px; }\n    .egr-title { margin:0; font-weight:600; font-size:20px; letter-spacing:-.01em; }\n    .egr-metric { text-align:right; }\n    .egr-metric b { display:block; font-weight:500; font-size:30px; line-height:1; font-variant-numeric:tabular-nums; }\n    .egr-metric small { font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }\n    .egr-list { display:flex; flex-direction:column; border-top:1px solid var(--line); overflow:auto; min-height:0; }\n    .egr-row { display:flex; align-items:center; gap:12px; padding:12px 4px; border-bottom:1px solid var(--line); cursor:pointer; transition:background .12s ease; }\n    .egr-row:hover { background:var(--line); }\n    .egr-row.egr-on { background:var(--accent); color:var(--on-accent); border-radius:var(--r-md); border-bottom-color:transparent; padding-left:12px; padding-right:12px; }\n    .egr-dot { flex:none; width:8px; height:8px; border-radius:50%; background:var(--muted); }\n    .egr-row.egr-low .egr-dot { background:transparent; border:2px solid var(--muted); box-sizing:border-box; }\n    .egr-name { flex:1; font-weight:500; font-size:15px; }\n    .egr-name small { display:block; margin-top:2px; font-weight:400; font-size:12px; color:var(--muted); }\n    .egr-row.egr-on .egr-name small { color:inherit; opacity:.65; }\n    .egr-qty { font-size:14px; font-variant-numeric:tabular-nums; }\n  `,\n  render(d2) {\n    const title = d2.param('panelTitle') || 'Estoque';\n    const value = d2.param('metricValue') || '1.284';\n    const label = d2.param('metricLabel') || 'unidades';\n    const given = d2.param('items');\n    const items = Array.isArray(given) && given.length ? given : [\n      { name: 'Jarra de cerâmica', meta: 'Prateleira A2', qty: 42, low: false },\n      { name: 'Sabonete de argila', meta: 'Prateleira B1', qty: 7, low: true },\n      { name: 'Vela de soja', meta: 'Prateleira B4', qty: 118, low: false },\n      { name: 'Toalha de linho', meta: 'Prateleira C3', qty: 5, low: true }\n    ];\n    const sel = d2.state.sel == null ? 0 : d2.state.sel;\n    const rows = items.map(function (it, i) {\n      return '<div class=\"egr-row' + (i === sel ? ' egr-on' : '') + (it.low ? ' egr-low' : '') + '\" data-key=\"r' + i + '\" data-i=\"' + i + '\">' +\n        '<span class=\"egr-dot\"></span><span class=\"egr-name\">' + it.name + '<small>' + (it.meta || '') + '</small></span>' +\n        '<span class=\"egr-qty\">' + it.qty + '</span></div>';\n    }).join('');\n    return `<div class=\"egr-wrap\"><div class=\"card\">\n      <div class=\"egr-head\"><h3 class=\"egr-title\">${title}</h3><div class=\"egr-metric\"><b>${value}</b><small>${label}</small></div></div>\n      <div class=\"egr-list\">${rows}</div>\n    </div></div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '.egr-row', function (e, el) {\n      d2.setState({ sel: Number(el.getAttribute('data-i')) });\n    });\n  }\n}"}},"panelTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panelTitle"}}}},"metricValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"metricValue"}}}},"metricLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"metricLabel"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}}}}}}}},{"_id":"6ae87c40abf64c835dca1093","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"relogio-entrega-neon","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  ui: {\n    theme: 'dark',\n    accent: '#00e5ff',\n    radius: 'lg',\n    font: '\"Rajdhani\", system-ui, sans-serif',\n    tokens: { bg: '#0a0a1a', surface: '#1a1a2e', line: '#2a3358', text: '#d8ffff' }\n  },\n  css: `\n    :host { display:block; }\n    .nrt-wrap { width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; align-items:center; justify-content:center; }\n    .nrt-card { position:relative; width:100%; max-width:380px; background:var(--surface); border:1px solid var(--line);\n      border-radius:var(--r-xl); padding:24px; display:flex; flex-direction:column; gap:18px; overflow:hidden;\n      box-shadow:0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent), 0 0 38px -8px color-mix(in srgb, var(--accent) 45%, transparent); }\n    .nrt-card::before { content:''; position:absolute; inset:0; pointer-events:none;\n      background:repeating-linear-gradient(180deg, transparent 0 3px, color-mix(in srgb, var(--accent) 5%, transparent) 3px 4px); }\n    .nrt-top { position:relative; display:flex; align-items:center; justify-content:space-between; }\n    .nrt-code { font-size:14px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); }\n    .nrt-status { display:flex; align-items:center; gap:8px; font-size:13px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--accent); }\n    .nrt-dot { width:8px; height:8px; border-radius:50%; background:var(--accent); box-shadow:0 0 10px var(--accent); }\n    .nrt-time { position:relative; font-size:68px; font-weight:700; line-height:1; letter-spacing:.04em; font-variant-numeric:tabular-nums;\n      color:var(--accent); text-shadow:0 0 18px color-mix(in srgb, var(--accent) 70%, transparent); }\n    .nrt-date { position:relative; margin-top:-8px; font-size:15px; letter-spacing:.12em; text-transform:uppercase; color:var(--muted); }\n    .nrt-rain { position:relative; display:flex; gap:5px; align-items:flex-end; height:34px; }\n    .nrt-drop { flex:1; border-radius:2px; background:linear-gradient(180deg, transparent, var(--accent)); opacity:.55; }\n    .nrt-actions { position:relative; display:flex; gap:10px; }\n    .nrt-actions .btn { flex:1; }\n  `,\n  render(d2) {\n    const time = d2.param('timeLabel') || '21:47';\n    const date = d2.param('dateLabel') || 'Ter · 02 Nov';\n    const status = d2.param('statusLabel') || 'Em rota';\n    const code = d2.param('orderCode') || 'Pedido #4821';\n    const cta = d2.param('cta') || 'Acompanhar';\n    const hs = [40, 72, 28, 90, 55, 34, 80, 48, 66, 30, 84, 52];\n    const drops = hs.map((h, i) => '<span class=\"nrt-drop\" data-key=\"d' + i + '\" style=\"height:' + h + '%\"></span>').join('');\n    return `<div class=\"nrt-wrap\"><div class=\"nrt-card\">\n      <div class=\"nrt-top\"><span class=\"nrt-code\">${code}</span><span class=\"nrt-status\"><span class=\"nrt-dot\"></span>${status}</span></div>\n      <div class=\"nrt-time\">${time}</div>\n      <div class=\"nrt-date\">${date}</div>\n      <div class=\"nrt-rain\">${drops}</div>\n      <div class=\"nrt-actions\"><button class=\"btn pri\">${cta}</button><button class=\"btn ghost\">Detalhes</button></div>\n    </div></div>`;\n  }\n}"}},"timeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"timeLabel"}}}},"dateLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dateLabel"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"orderCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderCode"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}}}}}}}},{"_id":"6ae881f8abf64c835dca1974","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"relogio-entrega-neon-v1","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  ui: {\n    theme: 'dark',\n    accent: '#00e5ff',\n    radius: 'md',\n    font: '\"Rajdhani\", system-ui, sans-serif',\n    tokens: { bg: '#0a0a1a', surface: '#1a1a2e', line: '#2a3358', text: '#d8ffff' }\n  },\n  css: `\n    :host { display:block; }\n    .nv1-wrap { width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; align-items:center; justify-content:center; }\n    .nv1-strip { width:100%; max-width:440px; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md);\n      display:grid; grid-template-columns:auto 1fr; overflow:hidden;\n      box-shadow:0 0 30px -10px color-mix(in srgb, var(--accent) 50%, transparent); }\n    .nv1-time { padding:20px 24px; font-size:56px; font-weight:700; line-height:1; letter-spacing:.02em; font-variant-numeric:tabular-nums;\n      color:var(--accent); text-shadow:0 0 16px color-mix(in srgb, var(--accent) 65%, transparent);\n      border-right:1px solid var(--line); display:flex; align-items:center; }\n    .nv1-side { padding:16px 20px; display:flex; flex-direction:column; justify-content:space-between; gap:10px; min-width:0; }\n    .nv1-code { font-size:13px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); }\n    .nv1-date { font-size:18px; font-weight:600; letter-spacing:.06em; }\n    .nv1-badge { align-self:flex-start; display:inline-flex; align-items:center; gap:6px; padding:3px 10px; border-radius:999px;\n      border:1px solid var(--accent); color:var(--accent); font-size:12px; font-weight:700; letter-spacing:.1em; text-transform:uppercase; }\n    .nv1-pulse { width:6px; height:6px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent); }\n    .nv1-foot { grid-column:1 / -1; display:flex; align-items:center; gap:12px; padding:12px 16px; border-top:1px solid var(--line);\n      background:color-mix(in srgb, var(--bg) 55%, var(--surface)); }\n    .nv1-line { flex:1; height:3px; border-radius:2px; background:var(--line); position:relative; overflow:hidden; }\n    .nv1-line::after { content:''; position:absolute; inset:0 35% 0 0; background:var(--accent); box-shadow:0 0 10px var(--accent); }\n  `,\n  render(d2) {\n    const time = d2.param('timeLabel') || '21:47';\n    const date = d2.param('dateLabel') || 'Ter · 02 Nov';\n    const status = d2.param('statusLabel') || 'Em rota';\n    const code = d2.param('orderCode') || 'Pedido #4821';\n    const cta = d2.param('cta') || 'Acompanhar';\n    return `<div class=\"nv1-wrap\"><div class=\"nv1-strip\">\n      <div class=\"nv1-time\">${time}</div>\n      <div class=\"nv1-side\">\n        <span class=\"nv1-code\">${code}</span>\n        <span class=\"nv1-date\">${date}</span>\n        <span class=\"nv1-badge\"><span class=\"nv1-pulse\"></span>${status}</span>\n      </div>\n      <div class=\"nv1-foot\"><span class=\"nv1-line\"></span><button class=\"btn pri\">${cta}</button></div>\n    </div></div>`;\n  }\n}"}},"timeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"timeLabel"}}}},"dateLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dateLabel"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"orderCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderCode"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}}}}}}}},{"_id":"6ae881f8029daa81bbd5b3ec","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"relogio-entrega-neon-v2","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  ui: {\n    theme: 'dark',\n    accent: '#ff2bd6',\n    radius: 'xl',\n    font: '\"Orbitron\", system-ui, sans-serif',\n    tokens: { bg: '#0b0716', surface: '#1b1030', line: '#3a2461', text: '#ffe6fb' }\n  },\n  css: `\n    :host { display:block; }\n    .nv2-wrap { width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; align-items:center; justify-content:center; }\n    .nv2-card { position:relative; width:100%; max-width:380px; min-height:300px; background:var(--surface); border:1px solid var(--line);\n      border-radius:var(--r-xl); overflow:hidden; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px; padding:28px 24px;\n      text-align:center; box-shadow:0 0 44px -12px color-mix(in srgb, var(--accent) 55%, transparent); }\n    .nv2-rain { position:absolute; inset:0; display:flex; justify-content:space-around; pointer-events:none; }\n    .nv2-streak { width:2px; border-radius:2px; align-self:flex-start;\n      background:linear-gradient(180deg, transparent, color-mix(in srgb, var(--accent) 70%, transparent)); opacity:.5; }\n    .nv2-code { position:relative; font-size:11px; font-weight:500; letter-spacing:.3em; text-transform:uppercase; color:var(--muted); }\n    .nv2-time { position:relative; font-size:62px; font-weight:800; line-height:1; letter-spacing:.02em; font-variant-numeric:tabular-nums;\n      color:var(--text); text-shadow:0 0 6px var(--accent), 0 0 24px color-mix(in srgb, var(--accent) 75%, transparent); }\n    .nv2-date { position:relative; font-size:12px; letter-spacing:.24em; text-transform:uppercase; color:var(--muted); }\n    .nv2-status { position:relative; margin-top:8px; padding:5px 16px; border-radius:999px; background:color-mix(in srgb, var(--accent) 18%, transparent);\n      color:var(--accent); font-size:12px; font-weight:700; letter-spacing:.2em; text-transform:uppercase; }\n    .nv2-card .btn { position:relative; margin-top:14px; }\n  `,\n  render(d2) {\n    const time = d2.param('timeLabel') || '21:47';\n    const date = d2.param('dateLabel') || 'Ter · 02 Nov';\n    const status = d2.param('statusLabel') || 'Em rota';\n    const code = d2.param('orderCode') || 'Pedido #4821';\n    const cta = d2.param('cta') || 'Acompanhar';\n    const hs = [38, 64, 26, 80, 48, 70, 32, 58, 44];\n    const streaks = hs.map((h, i) => '<span class=\"nv2-streak\" data-key=\"k' + i + '\" style=\"height:' + h + '%;margin-top:' + ((i * 17) % 30) + 'px\"></span>').join('');\n    return `<div class=\"nv2-wrap\"><div class=\"nv2-card\">\n      <div class=\"nv2-rain\">${streaks}</div>\n      <span class=\"nv2-code\">${code}</span>\n      <div class=\"nv2-time\">${time}</div>\n      <span class=\"nv2-date\">${date}</span>\n      <span class=\"nv2-status\">${status}</span>\n      <button class=\"btn pri\">${cta}</button>\n    </div></div>`;\n  }\n}"}},"timeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"timeLabel"}}}},"dateLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dateLabel"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"orderCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderCode"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}}}}}}}},{"_id":"6ae881f985eb924c27426b45","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"relogio-entrega-neon-v3","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  ui: {\n    theme: 'dark',\n    accent: '#00ff88',\n    radius: 'lg',\n    font: '\"Space Mono\", ui-monospace, monospace',\n    tokens: { bg: '#050a0a', surface: '#0f1a18', line: '#1f3a33', text: '#e4fff3' }\n  },\n  css: `\n    :host { display:block; }\n    .nv3-wrap { width:100%; height:100%; box-sizing:border-box; padding:24px; background:var(--bg); color:var(--text);\n      font-family:var(--font); display:flex; align-items:center; justify-content:center; }\n    .nv3-card { width:100%; max-width:400px; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-lg);\n      padding:22px; display:grid; grid-template-columns:150px 1fr; gap:20px; align-items:center;\n      box-shadow:0 0 36px -14px color-mix(in srgb, var(--accent) 50%, transparent); }\n    .nv3-dial { position:relative; width:150px; height:150px; }\n    .nv3-dial svg { width:100%; height:100%; transform:rotate(-90deg); }\n    .nv3-track { fill:none; stroke:var(--line); stroke-width:6; }\n    .nv3-arc { fill:none; stroke:var(--accent); stroke-width:6; stroke-linecap:round; stroke-dasharray:264 377;\n      filter:drop-shadow(0 0 6px var(--accent)); }\n    .nv3-tick { fill:none; stroke:var(--muted); stroke-width:1; stroke-dasharray:1 9.4; opacity:.6; }\n    .nv3-time { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:26px; font-weight:700;\n      letter-spacing:-.02em; font-variant-numeric:tabular-nums; color:var(--accent); text-shadow:0 0 12px color-mix(in srgb, var(--accent) 60%, transparent); }\n    .nv3-info { display:flex; flex-direction:column; gap:8px; min-width:0; }\n    .nv3-code { font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:var(--muted); }\n    .nv3-date { font-size:15px; font-weight:700; }\n    .nv3-status { display:flex; align-items:center; gap:8px; font-size:12px; color:var(--accent); text-transform:uppercase; letter-spacing:.12em; }\n    .nv3-status::before { content:''; width:8px; height:8px; border-radius:2px; background:var(--accent); box-shadow:0 0 8px var(--accent); }\n    .nv3-info .btn { align-self:flex-start; margin-top:6px; }\n  `,\n  render(d2) {\n    const time = d2.param('timeLabel') || '21:47';\n    const date = d2.param('dateLabel') || 'Ter · 02 Nov';\n    const status = d2.param('statusLabel') || 'Em rota';\n    const code = d2.param('orderCode') || 'Pedido #4821';\n    const cta = d2.param('cta') || 'Acompanhar';\n    return `<div class=\"nv3-wrap\"><div class=\"nv3-card\">\n      <div class=\"nv3-dial\">\n        <svg viewBox=\"0 0 150 150\">\n          <circle class=\"nv3-tick\" cx=\"75\" cy=\"75\" r=\"68\"></circle>\n          <circle class=\"nv3-track\" cx=\"75\" cy=\"75\" r=\"60\"></circle>\n          <circle class=\"nv3-arc\" cx=\"75\" cy=\"75\" r=\"60\"></circle>\n        </svg>\n        <div class=\"nv3-time\">${time}</div>\n      </div>\n      <div class=\"nv3-info\">\n        <span class=\"nv3-code\">${code}</span>\n        <span class=\"nv3-date\">${date}</span>\n        <span class=\"nv3-status\">${status}</span>\n        <button class=\"btn pri\">${cta}</button>\n      </div>\n    </div></div>`;\n  }\n}"}},"timeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"timeLabel"}}}},"dateLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dateLabel"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"orderCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderCode"}}}},"cta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cta"}}}}}}}}}},{"_id":"6ae89a35cb0787adf1f6647d","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"lettering-delorean","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  ui: {\n    theme: 'dark',\n    accent: '#d2691e',\n    radius: 'md',\n    font: '\"Orbitron\", system-ui, sans-serif',\n    tokens: { bg: '#020419', surface: '#192836', line: '#454c55', text: '#e9e8e6' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@800&display=swap');\n    :host { display:block; }\n    .lt-stage { width:100%; height:100%; box-sizing:border-box; container-type:inline-size;\n      display:flex; align-items:center; justify-content:center; overflow:hidden;\n      background: linear-gradient(180deg, #192836 0%, #020419 100%); }\n    .lt-word { font-family:'Orbitron', var(--font); font-weight:800; white-space:nowrap;\n      color: #888787; background-image: linear-gradient(180deg, #e9e8e6 0%, #888787 50%, #454c55 100%);\n      -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n      text-shadow: 0 1px 2px rgba(0,0,0,0.8), 0 0 10px rgba(210, 105, 30, 0.2);\n      letter-spacing: 0.15em; text-transform: uppercase; font-size: 22cqi; font-style: normal; }\n  `,\n  render(d2) {\n    const raw = d2.param('text');\n    const text = String(raw == null || raw === '' ? 'DELOREAN' : raw)\n      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n    return `<div class=\"lt-stage\"><span class=\"lt-word\">${text}</span></div>`;\n  }\n}"}},"text":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"text"}}}}}}}}}},{"_id":"6ae89d2a018c4bc4723e000a","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"concept-vy9","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  ui: {\n    theme: 'light',\n    accent: '#c8ff00',\n    radius: 'md',\n    font: '\"Space Grotesk\", \"Inter\", system-ui, sans-serif',\n    tokens: { bg: '#f0f4f8', surface: '#d9e2ec', line: '#b0c4d9', text: '#283c5a' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Space+Grotesk:wght@500;700&display=swap');\n    :host { display:block; }\n    .cv9-app { width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column; min-height:0;\n      background:var(--bg); color:var(--text); font-family:var(--font); }\n    .cv9-head { padding:22px 20px 14px; border-bottom:1px solid var(--line); display:flex; flex-direction:column; gap:10px;\n      background-image:linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);\n      background-size:28px 28px; background-position:-1px -1px; }\n    .cv9-top { display:flex; align-items:center; justify-content:space-between; }\n    .cv9-brand { font-size:12px; font-weight:500; letter-spacing:.22em; text-transform:uppercase; color:var(--muted); }\n    .cv9-barcode { display:flex; gap:2px; align-items:stretch; height:14px; }\n    .cv9-barcode i { display:block; background:var(--accent); width:2px; }\n    .cv9-title { margin:0; font-size:40px; line-height:.92; font-weight:700; letter-spacing:-.04em; text-transform:uppercase; transform:scaleX(.9); transform-origin:left; }\n    .cv9-sub { margin:0; font-size:13px; line-height:1.4; color:var(--muted); max-width:30ch; }\n    .cv9-stats { display:flex; gap:8px; }\n    .cv9-stats .chip { font-variant-numeric:tabular-nums; }\n    .cv9-list { flex:1; min-height:0; overflow:auto; padding:14px 16px; display:flex; flex-direction:column; gap:10px; }\n    .cv9-card { display:flex; flex-direction:column; gap:10px; padding:14px 16px; cursor:pointer; background:var(--surface);\n      border:1px solid var(--line); border-radius:var(--r-md); transition:background .12s ease, border-color .12s ease; }\n    .cv9-card:hover { border-color:var(--text); }\n    .cv9-card.cv9-on { background:var(--text); color:var(--bg); border-color:var(--text); }\n    .cv9-row { display:flex; align-items:center; justify-content:space-between; gap:10px; }\n    .cv9-code { font-size:11px; font-weight:500; letter-spacing:.16em; text-transform:uppercase; color:var(--muted); font-variant-numeric:tabular-nums; }\n    .cv9-card.cv9-on .cv9-code { color:var(--line); }\n    .cv9-card h4 { margin:0; font-size:17px; font-weight:700; letter-spacing:-.01em; }\n    .cv9-card p { margin:0; font-size:13px; line-height:1.4; color:var(--muted); }\n    .cv9-card.cv9-on p { color:var(--line); }\n    .cv9-meter { height:4px; border-radius:2px; background:var(--line); overflow:hidden; }\n    .cv9-meter span { display:block; height:100%; background:var(--text); }\n    .cv9-card.cv9-on .cv9-meter { background:var(--muted); }\n    .cv9-card.cv9-on .cv9-meter span { background:var(--accent); }\n    .cv9-bar { display:grid; grid-template-columns:repeat(4, 1fr); border-top:1px solid var(--line); background:var(--bg); padding:6px 8px 10px; }\n    .cv9-tab { display:flex; flex-direction:column; align-items:center; gap:5px; padding:8px 0; cursor:pointer; font-size:11px; font-weight:500;\n      letter-spacing:.1em; text-transform:uppercase; color:var(--muted); border-radius:var(--r-sm); }\n    .cv9-tab:hover { color:var(--text); }\n    .cv9-tab.cv9-active { color:var(--text); }\n    .cv9-pip { width:18px; height:4px; border-radius:2px; background:var(--line); }\n    .cv9-tab.cv9-active .cv9-pip { width:28px; background:var(--accent); box-shadow:0 0 0 1px var(--text); }\n  `,\n  render(d2) {\n    const given = d2.param('items');\n    const items = Array.isArray(given) && given.length ? given : [\n      { code: 'FLX-00481', title: 'Fila de textos gerados', meta: '14.302 hoje · 0 lidos por alguém', tag: 'Em curso', load: 92 },\n      { code: 'FLX-00482', title: 'Resumos de resumos', meta: 'Cada cópia perde 11% do sentido', tag: 'Em curso', load: 71 },\n      { code: 'FLX-00483', title: 'Imagens sem autor', meta: '3.870 variações da mesma foto', tag: 'Fila', load: 48 },\n      { code: 'FLX-00484', title: 'Respostas automáticas', meta: 'Respondem outras respostas automáticas', tag: 'Fila', load: 33 },\n      { code: 'FLX-00485', title: 'Revisão humana', meta: 'Nenhum revisor alocado', tag: 'Parado', load: 0 }\n    ];\n    const sel = d2.state.sel == null ? 0 : d2.state.sel;\n    const tab = d2.state.tab == null ? 0 : d2.state.tab;\n    const total = items.length;\n    const bars = [3, 1, 2, 1, 3, 2, 1, 2, 3, 1, 2].map(function (w, i) {\n      return '<i data-key=\"b' + i + '\" style=\"width:' + w + 'px\"></i>';\n    }).join('');\n    const cards = items.map(function (it, i) {\n      return '<div class=\"cv9-card' + (i === sel ? ' cv9-on' : '') + '\" data-key=\"c' + i + '\" data-i=\"' + i + '\">' +\n        '<div class=\"cv9-row\"><span class=\"cv9-code\">' + it.code + '</span><span class=\"badge\">' + it.tag + '</span></div>' +\n        '<h4>' + it.title + '</h4><p>' + (it.meta || '') + '</p>' +\n        '<div class=\"cv9-meter\"><span style=\"width:' + (it.load || 0) + '%\"></span></div></div>';\n    }).join('');\n    const tabs = ['Fluxo', 'Filas', 'Alertas', 'Ajustes'].map(function (n, i) {\n      return '<div class=\"cv9-tab' + (i === tab ? ' cv9-active' : '') + '\" data-key=\"t' + i + '\" data-t=\"' + i + '\"><span class=\"cv9-pip\"></span>' + n + '</div>';\n    }).join('');\n    return `<div class=\"cv9-app\">\n      <div class=\"cv9-head\">\n        <div class=\"cv9-top\"><span class=\"cv9-brand\">Sala 04 · Zumbido</span><span class=\"cv9-barcode\">${bars}</span></div>\n        <h2 class=\"cv9-title\">Pensamento<br>humano</h2>\n        <p class=\"cv9-sub\">O silêncio do data center é o som de tudo sendo processado. Ninguém está lendo.</p>\n        <div class=\"cv9-stats\"><span class=\"chip\">${total} filas</span><span class=\"chip\">0 pessoas</span><span class=\"chip\">24h ligado</span></div>\n      </div>\n      <div class=\"cv9-list\">${cards}</div>\n      <div class=\"cv9-bar\">${tabs}</div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '.cv9-card', function (e, el) {\n      d2.setState({ sel: Number(el.getAttribute('data-i')) });\n    });\n    d2.on('click', '.cv9-tab', function (e, el) {\n      d2.setState({ tab: Number(el.getAttribute('data-t')) });\n    });\n  }\n}"}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}}}}}}}},{"_id":"6ae8be17f384931e6b82b37b","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"concept-26c","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  ui: {\n    theme: 'light',\n    accent: '#2f506e',\n    radius: 'sm',\n    font: '\"Space Grotesk\", system-ui, sans-serif',\n    tokens: { bg: '#f0f8ff', surface: '#c0d6e5', line: '#8aa9c2', text: '#0a2a45' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Michroma&family=Space+Grotesk:wght@400;500;700&display=swap');\n    :host { display:block; }\n    .s26-page { width:100%; height:100%; box-sizing:border-box; overflow:auto; background:var(--bg); color:var(--text); font-family:var(--font);\n      display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); }\n    .s26-art { position:relative; background:var(--text); color:var(--bg); overflow:hidden; min-height:520px; display:flex; flex-direction:column; justify-content:space-between; padding:28px; }\n    .s26-art svg { position:absolute; inset:0; width:100%; height:100%; }\n    .s26-brand { position:relative; font-family:'Michroma', var(--font); font-size:13px; letter-spacing:.3em; text-transform:uppercase; }\n    .s26-cap { position:relative; display:flex; flex-direction:column; gap:8px; }\n    .s26-cap b { font-family:'Michroma', var(--font); font-weight:400; font-size:26px; line-height:1.15; letter-spacing:.02em; }\n    .s26-cap span { font-size:12px; letter-spacing:.14em; text-transform:uppercase; opacity:.7; }\n    .s26-form { display:flex; flex-direction:column; justify-content:center; gap:20px; padding:48px 44px; }\n    .s26-tag { font-size:11px; font-weight:500; letter-spacing:.22em; text-transform:uppercase; color:var(--muted); }\n    .s26-title { margin:0; font-family:'Michroma', var(--font); font-weight:400; font-size:28px; line-height:1.1; letter-spacing:.01em; }\n    .s26-lead { margin:0; font-size:14px; line-height:1.5; color:var(--muted); max-width:36ch; }\n    .s26-field { display:flex; flex-direction:column; gap:6px; }\n    .s26-lbl { font-size:11px; font-weight:700; letter-spacing:.16em; text-transform:uppercase; }\n    .s26-field .in { width:100%; box-sizing:border-box; padding:13px 14px; font-family:var(--font); font-size:15px; }\n    .s26-row { display:flex; align-items:center; justify-content:space-between; font-size:13px; }\n    .s26-row a { color:var(--text); text-decoration:underline; text-underline-offset:3px; cursor:pointer; }\n    .s26-chk { display:flex; align-items:center; gap:8px; cursor:pointer; }\n    .s26-box { width:16px; height:16px; border:2px solid var(--text); border-radius:var(--r-sm); display:inline-flex; align-items:center; justify-content:center; font-size:11px; line-height:1; }\n    .s26-box.s26-on { background:var(--text); color:var(--bg); }\n    .s26-go { width:100%; justify-content:center; padding:15px 20px; font-weight:700; letter-spacing:.04em; }\n    .s26-or { display:flex; align-items:center; gap:12px; font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--muted); }\n    .s26-or::before, .s26-or::after { content:''; flex:1; height:1px; background:var(--line); }\n    .s26-soc { display:grid; grid-template-columns:repeat(var(--s26-n, 2),1fr); gap:10px; }\n    .s26-soc .btn { justify-content:center; padding:12px 14px; font-weight:500; }\n    .s26-msg { min-height:18px; font-size:12px; font-weight:500; letter-spacing:.04em; color:var(--text); }\n    .s26-foot { font-size:12px; color:var(--muted); }\n    .s26-foot a { color:var(--text); font-weight:700; cursor:pointer; text-decoration:underline; text-underline-offset:3px; }\n    @media (max-width:760px) { .s26-page { grid-template-columns:1fr; } .s26-art { min-height:240px; } .s26-form { padding:32px 24px; } }\n  `,\n  render(d2) {\n    const given = d2.param('providers');\n    const base = Array.isArray(given) && given.length ? given.slice(0, 3) : [\n      { key: 'google', label: 'Google' },\n      { key: 'github', label: 'GitHub' }\n    ];\n    const providers = base.map(function (p, i) {\n      const label = String(p.label || p.nome || p.name || p.title || 'Conta ' + (i + 1));\n      return { key: String(p.key || p.id || label).toLowerCase(), label: label };\n    });\n    const keep = d2.state.keep ? 1 : 0;\n    const msg = d2.state.msg || '';\n    const phtml = providers.map(function (p, i) {\n      return '<button class=\"btn ghost\" data-key=\"p' + i + '\" data-social=\"' + p.key + '\">' + p.label + '</button>';\n    }).join('');\n    const art = `<svg viewBox=\"0 0 400 600\" preserveAspectRatio=\"xMidYMid slice\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n      <g stroke-width=\"1\" opacity=\".18\">\n        <path d=\"M0 100H400M0 200H400M0 300H400M0 400H400M0 500H400M100 0V600M200 0V600M300 0V600\"/>\n      </g>\n      <g stroke-width=\"2.2\" opacity=\".95\">\n        <path d=\"M120 330 L120 250 L150 200 L250 200 L280 250 L280 330 L250 370 L150 370 Z\"/>\n        <path d=\"M150 200 L165 160 L235 160 L250 200\"/>\n        <path d=\"M165 160 L200 120 L235 160\"/>\n        <path d=\"M148 262 H252 L244 292 H156 Z\"/>\n        <path d=\"M200 120 V90 M200 90 L212 74\"/>\n        <path d=\"M120 330 L84 350 L70 420 M280 330 L316 350 L330 420\"/>\n        <path d=\"M160 370 L158 430 M240 370 L242 430\"/>\n        <circle cx=\"186\" cy=\"277\" r=\"4\" fill=\"currentColor\"/>\n        <circle cx=\"214\" cy=\"277\" r=\"4\" fill=\"currentColor\"/>\n      </g>\n      <g stroke-width=\"1.2\" opacity=\".55\">\n        <path d=\"M112 224 L132 244 M118 214 L140 236 M286 214 L266 236 M292 224 L272 244\"/>\n        <path d=\"M130 380 L124 440 M146 384 L140 448 M254 384 L260 448 M270 380 L276 440\"/>\n      </g>\n      <g fill=\"currentColor\" stroke=\"none\" opacity=\".9\">\n        <path d=\"M60 470 C52 452 74 444 86 452 C96 440 118 446 114 464 C130 468 128 490 110 490 C108 506 84 506 80 492 C62 496 52 480 60 470 Z\"/>\n        <circle cx=\"140\" cy=\"440\" r=\"4\"/><circle cx=\"152\" cy=\"460\" r=\"2.6\"/><circle cx=\"46\" cy=\"500\" r=\"3.4\"/>\n        <circle cx=\"330\" cy=\"120\" r=\"5\"/><circle cx=\"346\" cy=\"140\" r=\"2.4\"/><circle cx=\"318\" cy=\"150\" r=\"3\"/>\n        <path d=\"M300 90 C296 78 312 74 320 80 C330 72 346 80 340 92 C352 98 346 112 334 110 C330 122 312 120 310 108 C298 110 292 98 300 90 Z\" opacity=\".75\"/>\n      </g>\n      <g stroke-width=\"1.6\" opacity=\".7\">\n        <path d=\"M290 480 C310 470 330 500 352 490 M300 510 C320 500 338 524 360 514\"/>\n      </g>\n    </svg>`;\n    return `<div class=\"s26-page\">\n      <div class=\"s26-art\">\n        ${art}\n        <span class=\"s26-brand\">Unidade 07</span>\n        <div class=\"s26-cap\"><b>Armadura<br>de campo</b><span>Esboço nº 14 · tinta sobre papel</span></div>\n      </div>\n      <form class=\"s26-form\" data-form=\"1\" onsubmit=\"return false\">\n        <span class=\"s26-tag\">Acesso · Hangar 3</span>\n        <h1 class=\"s26-title\">Entrar<br>na cabine</h1>\n        <p class=\"s26-lead\">Identifique-se antes de liberar a unidade. Cada sessão fica registrada, com a marca do dedo na tinta.</p>\n        <div class=\"s26-field\"><label class=\"s26-lbl\">E-mail</label><input class=\"in\" type=\"email\" data-f=\"email\" placeholder=\"piloto@hangar.com\"></div>\n        <div class=\"s26-field\"><label class=\"s26-lbl\">Senha</label><input class=\"in\" type=\"password\" data-f=\"pass\" placeholder=\"••••••••\"></div>\n        <div class=\"s26-row\">\n          <span class=\"s26-chk\" data-keep=\"1\"><span class=\"s26-box${keep ? ' s26-on' : ''}\">${keep ? '✓' : ''}</span>Manter ligado</span>\n          <a data-forgot=\"1\">Perdi o acesso</a>\n        </div>\n        <button class=\"btn pri s26-go\" data-go=\"1\">Liberar unidade</button>\n        <div class=\"s26-msg\">${msg}</div>\n        <div class=\"s26-or\">ou entre com</div>\n        <div class=\"s26-soc\" style=\"--s26-n:${providers.length}\">${phtml}</div>\n        <div class=\"s26-foot\">Ainda sem credencial? <a data-new=\"1\">Pedir acesso</a></div>\n      </form>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '[data-keep]', function () { d2.setState({ keep: !d2.state.keep }); });\n    d2.on('click', '[data-go]', function () {\n      const email = (d2.$('[data-f=\"email\"]') || {}).value || '';\n      const pass = (d2.$('[data-f=\"pass\"]') || {}).value || '';\n      d2.setState({ msg: !email || !pass ? 'Preencha e-mail e senha.' : 'Credencial lida. Liberando…' });\n    });\n    d2.on('click', '[data-social]', function (e, el) { d2.setState({ msg: 'Abrindo ' + el.textContent + '…' }); });\n    d2.on('click', '[data-forgot]', function () { d2.setState({ msg: 'Enviamos um código para o seu e-mail.' }); });\n    d2.on('click', '[data-new]', function () { d2.setState({ msg: 'Pedido de acesso registrado.' }); });\n  }\n}"}},"providers":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"providers"}}}}}}}}}},{"_id":"6ae8c08f2b70e2042182ec96","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"luminous-flow-9f9","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  ui: {\n    theme: 'light',\n    accent: '#00acc1',\n    radius: 'xl',\n    font: '\"Source Sans 3\", system-ui, sans-serif',\n    tokens: { bg: '#e0f7fa', surface: '#f2fcfd', line: '#80deea', text: '#0d47a1' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Michroma&family=Source+Sans+3:wght@400;600&display=swap');\n    :host { display:block; }\n    .lf9-page { position:relative; width:100%; height:100%; box-sizing:border-box; overflow:auto; background:var(--bg); color:var(--text); font-family:var(--font); }\n    .lf9-nav { display:flex; align-items:center; justify-content:space-between; padding:20px 48px; }\n    .lf9-logo { font-family:'Michroma', var(--font); font-size:15px; letter-spacing:.08em; display:flex; align-items:center; gap:10px; }\n    .lf9-orb { width:14px; height:14px; border-radius:50%; background:var(--accent); box-shadow:0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent); animation:lf9-pulse 4.5s ease-in-out infinite; }\n    @keyframes lf9-pulse { 0%,100% { box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); } 50% { box-shadow:0 0 0 9px color-mix(in srgb, var(--accent) 8%, transparent); } }\n    .lf9-links { display:flex; gap:28px; font-size:15px; color:var(--muted); }\n    .lf9-links span { cursor:pointer; transition:color .3s ease; }\n    .lf9-links span:hover { color:var(--text); }\n    .lf9-hero { position:relative; padding:72px 48px 120px; overflow:hidden; display:flex; flex-direction:column; gap:24px; }\n    .lf9-wave { position:absolute; left:-5%; right:-5%; bottom:0; width:110%; height:55%; pointer-events:none; }\n    .lf9-wave path { fill:none; stroke:var(--line); stroke-width:1.5; }\n    .lf9-wave path.lf9-b { stroke:var(--accent); opacity:.5; }\n    .lf9-hero > * { position:relative; }\n    .lf9-title { margin:0; font-family:'Michroma', var(--font); font-weight:400; font-size:56px; line-height:1.1; max-width:15ch; }\n    .lf9-title em { font-style:normal; color:var(--accent); }\n    .lf9-lead { margin:0; font-size:20px; line-height:1.55; max-width:46ch; color:var(--muted); }\n    .lf9-act { display:flex; gap:12px; margin-top:8px; }\n    .lf9-act .btn { padding:14px 28px; border-radius:999px; font-weight:600; }\n    .lf9-feats { display:grid; grid-template-columns:repeat(3,1fr); gap:20px; padding:24px 48px 72px; }\n    .lf9-feats .card { display:flex; flex-direction:column; gap:12px; padding:28px; cursor:pointer; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-xl); transition:transform .4s ease, box-shadow .4s ease, border-color .4s ease; }\n    .lf9-feats .card:hover { transform:translateY(-4px); box-shadow:0 18px 40px -26px var(--accent); }\n    .lf9-feats .card.lf9-on { border-color:var(--accent); box-shadow:0 18px 40px -26px var(--accent); }\n    .lf9-dot { width:36px; height:36px; border-radius:50%; background:color-mix(in srgb, var(--accent) 18%, var(--surface)); display:flex; align-items:center; justify-content:center; font-family:'Michroma', var(--font); font-size:11px; color:var(--accent); }\n    .lf9-feats h3 { margin:0; font-family:'Michroma', var(--font); font-weight:400; font-size:17px; line-height:1.3; }\n    .lf9-feats p { margin:0; font-size:16px; line-height:1.55; color:var(--muted); }\n    .lf9-quote { margin:0 48px; padding:64px 56px; border-radius:var(--r-xl); text-align:center; background:var(--text); color:var(--bg); display:flex; flex-direction:column; gap:20px; align-items:center; }\n    .lf9-quote blockquote { margin:0; font-family:'Michroma', var(--font); font-size:26px; line-height:1.4; max-width:28ch; }\n    .lf9-quote cite { font-style:normal; font-size:15px; color:var(--line); }\n    .lf9-cta { display:flex; flex-direction:column; align-items:center; gap:18px; text-align:center; padding:88px 48px 72px; }\n    .lf9-cta h2 { margin:0; font-family:'Michroma', var(--font); font-weight:400; font-size:34px; line-height:1.2; max-width:18ch; }\n    .lf9-cta p { margin:0; color:var(--muted); font-size:17px; }\n    .lf9-form { display:flex; gap:10px; width:100%; max-width:460px; }\n    .lf9-form .in { flex:1; min-width:0; padding:14px 18px; border-radius:999px; font-family:var(--font); font-size:16px; }\n    .lf9-form .btn { padding:14px 26px; border-radius:999px; font-weight:600; white-space:nowrap; }\n    .lf9-msg { min-height:18px; font-size:14px; color:var(--accent); }\n    .lf9-foot { display:flex; justify-content:space-between; padding:20px 48px; font-size:13px; color:var(--muted); border-top:1px solid var(--line); }\n    @media (max-width:820px) { .lf9-feats { grid-template-columns:1fr; } .lf9-title { font-size:34px; } .lf9-links { display:none; } .lf9-quote blockquote { font-size:20px; } }\n  `,\n  render(d2) {\n    const given = d2.param('features');\n    const base = Array.isArray(given) && given.length ? given.slice(0, 3) : [\n      { title: 'Correnteza', text: 'Os dados entram, seguem o curso e chegam onde precisam. Sem fila visível.' },\n      { title: 'Luz própria', text: 'Cada evento acende um ponto. Você acompanha o fluxo pela cor, não por alarme.' },\n      { title: 'Fundo calmo', text: 'Poucos elementos, muito espaço. A tela descansa o olhar enquanto trabalha.' }\n    ];\n    const feats = base.map(function (f, i) {\n      return { title: String(f.title || f.nome || f.name || 'Recurso ' + (i + 1)), text: String(f.text || f.descricao || f.description || '') };\n    });\n    const q = d2.param('quote') || {};\n    const quote = String(q.text || 'Parece que o sistema respira. A gente só observa.');\n    const who = String(q.author || 'Marina, operações · cliente desde 2024');\n    const sel = d2.state.sel == null ? 0 : d2.state.sel;\n    const done = d2.state.done ? 1 : 0;\n    const msg = d2.state.msg || '';\n    const fhtml = feats.map(function (f, i) {\n      return '<div class=\"card' + (i === sel ? ' lf9-on' : '') + '\" data-key=\"f' + i + '\" data-i=\"' + i + '\"><span class=\"lf9-dot\">0' + (i + 1) + '</span><h3>' + f.title + '</h3><p>' + f.text + '</p></div>';\n    }).join('');\n    return `<div class=\"lf9-page\">\n      <div class=\"lf9-nav\"><span class=\"lf9-logo\"><span class=\"lf9-orb\"></span>Fluxo</span><div class=\"lf9-links\"><span>Produto</span><span>Como funciona</span><span>Preços</span></div><button class=\"btn ghost\">Entrar</button></div>\n      <section class=\"lf9-hero\">\n        <svg class=\"lf9-wave\" viewBox=\"0 0 800 200\" preserveAspectRatio=\"none\">\n          <path d=\"M0 120 C100 80 200 160 300 120 S500 80 600 120 S740 150 800 110\"/>\n          <path class=\"lf9-b\" d=\"M0 150 C120 110 220 190 330 150 S520 110 620 150 S750 175 800 140\"/>\n          <path d=\"M0 180 C130 150 240 210 350 180 S540 150 640 180 S760 200 800 170\"/>\n        </svg>\n        <span class=\"badge\">Fluxo luminoso</span>\n        <h1 class=\"lf9-title\">Dados que correm <em>sem ruído.</em></h1>\n        <p class=\"lf9-lead\">Um motor de informação silencioso. Cada dado segue o seu caminho, no ritmo certo, e você vê tudo com calma.</p>\n        <div class=\"lf9-act\"><button class=\"btn pri\" data-start=\"1\">Começar agora</button><button class=\"btn ghost\">Ver como funciona</button></div>\n      </section>\n      <section class=\"lf9-feats\">${fhtml}</section>\n      <section class=\"lf9-quote\"><blockquote>“${quote}”</blockquote><cite>${who}</cite></section>\n      <section class=\"lf9-cta\">\n        <h2>Deixe o fluxo trabalhar por você.</h2>\n        <p>Teste por 14 dias. Sem cartão.</p>\n        <div class=\"lf9-form\"><input class=\"in\" type=\"email\" data-email=\"1\" placeholder=\"seu@email.com\"><button class=\"btn pri\" data-join=\"1\">${done ? 'Enviado' : 'Criar conta'}</button></div>\n        <div class=\"lf9-msg\">${msg}</div>\n      </section>\n      <div class=\"lf9-foot\"><span>Fluxo © 2026</span><span>Feito com calma</span></div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '.lf9-feats .card', function (e, el) { d2.setState({ sel: Number(el.getAttribute('data-i')) }); });\n    d2.on('click', '[data-start]', function () {\n      const el = d2.$('[data-email]');\n      if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'center' });\n    });\n    d2.on('click', '[data-join]', function () {\n      const v = (d2.$('[data-email]') || {}).value || '';\n      if (v.indexOf('@') > 0) d2.setState({ done: true, msg: 'Pronto. Enviamos um link para o seu e-mail.' });\n      else d2.setState({ msg: 'Digite um e-mail válido.' });\n    });\n  }\n}"}},"features":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"features"}}}},"quote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quote"}}}}}}}}}},{"_id":"6ae8c9b9800882f440a2ae7b","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"deck-perf-concept","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":"// Perf concept — a deck on the Porcelain deck kit, written by the design board from the concept \"Perf concept\"\nexport default {\n  ui: {\"accent\":\"#0000aa\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#d5eddd\",\"surface\":\"#ffffff\",\"text\":\"#000077\",\"font-display\":\"'Fraunces', serif\"},\"font\":\"'Syne', system-ui, sans-serif\"},\n  render(d2) {\n    const S = [\"<div class=\\\"slide title\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Perf concept</div><h1 style=\\\"--i:1\\\">study</h1><p class=\\\"lead\\\" style=\\\"--i:2\\\">a frame around the first rows of pictures and the palettes</p><span class=\\\"num\\\">1 / 6</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Palette</div><h2 style=\\\"--i:1\\\">The colours</h2><div class=\\\"swatches\\\" style=\\\"--i:2\\\"><i style=\\\"--c:#0000aa\\\">#0000aa</i><i style=\\\"--c:#3246aa\\\">#3246aa</i><i style=\\\"--c:#648caa\\\">#648caa</i><i style=\\\"--c:#96d2aa\\\">#96d2aa</i><i style=\\\"--c:#c818aa\\\">#c818aa</i></div><span class=\\\"num\\\">2 / 6</span></div>\",\"<div class=\\\"slide ink\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Type</div><div class=\\\"type-sample\\\" style=\\\"--i:1\\\">Fraunces <span style=\\\"font-family:var(--font);font-size:.5em;color:var(--muted)\\\">with Syne</span></div><p class=\\\"lead\\\" style=\\\"--i:2\\\">Sabão de barra, feito devagar, com óleos prensados a frio</p><span class=\\\"num\\\">3 / 6</span></div>\",\"<div class=\\\"slide pattern-bg\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_xorkml5s%22%20width%3D%2248%22%20height%3D%2248%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%22-24%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2224%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2272%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23efe6d4%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_xorkml5s)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Pattern</div><h2 style=\\\"--i:1\\\">Perf concept</h2><span class=\\\"num\\\">4 / 6</span></div>\",\"<div class=\\\"slide accent\\\"><blockquote class=\\\"quote\\\" style=\\\"--i:0\\\">perf 4</blockquote><div class=\\\"who\\\" style=\\\"--i:1\\\">Perf concept</div><span class=\\\"num\\\">5 / 6</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Next</div><div class=\\\"cards\\\" style=\\\"--i:1\\\"><div class=\\\"card\\\"><b>Shift palette toward warm earth</b><span>Perf concept</span></div><div class=\\\"card\\\"><b>Test Fraunces against dot field</b><span>Perf concept</span></div><div class=\\\"card\\\"><b>Study color drift across frames</b><span>Perf concept</span></div></div><span class=\\\"num\\\">6 / 6</span></div>\"]\n    const i = Math.min(Math.max(d2.state.i || 0, 0), S.length - 1)\n    return '<div class=\"dk\" tabindex=\"0\">' + S.map((s, k) => (k === i ? s.replace('class=\"slide ', 'class=\"slide on ') : s)).join('') + '<div class=\"dk-bar\"><i style=\"--p:' + (((i + 1) / S.length) * 100).toFixed(1) + '%\"></i></div><div class=\"dk-nav\"><button data-go=\"-1\" aria-label=\"previous\">‹</button><button data-go=\"1\" aria-label=\"next\">›</button></div></div>'\n  },\n  mounted(d2) {\n    const n = 6\n    const go = (d) => d2.setState({ i: ((((d2.state.i || 0) + d) % n) + n) % n })\n    d2.on('[data-go]', 'click', (e, el) => { e.stopPropagation(); go(Number(el.dataset.go)) })\n    d2.on('.dk', 'click', (e) => { if (!e.target.closest('[data-go]')) go(1) })\n    d2.on('.dk', 'keydown', (e) => { if (e.key === 'ArrowRight' || e.key === ' ') go(1); else if (e.key === 'ArrowLeft') go(-1) })\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Fraunces:wght@400;500;600;700&family=Syne:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae8c9bed4a5bd3bf276353e","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"press-perf-concept","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":"// Perf concept — an article, a cover and a poster on the Porcelain press kit, written by the design board from the concept \"Perf concept\"\nexport default {\n  ui: {\"accent\":\"#0000aa\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#d5eddd\",\"surface\":\"#ffffff\",\"text\":\"#000077\",\"font-display\":\"'Fraunces', serif\"},\"font\":\"'Syne', system-ui, sans-serif\"},\n  css: '.pk-wrap { display: grid; gap: 12px; } .pk-wrap > .seg { justify-self: start; }',\n  render(d2) {\n    const V = {\"article\":\"<article class=\\\"pr wash\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_xorkml5s%22%20width%3D%2248%22%20height%3D%2248%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%22-24%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2224%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2272%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23efe6d4%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_xorkml5s)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"masthead\\\"><b>Perf concept</b><span class=\\\"sp\\\"></span><span>Edição 01 · 2026</span></div><div class=\\\"kicker\\\">Edição 01 · 2026</div><h1 class=\\\"hed\\\">study</h1><p class=\\\"standfirst\\\">Sabão de barra, feito devagar, com óleos prensados a frio</p><div class=\\\"byline\\\"><b>Design board</b><time>2 Nov 2026</time></div><p class=\\\"lede\\\">perf 4</p><div class=\\\"cols\\\"><p>a thought 0</p><p>a thought 1</p><p>a thought 2</p><p>a thought 3</p><blockquote class=\\\"pull\\\">Ver a coleção<cite>Perf concept</cite></blockquote></div><div class=\\\"folio\\\"><span>Perf concept</span><span>01</span></div></article>\",\"cover\":\"<div class=\\\"pr cover\\\"><div style=\\\"grid-area:1/1;background:linear-gradient(170deg,var(--accent),var(--text))\\\"></div><div class=\\\"over\\\"><div class=\\\"masthead\\\"><b>Perf concept</b><span class=\\\"sp\\\"></span><span>Edição 01 · 2026</span></div><div class=\\\"lines\\\"><b>study</b><span>Sabão de barra, feito devagar, com óleos prensados a frio</span></div></div></div>\",\"poster\":\"<div class=\\\"pr poster ink wash\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_xorkml5s%22%20width%3D%2248%22%20height%3D%2248%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%220%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%22-24%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2224%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2272%22%20cy%3D%2224%22%20r%3D%2224%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3Ccircle%20cx%3D%2248%22%20cy%3D%2248%22%20r%3D%2224%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%232f6b4f%22%20stroke-width%3D%221.5%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23efe6d4%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_xorkml5s)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\">Edição 01 · 2026</div><div class=\\\"stack\\\">study</div><div class=\\\"foot\\\"><span>Sabão de barra, feito devagar, com óleos prensad</span><span>Perf concept</span></div></div>\"}\n    const view = V[d2.state.view] ? d2.state.view : 'article'\n    return '<div class=\"pk-wrap\"><div class=\"seg sm\">' + Object.keys(V).map((v) => '<button data-view=\"' + v + '\" class=\"' + (v === view ? 'on' : '') + '\">' + v + '</button>').join('') + '</div>' + V[view] + '</div>'\n  },\n  mounted(d2) {\n    d2.on('[data-view]', 'click', (e, el) => d2.setState({ view: el.dataset.view }))\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Fraunces:wght@400;500;600;700&family=Syne:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae8d19947d0cb7ff3646e34","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"deck-concept","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":"// Concept — a deck on the Porcelain deck kit, written by the design board from the concept \"Concept\"\nexport default {\n  ui: {\"accent\":\"#585727\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#e8f0f8\",\"surface\":\"#ffffff\",\"text\":\"#3e3d1b\",\"font-display\":\"'Marcellus', serif\"},\"font\":\"'Work Sans', system-ui, sans-serif\"},\n  render(d2) {\n    const S = [\"<div class=\\\"slide title\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept</div><h1 style=\\\"--i:1\\\">Future Vision</h1><p class=\\\"lead\\\" style=\\\"--i:2\\\">Sun-bleached nostalgia for 1930s Rio: the quiet luxury of the Copacabana promenade.</p><span class=\\\"num\\\">1 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept</div><h2 style=\\\"--i:1\\\">An editorial portrait of a woman in a white 1930s dress stan</h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">Sun-bleached nostalgia for 1930s Rio: the quiet luxury of the Copacabana promenade.</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/XCq8LGo29FQgDYD3ncbiZ\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">2 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/BX0B0MMC1imq7VLetIl6e\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">A macro shot of the black and white Portuguese cobblestones </div><span class=\\\"num\\\">3 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept</div><h2 style=\\\"--i:1\\\">A minimalist typographic poster on textured #c5daee paper, f</h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">Sun-bleached nostalgia for 1930s Rio: the quiet luxury of the Copacabana promenade.</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/jshg-6riCk4Yx2MjJQzMZ\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">4 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/2dbUq88Ob3PnA8368lIy4\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">An editorial photograph of a woman in a white 1930s dress st</div><span class=\\\"num\\\">5 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept</div><h2 style=\\\"--i:1\\\">A macro shot of the black and white Portuguese cobblestones </h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">Sun-bleached nostalgia for 1930s Rio: the quiet luxury of the Copacabana promenade.</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/2Qrpe7D_Suc4-s08K-pme\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">6 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/QW7SwdaVgJ7leu1kAWWXK\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">A cinematic wide shot of the Copacabana promenade at dusk, w</div><span class=\\\"num\\\">7 / 12</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Palette</div><h2 style=\\\"--i:1\\\">Sea Glass Blue, Faded Denim, Dusty Taupe</h2><div class=\\\"swatches\\\" style=\\\"--i:2\\\"><i style=\\\"--c:#c5daee\\\">#c5daee</i><i style=\\\"--c:#a8b9c6\\\">#a8b9c6</i><i style=\\\"--c:#a59c97\\\">#a59c97</i><i style=\\\"--c:#867b76\\\">#867b76</i><i style=\\\"--c:#595657\\\">#595657</i><i style=\\\"--c:#585727\\\">#585727</i></div><span class=\\\"num\\\">8 / 12</span></div>\",\"<div class=\\\"slide ink\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Type</div><div class=\\\"type-sample\\\" style=\\\"--i:1\\\">Marcellus <span style=\\\"font-family:var(--font);font-size:.5em;color:var(--muted)\\\">with Work Sans</span></div><p class=\\\"lead\\\" style=\\\"--i:2\\\">Designing tomorrow's possibilities</p><span class=\\\"num\\\">9 / 12</span></div>\",\"<div class=\\\"slide pattern-bg\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_mppr5h8j%22%20width%3D%2280%22%20height%3D%2280%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cpath%20d%3D%22M0%2040%20L40%200%20L80%2040%22%20fill%3D%22none%22%20stroke%3D%22%23958978%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M0%2080%20L40%2040%20L80%2080%22%20fill%3D%22none%22%20stroke%3D%22%23363938%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23d5dbd7%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_mppr5h8j)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Pattern</div><h2 style=\\\"--i:1\\\">Concept</h2><span class=\\\"num\\\">10 / 12</span></div>\",\"<div class=\\\"slide accent\\\"><blockquote class=\\\"quote\\\" style=\\\"--i:0\\\">This concept is a sun-bleached, nostalgic look at 1930s Rio de Janeiro, focusing on the quiet luxury of the Copacabana promenade. Use it for editorial content, fashion, or travel t</blockquote><div class=\\\"who\\\" style=\\\"--i:1\\\">Concept</div><span class=\\\"num\\\">11 / 12</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Next</div><div class=\\\"cards\\\" style=\\\"--i:1\\\"><div class=\\\"card\\\"><b>Refine chevron to wave motif</b><span>Concept</span></div><div class=\\\"card\\\"><b>Add vintage gold accent</b><span>Concept</span></div><div class=\\\"card\\\"><b>Pair with elegant script</b><span>Concept</span></div></div><span class=\\\"num\\\">12 / 12</span></div>\"]\n    const i = Math.min(Math.max(d2.state.i || 0, 0), S.length - 1)\n    return '<div class=\"dk\" tabindex=\"0\">' + S.map((s, k) => (k === i ? s.replace('class=\"slide ', 'class=\"slide on ') : s)).join('') + '<div class=\"dk-bar\"><i style=\"--p:' + (((i + 1) / S.length) * 100).toFixed(1) + '%\"></i></div><div class=\"dk-nav\"><button data-go=\"-1\" aria-label=\"previous\">‹</button><button data-go=\"1\" aria-label=\"next\">›</button></div></div>'\n  },\n  mounted(d2) {\n    const n = 12\n    const go = (d) => d2.setState({ i: ((((d2.state.i || 0) + d) % n) + n) % n })\n    d2.on('[data-go]', 'click', (e, el) => { e.stopPropagation(); go(Number(el.dataset.go)) })\n    d2.on('.dk', 'click', (e) => { if (!e.target.closest('[data-go]')) go(1) })\n    d2.on('.dk', 'keydown', (e) => { if (e.key === 'ArrowRight' || e.key === ' ') go(1); else if (e.key === 'ArrowLeft') go(-1) })\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Marcellus:wght@400;500;600;700&family=Work+Sans:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae8d1c3f652262396bc060d","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"press-concept","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":"// Concept — an article, a cover and a poster on the Porcelain press kit, written by the design board from the concept \"Concept\"\nexport default {\n  ui: {\"accent\":\"#585727\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#e8f0f8\",\"surface\":\"#ffffff\",\"text\":\"#3e3d1b\",\"font-display\":\"'Marcellus', serif\"},\"font\":\"'Work Sans', system-ui, sans-serif\"},\n  css: '.pk-wrap { display: grid; gap: 12px; } .pk-wrap > .seg { justify-self: start; }',\n  render(d2) {\n    const V = {\"article\":\"<article class=\\\"pr wash\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_mppr5h8j%22%20width%3D%2280%22%20height%3D%2280%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cpath%20d%3D%22M0%2040%20L40%200%20L80%2040%22%20fill%3D%22none%22%20stroke%3D%22%23958978%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M0%2080%20L40%2040%20L80%2080%22%20fill%3D%22none%22%20stroke%3D%22%23363938%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23d5dbd7%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_mppr5h8j)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"masthead\\\"><b>Concept</b><span class=\\\"sp\\\"></span><span>Innovate. Create. Inspire.</span></div><div class=\\\"kicker\\\">Innovate. Create. Inspire.</div><h1 class=\\\"hed\\\">Future Vision</h1><p class=\\\"standfirst\\\">Designing tomorrow's possibilities</p><div class=\\\"byline\\\"><b>Design board</b><time>2 Nov 2026</time></div><figure class=\\\"fig wide\\\"><img src=\\\"https://gui.data2.media/file/XCq8LGo29FQgDYD3ncbiZ\\\" alt=\\\"\\\"><figcaption>An editorial portrait of a woman in a white 1930s dress stan</figcaption></figure><p class=\\\"lede\\\">This concept is a sun-bleached, nostalgic look at 1930s Rio de Janeiro, focusing on the quiet luxury of the Copacabana promenade. Use it for editorial content, fashion, or travel that evokes a sense of timeless elegance and a bygone era. Avoid modern, high-contrast, or overly saturated visuals that would break the soft, faded aesthetic.</p><figure class=\\\"fig inset right\\\"><img src=\\\"https://gui.data2.media/file/BX0B0MMC1imq7VLetIl6e\\\" alt=\\\"\\\"><figcaption>A macro shot of the black and white Portuguese cobblestones </figcaption></figure><div class=\\\"cols\\\"><blockquote class=\\\"pull\\\">Learn More<cite>Concept</cite></blockquote></div><div class=\\\"folio\\\"><span>Concept</span><span>01</span></div></article>\",\"cover\":\"<div class=\\\"pr cover\\\"><img src=\\\"https://gui.data2.media/file/XCq8LGo29FQgDYD3ncbiZ\\\" alt=\\\"\\\"><div class=\\\"over\\\"><div class=\\\"masthead\\\"><b>Concept</b><span class=\\\"sp\\\"></span><span>Innovate. Create. Inspire.</span></div><div class=\\\"lines\\\"><b>Future Vision</b><span>Designing tomorrow's possibilities</span></div></div></div>\",\"poster\":\"<div class=\\\"pr poster ink wash\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_mppr5h8j%22%20width%3D%2280%22%20height%3D%2280%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cpath%20d%3D%22M0%2040%20L40%200%20L80%2040%22%20fill%3D%22none%22%20stroke%3D%22%23958978%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3Cpath%20d%3D%22M0%2080%20L40%2040%20L80%2080%22%20fill%3D%22none%22%20stroke%3D%22%23363938%22%20stroke-width%3D%2210.4%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23d5dbd7%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_mppr5h8j)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\">Innovate. Create. Inspire.</div><div class=\\\"stack\\\">Future<br>Vision</div><div class=\\\"foot\\\"><span>Designing tomorrow's possibilities</span><span>Concept</span></div></div>\"}\n    const view = V[d2.state.view] ? d2.state.view : 'article'\n    return '<div class=\"pk-wrap\"><div class=\"seg sm\">' + Object.keys(V).map((v) => '<button data-view=\"' + v + '\" class=\"' + (v === view ? 'on' : '') + '\">' + v + '</button>').join('') + '</div>' + V[view] + '</div>'\n  },\n  mounted(d2) {\n    d2.on('[data-view]', 'click', (e, el) => d2.setState({ view: el.dataset.view }))\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Marcellus:wght@400;500;600;700&family=Work+Sans:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae8d200d4a5bd3bf27647ed","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"concept-n4h","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  ui: {\n    theme: 'light',\n    accent: '#585727',\n    radius: 'sm',\n    font: '\"Work Sans\", system-ui, sans-serif',\n    tokens: { bg: '#c5daee', surface: '#a8b9c6', line: '#867b76', text: '#595657' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Marcellus&family=Work+Sans:wght@300;400;500&display=swap');\n    :host { display:block; }\n    .n4h-page { width:100%; height:100%; box-sizing:border-box; overflow:auto; background:var(--bg); color:var(--text); font-family:var(--font); font-weight:300; }\n    .n4h-nav { display:flex; align-items:center; justify-content:space-between; padding:20px 48px; border-bottom:1px solid var(--line); }\n    .n4h-brand { font-family:'Marcellus', var(--font); font-size:20px; letter-spacing:.32em; text-transform:uppercase; }\n    .n4h-links { display:flex; gap:28px; font-size:12px; font-weight:400; letter-spacing:.2em; text-transform:uppercase; }\n    .n4h-links span { cursor:pointer; opacity:.8; }\n    .n4h-links span:hover { opacity:1; color:var(--accent); }\n    .n4h-hero { position:relative; display:grid; grid-template-columns:minmax(0,1.05fr) minmax(0,1fr); gap:48px; align-items:center; padding:72px 48px 64px; }\n    .n4h-kick { font-size:12px; font-weight:400; letter-spacing:.28em; text-transform:uppercase; color:var(--accent); }\n    .n4h-title { margin:14px 0 20px; font-family:'Marcellus', var(--font); font-weight:400; font-size:68px; line-height:1.02; letter-spacing:.01em; }\n    .n4h-lead { margin:0 0 30px; font-size:17px; line-height:1.65; max-width:44ch; }\n    .n4h-act { display:flex; gap:12px; flex-wrap:wrap; }\n    .n4h-act .btn { padding:14px 28px; letter-spacing:.14em; text-transform:uppercase; font-size:12px; font-weight:500; }\n    .n4h-promenade { position:relative; aspect-ratio:4 / 5; max-height:520px; background:linear-gradient(180deg, var(--surface) 0%, var(--bg) 60%, var(--surface) 100%);\n      border:1px solid var(--line); border-radius:var(--r-sm); overflow:hidden; }\n    .n4h-promenade svg { position:absolute; inset:0; width:100%; height:100%; }\n    .n4h-stamp { position:absolute; left:18px; bottom:16px; font-family:'Marcellus', var(--font); font-size:12px; letter-spacing:.26em; text-transform:uppercase; color:var(--text); }\n    .n4h-feats { display:grid; grid-template-columns:repeat(3,1fr); gap:0; margin:0 48px; border-top:1px solid var(--line); border-bottom:1px solid var(--line); }\n    .n4h-feat { padding:36px 28px 40px; border-right:1px solid var(--line); cursor:pointer; transition:background .2s ease; }\n    .n4h-feat:last-child { border-right:0; }\n    .n4h-feat:hover { background:var(--surface); }\n    .n4h-feat.n4h-on { background:var(--surface); }\n    .n4h-num { font-family:'Marcellus', var(--font); font-size:14px; letter-spacing:.2em; color:var(--accent); }\n    .n4h-feat h3 { margin:14px 0 10px; font-family:'Marcellus', var(--font); font-weight:400; font-size:28px; line-height:1.1; }\n    .n4h-feat p { margin:0; font-size:15px; line-height:1.6; }\n    .n4h-quote { margin:72px 0 0; padding:80px 48px; background:var(--surface); text-align:center; border-top:1px solid var(--line); border-bottom:1px solid var(--line); }\n    .n4h-quote blockquote { margin:0 auto; max-width:24ch; font-family:'Marcellus', var(--font); font-size:44px; line-height:1.16; }\n    .n4h-quote cite { display:block; margin-top:26px; font-style:normal; font-size:12px; font-weight:400; letter-spacing:.28em; text-transform:uppercase; color:var(--accent); }\n    .n4h-cta { display:grid; grid-template-columns:minmax(0,1fr) auto; gap:32px; align-items:center; padding:72px 48px; }\n    .n4h-cta h2 { margin:0 0 8px; font-family:'Marcellus', var(--font); font-weight:400; font-size:46px; line-height:1.05; }\n    .n4h-cta p { margin:0; font-size:16px; max-width:46ch; line-height:1.6; }\n    .n4h-form { display:flex; gap:10px; }\n    .n4h-form .in { min-width:240px; padding:14px 16px; font-family:var(--font); font-size:15px; }\n    .n4h-form .btn { padding:14px 26px; letter-spacing:.14em; text-transform:uppercase; font-size:12px; font-weight:500; }\n    .n4h-foot { display:flex; justify-content:space-between; padding:20px 48px; border-top:1px solid var(--line); font-size:12px; letter-spacing:.2em; text-transform:uppercase; }\n    @media (max-width:860px) { .n4h-hero, .n4h-cta { grid-template-columns:1fr; } .n4h-feats { grid-template-columns:1fr; } .n4h-feat { border-right:0; border-bottom:1px solid var(--line); } .n4h-title { font-size:46px; } }\n  `,\n  render(d2) {\n    const given = d2.param('features');\n    const base = Array.isArray(given) && given.length ? given.slice(0, 3) : [\n      { title: 'Calçada ondulada', text: 'Pedra portuguesa em preto e branco, assentada à mão, do Leme ao Posto 6.' },\n      { title: 'Hora do chá', text: 'Mesas na varanda do hotel, linho claro e uma brisa que vem do mar às quatro da tarde.' },\n      { title: 'Luz de domingo', text: 'O sol desbota as cores e deixa tudo mais gentil. É assim que a cidade se lembra.' }\n    ];\n    const feats = base.map(function (f, i) {\n      return { title: String(f.title || f.nome || f.name || 'Item ' + (i + 1)), text: String(f.text || f.descricao || f.description || '') };\n    });\n    const q = d2.param('quote') || {};\n    const quote = q.text || 'Em Copacabana, o tempo anda de chapéu e sem pressa.';\n    const who = q.author || 'Diário de uma hóspede · 1934';\n    const sel = d2.state.sel == null ? 1 : d2.state.sel;\n    const done = d2.state.done ? 1 : 0;\n    const fhtml = feats.map(function (f, i) {\n      return '<div class=\"n4h-feat' + (i === sel ? ' n4h-on' : '') + '\" data-key=\"f' + i + '\" data-i=\"' + i + '\"><span class=\"n4h-num\">0' + (i + 1) + '</span><h3>' + f.title + '</h3><p>' + f.text + '</p></div>';\n    }).join('');\n    const art = `<svg viewBox=\"0 0 400 500\" preserveAspectRatio=\"xMidYMid slice\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" style=\"color:var(--text)\">\n      <circle cx=\"292\" cy=\"120\" r=\"46\" stroke-width=\"1.2\" opacity=\".5\"/>\n      <circle cx=\"292\" cy=\"120\" r=\"30\" stroke-width=\"1\" opacity=\".35\"/>\n      <path d=\"M0 250 H400\" stroke-width=\"1\" opacity=\".5\"/>\n      <path d=\"M0 270 C60 262 100 278 160 270 S280 262 340 270 S390 276 400 272\" stroke-width=\"1\" opacity=\".45\"/>\n      <path d=\"M0 288 C70 280 110 296 180 288 S290 280 350 288 S392 292 400 290\" stroke-width=\"1\" opacity=\".35\"/>\n      <path d=\"M40 250 C70 200 110 200 140 250 M200 250 C230 214 270 214 300 250\" stroke-width=\"1.4\" opacity=\".7\"/>\n      <path d=\"M0 330 C50 310 90 350 140 330 S240 310 290 330 S370 350 400 336\" stroke-width=\"1.6\" opacity=\".8\"/>\n      <path d=\"M0 362 C50 342 90 382 140 362 S240 342 290 362 S370 382 400 368\" stroke-width=\"1.6\" opacity=\".7\"/>\n      <path d=\"M0 394 C50 374 90 414 140 394 S240 374 290 394 S370 414 400 400\" stroke-width=\"1.6\" opacity=\".6\"/>\n      <path d=\"M0 426 C50 406 90 446 140 426 S240 406 290 426 S370 446 400 432\" stroke-width=\"1.6\" opacity=\".5\"/>\n      <path d=\"M0 458 C50 438 90 478 140 458 S240 438 290 458 S370 478 400 464\" stroke-width=\"1.6\" opacity=\".4\"/>\n      <path d=\"M70 330 V300 M70 300 C70 290 82 290 82 300 M70 300 C70 290 58 290 58 300\" stroke-width=\"1.2\" opacity=\".55\"/>\n    </svg>`;\n    return `<div class=\"n4h-page\">\n      <div class=\"n4h-nav\"><span class=\"n4h-brand\">Calçadão</span><div class=\"n4h-links\"><span>Hotel</span><span>Praia</span><span>Crônicas</span></div></div>\n      <section class=\"n4h-hero\">\n        <div>\n          <span class=\"n4h-kick\">Rio de Janeiro · 1934</span>\n          <h1 class=\"n4h-title\">A elegância<br>que o sol desbotou.</h1>\n          <p class=\"n4h-lead\">Um passeio sem pressa pela orla de Copacabana, como ela era: linho claro, pedra ondulada e o mar ao fundo de cada conversa.</p>\n          <div class=\"n4h-act\"><button class=\"btn pri\">Reservar uma estadia</button><button class=\"btn ghost\">Ver as crônicas</button></div>\n        </div>\n        <div class=\"n4h-promenade\">${art}<span class=\"n4h-stamp\">Posto 4 · fim de tarde</span></div>\n      </section>\n      <section class=\"n4h-feats\">${fhtml}</section>\n      <section class=\"n4h-quote\"><blockquote>“${quote}”</blockquote><cite>${who}</cite></section>\n      <section class=\"n4h-cta\">\n        <div><h2>Guarde uma tarde<br>para a orla.</h2><p>${done ? 'Recebemos o seu e-mail. Em breve você ganha o convite, escrito à mão.' : 'Deixe o seu e-mail e receba, uma vez por mês, uma crônica da cidade antiga.'}</p></div>\n        <div class=\"n4h-form\"><input class=\"in\" type=\"email\" data-email=\"1\" placeholder=\"seu@email.com\"><button class=\"btn pri\" data-join=\"1\">${done ? 'Recebido' : 'Receber crônica'}</button></div>\n      </section>\n      <div class=\"n4h-foot\"><span>Calçadão · Rio</span><span>Desde 1934</span></div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '.n4h-feat', function (e, el) { d2.setState({ sel: Number(el.getAttribute('data-i')) }); });\n    d2.on('click', '[data-join]', function () {\n      const v = (d2.$('[data-email]') || {}).value || '';\n      if (v.indexOf('@') > 0) d2.setState({ done: true });\n    });\n  }\n}"}},"features":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"features"}}}},"quote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quote"}}}}}}}}}},{"_id":"6ae8d5bdbf88d77f965e0072","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"quiet-bazaar-eod","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  ui: {\n    theme: 'light',\n    accent: '#d99a2b',\n    radius: 'md',\n    font: '\"Inter\", system-ui, sans-serif',\n    tokens: { bg: '#f4f1ea', surface: '#ffffff', line: '#c4a484', text: '#1a2a3a' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Fraunces:wght@500;600&family=Inter:wght@400;500;600&display=swap');\n    :host { display:block; }\n    .qbz-page { width:100%; height:100%; box-sizing:border-box; overflow:auto; background:var(--bg); color:var(--text); font-family:var(--font); padding:28px 32px; display:flex; flex-direction:column; gap:20px; }\n    .qbz-head { display:flex; align-items:flex-end; justify-content:space-between; gap:16px; }\n    .qbz-title { margin:0; font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:34px; line-height:1.05; letter-spacing:-.01em; }\n    .qbz-sub { margin:6px 0 0; font-size:13px; color:var(--muted); }\n    .qbz-seg { display:flex; gap:6px; }\n    .qbz-seg .chip { cursor:pointer; }\n    .qbz-seg .chip.qbz-on { background:var(--text); color:var(--bg); border-color:var(--text); }\n    .qbz-kpis { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:14px; }\n    .qbz-kpi { padding:18px 20px; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md); border-top:3px solid var(--line); }\n    .qbz-kpi.qbz-hot { border-top-color:var(--accent); }\n    .qbz-kl { font-size:11px; font-weight:500; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); }\n    .qbz-kv { margin:8px 0 4px; font-family:'Fraunces', Georgia, serif; font-weight:600; font-size:34px; line-height:1; font-variant-numeric:tabular-nums; }\n    .qbz-kd { font-size:12px; font-weight:500; }\n    .qbz-up { color:var(--text); }\n    .qbz-up::before { content:'↑ '; color:var(--accent); }\n    .qbz-down::before { content:'↓ '; }\n    .qbz-mid { display:grid; grid-template-columns:minmax(0,1fr); }\n    .qbz-chart { padding:20px 22px 14px; background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md); }\n    .qbz-ch-h { display:flex; align-items:baseline; justify-content:space-between; margin-bottom:6px; }\n    .qbz-ch-h b { font-family:'Fraunces', Georgia, serif; font-weight:500; font-size:20px; }\n    .qbz-ch-h span { font-size:12px; color:var(--muted); }\n    .qbz-svg { width:100%; height:200px; display:block; }\n    .qbz-grid { stroke:var(--line); stroke-width:1; opacity:.35; }\n    .qbz-area { fill:var(--accent); opacity:.16; }\n    .qbz-line { fill:none; stroke:var(--text); stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }\n    .qbz-pt { fill:var(--surface); stroke:var(--text); stroke-width:2; cursor:pointer; }\n    .qbz-pt.qbz-on { fill:var(--accent); }\n    .qbz-xl { display:flex; justify-content:space-between; font-size:11px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); padding:0 2px; }\n    .qbz-orders { background:var(--surface); border:1px solid var(--line); border-radius:var(--r-md); overflow:hidden; }\n    .qbz-oh { display:flex; align-items:center; justify-content:space-between; padding:16px 22px; border-bottom:1px solid var(--line); }\n    .qbz-oh b { font-family:'Fraunces', Georgia, serif; font-weight:500; font-size:20px; }\n    .qbz-tr { display:grid; grid-template-columns:110px minmax(0,1.4fr) minmax(0,1fr) 110px 120px; gap:12px; align-items:center; padding:13px 22px; border-bottom:1px solid color-mix(in srgb, var(--line) 40%, transparent); font-size:14px; }\n    .qbz-tr:last-child { border-bottom:0; }\n    .qbz-tr.qbz-th { font-size:11px; font-weight:500; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); padding-top:10px; padding-bottom:10px; }\n    .qbz-tr.qbz-row { cursor:pointer; }\n    .qbz-tr.qbz-row:hover { background:var(--bg); }\n    .qbz-tr.qbz-sel { background:var(--bg); box-shadow:inset 3px 0 0 var(--accent); }\n    .qbz-code { font-variant-numeric:tabular-nums; color:var(--muted); }\n    .qbz-amt { text-align:right; font-variant-numeric:tabular-nums; font-weight:500; }\n    .qbz-st { justify-self:start; }\n  `,\n  render(d2) {\n    const gk = d2.param('kpis');\n    const kpis = (Array.isArray(gk) && gk.length ? gk : [\n      { label: 'Receita do dia', value: 'R$ 18.420', delta: '+8,2% sobre ontem', up: true, hot: true },\n      { label: 'Pedidos', value: '146', delta: '+12 desde as 9h', up: true },\n      { label: 'Ticket médio', value: 'R$ 126', delta: '-3,1% na semana', up: false },\n      { label: 'Devoluções', value: '4', delta: '1 a menos que ontem', up: true }\n    ]).slice(0, 4).map(function (k, i) {\n      const raw = k.value != null ? k.value : (k.valor != null ? k.valor : k.preco);\n      const val = typeof raw === 'number' ? 'R$ ' + raw.toLocaleString('pt-BR', { maximumFractionDigits: 2 }) : (raw != null ? String(raw) : '—');\n      return { label: String(k.label || k.nome || 'Indicador ' + (i + 1)), value: val, delta: String(k.delta || k.descricao || ''), up: k.up !== false, hot: k.hot != null ? !!k.hot : i === 0 };\n    });\n    const gs = d2.param('series');\n    const series = (Array.isArray(gs) && gs.length > 1 ? gs.slice(0, 7) : [22, 31, 26, 38, 34, 47, 52]).map(function (n) {\n      const v = (n && typeof n === 'object') ? (n.value != null ? n.value : (n.valor != null ? n.valor : n.preco)) : n;\n      return Number(v) || 0;\n    });\n    const days = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb', 'Dom'];\n    const go = d2.param('orders');\n    const orders = (Array.isArray(go) && go.length ? go : [\n      { code: '#2041', customer: 'Samira Haddad', item: 'Tapete kilim, 2 x 3 m', total: 'R$ 1.280', status: 'Enviado' },\n      { code: '#2040', customer: 'Tiago Moreira', item: 'Lanterna de latão', total: 'R$ 340', status: 'Pago' },\n      { code: '#2039', customer: 'Laila Nasser', item: 'Caixa de especiarias', total: 'R$ 96', status: 'Pendente' },\n      { code: '#2038', customer: 'Heitor Campos', item: 'Bandeja de cobre', total: 'R$ 210', status: 'Enviado' },\n      { code: '#2037', customer: 'Yasmin Farah', item: 'Almofadas de seda (4)', total: 'R$ 520', status: 'Devolvido' }\n    ]).slice(0, 6).map(function (o, i) {\n      const tot = o.total != null ? o.total : o.preco;\n      return { code: String(o.code || o.pedidoId || o.id || '#' + (i + 1)), customer: String(o.customer || o.cliente || o.nome || ('Cliente ' + (i + 1))), item: String(o.item || o.produto || o.descricao || ''), total: typeof tot === 'number' ? 'R$ ' + tot.toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) : String(tot || ''), status: String(o.status || 'Pago') };\n    });\n    const range = d2.state.range == null ? 1 : d2.state.range;\n    const sel = d2.state.sel == null ? 0 : d2.state.sel;\n    const pt = d2.state.pt == null ? series.length - 1 : d2.state.pt;\n    const W = 600, H = 200, padY = 18;\n    const mx = Math.max.apply(null, series), mn = Math.min.apply(null, series), sp = (mx - mn) || 1;\n    const xy = series.map(function (v, i) { return [(i / (series.length - 1)) * W, H - padY - ((v - mn) / sp) * (H - padY * 2)]; });\n    const path = xy.map(function (p, i) { return (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1); }).join(' ');\n    const dots = xy.map(function (p, i) { return '<circle class=\"qbz-pt' + (i === pt ? ' qbz-on' : '') + '\" data-key=\"p' + i + '\" data-pt=\"' + i + '\" cx=\"' + p[0].toFixed(1) + '\" cy=\"' + p[1].toFixed(1) + '\" r=\"' + (i === pt ? 6 : 4.5) + '\"></circle>'; }).join('');\n    const rows = [1, 2, 3].map(function (n) { return '<line class=\"qbz-grid\" x1=\"0\" x2=\"' + W + '\" y1=\"' + (n * H / 4) + '\" y2=\"' + (n * H / 4) + '\"></line>'; }).join('');\n    const lbl = series.map(function (v, i) { return '<span>' + (days[i] || i + 1) + '</span>'; }).join('');\n    const khtml = kpis.map(function (k, i) {\n      return '<div class=\"qbz-kpi' + (k.hot ? ' qbz-hot' : '') + '\" data-key=\"k' + i + '\"><div class=\"qbz-kl\">' + k.label + '</div><div class=\"qbz-kv\">' + k.value + '</div><div class=\"qbz-kd ' + (k.up ? 'qbz-up' : 'qbz-down') + '\">' + k.delta + '</div></div>';\n    }).join('');\n    const ohtml = orders.map(function (o, i) {\n      return '<div class=\"qbz-tr qbz-row' + (i === sel ? ' qbz-sel' : '') + '\" data-key=\"o' + i + '\" data-o=\"' + i + '\"><span class=\"qbz-code\">' + o.code + '</span><span>' + o.customer + '</span><span class=\"muted\">' + o.item + '</span><span class=\"qbz-amt\">' + o.total + '</span><span class=\"badge qbz-st\">' + o.status + '</span></div>';\n    }).join('');\n    const rg = ['Hoje', '7 dias', '30 dias'].map(function (n, i) { return '<span class=\"chip' + (i === range ? ' qbz-on' : '') + '\" data-key=\"r' + i + '\" data-r=\"' + i + '\">' + n + '</span>'; }).join('');\n    const cur = series[pt];\n    return `<div class=\"qbz-page\">\n      <div class=\"qbz-head\"><div><h1 class=\"qbz-title\">O dia no bazar</h1><p class=\"qbz-sub\">Tudo o que entrou, no seu tempo. O resto fica na prateleira.</p></div><div class=\"qbz-seg\">${rg}</div></div>\n      <div class=\"qbz-kpis\">${khtml}</div>\n      <div class=\"qbz-mid\"><div class=\"qbz-chart\">\n        <div class=\"qbz-ch-h\"><b>Vendas por dia</b><span>${days[pt] || pt + 1} · ${cur} pedidos</span></div>\n        <svg class=\"qbz-svg\" viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"none\">${rows}<path class=\"qbz-area\" d=\"${path} L${W} ${H} L0 ${H} Z\"></path><path class=\"qbz-line\" d=\"${path}\"></path>${dots}</svg>\n        <div class=\"qbz-xl\">${lbl}</div>\n      </div></div>\n      <div class=\"qbz-orders\">\n        <div class=\"qbz-oh\"><b>Pedidos recentes</b><button class=\"btn ghost\">Ver todos</button></div>\n        <div class=\"qbz-tr qbz-th\"><span>Pedido</span><span>Cliente</span><span>Item</span><span style=\"text-align:right\">Total</span><span>Estado</span></div>\n        ${ohtml}\n      </div>\n    </div>`;\n  },\n  mounted(d2) {\n    d2.on('click', '[data-r]', function (e, el) { d2.setState({ range: Number(el.getAttribute('data-r')) }); });\n    d2.on('click', '[data-o]', function (e, el) { d2.setState({ sel: Number(el.getAttribute('data-o')) }); });\n    d2.on('click', '[data-pt]', function (e, el) { d2.setState({ pt: Number(el.getAttribute('data-pt')) }); });\n  }\n}"}},"kpis":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kpis"}}}},"series":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"series"}}}},"orders":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orders"}}}}}}}}}},{"_id":"6ae8db7df9812c1f7cad894b","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"deck-concept-7","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":"// Concept 7 — a deck on the Porcelain deck kit, written by the design board\nexport default {\n  ui: {\"accent\":\"#0c0b16\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#fafafa\",\"surface\":\"#ffffff\",\"text\":\"#08080f\",\"font-display\":\"'Roboto Condensed', serif\"},\"font\":\"'Karla', system-ui, sans-serif\"},\n  render(d2) {\n    const S = [\"<div class=\\\"slide title\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept 7</div><h1 style=\\\"--i:1\\\">Concept 7</h1><span class=\\\"num\\\">1 / 10</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept 7</div><h2 style=\\\"--i:1\\\">colorful</h2></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/4G-GFJuzf5A4eMuOfX5mD\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">2 / 10</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Palette</div><h2 style=\\\"--i:1\\\"></h2><div class=\\\"swatches\\\" style=\\\"--i:2\\\"></div><span class=\\\"num\\\">3 / 10</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept 7</div><h2 style=\\\"--i:1\\\">Concept 7 · photo</h2></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/cIjVEBolgZA34kg5Z7fte\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">4 / 10</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/BEzyxXJNshRul1oL6lxOT\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">Concept 7 · texture</div><span class=\\\"num\\\">5 / 10</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Concept 7</div><h2 style=\\\"--i:1\\\">Concept 7 · scene</h2></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/O2Bji5FxVKP_5XvXvCexi\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">6 / 10</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Palette</div><h2 style=\\\"--i:1\\\">The colours</h2><div class=\\\"swatches\\\" style=\\\"--i:2\\\"><i style=\\\"--c:#a9a9a9\\\">#a9a9a9</i><i style=\\\"--c:#f3f3f3\\\">#f3f3f3</i><i style=\\\"--c:#878787\\\">#878787</i><i style=\\\"--c:#c5c5c5\\\">#c5c5c5</i><i style=\\\"--c:#0c0b16\\\">#0c0b16</i><i style=\\\"--c:#2a2a47\\\">#2a2a47</i></div><span class=\\\"num\\\">7 / 10</span></div>\",\"<div class=\\\"slide ink\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Type</div><div class=\\\"type-sample\\\" style=\\\"--i:1\\\">Roboto Condensed <span style=\\\"font-family:var(--font);font-size:.5em;color:var(--muted)\\\">with Karla</span></div><p class=\\\"lead\\\" style=\\\"--i:2\\\">Concept 7</p><span class=\\\"num\\\">8 / 10</span></div>\",\"<div class=\\\"slide pattern-bg\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_3w2plu3x%22%20width%3D%2280%22%20height%3D%2280%22%20patternUnits%3D%22userSpaceOnUse%22%20patternTransform%3D%22scale(1.00)%22%3E%3Crect%20width%3D'80'%20height%3D'80'%20fill%3D'%23f3f3f3'%2F%3E%3Cpath%20d%3D'M0%2040%20Q20%2020%2040%2040%20T80%2040'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'3'%2F%3E%3Cpath%20d%3D'M0%2040%20Q20%2020%2040%2040%20T80%2040'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'3'%20transform%3D'translate(0%2C80)'%2F%3E%3Cpath%20d%3D'M0%2040%20Q20%2020%2040%2040%20T80%2040'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'3'%20transform%3D'translate(0%2C-80)'%2F%3E%3Cpath%20d%3D'M0%2040%20Q20%2020%2040%2040%20T80%2040'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'3'%20transform%3D'translate(80%2C0)'%2F%3E%3Cpath%20d%3D'M0%2040%20Q20%2020%2040%2040%20T80%2040'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'3'%20transform%3D'translate(-80%2C0)'%2F%3E%3Ccircle%20cx%3D'20'%20cy%3D'20'%20r%3D'4'%20fill%3D'%230c0b16'%2F%3E%3Ccircle%20cx%3D'60'%20cy%3D'60'%20r%3D'4'%20fill%3D'%230c0b16'%2F%3E%3Cpath%20d%3D'M10%2060%20Q30%2070%2050%2060'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'2'%2F%3E%3Cpath%20d%3D'M30%2010%20Q50%2020%2070%2010'%20fill%3D'none'%20stroke%3D'%230c0b16'%20stroke-width%3D'2'%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23f3f3f3%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_3w2plu3x)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Pattern</div><h2 style=\\\"--i:1\\\">Concept 7</h2><span class=\\\"num\\\">9 / 10</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Next</div><div class=\\\"cards\\\" style=\\\"--i:1\\\"><div class=\\\"card\\\"><b>Warm beige background variations</b><span>Concept 7</span></div><div class=\\\"card\\\"><b>Integrate olive accent into palette</b><span>Concept 7</span></div><div class=\\\"card\\\"><b>Refine custom pattern with olive</b><span>Concept 7</span></div></div><span class=\\\"num\\\">10 / 10</span></div>\"]\n    const i = Math.min(Math.max(d2.state.i || 0, 0), S.length - 1)\n    return '<div class=\"dk\" tabindex=\"0\">' + S.map((s, k) => (k === i ? s.replace('class=\"slide ', 'class=\"slide on ') : s)).join('') + '<div class=\"dk-bar\"><i style=\"--p:' + (((i + 1) / S.length) * 100).toFixed(1) + '%\"></i></div><div class=\"dk-nav\"><button data-go=\"-1\" aria-label=\"previous\">‹</button><button data-go=\"1\" aria-label=\"next\">›</button></div></div>'\n  },\n  mounted(d2) {\n    const n = 10\n    const go = (d) => d2.setState({ i: ((((d2.state.i || 0) + d) % n) + n) % n })\n    d2.on('[data-go]', 'click', (e, el) => { e.stopPropagation(); go(Number(el.dataset.go)) })\n    d2.on('.dk', 'click', (e) => { if (!e.target.closest('[data-go]')) go(1) })\n    d2.on('.dk', 'keydown', (e) => { if (e.key === 'ArrowRight' || e.key === ' ') go(1); else if (e.key === 'ArrowLeft') go(-1) })\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Karla:wght@400;500;600;700&family=Roboto+Condensed:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae8e0c815f33bf4cf864efa","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"deck-probe-concept","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":"// Probe concept — a deck on the Porcelain deck kit, written by the design board\nexport default {\n  ui: {\"accent\":\"#c8963e\",\"theme\":\"light\",\"tokens\":{\"bg\":\"#f9f5ee\",\"surface\":\"#ffffff\",\"text\":\"#15130f\",\"font-display\":\"'Fraunces', serif\"},\"font\":\"'Karla', system-ui, sans-serif\"},\n  render(d2) {\n    const S = [\"<div class=\\\"slide title\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Probe concept</div><h1 style=\\\"--i:1\\\">Probe concept</h1><p class=\\\"lead\\\" style=\\\"--i:2\\\">sun-bleached luxury, slow and quiet</p><span class=\\\"num\\\">1 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Probe concept</div><h2 style=\\\"--i:1\\\">Desert Hotel · detail</h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">sun-bleached luxury, slow and quiet</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/ExG8UrhG9NbCBnxvHpbzM\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">2 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/J7rg_QQ5HiZBz2-_BKxwA\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">Desert Hotel · use</div><span class=\\\"num\\\">3 / 12</span></div>\",\"<div class=\\\"slide accent\\\"><blockquote class=\\\"quote\\\" style=\\\"--i:0\\\"></blockquote><div class=\\\"who\\\" style=\\\"--i:1\\\"></div><span class=\\\"num\\\">4 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Probe concept</div><h2 style=\\\"--i:1\\\">Probe concept · poster</h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">sun-bleached luxury, slow and quiet</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/GDACFH-fwzj-3Kik5oIvB\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">5 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/LMJULlyea9srXBQ1tGk8F\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">Probe concept · photo</div><span class=\\\"num\\\">6 / 12</span></div>\",\"<div class=\\\"slide split\\\"><div class=\\\"txt\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Probe concept</div><h2 style=\\\"--i:1\\\">Probe concept · texture</h2><p class=\\\"lead\\\" style=\\\"--i:2\\\">sun-bleached luxury, slow and quiet</p></div><div class=\\\"fig\\\" style=\\\"--i:1\\\"><img src=\\\"https://gui.data2.media/file/Nu_4ewR8ndxrfSaobIfxi\\\" alt=\\\"\\\"></div><span class=\\\"num\\\">7 / 12</span></div>\",\"<div class=\\\"slide figure\\\"><div class=\\\"fig\\\"><img src=\\\"https://gui.data2.media/file/O3RAvf_mdXHIw2H7TBoL7\\\" alt=\\\"\\\"></div><div class=\\\"cap\\\">Probe concept · scene</div><span class=\\\"num\\\">8 / 12</span></div>\",\"<div class=\\\"slide \\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Palette</div><h2 style=\\\"--i:1\\\">The colours</h2><div class=\\\"swatches\\\" style=\\\"--i:2\\\"><i style=\\\"--c:#1e1b16\\\">#1e1b16</i><i style=\\\"--c:#2f6b4f\\\">#2f6b4f</i><i style=\\\"--c:#c8963e\\\">#c8963e</i><i style=\\\"--c:#efe6d4\\\">#efe6d4</i></div><span class=\\\"num\\\">9 / 12</span></div>\",\"<div class=\\\"slide ink\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Type</div><div class=\\\"type-sample\\\" style=\\\"--i:1\\\">Fraunces <span style=\\\"font-family:var(--font);font-size:.5em;color:var(--muted)\\\">with Karla</span></div><p class=\\\"lead\\\" style=\\\"--i:2\\\">sun-bleached luxury, slow and quiet</p><span class=\\\"num\\\">10 / 12</span></div>\",\"<div class=\\\"slide pattern-bg\\\" style=\\\"--tile:url(&quot;data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22100%25%22%20height%3D%22100%25%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22pt-pattern_tstuytqu%22%20width%3D%2280%22%20height%3D%2280%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Ccircle%20cx%3D%220%22%20cy%3D%220%22%20r%3D%2240%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%2280%22%20cy%3D%220%22%20r%3D%2240%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%22-40%22%20cy%3D%2240%22%20r%3D%2240%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%2240%22%20cy%3D%2240%22%20r%3D%2240%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%22120%22%20cy%3D%2240%22%20r%3D%2240%22%20fill%3D%22%23efe6d4%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%220%22%20cy%3D%2280%22%20r%3D%2240%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3Ccircle%20cx%3D%2280%22%20cy%3D%2280%22%20r%3D%2240%22%20fill%3D%22%231e1b16%22%20stroke%3D%22%23c8963e%22%20stroke-width%3D%222.5%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22%23efe6d4%22%2F%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23pt-pattern_tstuytqu)%22%2F%3E%3C%2Fsvg%3E&quot;)\\\"><div class=\\\"kicker\\\" style=\\\"--i:0\\\">Pattern</div><h2 style=\\\"--i:1\\\">Probe concept</h2><span class=\\\"num\\\">11 / 12</span></div>\",\"<div class=\\\"slide accent\\\"><blockquote class=\\\"quote\\\" style=\\\"--i:0\\\">perf 4</blockquote><div class=\\\"who\\\" style=\\\"--i:1\\\">Probe concept</div><span class=\\\"num\\\">12 / 12</span></div>\"]\n    const i = Math.min(Math.max(d2.state.i || 0, 0), S.length - 1)\n    return '<div class=\"dk\" tabindex=\"0\">' + S.map((s, k) => (k === i ? s.replace('class=\"slide ', 'class=\"slide on ') : s)).join('') + '<div class=\"dk-bar\"><i style=\"--p:' + (((i + 1) / S.length) * 100).toFixed(1) + '%\"></i></div><div class=\"dk-nav\"><button data-go=\"-1\" aria-label=\"previous\">‹</button><button data-go=\"1\" aria-label=\"next\">›</button></div></div>'\n  },\n  mounted(d2) {\n    const n = 12\n    const go = (d) => d2.setState({ i: ((((d2.state.i || 0) + d) % n) + n) % n })\n    d2.on('[data-go]', 'click', (e, el) => { e.stopPropagation(); go(Number(el.dataset.go)) })\n    d2.on('.dk', 'click', (e) => { if (!e.target.closest('[data-go]')) go(1) })\n    d2.on('.dk', 'keydown', (e) => { if (e.key === 'ArrowRight' || e.key === ' ') go(1); else if (e.key === 'ArrowLeft') go(-1) })\n    const fl = document.createElement('link'); fl.rel = 'stylesheet'; fl.href = \"https://fonts.googleapis.com/css2?family=Karla:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&display=swap\"; if (!document.querySelector('link[href=\"' + fl.href + '\"]')) document.head.appendChild(fl)\n  },\n}"}}}}}}}},{"_id":"6ae95ca6289c9ba1bb7d1080","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"lettering-gothic-stamp","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  ui: {\n    theme: 'light',\n    accent: '#1a1a1a',\n    radius: 'md',\n    font: '\"UnifrakturMaguntia\", system-ui, serif',\n    tokens: { bg: '#f5f5f0', surface: '#e8e4dc', line: '#1a1a1a', text: '#1a1a1a' }\n  },\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=UnifrakturMaguntia&display=swap');\n    :host { display:block; }\n    .lt-stage { width:100%; height:100%; box-sizing:border-box; container-type:inline-size;\n      display:flex; align-items:center; justify-content:center; overflow:hidden;\n      background: linear-gradient(135deg, #f5f5f0 0%, #e8e4dc 100%); }\n    .lt-word { font-family:'UnifrakturMaguntia', var(--font); font-weight:800; white-space:nowrap;\n      color: #1a1a1a; text-shadow: 1px 1px 0px rgba(0,0,0,0.3), 2px 2px 4px rgba(0,0,0,0.2);\n      letter-spacing: -0.02em; font-size: 22cqi; filter: contrast(1.2) brightness(0.9); }\n  `,\n  render(d2) {\n    const raw = d2.param('text');\n    const text = String(raw == null || raw === '' ? 'Gothic' : raw)\n      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n    return `<div class=\"lt-stage\"><span class=\"lt-word\">${text}</span></div>`;\n  }\n}"}},"text":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"text"}}}}}}}}}},{"_id":"6ae9d315d4a5bd3bf277b698","component":"data2-core-types@Component","system":"6329b5535b2103060e128c0a","params":{"extends":"data2-core-primitives@Block","component":"soai-deck","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":"// State of AI · Edição 02 — deck as a Data² code component.\n// params: gen (rush), img (rush), list (rush)\n\nvar MEDIA = 'https://gui.data2.media/file/';\nvar IMG = {\n  adonis: 'DK-Vc3PUvYcLcW3nsPNVK', sergio: '5N1ycqRiPy8NK4qrkDyAM', pedro: '2mgON-k3nugb6XTvg3W2E', alexandre: 'zTi03wZ5lUaMPSqO2it84',\n  e01big: 'fIi16oJRutNWEKc8NyqGI', e01ren: 'iC0jQ8SJLyo5n2uECSy6u', e01dav: 'dHeIpxYwQ2znOqKtFUeUl', e01pan: '0z9NZDU9X29I3UpY5gjKn',\n  e01pla: 'K8hGOxCBtzdOCMYaUh_nX', e01sala: 'P-jsWJo2gXKAJIP_xzeua', e01spk: 'JHvCplvr08sRKHQx9U1RG', e01hh: 'obksUWINAW0YZzHFh1GWy',\n  viviB: '90ekcq8ZvOy_LrvT5-WWp', viviA: 'wXwOm0s-gD2w2-ZAycQHr',\n  wPrompt: 'FrJGiZJb26snzlJoZbn6o', wBrand: 'b1y7aFXqzZhI4nPm-uH_X', wMenu: 'LxmfmT-At00plxqrR3LfS'\n};\nfunction u(k) { return MEDIA + IMG[k]; }\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, function (c) {\n    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c];\n  });\n}\nfunction hex(h) { return /^#[0-9a-fA-F]{3,8}$/.test(String(h || '')) ? h : '#888'; }\n\nvar CADENCE = 45000;\nvar TIMEOUT_TEXT = 40000;\nvar TIMEOUT_IMG = 90000;\n\nvar PROMPTS = {\n  watch: 'Uma marca de relógios inspirada nos carros de corrida dos anos 60 e 70, com cores de pista, para colecionadores.',\n  fashion: 'Uma marca de upcycling que transforma roupa descartada em peças únicas, com estética de rua e tiragem limitada.'\n};\n\n// ---------------------------------------------------------------- slides\nvar S = [];\nfunction add(cls, time, html, notes, live) { S.push({ cls: cls, time: time, html: html, notes: notes, live: live || null }); }\n\nadd('dark s-cover', '20:00', `\n  <div class=\"label\">Edição 02</div>\n  <h1 class=\"display title\">STATE<br>OF AI</h1>\n  <div class=\"grow\"></div>\n  <div class=\"foot rule-top\">\n    <div class=\"tag\">Builders allowed.<br><span class=\"gray\">Providers tolerated.</span></div>\n    <div class=\"label\" style=\"text-align:right\">3 de novembro de 2026<br><span class=\"gray\">Roda de conversa</span></div>\n  </div>`, '20:00 · Abertura (~5 min). Boa noite, obrigado por virem.');\n\nadd('light s-what', '20:01', `\n  <div class=\"label rule-top\">O que é o State of AI</div>\n  <h2 class=\"display\">Não é uma organização.</h2>\n  <p class=\"sub gray\">É um espaço pra discutir IA, com quem constrói.</p>\n  <div class=\"grow\"></div>\n  <div class=\"eds\">\n    <div class=\"rule-top-thin\"><div class=\"label gray\">Edição 01</div><p>Agentes, times de IA, o experimento Mirokami.</p></div>\n    <div class=\"rule-top\"><div class=\"label\">Edição 02 · hoje</div><p><b>Onde fica a linha.</b></p></div>\n  </div>`, 'Explicar que não é uma organização, é um espaço pra discutir IA com quem constrói. Lembrar da edição 01 (agentes, times de IA, Mirokami). Hoje é roda de conversa, sem palco, sem palestra.');\n\nadd('dark s-mosaic', '20:02', `\n  <div class=\"head rule-top\"><div class=\"label\">Edição 01</div><div class=\"label gray\">Agentes · times de IA · Mirokami</div></div>\n  <div class=\"mosaic\">\n    <figure class=\"big\"><img src=\"${u('e01big')}\" alt=\"\"><figcaption>Painel com Rafa Cunha, Babi Vallim e Aline Sordili</figcaption></figure>\n    <figure><img src=\"${u('e01ren')}\" alt=\"\"><figcaption>Renato Shirakashi e o experimento Mirokami</figcaption></figure>\n    <figure><img src=\"${u('e01dav')}\" alt=\"\"><figcaption>David Lojudice e as sociedades de agentes</figcaption></figure>\n    <figure><img src=\"${u('e01pan')}\" alt=\"\"><figcaption>Painel</figcaption></figure>\n    <figure><img src=\"${u('e01pla')}\" alt=\"\"><figcaption>Plateia</figcaption></figure>\n    <figure class=\"wide\"><img src=\"${u('e01sala')}\" alt=\"\"><figcaption>Sala cheia</figcaption></figure>\n    <figure><img src=\"${u('e01spk')}\" alt=\"\"><figcaption>Speakers e hosts no fim da noite</figcaption></figure>\n    <figure><img src=\"${u('e01hh')}\" alt=\"\"><figcaption>Happy hour</figcaption></figure>\n  </div>\n  <div class=\"credit\"><div class=\"label gray\">Fotos · Alécio Cezar Batista Oliveira</div></div>`, 'Rápido (~30s). A edição 01 foi sobre agentes e times de IA: Rafa, Babi e Aline no painel, Renato com o Mirokami, David com as sociedades de agentes. Fotos do Alécio.');\n\nadd('light s-slop', '20:02', `\n  <div class=\"label rule-top\">Merriam-Webster · palavra do ano de 2025</div>\n  <div class=\"word\">slop</div>\n  <div class=\"pron\"><span class=\"ipa gray\">/ˈslɑːp/</span><span class=\"label gray\">Substantivo</span></div>\n  <p class=\"def\"><b>1.</b>conteúdo digital de baixa qualidade, geralmente produzido em quantidade por meio de inteligência artificial.</p>\n  <div class=\"grow\"></div>\n  <div class=\"label gray\">Fonte: Merriam-Webster, via AP</div>`, 'Merriam-Webster é o dicionário mais tradicional dos EUA (desde 1831). A palavra do ano sai em boa parte das buscas no site, ou seja: o mundo inteiro foi procurar o que era slop.');\n\nadd('dark s-manifesto', '20:03', `\n  <div class=\"cols\">\n    <div class=\"col\"><div class=\"label gray\">AI can</div><div class=\"word gray\">expand</div><div class=\"word gray\">copy</div></div>\n    <div class=\"divider\"></div>\n    <div class=\"col\"><div class=\"label\">AI can’t</div><div class=\"word\">innovate</div><div class=\"word\">concept</div></div>\n  </div>\n  <p class=\"line rule-top-thin\" style=\"border-color:var(--hair)\"><span class=\"gray\">Slop é o que sai quando ninguém decide onde a IA para.</span> Hoje é sobre quem sabe traçar essa linha.</p>`, 'IA estende, varia, copia. Não decide que o traço precisa mudar. Exemplo do Adonis: chegou no hiper-realismo e largou tudo. Nenhum modelo teria tomado essa decisão.');\n\nfunction card(k, name, role, text, pos) {\n  return `<div class=\"card\"><div class=\"ph\"><img src=\"${u(k)}\" alt=\"${esc(name)}\" style=\"object-position:${pos}\"></div><h3>${name}</h3><div class=\"label gray\">${role}</div><p>${text}</p></div>`;\n}\nadd('light s-builders', '20:05', `\n  <div class=\"head rule-top\"><div class=\"label\">Builders · Edição 02</div><div class=\"label gray\">04</div></div>\n  <h2 class=\"display\">Quem sabe onde fica a linha.</h2>\n  <div class=\"cards\">\n    ${card('adonis', 'Adonis Alcici', 'Artista plástico', 'Chegou no hiper-realismo e largou tudo pra pintar velocidade.', 'center 20%')}\n    ${card('sergio', 'Sergio Ferreira', 'Seteh7 · upcycling', 'Não constrói produtos. Fabrica ideias vestíveis.', 'center 18%')}\n    ${card('pedro', 'Pedro Guedes', 'Aloeus · joalheria', 'Games, música, roupa e móveis antes de chegar no anel.', '78% center')}\n    ${card('alexandre', 'Alexandre Iervolino', 'ROUE · relógios', 'Do mercado financeiro pra relógios inspirados em carros dos anos 60 e 70.', '82% center')}\n  </div>`, '20:05 · Apresentações (~8 min). 2 min cada: quem é você e onde a IA já entrou (ou não) no teu trabalho. Ordem: Adonis, Sergio, Pedro, Alexandre.');\n\nfunction q(n, tag, title, desc, follow) {\n  return `<div class=\"head rule-top\"><div class=\"label\">Pauta · ${n} / 04</div><div class=\"label gray\">${tag}</div></div>\n  <div class=\"body\"><div class=\"num\">${n}</div><div><h2 class=\"display\">${title}</h2>${follow ? `<h3 class=\"follow gray\">${follow}</h3>` : ''}<p class=\"gray\">${desc}</p></div></div>`;\n}\nadd('light s-q', '20:13', q('01', 'Expandir', 'Onde a IA já expande o seu trabalho?', 'Variações de uma peça, renderização, versões de cor, mockup. A parte do processo em que cada um já deixa a máquina trabalhar, e quantas horas isso devolve.'), '20:13 · Perguntas 01 e 02 juntas (~15 min).');\nadd('light s-q', '20:20', q('02', 'A linha', 'Em que ponto ela para de ajudar?', 'O momento em que a sugestão do modelo começa a parecer com tudo o que já existe. Como cada ofício percebe isso.'), 'Aqui aparece a linha de cada um.');\n\nadd('dark s-cases', '20:28', `\n  <div class=\"label rule-top\">Casos</div>\n  <h2 class=\"display\">Três casos de quem passou da linha.</h2>\n  <div class=\"cases\">\n    <div class=\"case rule-top\"><h3>Migrolino</h3><div class=\"label gray\">Suíça · conveniência</div><p>Sanduíche de frango com “receita gerada por IA” na embalagem. Virou piada.</p></div>\n    <div class=\"case rule-top\"><h3>Vivi Nova</h3><div class=\"label gray\">Suíça · 2023 · bebida</div><p>ChatGPT escolheu os ingredientes, Midjourney desenhou a lata. Dois dias do conceito ao produto.</p></div>\n    <div class=\"case rule-top\"><h3>Wonder Create</h3><div class=\"label gray\">EUA · 2026 · restaurantes</div><p>Um restaurante inteiro a partir de um prompt: nome, marca, cardápio e preço, por US$ 10 por mês.</p></div>\n  </div>\n  <div class=\"label gray src\">Fontes: 20 Minuten · FoodNavigator · SIAL · Semafor</div>`, '20:28 · Casos (~11 min no total, com Vivi e Wonder Create). Contar rápido, quase piada. Wonder: perguntaram pro Marc Lore pra quem era o produto e ele respondeu explicando o prompt. Se apertar: os casos viram uma frase só.');\n\nadd('light s-before', '20:30', `\n  <div class=\"txt\">\n    <div class=\"head rule-top\"><div class=\"label\">Vivi Nova · antes</div><div class=\"label gray\">2023</div></div>\n    <h2 class=\"display quote\">“Developed by AI.<br>100% natural.”</h2>\n    <p class=\"on-can gray\">Estava escrito na lata.</p>\n    <div class=\"facts\">\n      <div><span class=\"label gray\">Ingredientes</span><b>ChatGPT</b></div>\n      <div><span class=\"label gray\">Lata</span><b>Midjourney</b></div>\n      <div><span class=\"label gray\">Do conceito ao produto</span><b>2 dias</b></div>\n    </div>\n    <p class=\"prop\">A proposta era a IA.</p>\n  </div>\n  <div class=\"photo\"><img src=\"${u('viviB')}\" alt=\"Lata da Vivi Nova\"></div>`, 'A Vivi Nova saiu em 2023 com a IA como argumento de venda: ChatGPT escolheu os ingredientes, Midjourney desenhou a lata, dois dias do conceito ao produto. “Developed by AI” e “100% natural” na mesma lata.');\n\nadd('dark s-after', '20:31', `\n  <img class=\"full\" src=\"${u('viviA')}\" alt=\"Site da Vivi hoje\">\n  <div class=\"tag\"><div class=\"label\">Vivi · depois</div><div class=\"label gray\">Hoje, no site da marca</div></div>`, 'Deixar a tela falar e esperar a risada. Hoje a marca se vende como “Swiss beverage brand with tradition and natural flavour… local craftsmanship”, com fotos antigas em P&B. A IA saiu da vitrine. Quando o diferencial vira slop, a marca volta pra tradição. Gancho pra pergunta 03: o cliente sabe a diferença?');\n\nadd('light s-wprompt', '20:32', `\n  <div class=\"txt\">\n    <div class=\"head rule-top\"><div class=\"label\">Wonder Create · EUA</div><div class=\"label gray\">Lança em breve</div></div>\n    <h2 class=\"display\">Manda um prompt.<br><span class=\"gray\">Sai um restaurante.</span></h2>\n    <div class=\"facts\">\n      <div><span class=\"label gray\">Nome e marca</span><b>IA</b></div>\n      <div><span class=\"label gray\">Cardápio</span><b>IA</b></div>\n      <div><span class=\"label gray\">Preço e margem</span><b>IA</b></div>\n      <div><span class=\"label gray\">Assinatura</span><b>US$ 10 / mês</b></div>\n    </div>\n  </div>\n  <div class=\"shot\"><img src=\"${u('wPrompt')}\" alt=\"Prompt do Wonder Create\"><div class=\"label gray\">O prompt de exemplo, no site do Wonder Create</div></div>`, 'Wonder Create: você descreve o conceito e sai nome, marca, cardápio e preço, por US$ 10 por mês. O prompt de exemplo é literalmente “um conceito de bowl greco-mexicano, com branding barulhento pra Gen Z e Millennials”. Lembrar: perguntaram pro Marc Lore pra quem era o produto e ele respondeu explicando o prompt.');\n\nadd('dark s-wout', '20:33', `\n  <div class=\"head rule-top\"><div class=\"label\">Wonder Create · do outro lado</div><div class=\"label gray\">Brand generated</div></div>\n  <h2 class=\"display\">Oye. Greek × Mexican bowls. <span class=\"gray\">Em minutos.</span></h2>\n  <div class=\"pair\">\n    <figure><img src=\"${u('wBrand')}\" alt=\"\"><figcaption><span class=\"label gray\">01 · Marca</span><b>Nome, logo e paleta</b></figcaption></figure>\n    <figure><img src=\"${u('wMenu')}\" alt=\"\"><figcaption><span class=\"label gray\">02 · Cardápio</span><b>5 pratos · US$ 12,85 · margem 10%</b></figcaption></figure>\n  </div>`, 'O que sai: a marca “Oye”, logo, paleta (hot pink, tangerine, teal, cream), e um cardápio de 5 pratos com preço médio de US$ 12,85 e margem de 10%, montado a partir da biblioteca de ingredientes da Wonder. Ninguém decidiu nada ali.');\n\nadd('light s-abund', '20:34', `\n  <div class=\"label rule-top\">Sobre o Wonder Create</div>\n  <h2 class=\"display\">Abundância.</h2>\n  <div class=\"math\">\n    <div><span class=\"label gray\">Custo de uma marca</span><b>US$ 10</b></div>\n    <div><span class=\"label gray\">Tempo</span><b>Minutos</b></div>\n    <div><span class=\"label gray\">Quantas cabem</span><b>Infinitas</b></div>\n  </div>\n  <p class=\"q\">Se qualquer um lança uma marca com um prompt, <span class=\"gray\">o que ainda vale uma marca?</span></p>`, 'Virar o caso pra abundância. Quando a marca custa US$ 10 e sai em minutos, cabe uma infinidade delas. Slop pra uns, abundância pra outros. E se não fosse comida? (Próximos dois slides geram marcas AO VIVO.)');\n\nadd('dark s-live', '20:35', '', 'AO VIVO: uma marca de relógio nova a cada ~45s. O card da esquerda é a fábrica: mostra a próxima sendo criada; a marca só entra quando as imagens estão prontas. G gera agora, A pausa/retoma, clique no prompt pra reescrever (Enter envia, Esc sai). Pergunta direta pro Alexandre: o prompt descreve a ROUE. O que da ROUE não sai desse prompt?', 'watch');\nadd('light s-live', '20:37', '', 'AO VIVO: agora fashion. Pro Sergio: o prompt descreve a Seteh7. Dá pra gerar o lookbook, mas e a peça única? (Se der tempo: e joia, Pedro?) Daqui vai direto pra pergunta 03: o cliente sabe a diferença?', 'fashion');\n\nadd('light s-q', '20:39', q('03', 'Confiança', 'O cliente sabe a diferença?', 'Uma foto gerada mostra um produto que ninguém fez. O que acontece com a confiança de quem compra.', 'E quando ele não souber? Faz diferença?'), '20:39 · Perguntas 03 e 04 (~11 min). Migrolino, Vivi e Wonder como gancho. Primeiro: o cliente percebe? Depois a segunda camada: e quando ele não perceber, isso importa? Se o produto entrega, o processo importa pra quem compra?');\nadd('light s-q', '20:45', q('04', 'Valor', 'Quem fica com o que a IA economizou?', 'Vai pro cliente no preço, pra marca na margem, ou some junto com quem fazia esse trabalho?', 'Tempo de criação, design, foto de produto.'), 'O que a IA economiza: tempo de criação (a Vivi foi do conceito à lata em 2 dias), design, foto de produto, o catálogo inteiro. Pergunta: essa economia vira preço menor pro cliente, margem pra marca, ou desaparece com quem fazia? Bom pro Alexandre (preço) e pro Sergio (peça única).');\n\nadd('dark s-prov', '20:50', `\n  <div class=\"head rule-top\"><div class=\"label\">Sobre o slop · com a plateia</div><div class=\"label gray\">Microfone aberto</div></div>\n  <ol>\n    <li><span>01</span>É só temporário?</li>\n    <li><span>02</span>Vai melhorar?</li>\n    <li><span>03</span>Slop para uns, abundância para outros?</li>\n    <li><span>04</span>A abundância, mesmo com má qualidade, pode ter upsides?</li>\n  </ol>`, '20:50 · Plateia (~6 min). Escolher uma ou duas. É o bloco mais elástico: se apertar, encurta aqui.');\n\nfunction ord(k, n, name, pos) {\n  return `<div class=\"rule-top-thin\"><img src=\"${u(k)}\" alt=\"\" style=\"object-position:${pos}\"><div><span class=\"label gray\" style=\"font-size:15px\">${n}</span><b>${name}</b></div></div>`;\n}\nadd('light s-close', '20:56', `\n  <div class=\"label rule-top\">Pra fechar</div>\n  <h2 class=\"display\">Onde fica a tua linha?</h2>\n  <p class=\"sub gray\">Uma frase de cada builder.</p>\n  <div class=\"order\">\n    ${ord('adonis', '01', 'Adonis', 'center 20%')}${ord('sergio', '02', 'Sergio', 'center 18%')}${ord('pedro', '03', 'Pedro', '78% center')}${ord('alexandre', '04', 'Alexandre', '82% center')}\n  </div>`, '20:56 (~4 min). Mesma ordem das apresentações: Adonis, Sergio, Pedro, Alexandre.');\n\nadd('dark s-thanks', '21:00', `\n  <div class=\"label rule-top\">State of AI BR · Edição 02</div>\n  <h2 class=\"display\">Obrigado.</h2>\n  <p class=\"sub gray\">Happy hour até as 23h.</p>\n  <div class=\"foot rule-top-thin\" style=\"border-color:var(--hair); margin-top:auto\">\n    <div class=\"label\">stateofai.com.br</div>\n    <div class=\"label gray\">Fotos da Edição 01 · Alécio Cezar Batista Oliveira</div>\n  </div>`, 'Agradecer builders, plateia e time. Créditos das fotos da edição 01: Alécio.');\n\nvar N = S.length;\n\n// ---------------------------------------------------------------- live state (closure; d2.refresh() repaints)\nvar L = {\n  watch: { kind: 'watch', n: '01', title: 'E se fosse com relógio?', prompt: PROMPTS.watch, brands: [], pool: [], pipes: [], total: 0, session: 0, lastStart: 0, loaded: false, loading: false },\n  fashion: { kind: 'fashion', n: '02', title: 'E se fosse com fashion?', prompt: PROMPTS.fashion, brands: [], pool: [], pipes: [], total: 0, session: 0, lastStart: 0, loaded: false, loading: false }\n};\nvar AUTO = { on: true };\nvar MAXPIPES = 1;\nvar PREV = {};\n\nfunction brandCard(b) {\n  var pal = (b.palette || []).slice(0, 4).map(function (c) { return `<i style=\"background:${hex(c && c.hex)}\"></i>`; }).join('');\n  var prods = (b.products || []).slice(0, 4).map(function (p) { return `<li><span>${esc(p && p.name)}</span><b>${esc(p && p.price)}</b></li>`; }).join('');\n  var key = b._tmp || b._id;\n  var logo = b.logoUrl ? `<img src=\"${esc(b.logoUrl)}\" alt=\"\">` : `<span>${esc(String(b.name || '?').slice(0, 1))}</span>`;\n  return `<div class=\"bc\" data-key=\"${esc(key)}\">\n    <div class=\"bimg\"><img src=\"${esc(b.productUrl)}\" alt=\"\"></div>\n    <div class=\"bbody\">\n      <div class=\"brow\"><div class=\"blogo\">${logo}</div><div class=\"bname\"><b>${esc(b.name)}</b><span>${esc(b.tagline)}</span></div></div>\n      <div class=\"sw\">${pal}</div>\n      <ul>${prods}</ul>\n    </div>\n  </div>`;\n}\n\nfunction factoryCard(st, interactive) {\n  var p = st.pipes[0];\n  var secs = p ? Math.floor((Date.now() - p.t0) / 1000) : 0;\n  var left = Math.max(0, Math.ceil((CADENCE - (Date.now() - st.lastStart)) / 1000));\n  var head;\n  if (!interactive) head = 'ao vivo na tela';\n  else if (p) head = p.stage === 'text' ? 'inventando a marca' : (p.stage === 'images' ? 'desenhando produto e logo' : 'quase lá');\n  else if (!AUTO.on) head = 'pausado · A retoma';\n  else head = 'próxima em ' + left + 's';\n  var name = p && p.brand ? p.brand.name : '';\n  var tag = p && p.brand ? p.brand.tagline : '';\n  var tDone = !!(p && p.stage !== 'text');\n  var tOn = !!(p && p.stage === 'text');\n  var iOn = !!(p && p.stage === 'images');\n  var step = function (label, done, on) { return `<li class=\"${done ? 'ok' : (on ? 'on' : '')}\"><i></i>${label}</li>`; };\n  return `<div class=\"bc factory${p ? ' busy' : ''}\" data-key=\"factory-${st.kind}\">\n    <div class=\"bimg sk\"><div class=\"scan\"></div><div class=\"fx\"><b>${p ? secs + 's' : '···'}</b><span>${esc(head)}</span></div></div>\n    <div class=\"bbody\">\n      <div class=\"brow\"><div class=\"blogo sk\"></div><div class=\"bname\"><b class=\"${name ? 'reveal' : 'skl'}\">${name ? esc(name) : '&nbsp;'}</b><span class=\"${tag ? 'reveal' : 'skl'}\">${tag ? esc(tag) : '&nbsp;'}</span></div></div>\n      <ul class=\"steps\">\n        ${step('Nome e paleta', tDone, tOn)}${step('Coleção e preço', tDone, tOn)}${step('Foto do produto', !!(p && p.prodDone), iOn && !p.prodDone)}${step('Logo', !!(p && p.logoDone), iOn && !p.logoDone)}\n      </ul>\n    </div>\n    ${st.pipes.length > 1 ? `<div class=\"queue\">+${st.pipes.length - 1}</div>` : ''}\n  </div>`;\n}\n\nfunction liveHTML(kind, interactive) {\n  var st = L[kind];\n  var busy = st.pipes.length;\n  var status = busy ? ('● gerando' + (busy > 1 ? ' · ' + busy + ' em paralelo' : '')) : (!AUTO.on ? 'pausado' : 'uma marca a cada 45s');\n  var cards = factoryCard(st, interactive) + st.brands.slice(0, 4).map(brandCard).join('');\n  return `\n  <div class=\"head rule-top\"><div class=\"label\">E se… · ao vivo</div><div class=\"label gray\">${st.n} / 02 · ${esc(status)}</div></div>\n  <div class=\"top\">\n    <h2 class=\"display\">${st.title}</h2>\n    <div class=\"count\"><b>${st.total}</b><div><span class=\"label\">marcas geradas</span><span class=\"label gray\">${st.session} agora, ao vivo</span></div></div>\n  </div>\n  <div class=\"prompt\">\n    <div class=\"label gray\">Prompt</div>\n    <textarea class=\"pin\" data-kind=\"${kind}\" rows=\"2\" spellcheck=\"false\" ${interactive ? '' : 'readonly tabindex=\"-1\"'}>${esc(st.prompt)}</textarea>\n    <button class=\"send\" data-kind=\"${kind}\" ${busy >= MAXPIPES ? 'disabled' : ''}>↑</button>\n  </div>\n  <div class=\"grid\">${cards}</div>`;\n}\n\nfunction slideHTML(k, active, interactive) {\n  var sl = S[k];\n  var inner = sl.live ? liveHTML(sl.live, interactive) : sl.html;\n  return `<section class=\"slide ${sl.cls}${active ? ' active' : ''}\" data-k=\"${k}\">${inner}</section>`;\n}\n\n// ---------------------------------------------------------------- styles\nvar CSS = `\n:host { display:block; width:100%; height:100%; }\n* { margin:0; padding:0; box-sizing:border-box; }\n.root { position:absolute; inset:0; background:#000; overflow:hidden; font-family:'Inter', system-ui, sans-serif; -webkit-font-smoothing:antialiased;\n  --dark:#0c0c0b; --light:#f5f4f0; --gol:#5c5c58; --god:#8f8f8a; --lod:#3a3a38; --m:128px;\n  --display:'Space Grotesk', system-ui, sans-serif; --serif:'Source Serif 4', Georgia, serif; }\n.stage { position:absolute; left:50%; top:50%; width:1920px; height:1080px; transform-origin:center center; }\n.slide { position:absolute; inset:0; width:1920px; height:1080px; padding:var(--m); display:none; flex-direction:column; overflow:hidden; }\n.slide.active { display:flex; }\n.slide.dark  { background:var(--dark);  color:var(--light); --gray:var(--god); --rule:var(--light); --hair:var(--lod); }\n.slide.light { background:var(--light); color:var(--dark);  --gray:var(--gol); --rule:var(--dark); --hair:#d8d6cf; }\n.label { font-family:var(--display); font-weight:500; font-size:22px; letter-spacing:.24em; text-transform:uppercase; line-height:1.3; }\n.gray { color:var(--gray); }\n.display { font-family:var(--display); font-weight:600; letter-spacing:-.035em; line-height:.92; }\n.rule-top { border-top:6px solid var(--rule); padding-top:28px; }\n.rule-top-thin { border-top:2px solid var(--rule); padding-top:24px; }\n.head { display:flex; justify-content:space-between; align-items:baseline; }\n.grow { flex:1; }\nimg { display:block; }\n\n.s-cover .title { font-size:340px; margin-top:56px; letter-spacing:-.055em; line-height:.84; }\n.s-cover .foot { display:flex; justify-content:space-between; align-items:flex-end; }\n.s-cover .tag { font-family:var(--display); font-weight:500; font-size:56px; letter-spacing:-.02em; line-height:1.1; }\n\n.s-what h2 { font-size:168px; margin-top:96px; }\n.s-what .sub { font-family:var(--display); font-weight:500; font-size:64px; letter-spacing:-.02em; line-height:1.12; margin-top:40px; max-width:1500px; }\n.s-what .eds { display:grid; grid-template-columns:1fr 1fr; gap:64px; }\n.s-what .eds > div { padding-top:28px; }\n.s-what .eds p { font-size:32px; line-height:1.3; margin-top:14px; }\n\n.s-mosaic .mosaic { margin-top:48px; flex:1; display:grid; grid-template-columns:repeat(4,1fr); grid-template-rows:repeat(3,1fr); grid-auto-flow:dense; gap:12px; min-height:0; }\n.s-mosaic figure { position:relative; overflow:hidden; background:#222; }\n.s-mosaic figure.big { grid-column:span 2; grid-row:span 2; }\n.s-mosaic figure.wide { grid-column:span 2; }\n.s-mosaic img { width:100%; height:100%; object-fit:cover; filter:grayscale(1) contrast(1.05); }\n.s-mosaic figcaption { position:absolute; left:0; bottom:0; right:0; padding:40px 18px 14px; color:var(--light); font-family:var(--display); font-size:17px; letter-spacing:.06em; line-height:1.25; background:linear-gradient(transparent, rgba(12,12,11,.85)); }\n.s-mosaic .credit { margin-top:28px; }\n\n.s-slop .word { font-family:var(--serif); font-weight:600; font-size:300px; line-height:1; letter-spacing:-.03em; margin-top:24px; }\n.s-slop .pron { display:flex; align-items:baseline; gap:40px; margin-top:8px; }\n.s-slop .ipa { font-family:var(--serif); font-style:italic; font-size:56px; letter-spacing:.03em; }\n.s-slop .def { font-family:var(--serif); font-size:58px; line-height:1.22; max-width:1560px; margin-top:48px; }\n.s-slop .def b { font-weight:600; margin-right:.3em; }\n\n.s-manifesto .cols { flex:1; display:grid; grid-template-columns:1fr 6px 1fr; gap:0 96px; }\n.s-manifesto .divider { background:var(--light); }\n.s-manifesto .col { display:flex; flex-direction:column; justify-content:center; }\n.s-manifesto .col .word { font-family:var(--display); font-weight:600; font-size:210px; letter-spacing:-.045em; line-height:.95; }\n.s-manifesto .col .label { margin-bottom:40px; }\n.s-manifesto .line { font-family:var(--display); font-weight:500; font-size:48px; letter-spacing:-.015em; line-height:1.2; margin-top:64px; max-width:1500px; }\n\n.s-builders h2 { font-size:96px; margin-top:32px; }\n.cards { margin-top:48px; display:grid; grid-template-columns:repeat(4,1fr); gap:40px; flex:1; min-height:0; }\n.card { display:flex; flex-direction:column; min-height:0; }\n.card .ph { flex:none; height:372px; overflow:hidden; background:#ddd; border-bottom:6px solid var(--dark); }\n.card .ph img { width:100%; height:100%; object-fit:cover; filter:grayscale(1) contrast(1.05); }\n.card h3 { font-family:var(--display); font-weight:600; font-size:38px; letter-spacing:-.02em; margin-top:24px; }\n.card .label { font-size:17px; margin-top:8px; }\n.card p { font-size:25px; line-height:1.32; margin-top:16px; }\n\n.s-q .body { flex:1; display:grid; grid-template-columns:560px 1fr; gap:96px; align-items:end; }\n.s-q .num { font-family:var(--display); font-weight:600; font-size:480px; letter-spacing:-.07em; line-height:.74; margin-left:-.04em; }\n.s-q h2 { font-size:120px; letter-spacing:-.04em; line-height:.96; }\n.s-q p { font-size:38px; line-height:1.32; margin-top:48px; max-width:1000px; }\n.s-q .follow { font-family:var(--display); font-weight:600; font-size:64px; letter-spacing:-.03em; line-height:1.02; margin-top:32px; }\n\n.s-cases h2 { font-size:104px; margin-top:40px; max-width:1400px; }\n.cases { margin-top:auto; display:grid; grid-template-columns:repeat(3,1fr); gap:56px; }\n.case h3 { font-family:var(--display); font-weight:600; font-size:60px; letter-spacing:-.03em; line-height:1; }\n.case .label { font-size:17px; margin-top:16px; }\n.case p { font-size:29px; line-height:1.35; margin-top:28px; }\n.s-cases .src { margin-top:64px; font-size:17px; }\n\n.s-before { flex-direction:row; gap:80px; }\n.s-before .txt, .s-wprompt .txt { flex:1; display:flex; flex-direction:column; min-width:0; }\n.s-before .quote { font-size:92px; letter-spacing:-.045em; line-height:1; margin-top:56px; }\n.s-before .on-can { font-family:var(--display); font-weight:500; font-size:36px; letter-spacing:-.01em; margin-top:28px; }\n.facts { margin-top:auto; padding-top:40px; }\n.facts div { display:flex; justify-content:space-between; align-items:baseline; padding:18px 0; border-top:2px solid var(--hair); }\n.facts div:first-child { border-top:6px solid var(--rule); }\n.facts b { font-family:var(--display); font-weight:600; font-size:38px; letter-spacing:-.02em; }\n.facts .label { font-size:17px; }\n.s-before .prop { font-family:var(--display); font-weight:600; font-size:44px; letter-spacing:-.02em; line-height:1.15; margin-top:40px; }\n.s-before .photo { flex:none; width:760px; height:824px; overflow:hidden; border-bottom:6px solid var(--dark); }\n.s-before .photo img { width:100%; height:100%; object-fit:cover; }\n\n.s-after { padding:0; background:#000 !important; }\n.s-after .full { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }\n.s-after .tag { position:absolute; top:56px; left:var(--m); right:var(--m); display:flex; justify-content:space-between; }\n\n.s-wprompt { flex-direction:row; gap:80px; }\n.s-wprompt h2 { font-size:84px; letter-spacing:-.045em; line-height:.98; margin-top:64px; }\n.s-wprompt .shot { flex:none; width:800px; align-self:center; }\n.s-wprompt .shot img { width:100%; border-bottom:6px solid var(--dark); }\n.s-wprompt .shot .label { font-size:15px; margin-top:16px; }\n\n.s-wout h2 { font-size:72px; margin-top:36px; }\n.s-wout .pair { margin-top:auto; display:grid; grid-template-columns:1fr 1fr; gap:40px; }\n.s-wout figure img { width:100%; height:470px; object-fit:cover; }\n.s-wout figcaption { border-top:6px solid var(--rule); padding-top:22px; }\n.s-wout figcaption b { font-family:var(--display); font-weight:600; font-size:34px; letter-spacing:-.02em; display:block; margin-top:8px; }\n\n.s-abund h2 { font-size:260px; letter-spacing:-.06em; margin-top:72px; }\n.s-abund .math { margin-top:auto; display:grid; grid-template-columns:repeat(3,1fr); gap:40px; }\n.s-abund .math div { border-top:6px solid var(--rule); padding-top:24px; }\n.s-abund .math b { font-family:var(--display); font-weight:600; font-size:72px; letter-spacing:-.04em; display:block; line-height:1; margin-top:14px; }\n.s-abund .q { font-family:var(--display); font-weight:500; font-size:52px; letter-spacing:-.02em; line-height:1.15; margin-top:56px; }\n\n/* live */\n.s-live .top { display:flex; justify-content:space-between; align-items:flex-end; margin-top:22px; gap:40px; }\n.s-live .top h2 { font-size:88px; letter-spacing:-.05em; }\n.s-live .count { display:flex; align-items:flex-end; gap:20px; }\n.s-live .count b { font-family:var(--display); font-weight:600; font-size:112px; letter-spacing:-.05em; line-height:.8; font-variant-numeric:tabular-nums; }\n.s-live .count div { display:flex; flex-direction:column; gap:6px; padding-bottom:4px; }\n.s-live .count .label { font-size:17px; }\n.s-live .prompt { margin-top:24px; border:4px solid var(--rule); padding:16px 120px 16px 32px; position:relative; }\n.s-live .prompt .label { font-size:15px; }\n.s-live .pin { display:block; width:100%; margin-top:10px; background:transparent; color:inherit; border:0; outline:0; resize:none; font:400 30px/1.3 'Inter', system-ui, sans-serif; caret-color:currentColor; overflow:hidden; }\n.s-live .pin:focus { background:rgba(127,127,127,.08); }\n.s-live .send { position:absolute; right:24px; top:50%; margin-top:-32px; width:64px; height:64px; border:0; cursor:pointer; background:var(--rule); font:600 36px var(--display); }\n.s-live.dark .send { color:var(--dark); }\n.s-live.light .send { color:var(--light); }\n.s-live .send[disabled] { opacity:.35; cursor:default; }\n.s-live .grid { margin-top:24px; flex:1; min-height:0; display:grid; grid-auto-flow:column; grid-auto-columns:calc((100% - 84px) / 4); gap:28px; overflow:hidden; }\n.bc { position:relative; display:flex; flex-direction:column; min-height:0; background:#fff; color:#0c0c0b; overflow:hidden; will-change:transform; }\n.slide.light .bc { border:2px solid #0c0c0b; }\n.bimg { flex:1; min-height:0; position:relative; background:#fff; display:grid; place-items:center; overflow:hidden; border-bottom:2px solid #e6e4de; }\n.bimg img { position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }\n.bbody { flex:none; height:206px; padding:14px 16px 14px; }\n.brow { display:flex; gap:14px; align-items:center; }\n.blogo { flex:none; width:58px; height:58px; background:#f1f0ec; display:grid; place-items:center; overflow:hidden; font:600 28px var(--display); }\n.blogo img { width:100%; height:100%; object-fit:cover; }\n.bname { min-width:0; flex:1; }\n.bname b { display:block; font:600 28px/1.05 var(--display); letter-spacing:-.02em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.bname span { display:block; font-size:15px; line-height:1.25; color:#5c5c58; margin-top:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.sw { display:flex; gap:6px; margin-top:10px; }\n.sw i { flex:1; height:14px; }\n.bc ul { list-style:none; margin-top:10px; }\n.bc li { display:flex; justify-content:space-between; gap:10px; font-size:14px; line-height:1.4; border-top:1px solid #ecebe6; }\n.bc li span { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.bc li b { font-weight:600; white-space:nowrap; }\n@keyframes fade { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }\n@keyframes pulse { 50% { opacity:.35; } }\n.sk { background:linear-gradient(100deg, #efeee9 20%, #fbfaf7 40%, #efeee9 60%); background-size:300% 100%; animation:shimmer 1.6s linear infinite; }\n@keyframes shimmer { from { background-position:100% 0; } to { background-position:-50% 0; } }\n.scan { position:absolute; left:0; right:0; height:40%; top:-40%; background:linear-gradient(180deg, transparent, rgba(12,12,11,.07) 50%, transparent); animation:scan 2.4s cubic-bezier(.5,0,.5,1) infinite; }\n@keyframes scan { to { top:100%; } }\n.fx { position:relative; display:flex; flex-direction:column; align-items:center; gap:12px; text-align:center; padding:0 16px; }\n.fx b { font:600 72px/1 var(--display); letter-spacing:-.04em; font-variant-numeric:tabular-nums; color:#0c0c0b; }\n.fx span { font:500 14px var(--display); letter-spacing:.2em; text-transform:uppercase; color:#8f8f8a; }\n.factory.busy .fx span { color:#0c0c0b; animation:pulse 1.4s ease-in-out infinite; }\n.skl { background:#ecebe6; color:transparent !important; width:70%; }\n.bname span.skl { width:92%; }\n.reveal { animation:fade .5s ease both; }\n.steps { list-style:none; margin-top:12px; }\n.bc .steps li { justify-content:flex-start; align-items:center; gap:12px; font-size:14px; line-height:1.45; color:#a3a19b; padding:1px 0; }\n.steps li i { width:10px; height:10px; border:2px solid #c9c7c0; flex:none; transition:background .3s, border-color .3s; }\n.steps li.on { color:#0c0c0b; }\n.steps li.on i { border-color:#0c0c0b; animation:spin 1s linear infinite; }\n.steps li.ok { color:#0c0c0b; }\n.steps li.ok i { background:#0c0c0b; border-color:#0c0c0b; }\n@keyframes spin { to { transform:rotate(360deg); } }\n.queue { position:absolute; top:14px; right:14px; background:#0c0c0b; color:#f5f4f0; font:600 14px var(--display); letter-spacing:.1em; padding:6px 10px; }\n\n.s-prov ol { list-style:none; margin-top:auto; }\n.s-prov li { display:grid; grid-template-columns:120px 1fr; align-items:baseline; font-family:var(--display); font-weight:600; font-size:80px; letter-spacing:-.035em; line-height:1.05; padding:26px 0; border-top:2px solid var(--hair); }\n.s-prov li:first-child { border-top:6px solid var(--rule); }\n.s-prov li span { font-size:22px; letter-spacing:.24em; font-weight:500; color:var(--gray); }\n\n.s-close h2 { font-size:200px; margin-top:96px; letter-spacing:-.05em; }\n.s-close .sub { font-family:var(--display); font-weight:500; font-size:56px; letter-spacing:-.02em; margin-top:40px; }\n.order { margin-top:auto; display:grid; grid-template-columns:repeat(4,1fr); gap:40px; }\n.order .rule-top-thin { display:flex; align-items:center; gap:20px; }\n.order img { width:72px; height:72px; object-fit:cover; filter:grayscale(1); }\n.order b { font-family:var(--display); font-weight:600; font-size:28px; letter-spacing:-.01em; display:block; }\n\n.s-thanks h2 { font-size:300px; letter-spacing:-.06em; margin-top:auto; }\n.s-thanks .sub { font-family:var(--display); font-weight:500; font-size:64px; letter-spacing:-.02em; margin-top:40px; }\n.s-thanks .foot { display:flex; justify-content:space-between; }\n\n/* overlays */\n.notes { position:absolute; left:0; right:0; bottom:0; z-index:10; background:rgba(12,12,11,.94); color:#f5f4f0; font:20px/1.45 'Inter', sans-serif; padding:20px 28px; border-top:2px solid #3a3a38; }\n.notes .meta { font:500 13px var(--display); letter-spacing:.2em; text-transform:uppercase; color:#8f8f8a; margin-bottom:6px; }\n\n/* login */\n.login { position:absolute; inset:0; background:#0c0c0b; color:#f5f4f0; display:grid; place-items:center; padding:16px; }\n.login form { width:min(560px, 100%); }\n.login .label { font-size:13px; color:#8f8f8a; }\n.login h1 { font:600 clamp(56px, 12vw, 120px)/.9 var(--display); letter-spacing:-.05em; margin:18px 0 36px; }\n.login .row { display:flex; border:3px solid #f5f4f0; }\n.login input { flex:1; min-width:0; background:transparent; border:0; outline:0; color:#f5f4f0; font:500 24px var(--display); letter-spacing:.1em; padding:18px 20px; }\n.login button { background:#f5f4f0; color:#0c0c0b; border:0; padding:0 26px; font:600 14px var(--display); letter-spacing:.2em; text-transform:uppercase; cursor:pointer; }\n.login .err { margin-top:16px; min-height:20px; font:500 13px var(--display); letter-spacing:.16em; text-transform:uppercase; color:#ff7a6b; }\n\n/* presenter */\n.pres { position:absolute; inset:0; background:#111110; color:#f5f4f0; padding:20px; display:grid; grid-template-columns:1.55fr 1fr; grid-template-rows:auto 1fr; gap:20px; overflow:auto; }\n.pbar { grid-column:1 / -1; display:flex; gap:40px; align-items:baseline; font-family:var(--display); }\n.pbar .big { font-size:44px; font-weight:600; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }\n.pbar .lab { font-size:12px; letter-spacing:.2em; text-transform:uppercase; color:#8f8f8a; display:block; margin-bottom:4px; }\n.pbar button { margin-left:auto; align-self:center; font:500 12px var(--display); letter-spacing:.16em; text-transform:uppercase; background:none; color:#f5f4f0; border:2px solid #3a3a38; padding:10px 14px; cursor:pointer; }\n.pcol { display:flex; flex-direction:column; gap:16px; min-height:0; }\n.pframe { position:relative; overflow:hidden; background:#000; outline:2px solid #3a3a38; flex:none; }\n.pframe .stage { left:0; top:0; transform-origin:0 0; }\n.pcap { font:500 12px var(--display); letter-spacing:.2em; text-transform:uppercase; color:#8f8f8a; }\n.pnotes { flex:1; overflow:auto; font:26px/1.45 'Inter', sans-serif; }\n.pnotes .time { font:600 18px var(--display); letter-spacing:.12em; color:#8f8f8a; margin-bottom:10px; text-transform:uppercase; }\n`;\n\n// ---------------------------------------------------------------- helpers\nfunction unwrap(r) {\n  if (r && r.data && !r.brand && !r.url && !r.items && !r.error && !r.ok) return r.data;\n  return r;\n}\nfunction withTimeout(p, ms) {\n  return new Promise(function (res, rej) {\n    var t = setTimeout(function () { rej(new Error('timeout')); }, ms);\n    p.then(function (v) { clearTimeout(t); res(v); }, function (e) { clearTimeout(t); rej(e); });\n  });\n}\nfunction lsGet(k) { try { return localStorage.getItem(k); } catch (e) { return null; } }\nfunction lsSet(k, v) { try { localStorage.setItem(k, v); } catch (e) {} }\nfunction isPresenter() { try { return /[?&]presenter=1/.test(location.search); } catch (e) { return false; } }\n\nexport default {\n  css: CSS,\n\n  render(d2) {\n    var s = d2.state;\n    var vw = s.vw || (typeof window !== 'undefined' ? window.innerWidth : 1920);\n    var vh = s.vh || (typeof window !== 'undefined' ? window.innerHeight : 1080);\n\n    if (!s.key) {\n      return `<div class=\"root\"><div class=\"login\"><form class=\"lf\" autocomplete=\"off\">\n        <div class=\"label\">State of AI · Edição 02</div>\n        <h1>Senha.</h1>\n        <div class=\"row\"><input class=\"pw\" type=\"password\" placeholder=\"••••••\" ${s.checking ? 'disabled' : ''}><button type=\"submit\" ${s.checking ? 'disabled' : ''}>${s.checking ? '…' : 'Entrar'}</button></div>\n        <div class=\"err\">${esc(s.err || '')}</div>\n      </form></div></div>`;\n    }\n\n    var i = s.i || 0;\n\n    if (s.presenter) {\n      var colW = Math.max(200, (vw - 60) * 1.55 / 2.55);\n      var nextW = Math.max(160, (vw - 60) * 1 / 2.55);\n      var e = Math.floor((Date.now() - (s.t0 || Date.now())) / 1000);\n      var clock = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });\n      var frame = function (k, w) {\n        if (k >= N) return `<div class=\"pframe\" style=\"width:${w}px;height:${w * 9 / 16}px;display:grid;place-items:center\"><span class=\"pcap\">Fim</span></div>`;\n        return `<div class=\"pframe\" style=\"width:${w}px;height:${w * 9 / 16}px\"><div class=\"stage\" style=\"transform:scale(${w / 1920})\">${slideHTML(k, true, false)}</div></div>`;\n      };\n      return `<div class=\"root\"><div class=\"pres\">\n        <div class=\"pbar\">\n          <div><span class=\"lab\">Agora</span><span class=\"big\">${clock}</span></div>\n          <div><span class=\"lab\">Decorrido</span><span class=\"big\">${String(Math.floor(e / 60)).padStart(2, '0')}:${String(e % 60).padStart(2, '0')}</span></div>\n          <div><span class=\"lab\">Slide</span><span class=\"big\">${i + 1} / ${N}</span></div>\n          <div><span class=\"lab\">Planejado</span><span class=\"big\">${S[i].time}</span></div>\n          <button class=\"reset\">Zerar cronômetro (R)</button>\n        </div>\n        <div class=\"pcol\">${frame(i, colW)}<div class=\"pcap\">← → / espaço navegam nas duas janelas</div></div>\n        <div class=\"pcol\"><div class=\"pcap\">Próximo</div>${frame(i + 1, nextW)}\n          <div class=\"pnotes\"><div class=\"time\">Planejado ${S[i].time}</div><div>${esc(S[i].notes)}</div></div></div>\n      </div></div>`;\n    }\n\n    var sc = Math.min(vw / 1920, vh / 1080);\n    var html = '';\n    for (var k = 0; k < N; k++) {\n      if (Math.abs(k - i) <= 1 || !S[k].live) html += slideHTML(k, k === i, true);\n    }\n    return `<div class=\"root\" style=\"cursor:${s.cursor === false ? 'none' : 'default'}\">\n      <div class=\"stage\" style=\"transform:translate(-50%,-50%) scale(${sc})\">${html}</div>\n      ${s.notes ? `<div class=\"notes\"><div class=\"meta\">${i + 1} / ${N} · ${S[i].time}</div><div>${esc(S[i].notes)}</div></div>` : ''}\n    </div>`;\n  },\n\n  // FLIP: cards keep their identity (data-key); when one is inserted the others glide right\n  updated(d2) {\n    var els = d2.$$('.slide.active .grid .bc');\n    if (!els.length) return;\n    var slideKey = els[0].closest('.slide').getAttribute('data-k');\n    var prev = PREV[slideKey];\n    var now = {};\n    els.forEach(function (el) {\n      var key = el.getAttribute('data-key');\n      var x = el.offsetLeft;\n      now[key] = x;\n      if (!prev || !el.animate) return;\n      if (key in prev) {\n        var dx = prev[key] - x;\n        if (Math.abs(dx) > 2) el.animate([{ transform: 'translateX(' + dx + 'px)' }, { transform: 'none' }], { duration: 1000, easing: 'cubic-bezier(.2,.8,.2,1)' });\n      } else {\n        el.animate([\n          { opacity: 0, transform: 'translateX(-60px) scale(.9)', filter: 'blur(12px)' },\n          { opacity: 1, transform: 'none', filter: 'blur(0)' }\n        ], { duration: 1100, delay: 150, easing: 'cubic-bezier(.2,.8,.2,1)', fill: 'backwards' });\n      }\n    });\n    PREV[slideKey] = now;\n  },\n\n  mounted(d2, root) {\n    if (d2.context && d2.context.isBuilder && !d2.context.isPlaying) return;\n    var s = d2.state;\n    var presenter = isPresenter();\n    var peer = null;\n    s.presenter = presenter;\n    s.t0 = Date.now();\n    s.i = Math.max(0, Math.min(N - 1, parseInt(lsGet('soai-i') || '0', 10) || 0));\n\n    try {\n      if (!document.getElementById('soai-fonts')) {\n        var l = document.createElement('link');\n        l.id = 'soai-fonts'; l.rel = 'stylesheet';\n        l.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Source+Serif+4:ital,opsz,wght@0,8..60,400;0,8..60,600;1,8..60,400&family=Space+Grotesk:wght@400;500;600;700&display=swap';\n        document.head.appendChild(l);\n      }\n    } catch (e) {}\n\n    var measure = function () { s.vw = root.clientWidth || window.innerWidth; s.vh = root.clientHeight || window.innerHeight; };\n    measure();\n    if (typeof ResizeObserver !== 'undefined') { var ro = new ResizeObserver(measure); ro.observe(root); d2.onUnmount(function () { ro.disconnect(); }); }\n    d2.on(window, 'resize', measure);\n\n    var headers = function () { return { headers: { 'x-deck-key': s.key || '' } }; };\n    var call = function (param, body, ms) {\n      return withTimeout(d2.rush(d2.param(param)).invoke(body, headers()), ms).then(unwrap);\n    };\n    var preload = function (urls) {\n      return withTimeout(Promise.all(urls.filter(Boolean).map(function (src) {\n        return new Promise(function (res) {\n          var im = new Image();\n          im.onload = function () { if (im.decode) im.decode().then(res, res); else res(); };\n          im.onerror = res;\n          im.src = src;\n        });\n      })), 20000).catch(function () {});\n    };\n\n    var seed = function (kind, r) {\n      var st = L[kind];\n      var items = ((r && r.items) || []).filter(function (b) { return b.productUrl; });\n      st.pool = items.slice();\n      st.total = Math.max(st.total, (r && r.total) || items.length);\n      var first = items.slice(0, 4);\n      preload([].concat.apply([], first.map(function (b) { return [b.productUrl, b.logoUrl]; }))).then(function () {\n        if (!st.brands.length) st.brands = first;\n        st.loaded = true; st.loading = false;\n        d2.refresh();\n      });\n    };\n\n    var tryKey = function (key, silent) {\n      s.checking = true; s.err = '';\n      return withTimeout(d2.rush(d2.param('list')).invoke({ kind: 'watch' }, { headers: { 'x-deck-key': key } }), 15000).then(unwrap)\n        .then(function (r) {\n          s.checking = false;\n          if (r && r.ok) { lsSet('soai-key', key); s.key = key; L.watch.loading = true; seed('watch', r); }\n          else if (!silent) s.err = 'Senha incorreta';\n        }, function () { s.checking = false; if (!silent) s.err = 'Sem conexão, tenta de novo'; });\n    };\n    d2.on('.lf', 'submit', function (e) { e.preventDefault(); var el = d2.$('.pw'); if (el && el.value) tryKey(el.value.trim(), false); });\n    var saved = lsGet('soai-key');\n    if (saved) tryKey(saved, true);\n\n    var load = function (kind) {\n      var st = L[kind];\n      if (st.loaded || st.loading || !s.key) return;\n      st.loading = true;\n      call('list', { kind: kind }, 20000).then(function (r) { seed(kind, r); }, function () { st.loading = false; st.loaded = true; });\n    };\n    // a brand only enters the grid once its images are generated AND decoded in the browser\n    var reveal = function (kind, b, isNew) {\n      var st = L[kind];\n      return preload([b.productUrl, b.logoUrl]).then(function () {\n        st.brands.unshift(b); st.brands = st.brands.slice(0, 24);\n        if (isNew) { st.total++; st.session++; }\n        d2.refresh();\n      });\n    };\n    var fallback = function (kind) {\n      var st = L[kind];\n      var shown = st.brands.slice(0, 5).map(function (b) { return b._id; });\n      var pick = st.pool.filter(function (b) { return b.productUrl && shown.indexOf(b._id) < 0; });\n      if (!pick.length) return Promise.resolve();\n      var b = Object.assign({}, pick[Math.floor(Math.random() * pick.length)]);\n      b._tmp = 'r' + Date.now() + Math.random().toString(36).slice(2, 6);\n      return reveal(kind, b, false);\n    };\n    var start = function (kind) {\n      var st = L[kind];\n      if (!s.key || presenter || st.pipes.length >= MAXPIPES) return;\n      var p = { t0: Date.now(), stage: 'text', brand: null, prodDone: false, logoDone: false };\n      st.pipes.push(p); st.lastStart = Date.now();\n      d2.refresh();\n      var done = function () { st.pipes = st.pipes.filter(function (x) { return x !== p; }); d2.refresh(); };\n      call('gen', { kind: kind, prompt: st.prompt }, TIMEOUT_TEXT).then(function (r) {\n        var b = r && r.brand;\n        if (!b || !b.name || !b._id) throw new Error('empty');\n        p.brand = b; p.stage = 'images';\n        d2.refresh();\n        var img = function (which) {\n          var prompt = which === 'logo' ? b.logoPrompt : b.productPrompt;\n          if (!prompt) return Promise.resolve();\n          return call('img', { id: b._id, which: which, prompt: prompt }, TIMEOUT_IMG).then(function (x) {\n            if (x && x.url) { if (which === 'logo') b.logoUrl = x.url; else b.productUrl = x.url; }\n          }, function () {}).then(function () {\n            if (which === 'logo') p.logoDone = true; else p.prodDone = true;\n            d2.refresh();\n          });\n        };\n        return Promise.all([img('product'), img('logo')]).then(function () {\n          if (!b.productUrl) throw new Error('noimg');\n          p.stage = 'preload';\n          d2.refresh();\n          st.pool.unshift(b);\n          return reveal(kind, b, true);\n        });\n      }).catch(function () { return fallback(kind); }).then(done, done);\n    };\n\n    var tick = setInterval(function () {\n      if (!s.key) return;\n      if (presenter) { d2.refresh(); return; }\n      var sl = S[s.i || 0];\n      if (!sl || !sl.live) return;\n      var st = L[sl.live];\n      load(sl.live);\n      if (AUTO.on && !st.loading && Date.now() - st.lastStart >= CADENCE) start(sl.live);\n      d2.refresh();\n    }, 1000);\n    d2.onUnmount(function () { clearInterval(tick); });\n\n    d2.on('.pin', 'input', function (e, el) { var k = el.getAttribute('data-kind'); if (L[k]) L[k].prompt = el.value; });\n    d2.on('.send', 'click', function (e, el) { var k = el.getAttribute('data-kind'); if (L[k]) start(k); });\n\n    var show = function (n, fromPeer) {\n      var v = Math.max(0, Math.min(N - 1, n));\n      if (v !== s.i) {\n        var prev = S[s.i || 0];\n        s.i = v;\n        var sl = S[v];\n        if (sl.live) { load(sl.live); L[sl.live].lastStart = 0; }\n      }\n      if (!presenter) lsSet('soai-i', String(s.i));\n      if (!fromPeer && peer && !peer.closed) peer.postMessage({ sotai: 'go', i: s.i }, '*');\n    };\n    var openPresenter = function () {\n      if (peer && !peer.closed) { peer.focus(); return; }\n      var href = location.href.split('#')[0];\n      href += (href.indexOf('?') >= 0 ? '&' : '?') + 'presenter=1';\n      peer = window.open(href, 'sotai-presenter', 'width=1400,height=860');\n    };\n    d2.on(window, 'message', function (e) {\n      var m = e.data; if (!m || !m.sotai) return;\n      if (m.sotai === 'go') show(m.i, true);\n      if (m.sotai === 'hello') { peer = e.source; try { peer.postMessage({ sotai: 'go', i: s.i }, '*'); } catch (x) {} }\n    });\n    if (presenter && window.opener) { peer = window.opener; try { peer.postMessage({ sotai: 'hello' }, '*'); } catch (x) {} }\n    d2.on('.reset', 'click', function () { s.t0 = Date.now(); });\n\n    var numBuf = '', numT = null;\n    d2.on(window, 'keydown', function (e) {\n      if (!s.key) return;\n      var t = (e.composedPath && e.composedPath()[0]) || e.target;\n      var tag = t && t.tagName;\n      if (tag === 'TEXTAREA' || tag === 'INPUT') {\n        if (e.key === 'Escape') t.blur();\n        if (e.key === 'Enter' && !e.shiftKey && tag === 'TEXTAREA') { e.preventDefault(); var k0 = t.getAttribute('data-kind'); t.blur(); if (L[k0]) start(k0); }\n        return;\n      }\n      if (e.metaKey || e.ctrlKey || e.altKey) return;\n      var k = e.key;\n      var sl = S[s.i || 0];\n      if (['ArrowRight', 'ArrowDown', 'PageDown', ' ', 'Enter'].indexOf(k) >= 0) { e.preventDefault(); show((s.i || 0) + 1); }\n      else if (['ArrowLeft', 'ArrowUp', 'PageUp', 'Backspace'].indexOf(k) >= 0) { e.preventDefault(); show((s.i || 0) - 1); }\n      else if (k === 'Home') show(0);\n      else if (k === 'End') show(N - 1);\n      else if (k === 'f' || k === 'F') { try { document.fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen(); } catch (x) {} }\n      else if ((k === 'n' || k === 'N') && !presenter) s.notes = !s.notes;\n      else if ((k === 'p' || k === 'P') && !presenter) openPresenter();\n      else if ((k === 'r' || k === 'R') && presenter) s.t0 = Date.now();\n      else if ((k === 'g' || k === 'G') && sl && sl.live) start(sl.live);\n      else if ((k === 'a' || k === 'A') && sl && sl.live) { AUTO.on = !AUTO.on; if (AUTO.on) L[sl.live].lastStart = 0; d2.refresh(); }\n      else if (/^[0-9]$/.test(k)) {\n        numBuf += k; clearTimeout(numT);\n        numT = setTimeout(function () { show(parseInt(numBuf, 10) - 1); numBuf = ''; }, 450);\n      }\n    });\n\n    var idle = null;\n    d2.on(window, 'mousemove', function () {\n      if (presenter) return;\n      if (s.cursor === false) s.cursor = true;\n      clearTimeout(idle);\n      idle = setTimeout(function () { s.cursor = false; }, 2000);\n    });\n    d2.onUnmount(function () { clearTimeout(idle); clearTimeout(numT); });\n  }\n};\n"}},"gen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"gen"}}}},"img":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"img"}}}},"list":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"list"}}}}}}}}}},{"_id":"5ed9bed7b9e0891ce913e884","component":"data2-core-types@Component","params":{"package":"data2-core","component":"DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}},{"_id":"5f472ae463c680de4ff296e9","component":"data2-core-types@Component","params":{"package":"data2-core","component":"Data2TableDataSource","extends":"data2-core@DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"component":"data2-core-types@Getter","displayName":"isData2User","params":{"module":{"component":"data2-core-functions@Match","params":{"pattern":{"component":"data2-core-types@Regex","params":{"pattern":"@data2\\.app$","flags":"i"}},"text":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}}},"prop":{"component":"data2-core-primitives@String","params":{"content":"0"}}}},"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":{"component":"data2-core-flow@If","displayName":"management exception never picks the system","params":{"compare":{"component":"data2-core-logical-operators@Or","params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-logical-operators@Not","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}}}]}},"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"d2_app":"management"}}},"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-10-29T19:05:43.007Z","updateMetadata":{"updatedBy":{"user":"5f1609be1ca96def427e1559"}}},{"_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"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_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":[{"_id":"6ae49717cd54784ddc241d76","component":"data2-rush@HTTPListener","reference":"checkout","system":"6329b5535b2103060e128c0a","params":{}},{"_id":"6ae9cf5ccce886542c37ac66","component":"data2-rush@HTTPListener","reference":"soai-list","system":"6329b5535b2103060e128c0a","params":{}},{"_id":"6ae9cf59d79019a0ae220382","component":"data2-rush@HTTPListener","reference":"soai-img","system":"6329b5535b2103060e128c0a","params":{}},{"_id":"6ae9cf57978ab41527b278cb","component":"data2-rush@HTTPListener","reference":"soai-gen","system":"6329b5535b2103060e128c0a","params":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6ae9d31d1b16986ad56630f6","component":"data2-core-primitives@Artboard","system":"6329b5535b2103060e128c0a","displayName":"soai · deck ao vivo","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"soai · deck ao vivo"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1920,"height":1080}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"CyYjBHgXBbe6wWAAywmzr"},{"_id":"6047c254396a969058cae1d5","parent":"6ae9d31d1b16986ad56630f6","component":"gui@soai-deck","system":"6329b5535b2103060e128c0a","displayName":"deck","params":{"gen":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"soai-gen"}},"img":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"soai-img"}},"list":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"soai-list"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1920,"height":1080}}}},"reference":"Xrgdv7lVq41VhKFm3bcLV"}]}},{"_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":"6ae9d328d4a5bd3bf277b6b9","displayName":"State of AI 02 · deck","reference":"DWxYqpyWi3xmOuIMIAXBZ","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"stateofai02-deck"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"CyYjBHgXBbe6wWAAywmzr"}}},"system":"6329b5535b2103060e128c0a","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6ae494cc3dd0a823bb4cf62a","component":"data2-core@Data2TableDataSource","system":"6329b5535b2103060e128c0a","params":{"table":"produtos","schema":[{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"slug","title":"Slug","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"categoria","title":"Categoria","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"preco","title":"Preço","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"descricao","title":"Descrição","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"estoque","title":"Estoque","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"destaque","title":"Destaque","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"cor","title":"Cor do rótulo","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"imagem","title":"Imagem (URL)","component":"data2-core-primitives@String"}}]},"reference":"produtos"},{"_id":"6ae494d13dd0a823bb4cf640","component":"data2-core@Data2TableDataSource","system":"6329b5535b2103060e128c0a","params":{"table":"carrinhos","schema":[{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Cliente (e-mail)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"itens","title":"Itens","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"total","title":"Total","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"status","title":"Status","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"atualizado_em","title":"Atualizado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"lembrete_em","title":"Lembrete enviado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"pedido","title":"Pedido gerado","component":"data2-core-primitives@String"}}]},"reference":"carrinhos"},{"_id":"6ae494d6c0028ad81045eea4","component":"data2-core@Data2TableDataSource","system":"6329b5535b2103060e128c0a","params":{"table":"pedidos","schema":[{"component":"data2-core-types@Field","params":{"key":"numero","title":"Número","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Cliente","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"endereco","title":"Endereço","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"itens","title":"Itens","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"subtotal","title":"Subtotal","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"frete","title":"Frete","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"total","title":"Total","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"status","title":"Status","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"pagamento","title":"Pagamento","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"rastreio","title":"Rastreio","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"operador","title":"Operador (separação)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"motorista","title":"Motorista","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nps","title":"NPS","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"criado_em","title":"Criado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"pago_em","title":"Pago em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"separado_em","title":"Separado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"enviado_em","title":"Enviado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"entregue_em","title":"Entregue em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"nps_enviado_em","title":"NPS enviado em","component":"data2-core-primitives@Date"}}]},"reference":"pedidos"},{"_id":"6ae494d9c2082658d252decf","component":"data2-core@Data2TableDataSource","system":"6329b5535b2103060e128c0a","params":{"table":"avaliacoes","schema":[{"component":"data2-core-types@Field","params":{"key":"pedido","title":"Pedido","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Cliente","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nota","title":"Nota (0–10)","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"comentario","title":"Comentário","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"criado_em","title":"Criado em","component":"data2-core-primitives@Date"}}]},"reference":"avaliacoes"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}