{"context":{"domain":"people-gabi-carmo"},"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":"69a704a047c6ef5f14b2dd47","name":"Gabriela Carmo","domain":"people-gabi-carmo","visibility":"private","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T19:35:06.936Z","createdAt":"2026-03-03T15:56:16.236Z"}]}},{"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":"6a397f78e5a95378ceed5bb1","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"arezzoApp","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Arezzo experimente — rebuild marker\nconst PRIMARY = (p, fmt) => {\n  const imgs = (p && p.images) || [];\n  return (imgs.find(i => i.imageType === 'PRIMARY' && i.format === fmt)\n       || imgs.find(i => i.imageType === 'PRIMARY') || {}).url || '';\n};\nconst readAsDataURL = (file) => new Promise((resolve, reject) => {\n  const r = new FileReader();\n  r.onload = () => {\n    const img = new Image();\n    img.onload = () => {\n      try {\n        const MAX = 1024;\n        let w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;\n        if (w > h && w > MAX) { h = Math.round(h * MAX / w); w = MAX; }\n        else if (h >= w && h > MAX) { w = Math.round(w * MAX / h); h = MAX; }\n        const canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        canvas.getContext('2d').drawImage(img, 0, 0, w, h);\n        resolve(canvas.toDataURL('image/jpeg', 0.85));\n      } catch (e) { resolve(r.result); }\n    };\n    img.onerror = () => resolve(r.result);\n    img.src = r.result;\n  };\n  r.onerror = reject;\n  r.readAsDataURL(file);\n});\nconst CATS = [\n  { label: 'Todos', code: 'SAPATOS' },\n  { label: 'Scarpins', code: 'SCARPINS' },\n  { label: 'Botas', code: 'BOTAS' },\n  { label: 'Sandálias', code: 'SANDALIAS' },\n  { label: 'Tênis', code: 'TENIS' },\n  { label: 'Mules', code: 'MULES' },\n  { label: 'Sapatilhas', code: 'SAPATILHAS' },\n  { label: 'Mocassins', code: 'MOCASSINS' },\n  { label: 'Bolsas', code: 'BOLSAS' }\n];\nconst esc = (s) => String(s == null ? '' : s).replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600;700&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .app { display: flex; flex-direction: column; height: 100%; font-family: 'Commissioner', 'Helvetica Neue', Arial, sans-serif; background: #FFFFFF; overflow: hidden; }\n    .header { display: flex; align-items: center; padding: 0 40px; height: 78px; background: #FFFFFF; border-bottom: 1px solid #EEEEEE; flex-shrink: 0; gap: 24px; }\n    .logo { font-size: 24px; font-weight: 600; letter-spacing: 9px; color: #1C1C1C; text-transform: uppercase; }\n    .search-wrap { margin-left: auto; display: flex; align-items: center; background: #F6F6F6; border: 1px solid #E5E5E5; border-radius: 24px; height: 42px; padding: 0 6px 0 18px; width: 340px; max-width: 38%; }\n    .search-input { flex: 1; background: transparent; border: none; outline: none; color: #1C1C1C; font-size: 14px; font-family: inherit; }\n    .search-input::placeholder { color: #999; }\n    .search-btn, .search-clear { width: 32px; height: 32px; border: none; background: transparent; color: #888; cursor: pointer; font-size: 15px; border-radius: 50%; transition: all 0.2s; }\n    .search-btn:hover, .search-clear:hover { background: #ECECEC; color: #1C1C1C; }\n    .filter-bar { display: flex; align-items: center; padding: 0 40px; gap: 8px; height: 56px; background: #FFFFFF; border-bottom: 1px solid #EEEEEE; flex-shrink: 0; overflow-x: auto; }\n    .filter-btn { padding: 6px 18px; border: 1.5px solid transparent; border-radius: 20px; background: transparent; font-size: 13px; color: #666666; cursor: pointer; transition: all 0.2s; font-family: inherit; letter-spacing: 0.3px; white-space: nowrap; }\n    .filter-btn:hover { background: #F5F3F0; color: #1C1C1C; }\n    .filter-btn.active { background: #1C1C1C; color: #FFFFFF; border-color: #1C1C1C; }\n    .count { margin-left: auto; font-size: 12px; color: #AAA; white-space: nowrap; padding-left: 16px; }\n    .content { display: flex; flex: 1; overflow: hidden; }\n    .grid-wrapper { flex: 1; overflow-y: auto; padding: 22px; background: #FAFAFA; }\n    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); grid-auto-rows: 284px; gap: 16px; }\n    .card { background: #FFFFFF; border: 1px solid #F0F0F0; border-radius: 10px; overflow: hidden; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; display: flex; flex-direction: column; }\n    .card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgba(0,0,0,0.12); }\n    .card-img { width: 100%; height: 192px; object-fit: contain; background: #FFFFFF; display: block; padding: 6px; }\n    .card-body { padding: 12px 16px; flex: 1; }\n    .card-name { font-size: 13px; font-weight: 500; color: #1C1C1C; line-height: 1.4; margin-bottom: 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .card-price { font-size: 15px; font-weight: 700; color: #E4002B; }\n    .state-msg { grid-column: 1/-1; text-align: center; padding: 80px 0; color: #999; font-size: 15px; }\n    .state-full { display: flex; align-items: center; justify-content: center; height: 100%; color: #999; font-size: 15px; gap: 12px; }\n    .spinner-sm { width: 22px; height: 22px; border: 3px solid #EEE; border-top-color: #E4002B; border-radius: 50%; animation: spin 0.8s linear infinite; }\n    .detail-panel { width: 560px; background: #FFFFFF; border-left: 1px solid #EEEEEE; display: flex; flex-direction: column; overflow-y: auto; flex-shrink: 0; animation: slideIn 0.25s ease; }\n    @keyframes slideIn { from { transform: translateX(40px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }\n    .detail-header { display: flex; align-items: center; justify-content: space-between; padding: 0 24px; height: 56px; border-bottom: 1px solid #EEEEEE; flex-shrink: 0; position: sticky; top: 0; background: #FFFFFF; z-index: 2; }\n    .detail-title { font-size: 14px; font-weight: 600; color: #1C1C1C; letter-spacing: 0.3px; }\n    .close-btn { width: 32px; height: 32px; border: none; background: #F5F3F0; border-radius: 50%; font-size: 14px; cursor: pointer; color: #666; display: flex; align-items: center; justify-content: center; transition: background 0.2s; font-family: inherit; }\n    .close-btn:hover { background: #E0DDD8; }\n    .detail-img { width: 100%; height: 360px; object-fit: contain; background: #FFFFFF; display: block; padding: 16px; }\n    .detail-info { padding: 24px; }\n    .detail-name { font-size: 16px; font-weight: 600; color: #1C1C1C; line-height: 1.4; margin-bottom: 8px; }\n    .detail-price { font-size: 22px; font-weight: 700; color: #E4002B; margin-bottom: 24px; }\n    .size-label { font-size: 11px; font-weight: 600; color: #999; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 1px; }\n    .sizes-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 28px; }\n    .size-btn { min-width: 48px; height: 44px; padding: 0 12px; border: 1.5px solid #DDDDDD; border-radius: 8px; background: #FFFFFF; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.15s; font-family: inherit; color: #1C1C1C; }\n    .size-btn:hover { border-color: #1C1C1C; }\n    .size-btn.active { background: #1C1C1C; color: #FFFFFF; border-color: #1C1C1C; }\n    .tryon-section { border-top: 1px solid #EEEEEE; padding-top: 24px; }\n    .cta-btn { width: 100%; height: 52px; background: #1C1C1C; color: #FFFFFF; border: none; border-radius: 8px; font-size: 15px; font-weight: 600; letter-spacing: 0.5px; cursor: pointer; transition: background 0.2s; font-family: inherit; display: flex; align-items: center; justify-content: center; gap: 8px; }\n    .cta-btn:hover:not(:disabled) { background: #333333; }\n    .cta-hint { margin-top: 12px; font-size: 12px; color: #AAAAAA; text-align: center; line-height: 1.5; }\n    .photo-input { display: none; }\n    .gen-box { margin-top: 16px; text-align: center; }\n    .spinner { width: 40px; height: 40px; border: 3px solid #EEEEEE; border-top-color: #E4002B; border-radius: 50%; margin: 24px auto 12px; animation: spin 0.8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gen-status { font-size: 13px; color: #666; }\n    .gen-sub { font-size: 11px; color: #AAA; margin-top: 4px; }\n    .result-img { width: 100%; border-radius: 12px; display: block; margin-bottom: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); }\n    .result-badge { font-size: 11px; color: #999; text-align: center; margin-bottom: 16px; }\n    .ghost-btn { width: 100%; height: 44px; background: #FFFFFF; color: #1C1C1C; border: 1.5px solid #DDDDDD; border-radius: 8px; font-size: 13px; font-weight: 500; cursor: pointer; font-family: inherit; transition: border-color 0.2s; }\n    .ghost-btn:hover { border-color: #1C1C1C; }\n    .err-box { margin-top: 16px; padding: 14px 16px; background: #FDECEC; color: #C0392B; font-size: 12px; border-radius: 8px; line-height: 1.5; }\n    .warn-box { margin-bottom: 16px; padding: 14px 16px; background: #FFF4E5; color: #9A5B00; font-size: 13px; border-radius: 8px; line-height: 1.5; }\n    .product-url { display: block; margin-top: 16px; text-align: center; font-size: 12px; color: #999; text-decoration: none; border-top: 1px solid #EEEEEE; padding-top: 16px; }\n    .product-url:hover { color: #1C1C1C; }\n    .gate { position: absolute; inset: 0; z-index: 50; background: linear-gradient(135deg, #FFFFFF 0%, #F5F3F0 100%); display: flex; align-items: center; justify-content: center; padding: 24px; font-family: 'Commissioner', 'Helvetica Neue', Arial, sans-serif; }\n    .gate-card { width: 100%; max-width: 420px; background: #FFFFFF; border: 1px solid #EEEEEE; border-radius: 16px; padding: 40px 36px; box-shadow: 0 20px 60px rgba(0,0,0,0.08); text-align: center; }\n    .gate-logo { font-size: 26px; font-weight: 600; letter-spacing: 9px; text-transform: uppercase; color: #1C1C1C; margin-bottom: 14px; }\n    .gate-title { font-size: 19px; font-weight: 600; color: #1C1C1C; margin-bottom: 8px; }\n    .gate-sub { font-size: 13px; color: #999; line-height: 1.5; margin-bottom: 28px; }\n    .gate-field { text-align: left; margin-bottom: 16px; }\n    .gate-field label { display: block; font-size: 11px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; }\n    .gate-field input { width: 100%; height: 48px; border: 1.5px solid #DDDDDD; border-radius: 8px; padding: 0 16px; font-size: 14px; font-family: inherit; outline: none; transition: border-color 0.2s; }\n    .gate-field input:focus { border-color: #1C1C1C; }\n    .gate-btn { width: 100%; height: 52px; margin-top: 8px; background: #1C1C1C; color: #FFFFFF; border: none; border-radius: 8px; font-size: 15px; font-weight: 600; letter-spacing: 0.5px; cursor: pointer; font-family: inherit; transition: background 0.2s; }\n    .gate-btn:hover:not(:disabled) { background: #333333; }\n    .gate-btn:disabled { opacity: 0.6; cursor: default; }\n    .gate-err { margin-top: 14px; font-size: 12px; color: #C0392B; }\n    .gate-note { margin-top: 18px; font-size: 11px; color: #BBB; line-height: 1.5; }\n  `,\n\n  render(d2) {\n    const {\n      category = 'SAPATOS', search = '', products = [], gridStatus = 'loading', gridError = null, total = 0,\n      detailOpen = false, selectedProduct = null, selectedSize = null,\n      tryStatus = 'idle', tryImage = null, tryError = null, tryAttempt = 1,\n      gated = true, gateError = null, gateSubmitting = false\n    } = d2.state;\n\n    if (gated) {\n      return `\n        <div class=\"gate\">\n          <div class=\"gate-card\">\n            <div class=\"gate-logo\">Arezzo</div>\n            <div class=\"gate-title\">Experimente antes de comprar</div>\n            <div class=\"gate-sub\">Preencha seus dados para provar virtualmente os produtos da Arezzo com a sua foto.</div>\n            <div class=\"gate-field\">\n              <label>Nome</label>\n              <input class=\"gate-name\" type=\"text\" placeholder=\"Seu nome\" />\n            </div>\n            <div class=\"gate-field\">\n              <label>E-mail</label>\n              <input class=\"gate-email\" type=\"email\" placeholder=\"seu@email.com\" />\n            </div>\n            <button class=\"gate-btn\"${gateSubmitting ? ' disabled' : ''}>${gateSubmitting ? 'Entrando...' : 'Começar a experimentar'}</button>\n            ${gateError ? '<div class=\"gate-err\">' + esc(gateError) + '</div>' : ''}\n            <div class=\"gate-note\">Usamos seus dados apenas para esta experiência.</div>\n          </div>\n        </div>`;\n    }\n\n    const filterBtns = CATS.map(c =>\n      `<button class=\"filter-btn${category === c.code && !search ? ' active' : ''}\" data-code=\"${c.code}\">${c.label}</button>`\n    ).join('');\n\n    let gridHTML;\n    if (gridStatus === 'loading') {\n      gridHTML = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando produtos...</span></div>`;\n    } else if (gridStatus === 'error') {\n      gridHTML = `<div class=\"state-full\"><span>Erro ao carregar: ${esc(gridError)}</span></div>`;\n    } else if (products.length === 0) {\n      gridHTML = `<div class=\"state-msg\">Nenhum produto encontrado${search ? ' para \"' + esc(search) + '\"' : ''}.</div>`;\n    } else {\n      const cards = products.map(p =>\n        `<div class=\"card\" data-code=\"${esc(p.code)}\" data-key=\"${esc(p.code)}\">\n          <img class=\"card-img\" src=\"${PRIMARY(p, 'product')}\" alt=\"\" loading=\"lazy\" />\n          <div class=\"card-body\">\n            <div class=\"card-name\">${esc(p.name)}</div>\n            <div class=\"card-price\">${esc((p.price || {}).formattedValue)}</div>\n          </div>\n        </div>`\n      ).join('');\n      gridHTML = `<div class=\"grid\">${cards}</div>`;\n    }\n\n    const countLabel = gridStatus === 'done' && total ? `${total} ${total === 1 ? 'produto' : 'produtos'}` : '';\n\n    let detailHTML = '';\n    if (detailOpen && selectedProduct) {\n      const sizes = (selectedProduct.variantOptions || []).map(v => {\n        const label = (v.code || '').split('-').pop();\n        return `<button class=\"size-btn${selectedSize === v.code ? ' active' : ''}\" data-size=\"${esc(v.code)}\" data-key=\"${esc(v.code)}\">${esc(label)}</button>`;\n      }).join('');\n\n      let tryOnHTML;\n      if (tryStatus === 'checking' || tryStatus === 'uploading' || tryStatus === 'generating') {\n        const msg = tryStatus === 'checking' ? 'Analisando sua foto...' : (tryStatus === 'uploading' ? 'Preparando sua foto...' : 'Gerando sua prova virtual com IA...');\n        const sub = tryStatus === 'generating' ? ((tryAttempt > 1 ? 'Tentativa ' + tryAttempt + ' de 3 — ' : '') + 'isso pode levar até 30 segundos ✨') : 'Só um instante';\n        tryOnHTML = `\n          <div class=\"gen-box\">\n            <div class=\"spinner\"></div>\n            <div class=\"gen-status\">${msg}</div>\n            <div class=\"gen-sub\">${sub}</div>\n          </div>`;\n      } else if (tryStatus === 'rejected') {\n        tryOnHTML = `\n          <div class=\"warn-box\">📷 ${esc(tryError)}</div>\n          <button class=\"cta-btn tryon-btn\">📸 Enviar outra foto</button>`;\n      } else if (tryStatus === 'done' && tryImage) {\n        tryOnHTML = `\n          <div class=\"gen-box\">\n            <img class=\"result-img\" src=\"${tryImage}\" alt=\"Prova virtual\" />\n            <div class=\"result-badge\">✨ Prova virtual gerada por IA</div>\n            <button class=\"ghost-btn try-again\">Experimentar com outra foto</button>\n          </div>`;\n      } else if (tryStatus === 'error') {\n        tryOnHTML = `\n          <button class=\"cta-btn tryon-btn\">📸 Tentar novamente</button>\n          <div class=\"err-box\">Não consegui gerar a prova virtual: ${esc(tryError)}</div>`;\n      } else {\n        tryOnHTML = `\n          <button class=\"cta-btn tryon-btn\">📸 Experimentar com sua foto</button>\n          <p class=\"cta-hint\">Suba uma foto sua (${selectedProduct.productType === 'bag' ? 'da cintura para cima já funciona' : 'corpo inteiro funciona melhor'}) e a IA vai te mostrar usando ${selectedProduct.productType === 'bag' ? ((/mochila/i.test(selectedProduct.name || '') || (selectedProduct.categories || []).some(c => c.code === 'MOCHILA')) ? 'esta mochila' : 'esta bolsa') : 'este calçado'}.</p>`;\n      }\n\n      detailHTML = `\n        <div class=\"detail-panel\">\n          <div class=\"detail-header\">\n            <span class=\"detail-title\">Detalhes do Produto</span>\n            <button class=\"close-btn\">✕</button>\n          </div>\n          <img class=\"detail-img\" src=\"${PRIMARY(selectedProduct, 'zoom') || PRIMARY(selectedProduct, 'product')}\" alt=\"\" />\n          <div class=\"detail-info\">\n            <h2 class=\"detail-name\">${esc(selectedProduct.name)}</h2>\n            <div class=\"detail-price\">${esc((selectedProduct.price || {}).formattedValue)}</div>\n            <div class=\"size-label\">Tamanhos disponíveis</div>\n            <div class=\"sizes-row\">${sizes}</div>\n            <div class=\"tryon-section\">\n              ${tryOnHTML}\n              <input type=\"file\" class=\"photo-input\" accept=\"image/*\" />\n            </div>\n            <a class=\"product-url\" href=\"https://www.arezzo.com.br${esc(selectedProduct.url)}\" target=\"_blank\">Ver no site da Arezzo →</a>\n          </div>\n        </div>`;\n    }\n\n    return `\n      <div class=\"app\">\n        <header class=\"header\">\n          <span class=\"logo\">Arezzo</span>\n          <div class=\"search-wrap\">\n            <input class=\"search-input\" placeholder=\"Buscar produtos...\" value=\"${esc(search)}\" />\n            ${search ? '<button class=\"search-clear\" title=\"Limpar\">✕</button>' : ''}\n            <button class=\"search-btn\" title=\"Buscar\">🔍</button>\n          </div>\n        </header>\n        <div class=\"filter-bar\">\n          ${filterBtns}\n          <span class=\"count\">${countLabel}</span>\n        </div>\n        <div class=\"content\">\n          <div class=\"grid-wrapper\">${gridHTML}</div>\n          ${detailHTML}\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    const track = (event, prod) => {\n      try {\n        const kind = prod ? (prod.productType === 'bag' ? ((/mochila/i.test(prod.name || '') || (prod.categories || []).some(c => c.code === 'MOCHILA')) ? 'backpack' : 'bag') : 'shoe') : '';\n        const p = d2.rush('arezzo-track').invoke({ event, productCode: (prod && prod.code) || '', productName: (prod && prod.name) || '', kind });\n        if (p && p.catch) p.catch(() => {});\n      } catch (e) {}\n    };\n\n    let savedLead = null;\n    try { savedLead = JSON.parse(window.localStorage.getItem('arezzo_lead') || 'null'); } catch (e) {}\n    d2.setState({ gated: !(savedLead && savedLead.email) });\n\n    d2.on('.gate-btn', 'click', async () => {\n      const nameEl = d2.$('.gate-name');\n      const emailEl = d2.$('.gate-email');\n      const name = (nameEl && nameEl.value.trim()) || '';\n      const email = (emailEl && emailEl.value.trim()) || '';\n      if (!name) { d2.setState({ gateError: 'Por favor, preencha seu nome.' }); return; }\n      if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(email)) { d2.setState({ gateError: 'Digite um e-mail válido.' }); return; }\n      d2.setState({ gateError: null, gateSubmitting: true });\n      try { await d2.rush('arezzo-lead').invoke({ name, email }); } catch (e) {}\n      try { window.localStorage.setItem('arezzo_lead', JSON.stringify({ name, email })); } catch (e) {}\n      d2.setState({ gated: false, gateSubmitting: false });\n    });\n    d2.on('.gate-email', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.gate-btn'); if (b) b.click(); } });\n\n    const fetchProducts = async (category, search) => {\n      d2.setState({ gridStatus: 'loading', category, search });\n      try {\n        const r = await d2.rush('arezzo-products').invoke({ category: category || 'SAPATOS', search: search || '' });\n        const payload = (r && r.data) ? r.data : r;\n        const products = (payload && payload.products) || [];\n        const total = (payload && payload.total) || products.length;\n        d2.setState({ products, total, gridStatus: 'done' });\n      } catch (err) {\n        d2.setState({ gridStatus: 'error', gridError: String((err && err.message) || err) });\n      }\n    };\n\n    d2.on('.filter-btn', 'click', (e) => {\n      const el = e.target.closest('.filter-btn');\n      if (el) fetchProducts(el.dataset.code, '');\n    });\n\n    d2.on('.search-btn', 'click', () => {\n      const i = d2.$('.search-input');\n      fetchProducts(d2.state.category || 'SAPATOS', (i && i.value.trim()) || '');\n    });\n\n    d2.on('.search-input', 'keydown', (e) => {\n      if (e.key === 'Enter') fetchProducts(d2.state.category || 'SAPATOS', e.target.value.trim());\n    });\n\n    d2.on('.search-clear', 'click', () => {\n      fetchProducts(d2.state.category || 'SAPATOS', '');\n    });\n\n    d2.on('.card', 'click', (e) => {\n      const el = e.target.closest('.card');\n      const code = el && el.dataset.code;\n      const prod = (d2.state.products || []).find(p => p.code === code);\n      if (prod) d2.setState({ selectedProduct: prod, detailOpen: true, selectedSize: null, tryStatus: 'idle', tryImage: null, tryError: null });\n    });\n\n    d2.on('.close-btn', 'click', () => {\n      d2.setState({ detailOpen: false, selectedProduct: null, tryStatus: 'idle', tryImage: null, tryError: null });\n    });\n\n    d2.on('.size-btn', 'click', (e) => {\n      const el = e.target.closest('.size-btn');\n      if (el) d2.setState({ selectedSize: el.dataset.size });\n    });\n\n    d2.on('.tryon-btn', 'click', () => {\n      track('tryon_click', d2.state.selectedProduct);\n      const input = d2.$('.photo-input');\n      if (input) { input.value = ''; input.click(); }\n    });\n\n    d2.on('.try-again', 'click', () => {\n      d2.setState({ tryStatus: 'idle', tryImage: null, tryError: null });\n      const input = d2.$('.photo-input');\n      if (input) input.value = '';\n    });\n\n    d2.on('.photo-input', 'change', async (e) => {\n      const file = e.target.files && e.target.files[0];\n      if (!file) return;\n      const prod = d2.state.selectedProduct;\n      if (!prod) return;\n\n      d2.setState({ tryStatus: 'checking', tryImage: null, tryError: null });\n      try {\n        const isBag = prod.productType === 'bag';\n        const isBackpack = isBag && (/mochila/i.test(prod.name || '') || (prod.categories || []).some(c => c.code === 'MOCHILA'));\n        const kind = isBag ? 'bag' : 'shoe';\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('arezzo-photo-check').invoke({ photoUrl: userPhotoUrl, kind });\n        const cp = (check && check.data) ? check.data : check;\n        const verdict = (cp && cp.verdict) || {};\n        if (verdict.ok === false) {\n          d2.setState({ tryStatus: 'rejected', tryError: verdict.motivo || 'A foto não ficou boa para a prova virtual. Tente outra com o corpo inteiro e os pés bem visíveis.' });\n          return;\n        }\n        const shoeUrl = PRIMARY(prod, 'product') || PRIMARY(prod, 'zoom');\n        const shoePrompt = 'Imagem 1 = uma pessoa. Imagem 2 = um calçado da Arezzo (\"' + (prod.name || '') + '\"). A Imagem 2 pode mostrar o calçado sozinho OU em uma modelo — use APENAS o calçado, ignore completamente qualquer pessoa, perna ou pé que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesmo rosto, corpo, roupa, pose, iluminação e fundo), trocando o que ela usa nos pés por esse calçado da Arezzo. Remova qualquer outro calçado, pantufa, chinelo, sandália ou sapato que já apareça na Imagem 1 — inclusive atrás, ao lado ou embaixo dos pés — de modo que ela use SOMENTE o calçado da Arezzo, sem nenhum outro calçado visível na cena. Calce o MESMO calçado nos DOIS pés, formando um PAR IDÊNTICO: exatamente o mesmo modelo, mesma altura de cano, mesmo formato de bico, mesmo salto, mesma cor e os mesmos detalhes nos dois pés. NUNCA gere dois calçados diferentes nem de alturas diferentes entre o pé esquerdo e o direito. Use tamanho, ângulo e perspectiva corretos em cada pé e contato natural com o chão. IMPORTANTE: se o calçado for aberto (sandália, rasteira, mule, chinelo, slide, peep toe, anabela aberta, etc.) ou deixar qualquer parte do pé à mostra, o pé deve aparecer DESCALÇO, com pele natural, SEM meia, soquete ou qualquer tecido cobrindo o pé. Nunca adicione meias. Não altere mais nada além do calçado.';\n        const bagPrompt = 'Imagem 1 = uma pessoa. Imagem 2 = uma bolsa da Arezzo (\"' + (prod.name || '') + '\"). A Imagem 2 pode mostrar a bolsa sozinha OU em uma modelo — use APENAS a bolsa, ignore completamente qualquer pessoa que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesmo rosto, corpo, roupa, pose, iluminação e fundo), agora carregando essa bolsa da Arezzo de forma natural e condizente com o tipo dela: bolsa tiracolo/transversal atravessada no corpo com a alça sobre um ombro, bolsa shoulder apoiada no ombro, bolsa de mão segurada pela alça na mão, clutch/carteira segurada na mão. Posicione a mão da pessoa interagindo com a alça quando fizer sentido. Mantenha o formato, o tamanho proporcional, a cor, a alça, as ferragens e todos os detalhes IDÊNTICOS aos da Imagem 2. Remova qualquer outra bolsa que já apareça na Imagem 1. Não altere mais nada na pessoa nem no fundo além de adicionar essa bolsa.';\n        const backpackPrompt = 'Imagem 1 = uma pessoa. Imagem 2 = uma mochila da Arezzo (\"' + (prod.name || '') + '\"). Use APENAS a mochila da Imagem 2 (mesma cor, formato, alças, bolsos, ferragens e detalhes); IGNORE completamente como ela aparece posicionada na Imagem 2 e qualquer modelo que apareça lá — NÃO copie a pose nem o jeito que a mochila está sendo carregada na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesma roupa, cabelo, corpo, iluminação e fundo), mas VISTA POR TRÁS: a pessoa deve estar girada DE COSTAS para a câmera, de modo que vejamos as COSTAS dela e a parte de trás da cabeça/cabelo (pode ser uma vista de três quartos de costas, no máximo um leve perfil do rosto). A mochila fica CENTRALIZADA NAS COSTAS da pessoa, com as DUAS alças passando por cima dos dois ombros, exatamente como uma mochila é usada de verdade. É TERMINANTEMENTE PROIBIDO colocar a mochila na frente do corpo, no peito, na barriga, no colo, pendurada no braço, de lado ou atravessada/transversal — ela aparece SOMENTE nas costas. Remova qualquer outra bolsa ou mochila que já apareça na Imagem 1. Não altere mais nada além de virar a pessoa de costas e adicionar a mochila nas costas.';\n        const prompt = isBackpack ? backpackPrompt : (isBag ? bagPrompt : shoePrompt);\n\n        let img = null, lastErr = null;\n        for (let attempt = 1; attempt <= 3 && !img; attempt++) {\n          d2.setState({ tryStatus: 'generating', tryAttempt: attempt });\n          try {\n            const res = await d2.rush('arezzo-tryon').invoke({ userPhotoUrl, shoeUrl, prompt });\n            const payload = (res && res.data) ? res.data : res;\n            img = (payload && payload.image) || null;\n            if (!img) throw new Error('Resposta sem imagem');\n          } catch (e) { lastErr = e; }\n        }\n        if (img) d2.setState({ tryStatus: 'done', tryImage: img });\n        else d2.setState({ tryStatus: 'error', tryError: 'Não consegui gerar após 3 tentativas. ' + String((lastErr && lastErr.message) || lastErr || '') });\n      } catch (err) {\n        d2.setState({ tryStatus: 'error', tryError: String((err && err.message) || err) });\n      }\n    });\n\n    track('app_open');\n    fetchProducts('SAPATOS', '');\n  }\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6a3ae16875773bb7084bc723","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"BarChartGabi","package":"people-gabi-carmo","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: `.wrap{display:flex;flex-direction:column;height:100%;box-sizing:border-box;padding:24px;\n        background:#fff;border-radius:16px;border:1px solid #eee;font-family:system-ui,sans-serif}\n        .ttl{font-size:18px;font-weight:800;color:#111;margin:0 0 4px}\n        .sub{font-size:12px;color:#999;margin:0 0 20px}\n        .chart{flex:1;min-height:0}\n        svg{width:100%;height:100%;display:block;overflow:visible}\n        .bar{transition:height .6s cubic-bezier(.22,1,.36,1),y .6s cubic-bezier(.22,1,.36,1)}\n        .lbl{font:11px system-ui,sans-serif;fill:#888}\n        .val{font:600 11px system-ui,sans-serif;fill:#555}\n        .grid{stroke:#f0f0f0;stroke-width:1}`,\n  render(d2){\n    const accent = d2.param('accent') || '#7c4dff'\n    const data = [\n      { label: 'Jan', value: 42 }, { label: 'Fev', value: 58 },\n      { label: 'Mar', value: 35 }, { label: 'Abr', value: 71 },\n      { label: 'Mai', value: 64 }, { label: 'Jun', value: 88 },\n    ]\n    const grown = d2.state.grown\n    const W = 600, H = 280, padL = 36, padB = 28, padT = 8\n    const innerW = W - padL, innerH = H - padB - padT\n    const max = Math.max(...data.map(d => d.value))\n    const niceMax = Math.ceil(max / 20) * 20\n    const gap = 18\n    const bw = (innerW - gap * (data.length - 1)) / data.length\n    const lines = [0, 0.25, 0.5, 0.75, 1].map(t => {\n      const y = padT + innerH * (1 - t)\n      return `<line class=\"grid\" x1=\"${padL}\" y1=\"${y}\" x2=\"${W}\" y2=\"${y}\"/>\n              <text class=\"lbl\" x=\"${padL - 8}\" y=\"${y + 3}\" text-anchor=\"end\">${Math.round(niceMax * t)}</text>`\n    }).join('')\n    const bars = data.map((d, i) => {\n      const full = (d.value / niceMax) * innerH\n      const h = grown ? full : 0\n      const x = padL + i * (bw + gap)\n      const y = padT + innerH - h\n      return `<rect class=\"bar\" x=\"${x}\" y=\"${y}\" width=\"${bw}\" height=\"${h}\" rx=\"6\" fill=\"${accent}\"/>\n              <text class=\"val\" x=\"${x + bw / 2}\" y=\"${y - 6}\" text-anchor=\"middle\">${grown ? d.value : ''}</text>\n              <text class=\"lbl\" x=\"${x + bw / 2}\" y=\"${H - 8}\" text-anchor=\"middle\">${d.label}</text>`\n    }).join('')\n    return `<div class=\"wrap\">\n      <p class=\"ttl\">${(d2.param('chartTitle', 'Vendas por mês') + '').replace(/</g, '&lt;')}</p>\n      <p class=\"sub\">Receita mensal (em milhares)</p>\n      <div class=\"chart\"><svg viewBox=\"0 0 ${W} ${H}\" preserveAspectRatio=\"xMidYMid meet\">${lines}${bars}</svg></div>\n    </div>`\n  },\n  mounted(d2){\n    setTimeout(() => { d2.state.grown = true }, 80)\n  },\n}"}},"chartTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chartTitle"}}}},"accent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accent"}}}}}}}}}},{"_id":"6a3c0b104a843044399e0257","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"arezzoStats","package":"people-gabi-carmo","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 KIND = { shoe: 'Calçado', bag: 'Bolsa', backpack: 'Mochila' };\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst fmtTime = (iso) => { try { return new Date(iso).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Commissioner:wght@300;400;500;600;700&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .dash { height: 100%; overflow-y: auto; font-family: 'Commissioner', 'Helvetica Neue', Arial, sans-serif; background: #FAFAFA; color: #1C1C1C; }\n    .top { display: flex; align-items: center; padding: 24px 40px; background: #FFFFFF; border-bottom: 1px solid #EEEEEE; position: sticky; top: 0; z-index: 2; }\n    .title { font-size: 18px; font-weight: 600; letter-spacing: 0.3px; }\n    .title .logo { letter-spacing: 7px; text-transform: uppercase; margin-right: 10px; }\n    .right { margin-left: auto; display: flex; align-items: center; gap: 14px; }\n    .updated { font-size: 12px; color: #AAA; }\n    .refresh { height: 38px; padding: 0 18px; border: 1.5px solid #DDD; border-radius: 20px; background: #FFF; font-family: inherit; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; }\n    .refresh:hover { border-color: #1C1C1C; }\n    .wrap { padding: 28px 40px; }\n    .section-label { font-size: 11px; font-weight: 600; color: #999; text-transform: uppercase; letter-spacing: 1px; margin: 28px 0 14px; }\n    .section-label:first-child { margin-top: 0; }\n    .kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }\n    .kpi { background: #FFFFFF; border: 1px solid #F0F0F0; border-radius: 12px; padding: 22px 24px; }\n    .kpi .label { font-size: 13px; color: #888; margin-bottom: 10px; }\n    .kpi .val { font-size: 38px; font-weight: 700; line-height: 1; }\n    .kpi .val.accent { color: #E4002B; }\n    .kpi .sub { font-size: 12px; color: #AAA; margin-top: 8px; }\n    .mini { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n    .mini .kpi .val { font-size: 28px; }\n    .panel { background: #FFFFFF; border: 1px solid #F0F0F0; border-radius: 12px; overflow: hidden; }\n    .row { display: flex; align-items: center; padding: 14px 22px; border-bottom: 1px solid #F4F4F4; font-size: 14px; }\n    .row:last-child { border-bottom: none; }\n    .row .rank { width: 28px; color: #BBB; font-weight: 600; font-size: 13px; }\n    .row .name { flex: 1; font-weight: 500; }\n    .row .count { font-weight: 700; color: #E4002B; }\n    .row .badge { font-size: 11px; font-weight: 600; padding: 3px 10px; border-radius: 12px; background: #F5F3F0; color: #666; margin: 0 12px; }\n    .row .time { color: #AAA; font-size: 12px; min-width: 96px; text-align: right; }\n    .row .email { flex: 1; text-align: right; color: #888; font-size: 13px; padding-right: 16px; }\n    .empty { padding: 40px; text-align: center; color: #AAA; font-size: 14px; }\n    .state { display: flex; align-items: center; justify-content: center; height: 100%; gap: 12px; color: #999; font-size: 15px; }\n    .spinner { width: 24px; height: 24px; border: 3px solid #EEE; border-top-color: #E4002B; border-radius: 50%; animation: spin 0.8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gate { min-height: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; background: linear-gradient(135deg, #FFFFFF 0%, #F5F3F0 100%); }\n    .gate-card { width: 100%; max-width: 380px; background: #FFFFFF; border: 1px solid #EEEEEE; border-radius: 16px; padding: 36px 32px; box-shadow: 0 20px 60px rgba(0,0,0,0.08); text-align: center; }\n    .gate-logo { font-size: 24px; font-weight: 600; letter-spacing: 8px; text-transform: uppercase; margin-bottom: 12px; }\n    .gate-title { font-size: 16px; font-weight: 600; margin-bottom: 6px; }\n    .gate-sub { font-size: 13px; color: #999; margin-bottom: 24px; }\n    .gate-input { width: 100%; height: 48px; border: 1.5px solid #DDD; border-radius: 8px; padding: 0 16px; font-size: 14px; font-family: inherit; outline: none; margin-bottom: 14px; }\n    .gate-input:focus { border-color: #1C1C1C; }\n    .gate-btn { width: 100%; height: 50px; background: #1C1C1C; color: #FFF; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit; }\n    .gate-btn:disabled { opacity: 0.6; }\n    .gate-err { margin-top: 12px; font-size: 12px; color: #C0392B; }\n  `,\n\n  render(d2) {\n    const { status = 'loading', s = null, error = null, updatedAt = '', pwError = null, pwSubmitting = false } = d2.state;\n\n    if (status === 'locked') {\n      return `\n        <div class=\"dash\"><div class=\"gate\"><div class=\"gate-card\">\n          <div class=\"gate-logo\">Arezzo</div>\n          <div class=\"gate-title\">Painel protegido</div>\n          <div class=\"gate-sub\">Digite a senha para ver os números.</div>\n          <input class=\"gate-input\" type=\"password\" placeholder=\"Senha\" />\n          <button class=\"gate-btn\"${pwSubmitting ? ' disabled' : ''}>${pwSubmitting ? 'Entrando...' : 'Entrar'}</button>\n          ${pwError ? '<div class=\"gate-err\">' + esc(pwError) + '</div>' : ''}\n        </div></div></div>`;\n    }\n    if (status === 'loading') {\n      return `<div class=\"dash\"><div class=\"state\"><div class=\"spinner\"></div><span>Carregando dados...</span></div></div>`;\n    }\n    if (status === 'error') {\n      return `<div class=\"dash\"><div class=\"state\"><span>Erro ao carregar: ${esc(error)}</span></div></div>`;\n    }\n\n    const opens = (s && s.opens && s.opens.total) || 0;\n    const clicks = (s && s.clicks && s.clicks.total) || 0;\n    const shoe = (s && s.shoe && s.shoe.total) || 0;\n    const bag = (s && s.bag && s.bag.total) || 0;\n    const backpack = (s && s.backpack && s.backpack.total) || 0;\n    const rate = opens > 0 ? Math.round((clicks / opens) * 100) : 0;\n    const recent = (s && s.recent && s.recent.data) || [];\n    const leads = (s && s.leads && s.leads.total) || 0;\n    const recentLeads = (s && s.recentLeads && s.recentLeads.data) || [];\n\n    const byProduct = {};\n    recent.forEach(r => { const n = r.productName || '—'; byProduct[n] = (byProduct[n] || 0) + 1; });\n    const top = Object.keys(byProduct).map(n => ({ name: n, count: byProduct[n] })).sort((a, b) => b.count - a.count).slice(0, 8);\n\n    const topHTML = top.length\n      ? top.map((p, i) => `<div class=\"row\"><span class=\"rank\">${i + 1}</span><span class=\"name\">${esc(p.name)}</span><span class=\"count\">${p.count}</span></div>`).join('')\n      : `<div class=\"empty\">Ainda não houve cliques no try-on.</div>`;\n\n    const recentHTML = recent.length\n      ? recent.slice(0, 20).map(r => `<div class=\"row\"><span class=\"name\">${esc(r.productName || '—')}</span><span class=\"badge\">${esc(KIND[r.kind] || r.kind || '—')}</span><span class=\"time\">${esc(fmtTime(r.createdAt))}</span></div>`).join('')\n      : `<div class=\"empty\">Sem atividade recente.</div>`;\n\n    const leadsHTML = recentLeads.length\n      ? recentLeads.map(l => `<div class=\"row\"><span class=\"name\">${esc(l.name || '—')}</span><span class=\"email\">${esc(l.email || '')}</span><span class=\"time\">${esc(fmtTime(l.createdAt))}</span></div>`).join('')\n      : `<div class=\"empty\">Nenhum cadastro ainda.</div>`;\n\n    return `\n      <div class=\"dash\">\n        <div class=\"top\">\n          <div class=\"title\"><span class=\"logo\">Arezzo</span> Painel do Try-On</div>\n          <div class=\"right\">\n            <span class=\"updated\">${updatedAt ? 'Atualizado às ' + esc(updatedAt) : ''}</span>\n            <button class=\"refresh\">↻ Atualizar</button>\n          </div>\n        </div>\n        <div class=\"wrap\">\n          <div class=\"section-label\">Visão geral</div>\n          <div class=\"kpis\">\n            <div class=\"kpi\"><div class=\"label\">Cadastros</div><div class=\"val accent\">${leads}</div><div class=\"sub\">preencheram o formulário</div></div>\n            <div class=\"kpi\"><div class=\"label\">Aberturas do app</div><div class=\"val\">${opens}</div><div class=\"sub\">cada vez que abriram o link</div></div>\n            <div class=\"kpi\"><div class=\"label\">Cliques no try-on</div><div class=\"val accent\">${clicks}</div><div class=\"sub\">clicaram em \"Experimentar com sua foto\"</div></div>\n            <div class=\"kpi\"><div class=\"label\">Taxa de clique</div><div class=\"val\">${rate}%</div><div class=\"sub\">cliques ÷ aberturas</div></div>\n          </div>\n\n          <div class=\"section-label\">Cliques por tipo de produto</div>\n          <div class=\"mini\">\n            <div class=\"kpi\"><div class=\"label\">Calçados</div><div class=\"val\">${shoe}</div></div>\n            <div class=\"kpi\"><div class=\"label\">Bolsas</div><div class=\"val\">${bag}</div></div>\n            <div class=\"kpi\"><div class=\"label\">Mochilas</div><div class=\"val\">${backpack}</div></div>\n          </div>\n\n          <div class=\"section-label\">Mais experimentados (últimos 50 cliques)</div>\n          <div class=\"panel\">${topHTML}</div>\n\n          <div class=\"section-label\">Atividade recente</div>\n          <div class=\"panel\">${recentHTML}</div>\n\n          <div class=\"section-label\">Cadastros recentes</div>\n          <div class=\"panel\">${leadsHTML}</div>\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    const PW_KEY = 'arezzo_stats_pw';\n    const getPw = () => { try { return localStorage.getItem(PW_KEY) || ''; } catch (e) { return ''; } };\n\n    const load = async (pw) => {\n      d2.setState({ status: 'loading', error: null });\n      try {\n        const r = await d2.rush('arezzo-stats').invoke({ password: pw });\n        const s = (r && r.data) ? r.data : r;\n        if (!s || s.ok !== true) {\n          try { localStorage.removeItem(PW_KEY); } catch (e) {}\n          d2.setState({ status: 'locked', pwSubmitting: false, pwError: pw ? 'Senha incorreta.' : null });\n          return;\n        }\n        try { localStorage.setItem(PW_KEY, pw); } catch (e) {}\n        let now = '';\n        try { now = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } catch (e) {}\n        d2.setState({ status: 'done', s, updatedAt: now, pwSubmitting: false, pwError: null });\n      } catch (err) {\n        d2.setState({ status: 'error', error: String((err && err.message) || err) });\n      }\n    };\n\n    d2.on('.refresh', 'click', () => load(getPw()));\n    d2.on('.gate-btn', 'click', () => {\n      const el = d2.$('.gate-input');\n      const pw = (el && el.value) || '';\n      if (!pw) { d2.setState({ pwError: 'Digite a senha.' }); return; }\n      d2.setState({ pwSubmitting: true, pwError: null });\n      load(pw);\n    });\n    d2.on('.gate-input', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.gate-btn'); if (b) b.click(); } });\n\n    const saved = getPw();\n    if (saved) { load(saved); } else { d2.setState({ status: 'locked' }); }\n  }\n}"}}}}}}}},{"_id":"6a3c2c8dbba9168ecfc12c2d","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"landing-data2-contato","package":"people-gabi-carmo","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@font-face{font-family:'Circular Air Light';src:url('https://www.data2.media/file/DWcXnLLnDWwlhRgYpsi1-') format('woff2'),url('https://www.data2.media/file/TDX5oH7NTx-vDAtRBcNeY') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Medium';src:url('https://www.data2.media/file/EByR5T2BNUoKCejgqK1JQ') format('woff2'),url('https://www.data2.media/file/tnIrecxS9FEqh9vh_Go-8') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Bold';src:url('https://www.data2.media/file/lALU-3wyCL2kksdactSyo') format('woff2'),url('https://www.data2.media/file/R3eunIkdQ9ors671taObT') format('woff');font-display:swap}\n*{box-sizing:border-box;margin:0;padding:0}\n.page{height:100%;overflow-y:auto;background:#20242e;color:#f5f6f8;font-family:'Circular Air Light','Circular Air Medium',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;-webkit-font-smoothing:antialiased;scroll-behavior:smooth}\n.wrap{max-width:1120px;margin:0 auto;padding:0 28px}\n.nav{position:sticky;top:0;z-index:30;background:rgba(32,36,46,.6);backdrop-filter:blur(14px);border-bottom:1px solid rgba(255,255,255,.07);overflow:hidden}\n.nav .wrap{display:flex;align-items:center;justify-content:space-between;height:70px}\n.brand{font-family:'Circular Air Light',sans-serif;font-size:28px;letter-spacing:.01em;color:#fff;display:inline-flex;align-items:flex-start}\n.brand sup{font-size:.55em;font-family:'Circular Air Bold',sans-serif;margin-left:1px;margin-top:.15em}\n.navlinks{display:flex;align-items:center;gap:30px}\n.navlinks a{color:rgba(255,255,255,.82);text-decoration:none;font-size:16px;cursor:pointer;transition:color .15s}\n.navlinks a:hover{color:#fff}\n.btn{cursor:pointer;border:0;border-radius:999px;font-family:'Circular Air Bold',sans-serif;font-size:15px;padding:13px 26px;transition:transform .15s,box-shadow .25s,opacity .2s;color:#fff}\n.btn:active{transform:translateY(1px)}\n.btn-grad{background:linear-gradient(90deg,#FA4747,#FA4783);box-shadow:0 8px 24px rgba(250,71,131,.32)}\n.btn-grad:hover{box-shadow:0 12px 34px rgba(250,71,131,.5)}\n.btn-glass{background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.35);backdrop-filter:blur(6px)}\n.btn-glass:hover{background:rgba(255,255,255,.2)}\n.nav .btn{padding:10px 20px;font-size:14px}\n@keyframes gflow{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}\n.hero{position:relative;min-height:92vh;display:flex;align-items:center;overflow:hidden;background:linear-gradient(125deg,#FA5A4E 0%,#FA4783 38%,#A14BF5 70%,#6E47FA 100%);background-size:220% 220%;animation:gflow 22s ease-in-out infinite}\n.hero::after{content:'';position:absolute;inset:0;background:radial-gradient(120% 90% at 80% 8%,rgba(255,255,255,.2),transparent 50%),linear-gradient(180deg,transparent 55%,rgba(32,36,46,.6) 100%);pointer-events:none}\n.hero .wrap{position:relative;z-index:2;text-align:center;width:100%;padding:70px 28px}\n.kicker{display:inline-block;font-family:'Circular Air Bold',sans-serif;font-size:13px;letter-spacing:.22em;text-transform:uppercase;color:#fff;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.3);border-radius:999px;padding:8px 18px;margin-bottom:30px}\nh1{font-family:'Circular Air Bold',sans-serif;font-size:clamp(40px,7vw,82px);line-height:1.03;letter-spacing:-.01em;text-transform:uppercase;color:#fff;max-width:1000px;margin:0 auto 26px;text-shadow:0 6px 30px rgba(0,0,0,.18)}\n.lede{font-size:clamp(17px,2vw,21px);color:rgba(255,255,255,.92);max-width:680px;margin:0 auto 38px;line-height:1.55}\n.cta{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}\n.proof{display:flex;gap:16px;justify-content:center;flex-wrap:wrap;margin-top:64px}\n.pcard{flex:1;min-width:180px;max-width:260px;background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.22);border-radius:18px;padding:24px 18px;backdrop-filter:blur(6px)}\n.pcard .num{font-family:'Circular Air Bold',sans-serif;font-size:34px;color:#fff}\n.pcard .cap{font-size:13.5px;color:rgba(255,255,255,.85);margin-top:8px;line-height:1.45}\nsection{padding:90px 0}\n.eyebrow{font-family:'Circular Air Bold',sans-serif;font-size:13px;letter-spacing:.2em;text-transform:uppercase;text-align:center;background:linear-gradient(90deg,#FA4783,#8347FA);-webkit-background-clip:text;background-clip:text;color:transparent}\n.h2{font-family:'Circular Air Bold',sans-serif;font-size:clamp(28px,4vw,46px);text-transform:uppercase;letter-spacing:-.01em;text-align:center;margin:14px auto 12px;max-width:760px;color:#fff}\n.sub{font-size:16.5px;color:rgba(255,255,255,.62);text-align:center;max-width:640px;margin:0 auto 52px;line-height:1.6}\n.grid3{display:grid;grid-template-columns:repeat(3,1fr);gap:22px}\n.pillar{background:#272c38;border:1px solid rgba(255,255,255,.07);border-radius:20px;padding:30px 26px;transition:transform .2s,border-color .2s,box-shadow .2s}\n.pillar:hover{transform:translateY(-4px);border-color:rgba(250,71,131,.4);box-shadow:0 18px 40px rgba(0,0,0,.25)}\n.dot{width:50px;height:50px;border-radius:14px;display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 8px 20px rgba(0,0,0,.25)}\n.dot svg{width:25px;height:25px;stroke:#fff;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}\n.pillar h3{font-family:'Circular Air Bold',sans-serif;font-size:20px;margin-bottom:10px;color:#fff}\n.pillar p{font-size:15px;color:rgba(255,255,255,.66);line-height:1.6}\n.case{position:relative;background:#272c38;border:1px solid rgba(255,255,255,.07);border-radius:20px;padding:0 28px 28px;display:flex;flex-direction:column;gap:14px;overflow:hidden;transition:transform .2s,box-shadow .2s}\n.case:hover{transform:translateY(-4px);box-shadow:0 18px 40px rgba(0,0,0,.25)}\n.case .bar{height:6px;margin:0 -28px 26px}\n.tag{align-self:flex-start;font-family:'Circular Air Bold',sans-serif;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:#fff;padding:6px 13px;border-radius:999px}\n.case h3{font-family:'Circular Air Bold',sans-serif;font-size:21px;line-height:1.22;color:#fff}\n.case p{font-size:14.5px;color:rgba(255,255,255,.66);line-height:1.6;flex:1}\n.metrics{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}\n.metric{font-size:12.5px;color:rgba(255,255,255,.9);background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.12);border-radius:9px;padding:6px 11px}\n.formsec{position:relative;background:linear-gradient(135deg,#6E47FA 0%,#A14BF5 30%,#FA4783 65%,#FA5A4E 100%);background-size:220% 220%;animation:gflow 24s ease-in-out infinite}\n.formsec::before{content:'';position:absolute;inset:0;background:radial-gradient(100% 80% at 20% 0%,rgba(255,255,255,.16),transparent 55%);pointer-events:none}\n.formsec .wrap{position:relative;z-index:2}\n.formsec .eyebrow{color:#fff;background:none;-webkit-text-fill-color:#fff}\n.formsec .h2{color:#fff}\n.formsec .sub{color:rgba(255,255,255,.9)}\n.formcard{max-width:640px;margin:0 auto;background:rgba(28,32,41,.94);border:1px solid rgba(255,255,255,.12);border-radius:24px;padding:38px;box-shadow:0 40px 90px rgba(0,0,0,.4)}\n.field{margin-bottom:17px}\n.field label{display:block;font-family:'Circular Air Medium',sans-serif;font-size:13.5px;color:#e7e8ec;margin-bottom:8px}\n.field input,.field textarea{width:100%;background:#2b303c;border:1px solid rgba(255,255,255,.13);border-radius:13px;padding:14px 15px;color:#f5f6f8;font-size:15px;font-family:inherit;outline:none;transition:border .15s,box-shadow .15s}\n.field input:focus,.field textarea:focus{border-color:#FA4783;box-shadow:0 0 0 3px rgba(250,71,131,.25)}\n.field textarea{min-height:116px;resize:vertical}\n.req{color:#FA4783}\n.note{font-size:12.5px;color:rgba(255,255,255,.5);margin:-6px 0 18px}\n.submit{width:100%;padding:16px;font-size:16px;margin-top:6px}\n.err{color:#ffb3b3;font-size:14px;margin-top:13px;text-align:center}\n.ok{text-align:center;padding:36px 10px}\n.ok .em{font-size:50px}\n.ok h3{font-family:'Circular Air Bold',sans-serif;font-size:26px;margin:16px 0 8px;color:#fff}\n.ok p{color:rgba(255,255,255,.7);font-size:15.5px;line-height:1.6;max-width:430px;margin:0 auto}\nfooter{background:#191c24;border-top:1px solid rgba(255,255,255,.06);padding:38px 0}\nfooter .wrap{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px}\nfooter .brand{font-size:22px}\nfooter a{color:#fff;text-decoration:none;font-size:15px;opacity:.85}\nfooter a:hover{opacity:1}\n.reveal{opacity:0;transform:translateY(24px);transition:opacity .7s cubic-bezier(.2,.7,.2,1),transform .7s cubic-bezier(.2,.7,.2,1)}\n.reveal.in{opacity:1;transform:none}\n@media(max-width:820px){.grid3{grid-template-columns:1fr}.navlinks .nlink{display:none}.hero{min-height:86vh}section{padding:60px 0}.formcard{padding:26px}}\n@media(prefers-reduced-motion:reduce){.hero,.formsec{animation:none}.reveal{transition:none;opacity:1;transform:none}}\n  `,\n  render(d2){\n    const st = d2.state.status || 'idle'\n    const f = d2.state.form || {}\n    const err = d2.state.error || ''\n    const rv = d2.state.revealed || {}\n    const esc = s => (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;')\n    const rc = (id,delay) => `class=\"reveal${rv[id]?' in':''}\" data-rev=\"${id}\"${delay?` style=\"transition-delay:${delay}ms\"`:''}`\n    const I = {\n      code:'<svg viewBox=\"0 0 24 24\"><polyline points=\"16 18 22 12 16 6\"/><polyline points=\"8 6 2 12 8 18\"/></svg>',\n      ai:'<svg viewBox=\"0 0 24 24\"><path d=\"M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z\"/><path d=\"M19 14l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7z\"/></svg>',\n      grow:'<svg viewBox=\"0 0 24 24\"><polyline points=\"3 17 9 11 13 15 21 7\"/><polyline points=\"15 7 21 7 21 13\"/></svg>'\n    }\n    const proof = [\n      {num:'~45%',cap:'de redução de custo em auditoria com IA'},\n      {num:'22%',cap:'dos novos profissionais de um setor captados via IA'},\n      {num:'1.000+',cap:'leads contatados por dia, sem ampliar o time'}\n    ]\n    const pillars = [\n      {ic:I.code,g:'linear-gradient(135deg,#4765FA,#8347FA)',h:'Software sob medida',p:'Sistemas e plataformas construídos para o seu processo — do MVP à operação em escala, com a sua cara.'},\n      {ic:I.ai,g:'linear-gradient(135deg,#FA4747,#FA4783)',h:'Soluções com IA',p:'Assistentes conversacionais, automação inteligente e visão computacional aplicados a problemas reais do negócio.'},\n      {ic:I.grow,g:'linear-gradient(135deg,#13C2A3,#47FAA1)',h:'Da ideia ao resultado',p:'Acompanhamos da concepção à métrica de impacto. O que importa é mover o ponteiro — custo, conversão, eficiência.'}\n    ]\n    const cases = [\n      {tag:'Logística',g:'linear-gradient(90deg,#4765FA,#8347FA)',h:'Auditoria inteligente de canhotos de entrega',p:'Pipeline automatizado com IA que valida imagem, assinatura, data e dados de cada comprovante de entrega — classificando válido ou inválido em larga escala.',m:['~45% de redução de custo','38 mil+ canhotos por ciclo','cobertura de 10% → 100%']},\n      {tag:'Financeiro',g:'linear-gradient(90deg,#FA4747,#FA4783)',h:'Assistente de IA conversacional para captação de leads',p:'Assistente no WhatsApp que engaja milhares de leads de forma natural, detecta os momentos-chave do funil e aciona o time comercial na hora certa.',m:['22% dos novos profissionais do setor','1.000+ leads/dia','1,9% de conversão ponta a ponta']},\n      {tag:'Varejo & Moda',g:'linear-gradient(90deg,#13C2A3,#47FAA1)',h:'Virtual try-on com IA',p:'Experimentação virtual de produtos: o cliente se vê usando o item a partir de uma única foto — mais confiança na compra e mais engajamento.',m:['Visão computacional','Experiência imersiva','Pronto para e-commerce']}\n    ]\n    const formInner = st === 'success'\n      ? `<div class=\"ok\"><div class=\"em\">🎉</div><h3>Contato recebido!</h3><p>Obrigada por escrever. Vou analisar sua mensagem e retornar em breve para agendarmos uma conversa.</p></div>`\n      : `<div class=\"field\"><label>Nome <span class=\"req\">*</span></label><input data-f=\"nome\" value=\"${esc(f.nome)}\" placeholder=\"Como podemos te chamar?\"/></div>\n         <div class=\"field\"><label>E-mail</label><input data-f=\"email\" type=\"email\" value=\"${esc(f.email)}\" placeholder=\"voce@empresa.com\"/></div>\n         <div class=\"field\"><label>Telefone / WhatsApp</label><input data-f=\"telefone\" value=\"${esc(f.telefone)}\" placeholder=\"(11) 90000-0000\"/></div>\n         <p class=\"note\">Informe ao menos um e-mail ou telefone para contato.</p>\n         <div class=\"field\"><label>O que você precisa?</label><textarea data-f=\"mensagem\" placeholder=\"Conte rapidamente o desafio, projeto ou ideia que você quer discutir.\">${esc(f.mensagem)}</textarea></div>\n         <button class=\"btn btn-grad submit\" data-submit ${st==='sending'?'disabled':''}>${st==='sending'?'Enviando…':'Enviar contato'}</button>\n         ${err?`<div class=\"err\">${esc(err)}</div>`:''}`\n    return `<div class=\"page\">\n      <nav class=\"nav\"><div class=\"wrap\">\n        <div class=\"brand\" style=\"font-family:'Circular Air Light',sans-serif\">Data<sup>2</sup></div>\n        <div class=\"navlinks\">\n          <a class=\"nlink\" data-scroll=\"fazemos\">O que fazemos</a>\n          <a class=\"nlink\" data-scroll=\"cases\">Cases</a>\n          <button class=\"btn btn-grad\" data-scroll=\"form\">Falar com a gente</button>\n        </div>\n      </div></nav>\n\n      <header class=\"hero\"><div class=\"wrap\">\n        <span class=\"kicker reveal${rv.hk?' in':''}\" data-rev=\"hk\">Software &amp; Inteligência Artificial</span>\n        <h1 ${rc('hh',90)}>Tecnologia e IA que viram resultado.</h1>\n        <p class=\"lede reveal${rv.hl?' in':''}\" data-rev=\"hl\" style=\"transition-delay:180ms\">Na Data² desenvolvemos software sob medida e soluções de IA que resolvem gargalos reais — da automação de processos a assistentes conversacionais e visão computacional.</p>\n        <div class=\"cta reveal${rv.hc?' in':''}\" data-rev=\"hc\" style=\"transition-delay:270ms\">\n          <button class=\"btn btn-glass\" data-scroll=\"form\">Quero conversar</button>\n          <button class=\"btn btn-glass\" data-scroll=\"cases\">Ver o que já entregamos</button>\n        </div>\n        <div class=\"proof\">\n          ${proof.map((p,i)=>`<div class=\"pcard reveal${rv['pr'+i]?' in':''}\" data-rev=\"pr${i}\" style=\"transition-delay:${360+i*100}ms\"><div class=\"num\">${p.num}</div><div class=\"cap\">${p.cap}</div></div>`).join('')}\n        </div>\n      </div></header>\n\n      <section id=\"fazemos\"><div class=\"wrap\">\n        <div class=\"eyebrow reveal${rv.fe?' in':''}\" data-rev=\"fe\">O que fazemos</div>\n        <div class=\"h2 reveal${rv.fh?' in':''}\" data-rev=\"fh\" style=\"transition-delay:60ms\">Tecnologia aplicada ao seu negócio</div>\n        <p class=\"sub reveal${rv.fs?' in':''}\" data-rev=\"fs\" style=\"transition-delay:120ms\">Unimos engenharia de software e inteligência artificial para construir soluções com impacto mensurável.</p>\n        <div class=\"grid3\">\n          ${pillars.map((p,i)=>`<div class=\"pillar reveal${rv['pi'+i]?' in':''}\" data-rev=\"pi${i}\" style=\"transition-delay:${i*110}ms\"><div class=\"dot\" style=\"background:${p.g}\">${p.ic}</div><h3>${p.h}</h3><p>${p.p}</p></div>`).join('')}\n        </div>\n      </div></section>\n\n      <section id=\"cases\"><div class=\"wrap\">\n        <div class=\"eyebrow reveal${rv.ce?' in':''}\" data-rev=\"ce\">Cases de sucesso</div>\n        <div class=\"h2 reveal${rv.ch?' in':''}\" data-rev=\"ch\" style=\"transition-delay:60ms\">Soluções que já estão em operação</div>\n        <p class=\"sub reveal${rv.cs?' in':''}\" data-rev=\"cs\" style=\"transition-delay:120ms\">Alguns dos projetos de IA e software que desenvolvemos e colocamos para rodar em escala.</p>\n        <div class=\"grid3\">\n          ${cases.map((c,i)=>`<div class=\"case reveal${rv['ci'+i]?' in':''}\" data-rev=\"ci${i}\" style=\"transition-delay:${i*110}ms\"><div class=\"bar\" style=\"background:${c.g}\"></div><span class=\"tag\" style=\"background:${c.g}\">${c.tag}</span><h3>${c.h}</h3><p>${c.p}</p><div class=\"metrics\">${c.m.map(x=>`<span class=\"metric\">${x}</span>`).join('')}</div></div>`).join('')}\n        </div>\n      </div></section>\n\n      <section class=\"formsec\" id=\"form\"><div class=\"wrap\">\n        <div class=\"eyebrow reveal${rv.xe?' in':''}\" data-rev=\"xe\">Vamos conversar</div>\n        <div class=\"h2 reveal${rv.xh?' in':''}\" data-rev=\"xh\" style=\"transition-delay:60ms\">Conte o que você precisa</div>\n        <p class=\"sub reveal${rv.xs?' in':''}\" data-rev=\"xs\" style=\"transition-delay:120ms\">Quer conhecer mais sobre a Data² ou discutir uma solução de software e IA para a sua empresa? Deixe seu contato — retorno em breve para agendar uma conversa.</p>\n        <div class=\"formcard reveal${rv.xc?' in':''}\" data-rev=\"xc\" style=\"transition-delay:160ms\">${formInner}</div>\n      </div></section>\n\n      <footer><div class=\"wrap\">\n        <div class=\"brand\" style=\"font-family:'Circular Air Light',sans-serif\">Data<sup style=\"font-size:.55em;font-family:'Circular Air Bold',sans-serif\">2</sup></div>\n        <a href=\"mailto:gabi.carmo@data2.app\">gabi.carmo@data2.app</a>\n      </div></footer>\n    </div>`\n  },\n  mounted(d2){\n    d2.on('[data-scroll]','click',(e,el)=>{ const t=d2.$('#'+el.dataset.scroll); if(t) t.scrollIntoView({behavior:'smooth',block:'start'}) })\n    const grab = sel => { const n=d2.$(sel); return n? (n.value||'').trim() : '' }\n    d2.on('[data-submit]','click',async ()=>{\n      const nome=grab('[data-f=nome]'), email=grab('[data-f=email]'), telefone=grab('[data-f=telefone]'), mensagem=grab('[data-f=mensagem]')\n      d2.state.form={nome,email,telefone,mensagem}\n      if(!nome){ d2.state.error='Por favor, informe seu nome.'; return }\n      if(!email && !telefone){ d2.state.error='Informe um e-mail ou telefone para contato.'; return }\n      d2.state.error=''; d2.state.status='sending'\n      try{\n        await d2.rush('contato-landing').invoke({nome,email,telefone,mensagem,origem:'site-linkedin'})\n        d2.state.status='success'\n      }catch(err){ d2.state.status='idle'; d2.state.error='Não foi possível enviar agora. Tente novamente em instantes.' }\n    })\n    // scroll reveal\n    const mark = () => { d2.state.revealed = Object.assign({}, d2.state.revealed) }\n    if(typeof IntersectionObserver !== 'undefined'){\n      const root = d2.$('.page')\n      const io = new IntersectionObserver((entries)=>{\n        let changed=false\n        entries.forEach(en=>{ if(en.isIntersecting){ const id=en.target.getAttribute('data-rev'); if(id && !(d2.state.revealed&&d2.state.revealed[id])){ d2.state.revealed=Object.assign({},d2.state.revealed,{[id]:true}); changed=true } io.unobserve(en.target) } })\n      }, {root, threshold:0.12, rootMargin:'0px 0px -8% 0px'})\n      d2.$$('[data-rev]').forEach(el=>io.observe(el))\n      d2.onUnmount(()=>io.disconnect())\n    } else {\n      const all={}; d2.$$('[data-rev]').forEach(el=>all[el.getAttribute('data-rev')]=true); d2.state.revealed=all\n    }\n  }\n}"}}}}}}}},{"_id":"6a3c2eb7da1785600d975472","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"painel-leads-data2","package":"people-gabi-carmo","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*{box-sizing:border-box;margin:0;padding:0}\n.page{height:100%;overflow-y:auto;background:#08080c;color:#f4f4f6;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Roboto,sans-serif;-webkit-font-smoothing:antialiased}\n.center{min-height:100%;display:flex;align-items:center;justify-content:center;padding:24px}\n.logincard{width:100%;max-width:400px;background:#101018;border:1px solid rgba(255,255,255,.09);border-radius:22px;padding:34px;box-shadow:0 30px 80px rgba(0,0,0,.45)}\n.brand{font-size:24px;font-weight:800;letter-spacing:-.02em;text-align:center;margin-bottom:6px}\n.brand .d{background:linear-gradient(90deg,#a78bfa,#7c4dff);-webkit-background-clip:text;background-clip:text;color:transparent}\n.loginsub{text-align:center;color:#9a9aa6;font-size:14px;margin-bottom:24px}\n.lbl{display:block;font-size:13.5px;font-weight:600;color:#cfcfd8;margin-bottom:7px}\n.inp{width:100%;background:#181822;border:1px solid rgba(255,255,255,.12);border-radius:12px;padding:13px 14px;color:#f4f4f6;font-size:15px;font-family:inherit;outline:none;transition:border .15s,box-shadow .15s}\n.inp:focus{border-color:#7c4dff;box-shadow:0 0 0 3px rgba(124,77,255,.2)}\n.btn{cursor:pointer;border:0;border-radius:12px;font-weight:600;font-size:15px;padding:13px 18px;transition:transform .15s,box-shadow .2s,background .2s}\n.btn:active{transform:translateY(1px)}\n.btn-primary{background:linear-gradient(90deg,#7c4dff,#9d6bff);color:#fff;box-shadow:0 8px 24px rgba(124,77,255,.35)}\n.btn-primary:hover{box-shadow:0 10px 30px rgba(124,77,255,.5)}\n.btn-ghost{background:rgba(255,255,255,.06);color:#f4f4f6;border:1px solid rgba(255,255,255,.14)}\n.btn-ghost:hover{background:rgba(255,255,255,.1)}\n.loginbtn{width:100%;margin-top:18px}\n.err{color:#ff8a8a;font-size:13.5px;margin-top:12px;text-align:center}\n.wrap{max-width:1000px;margin:0 auto;padding:28px 22px 60px}\n.top{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:14px;margin-bottom:8px}\n.title{font-size:24px;font-weight:800;letter-spacing:-.02em}\n.title .d{background:linear-gradient(90deg,#a78bfa,#7c4dff);-webkit-background-clip:text;background-clip:text;color:transparent}\n.count{color:#9a9aa6;font-size:14px;margin-top:2px}\n.actions{display:flex;gap:10px;flex-wrap:wrap}\n.btn-sm{padding:10px 15px;font-size:14px}\n.toolbar{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin:18px 0 18px}\n.search{flex:1;min-width:220px}\n.list{display:flex;flex-direction:column;gap:14px}\n.lead{background:linear-gradient(180deg,rgba(255,255,255,.045),rgba(255,255,255,.015));border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:20px 22px}\n.lhead{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;margin-bottom:10px}\n.lname{font-size:18px;font-weight:700}\n.ldate{font-size:12.5px;color:#8a8a96}\n.contacts{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:12px}\n.chip{font-size:13px;color:#d6d0f5;background:rgba(124,77,255,.12);border:1px solid rgba(124,77,255,.25);border-radius:10px;padding:7px 12px;text-decoration:none;display:inline-flex;align-items:center;gap:7px;transition:background .15s}\n.chip:hover{background:rgba(124,77,255,.22)}\n.msg{font-size:14.5px;color:#c4c4cf;line-height:1.6;white-space:pre-wrap;background:rgba(255,255,255,.02);border-left:3px solid rgba(124,77,255,.5);padding:10px 14px;border-radius:0 10px 10px 0}\n.tag{display:inline-block;margin-top:12px;font-size:11.5px;font-weight:600;color:#9a9aa6;background:rgba(255,255,255,.05);border-radius:999px;padding:4px 10px}\n.empty{text-align:center;color:#8a8a96;padding:60px 20px;font-size:15px}\n.spin{text-align:center;color:#9a9aa6;padding:50px;font-size:15px}\n@media(max-width:600px){.wrap{padding:20px 16px 50px}.lead{padding:16px}}\n  `,\n  render(d2){\n    const esc = s => (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;')\n    if(!d2.state.unlocked){\n      const err = d2.state.error || ''\n      const loading = d2.state.loading\n      return `<div class=\"page\"><div class=\"center\"><div class=\"logincard\">\n        <div class=\"brand\">Data<span class=\"d\">²</span></div>\n        <div class=\"loginsub\">Painel de contatos · acesso restrito</div>\n        <label class=\"lbl\">Senha de acesso</label>\n        <input class=\"inp\" data-pw type=\"password\" placeholder=\"Digite a senha\" ${loading?'disabled':''}/>\n        <button class=\"btn btn-primary loginbtn\" data-enter ${loading?'disabled':''}>${loading?'Entrando…':'Entrar'}</button>\n        ${err?`<div class=\"err\">${esc(err)}</div>`:''}\n      </div></div></div>`\n    }\n    const all = d2.state.leads || []\n    const q = (d2.state.q || '').toLowerCase().trim()\n    const leads = q ? all.filter(l => [l.nome,l.email,l.telefone,l.mensagem].some(v => (v||'').toLowerCase().includes(q))) : all\n    const fmtDate = v => { if(!v) return ''; try{ return d2.fmt.date(v) }catch(e){ return '' } }\n    const body = leads.length ? leads.map(l => {\n      const contacts = []\n      if(l.email) contacts.push(`<a class=\"chip\" href=\"mailto:${esc(l.email)}\">✉️ ${esc(l.email)}</a>`)\n      if(l.telefone){ const tel = String(l.telefone).replace(/[^0-9+]/g,''); contacts.push(`<a class=\"chip\" href=\"tel:${esc(tel)}\">📞 ${esc(l.telefone)}</a>`) }\n      return `<div class=\"lead\" data-key=\"${esc(l._id)}\">\n        <div class=\"lhead\"><div class=\"lname\">${esc(l.nome)||'(sem nome)'}</div><div class=\"ldate\">${esc(fmtDate(l.createdAt))}</div></div>\n        ${contacts.length?`<div class=\"contacts\">${contacts.join('')}</div>`:''}\n        ${l.mensagem?`<div class=\"msg\">${esc(l.mensagem)}</div>`:''}\n        ${l.origem?`<span class=\"tag\">origem: ${esc(l.origem)}</span>`:''}\n      </div>`\n    }).join('') : `<div class=\"empty\">${q?'Nenhum contato encontrado para essa busca.':'Ainda não há contatos. Eles aparecerão aqui assim que alguém enviar o formulário.'}</div>`\n    return `<div class=\"page\"><div class=\"wrap\">\n      <div class=\"top\">\n        <div><div class=\"title\">Data<span class=\"d\">²</span> · Contatos</div><div class=\"count\">${all.length} contato${all.length===1?'':'s'} no total</div></div>\n        <div class=\"actions\">\n          <button class=\"btn btn-ghost btn-sm\" data-refresh>Atualizar</button>\n          <button class=\"btn btn-ghost btn-sm\" data-export>Exportar CSV</button>\n          <button class=\"btn btn-ghost btn-sm\" data-logout>Sair</button>\n        </div>\n      </div>\n      <div class=\"toolbar\">\n        <input class=\"inp search\" data-q value=\"${esc(d2.state.q||'')}\" placeholder=\"Buscar por nome, e-mail, telefone ou mensagem…\"/>\n      </div>\n      <div class=\"list\">${body}</div>\n    </div></div>`\n  },\n  mounted(d2){\n    const KEY = 'd2_painel_pw'\n    const fetchLeads = async (pw) => {\n      d2.state.loading = true; d2.state.error = ''\n      try{\n        const res = await d2.rush('leads-list').invoke({ password: pw })\n        const payload = (res && res.data !== undefined) ? res.data : res\n        if(payload && payload.ok){\n          d2.state.leads = payload.leads || []\n          d2.state.unlocked = true\n          try{ sessionStorage.setItem(KEY, pw) }catch(e){}\n        } else {\n          d2.state.error = 'Senha incorreta. Tente novamente.'\n          try{ sessionStorage.removeItem(KEY) }catch(e){}\n        }\n      }catch(e){\n        d2.state.error = 'Não foi possível conectar. Tente novamente.'\n      }\n      d2.state.loading = false\n    }\n    // auto-unlock if a valid password is cached for this tab\n    let cached = null\n    try{ cached = sessionStorage.getItem(KEY) }catch(e){}\n    if(cached) fetchLeads(cached)\n\n    const submit = () => { const el = d2.$('[data-pw]'); const pw = el ? (el.value||'').trim() : ''; if(pw) fetchLeads(pw) }\n    d2.on('[data-enter]','click',submit)\n    d2.on('[data-pw]','keydown',(e)=>{ if(e.key==='Enter') submit() })\n    d2.on('[data-q]','input',(e,el)=>{ d2.state.q = el.value })\n    d2.on('[data-refresh]','click',()=>{ let pw=null; try{pw=sessionStorage.getItem(KEY)}catch(e){}; if(pw) fetchLeads(pw) })\n    d2.on('[data-logout]','click',()=>{ try{sessionStorage.removeItem(KEY)}catch(e){}; d2.state.unlocked=false; d2.state.leads=[]; d2.state.q=''; d2.state.error='' })\n    d2.on('[data-export]','click',()=>{\n      const rows = d2.state.leads || []\n      const head = ['Nome','E-mail','Telefone','Mensagem','Origem','Data']\n      const cell = v => '\"'+String(v==null?'':v).replace(/\"/g,'\"\"')+'\"'\n      const lines = [head.map(cell).join(',')]\n      rows.forEach(l => lines.push([l.nome,l.email,l.telefone,l.mensagem,l.origem,l.createdAt].map(cell).join(',')))\n      const csv = '﻿'+lines.join('\\r\\n')\n      const blob = new Blob([csv],{type:'text/csv;charset=utf-8'})\n      const url = URL.createObjectURL(blob)\n      const a = document.createElement('a'); a.href=url; a.download='contatos-data2.csv'; a.click()\n      setTimeout(()=>URL.revokeObjectURL(url),1500)\n    })\n  }\n}"}}}}}}}},{"_id":"6a3d5a52e44ca0e8495882dd","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"og-maker","package":"people-gabi-carmo","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@font-face{font-family:'Circular Air Light';src:url('https://www.data2.media/file/DWcXnLLnDWwlhRgYpsi1-') format('woff2');font-display:swap}\n@font-face{font-family:'Circular Air Bold';src:url('https://www.data2.media/file/lALU-3wyCL2kksdactSyo') format('woff2');font-display:swap}\n.box{padding:20px;color:#fff;font-family:sans-serif;background:#15171d;min-height:100%}\n#ogcv{width:640px;max-width:100%;height:auto;border:1px solid #333;border-radius:8px;display:block}\n#og-url{margin-top:14px;font-size:13px;word-break:break-all;color:#9fe}\n  `,\n  render(d2){\n    return `<div class=\"box\">\n      <canvas id=\"ogcv\" width=\"1200\" height=\"630\" data-d2-static></canvas>\n      <div id=\"og-url\">${d2.state.url ? ('OK: '+d2.state.url) : (d2.state.err ? ('ERRO: '+d2.state.err) : 'gerando imagem…')}</div>\n    </div>`\n  },\n  async mounted(d2){\n    try{\n      try{ await document.fonts.load(\"700 76px 'Circular Air Bold'\"); await document.fonts.load(\"400 64px 'Circular Air Light'\"); await document.fonts.ready }catch(e){}\n      const cv = d2.$('#ogcv'); const x = cv.getContext('2d'); const W=1200,H=630\n      const g = x.createLinearGradient(0,0,W,H)\n      g.addColorStop(0,'#FA5A4E'); g.addColorStop(.38,'#FA4783'); g.addColorStop(.7,'#A14BF5'); g.addColorStop(1,'#6E47FA')\n      x.fillStyle=g; x.fillRect(0,0,W,H)\n      const rg = x.createRadialGradient(W*0.82,H*0.1,40,W*0.82,H*0.1,W*0.7)\n      rg.addColorStop(0,'rgba(255,255,255,.22)'); rg.addColorStop(1,'rgba(255,255,255,0)')\n      x.fillStyle=rg; x.fillRect(0,0,W,H)\n      const vg=x.createLinearGradient(0,H*0.45,0,H); vg.addColorStop(0,'rgba(20,23,30,0)'); vg.addColorStop(1,'rgba(20,23,30,.42)')\n      x.fillStyle=vg; x.fillRect(0,0,W,H)\n      const L=86\n      x.textBaseline='alphabetic'; x.fillStyle='#fff'\n      // logo\n      x.font=\"400 60px 'Circular Air Light', Arial\"; x.fillText('Data',L,116)\n      const dw=x.measureText('Data').width\n      x.font=\"700 34px 'Circular Air Bold', Arial\"; x.fillText('2',L+dw+4,96)\n      // headline\n      x.font=\"700 82px 'Circular Air Bold', Arial\"\n      x.fillText('Software & IA', L, 300)\n      x.fillText('que viram resultado.', L, 392)\n      // sub\n      x.font=\"400 30px 'Circular Air Light', Arial\"; x.fillStyle='rgba(255,255,255,.94)'\n      x.fillText('Projetos de software e inteligência artificial com impacto real.', L, 462)\n      // chips\n      const chips=['~45% menos custo','22% dos novos profissionais','1.000+ leads/dia']\n      x.font=\"700 24px 'Circular Air Bold', Arial\"\n      let cx=L\n      const cy=520, ch=52\n      chips.forEach(t=>{ const tw=x.measureText(t).width; const pw=tw+44; x.fillStyle='rgba(255,255,255,.16)'; const r=26; x.beginPath(); x.moveTo(cx+r,cy); x.arcTo(cx+pw,cy,cx+pw,cy+ch,r); x.arcTo(cx+pw,cy+ch,cx,cy+ch,r); x.arcTo(cx,cy+ch,cx,cy,r); x.arcTo(cx,cy,cx+pw,cy,r); x.closePath(); x.fill(); x.fillStyle='#fff'; x.fillText(t,cx+22,cy+34); cx+=pw+16 })\n      // url bottom-right\n      x.font=\"700 26px 'Circular Air Bold', Arial\"; x.fillStyle='#fff'; x.textAlign='right'; x.fillText('data2.app', W-L, H-46); x.textAlign='left'\n      const blob = await new Promise(res=>cv.toBlob(res,'image/png',0.95))\n      const file = new File([blob], 'og-data2.png', {type:'image/png'})\n      const out = await d2.drive('media').uploadFromFiles([file])\n      const url = (out && out[0] && out[0].fileUrl) || ''\n      d2.log('OG_FILE_URL', url)\n      d2.state.url = url\n    }catch(e){ d2.state.err = String(e && e.message || e) }\n  }\n}"}}}}}}}},{"_id":"6a3ea7469936088eede46993","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"crmApp","package":"people-gabi-carmo","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 STAGES = [\n  { key: 'prospeccao', label: 'Prospecção', color: '#64748b' },\n  { key: 'mensagem',   label: 'Mensagem enviada', color: '#0ea5e9' },\n  { key: 'respondido', label: 'Respondeu', color: '#6366f1' },\n  { key: 'reuniao',    label: 'Reunião feita', color: '#8b5cf6' },\n  { key: 'fup',        label: 'Follow-up (7d)', color: '#f59e0b' },\n  { key: 'demo',       label: 'Demo', color: '#ec4899' },\n  { key: 'fechamento', label: 'Fechamento', color: '#14b8a6' },\n  { key: 'ganho',      label: 'Ganho', color: '#22c55e' },\n  { key: 'perdido',    label: 'Perdido', color: '#ef4444' },\n];\nconst STAGE_MAP = Object.fromEntries(STAGES.map(s => [s.key, s]));\nconst STATUS = [\n  { key: 'sem_resposta', label: 'Sem resposta' },\n  { key: 'positiva', label: 'Resposta positiva' },\n  { key: 'negativa', label: 'Resposta negativa' },\n];\n\nfunction esc(s) { return (s == null ? '' : String(s)).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;'); }\nfunction todayStr() { return new Date().toISOString().slice(0, 10); }\nfunction dval(s) { return s ? String(s).slice(0, 10) : ''; }\nfunction addDays(s, n) { const d = new Date(dval(s) + 'T00:00:00'); d.setDate(d.getDate() + n); return d.toISOString().slice(0, 10); }\nfunction diffDays(aStr, bStr) { const a = new Date(dval(aStr) + 'T00:00:00'); const b = new Date(dval(bStr) + 'T00:00:00'); return Math.round((a - b) / 86400000); }\nfunction fmtDate(s) { if (!s) return '—'; const d = new Date(dval(s) + 'T00:00:00'); return d.toLocaleDateString('pt-BR'); }\n\nfunction extractProspects(res) {\n  try {\n    let p = res;\n    if (p && p.data !== undefined && (p.result === undefined && !Array.isArray(p))) p = p.data;\n    let r = (p && p.result !== undefined) ? p.result : p;\n    if (typeof r === 'string') r = JSON.parse(r);\n    let list = r && r.prospects ? r.prospects : (Array.isArray(r) ? r : []);\n    return Array.isArray(list) ? list : [];\n  } catch (e) { return []; }\n}\n\nfunction kpis(d2, rows) {\n  const today = todayStr();\n  const total = rows.length;\n  const ativos = rows.filter(r => r.stage !== 'ganho' && r.stage !== 'perdido').length;\n  const fupDue = rows.filter(r => (r.stage === 'reuniao' || r.stage === 'fup') && r.dataFup && diffDays(r.dataFup, today) <= 0).length;\n  const ganhos = rows.filter(r => r.stage === 'ganho');\n  const valorGanho = ganhos.reduce((a, r) => a + (Number(r.valorDeal) || 0), 0);\n  const valorPipe = rows.filter(r => r.stage !== 'ganho' && r.stage !== 'perdido').reduce((a, r) => a + (Number(r.valorDeal) || 0), 0);\n  const card = (g, l, v, c) => `<div class=\"kpi\"><div class=\"kt\"><span class=\"kg\">${g}</span><span class=\"kl\">${l}</span></div><div class=\"kv\" style=\"color:${c}\">${v}</div></div>`;\n  return `<div class=\"kpis\">\n    ${card('👥', 'Total de prospects', total, '#0f172a')}\n    ${card('🔥', 'No pipeline', ativos, '#6366f1')}\n    ${card('⏰', 'Follow-ups a fazer', fupDue, fupDue > 0 ? '#ef4444' : '#16a34a')}\n    ${card('🏆', 'Ganhos', ganhos.length + ' · ' + d2.fmt.currency(valorGanho, 'BRL'), '#16a34a')}\n    ${card('💰', 'Valor no pipeline', d2.fmt.currency(valorPipe, 'BRL'), '#14b8a6')}\n  </div>`;\n}\n\nfunction header(d2, rows) {\n  return `<div class=\"hdr\">\n    <div class=\"brand\"><div class=\"logo\">d2</div><div><div class=\"btitle\">data2 · CRM de Prospects</div><div class=\"bsub\">Da prospecção ao fechamento</div></div></div>\n    <button class=\"logoutbtn\">Sair</button>\n  </div>${kpis(d2, rows)}`;\n}\n\nfunction tabsBar(d2) {\n  const tab = d2.state.tab || 'funil';\n  return `<div class=\"tabsrow\">\n    <div class=\"tabs\">\n      <button class=\"tab ${tab === 'funil' ? 'on' : ''}\" data-tab=\"funil\">📋 Funil</button>\n      <button class=\"tab ${tab === 'prospec' ? 'on' : ''}\" data-tab=\"prospec\">✨ Prospecção IA</button>\n    </div>\n    ${tab === 'funil' ? `<button class=\"newbtn\">+ Novo prospect</button>` : ''}\n  </div>`;\n}\n\nfunction cardHtml(d2, r) {\n  const today = todayStr();\n  let fup = '';\n  if ((r.stage === 'reuniao' || r.stage === 'fup') && r.dataFup) {\n    const d = diffDays(r.dataFup, today);\n    if (d < 0) fup = `<span class=\"badge red\">⏰ FUP atrasado ${-d}d</span>`;\n    else if (d === 0) fup = `<span class=\"badge red\">⏰ FUP hoje</span>`;\n    else fup = `<span class=\"badge amber\">⏰ FUP em ${d}d</span>`;\n  }\n  let st = '';\n  if (r.statusResposta === 'positiva') st = `<span class=\"badge green\">＋ Resposta</span>`;\n  else if (r.statusResposta === 'negativa') st = `<span class=\"badge gray\">－ Resposta</span>`;\n  const val = (Number(r.valorDeal) > 0) ? `<span class=\"badge teal\">${d2.fmt.compact(Number(r.valorDeal))}</span>` : '';\n  return `<div class=\"card\" draggable=\"true\" data-key=\"${r._id}\" data-id=\"${r._id}\">\n    <div class=\"cname\">${esc(r.nome)}</div>\n    <div class=\"cmeta\">${esc(r.empresa || '')}${r.cargo ? ' · ' + esc(r.cargo) : ''}</div>\n    <div class=\"cbadges\">${r.setor ? `<span class=\"tag\">${esc(r.setor)}</span>` : ''}${st}${fup}${val}</div>\n  </div>`;\n}\n\nfunction board(d2, rows) {\n  const cols = STAGES.map(s => {\n    const items = rows.filter(r => (r.stage || 'prospeccao') === s.key);\n    return `<div class=\"col\" data-lane=\"${s.key}\">\n      <div class=\"colhead\"><span class=\"dot\" style=\"background:${s.color}\"></span>${s.label}<span class=\"cnt\">${items.length}</span></div>\n      <div class=\"colbody\">${items.map(r => cardHtml(d2, r)).join('') || `<div class=\"empty\">—</div>`}</div>\n    </div>`;\n  }).join('');\n  return `<div class=\"board\">${cols}</div>`;\n}\n\nfunction prospec(d2) {\n  const icp = d2.state.icp || '';\n  const q = d2.state.query || '';\n  const searching = d2.state.searching;\n  const sugg = d2.state.suggestions || [];\n  const added = d2.state.added || {};\n  const err = d2.state.searchError;\n  const list = sugg.map((s, i) => `<div class=\"scard\">\n      <div class=\"srow\"><div class=\"sname\">${esc(s.nome)}</div><div class=\"scompany\">${esc(s.empresa || '')}</div></div>\n      <div class=\"scargo\">${esc(s.cargo || '')}${s.setor ? ' · ' + esc(s.setor) : ''}</div>\n      <div class=\"smotivo\">${esc(s.motivoAbordagem || '')}</div>\n      ${s.mensagemRascunho ? `<div class=\"smsg\">💬 ${esc(s.mensagemRascunho)}</div>` : ''}\n      <div class=\"sfoot\">\n        ${s.linkedinUrl ? `<a class=\"slink\" href=\"${esc(s.linkedinUrl)}\" target=\"_blank\">LinkedIn ↗</a>` : '<span></span>'}\n        <button class=\"addbtn ${added[i] ? 'done' : ''}\" data-idx=\"${i}\" ${added[i] ? 'disabled' : ''}>${added[i] ? '✓ Adicionado' : '+ Adicionar ao funil'}</button>\n      </div>\n    </div>`).join('');\n  return `<div class=\"prospec\">\n    <div class=\"pform\">\n      <div class=\"plabel\">Perfil de Cliente Ideal (ICP)</div>\n      <textarea class=\"icp\" placeholder=\"Ex.: Empresas de varejo de moda no Brasil, médio porte, que querem digitalizar a experiência de compra. Cargos: Head de Marketing, Diretor de E-commerce, CTO.\">${esc(icp)}</textarea>\n      <div class=\"plabel\">Termo de busca (opcional)</div>\n      <input class=\"query\" placeholder=\"Deixe vazio para usar o ICP\" value=\"${esc(q)}\"/>\n      <button class=\"searchbtn\" ${searching ? 'disabled' : ''}>${searching ? '⏳ Buscando na web + IA…' : '🔎 Buscar prospects com IA'}</button>\n      ${err ? `<div class=\"serr\">${esc(err)}</div>` : ''}\n      <div class=\"phint\">A IA busca empresas reais na web e sugere quem abordar, por quê, e um rascunho de mensagem.</div>\n    </div>\n    <div class=\"results\">${searching ? `<div class=\"loading\">Consultando a web e gerando sugestões…</div>` : (list || `<div class=\"loading\">Descreva o ICP e clique em buscar.</div>`)}</div>\n  </div>`;\n}\n\nfunction field(label, name, val, type) {\n  return `<label class=\"fl\"><span>${label}</span><input class=\"f\" data-field=\"${name}\" type=\"${type || 'text'}\" value=\"${esc(dval(val) && type === 'date' ? dval(val) : (val == null ? '' : val))}\"/></label>`;\n}\nfunction area(label, name, val) {\n  return `<label class=\"fl full\"><span>${label}</span><textarea class=\"f\" data-field=\"${name}\">${esc(val || '')}</textarea></label>`;\n}\nfunction select(label, name, val, opts) {\n  return `<label class=\"fl\"><span>${label}</span><select class=\"f\" data-field=\"${name}\">${opts.map(o => `<option value=\"${o.key}\" ${o.key === val ? 'selected' : ''}>${o.label}</option>`).join('')}</select></label>`;\n}\n\nfunction modal(d2) {\n  const f = d2.state.form || {};\n  const isNew = !f._id;\n  let fupLine = '';\n  if (f.dataReuniao) fupLine = `<div class=\"fuphint\">Follow-up sugerido (7d após reunião): <b>${fmtDate(addDays(f.dataReuniao, 7))}</b></div>`;\n  return `<div class=\"scrim\">\n    <div class=\"modal\">\n      <div class=\"mhead\"><div class=\"mtitle\">${isNew ? 'Novo prospect' : esc(f.nome || 'Prospect')}</div><button class=\"closebtn\">✕</button></div>\n      <div class=\"mbody\">\n        <div class=\"grid2\">\n          ${field('Nome', 'nome', f.nome)}\n          ${field('Empresa', 'empresa', f.empresa)}\n          ${field('Cargo', 'cargo', f.cargo)}\n          ${field('Setor', 'setor', f.setor)}\n          ${field('E-mail', 'email', f.email)}\n          ${field('LinkedIn', 'linkedinUrl', f.linkedinUrl)}\n          ${select('Etapa', 'stage', f.stage || 'prospeccao', STAGES)}\n          ${select('Status da resposta', 'statusResposta', f.statusResposta || 'sem_resposta', STATUS)}\n          ${field('Origem', 'origem', f.origem)}\n          ${field('Valor do deal (R$)', 'valorDeal', f.valorDeal, 'number')}\n          ${field('Data mensagem', 'dataMensagem', f.dataMensagem, 'date')}\n          ${field('Data reunião', 'dataReuniao', f.dataReuniao, 'date')}\n          ${field('Data follow-up', 'dataFup', f.dataFup, 'date')}\n          ${field('Data demo', 'dataDemo', f.dataDemo, 'date')}\n          ${field('Data fechamento', 'dataFechamento', f.dataFechamento, 'date')}\n        </div>\n        ${fupLine}\n        <button class=\"mtgnow\">📅 Marcar reunião feita hoje (agenda FUP +7d)</button>\n        ${area('Acordado pós-reunião', 'acordadoPosReuniao', f.acordadoPosReuniao)}\n        ${area('Motivo da abordagem', 'motivoAbordagem', f.motivoAbordagem)}\n        ${area('Rascunho de mensagem', 'mensagemRascunho', f.mensagemRascunho)}\n        ${area('Notas', 'notas', f.notas)}\n      </div>\n      <div class=\"mfoot\">\n        ${!isNew ? `<button class=\"delbtn\">🗑 Excluir</button>` : '<span></span>'}\n        <div class=\"mfr\"><button class=\"closebtn ghost\">Cancelar</button><button class=\"savebtn\" ${d2.state.saving ? 'disabled' : ''}>${d2.state.saving ? 'Salvando…' : 'Salvar'}</button></div>\n      </div>\n    </div>\n  </div>`;\n}\n\nexport default {\n  css: `\n  .app{position:absolute;inset:0;display:flex;flex-direction:column;background:#f1f5f9;font-family:Inter,system-ui,sans-serif;color:#0f172a;overflow:hidden}\n  .boot{padding:40px;font:15px sans-serif;color:#64748b}\n  .hdr{display:flex;align-items:center;justify-content:space-between;padding:16px 22px 0}\n  .brand{display:flex;align-items:center;gap:12px}\n  .logo{width:40px;height:40px;border-radius:11px;background:linear-gradient(135deg,#16c784,#0ea5e9);color:#fff;font-weight:800;display:flex;align-items:center;justify-content:center;font-size:17px}\n  .btitle{font-size:17px;font-weight:800}\n  .bsub{font-size:12px;color:#64748b}\n  .logoutbtn{background:#fff;border:1px solid #e2e8f0;border-radius:9px;padding:8px 14px;cursor:pointer;font-size:13px;color:#475569}\n  .logoutbtn:hover{background:#f8fafc}\n  .kpis{display:flex;gap:12px;padding:14px 22px 4px;flex-wrap:wrap}\n  .kpi{flex:1;min-width:150px;background:#fff;border:1px solid #e2e8f0;border-radius:14px;padding:13px 15px}\n  .kt{display:flex;align-items:center;gap:7px}\n  .kg{font-size:16px}.kl{font-size:11px;color:#64748b;text-transform:uppercase;letter-spacing:.03em;font-weight:600}\n  .kv{font-size:22px;font-weight:800;margin-top:4px}\n  .tabsrow{display:flex;align-items:center;justify-content:space-between;padding:10px 22px 6px}\n  .tabs{display:flex;gap:6px}\n  .tab{background:transparent;border:0;border-radius:9px;padding:9px 15px;cursor:pointer;font-size:14px;font-weight:600;color:#64748b}\n  .tab.on{background:#0f172a;color:#fff}\n  .newbtn{background:#16c784;color:#fff;border:0;border-radius:9px;padding:9px 15px;cursor:pointer;font-weight:700;font-size:13px}\n  .newbtn:hover{filter:brightness(.95)}\n  .board{flex:1;display:flex;gap:12px;overflow-x:auto;overflow-y:hidden;padding:8px 22px 22px;align-items:flex-start}\n  .col{flex:0 0 236px;width:236px;background:#e9eef5;border-radius:14px;display:flex;flex-direction:column;max-height:100%}\n  .colhead{display:flex;align-items:center;gap:7px;padding:12px 13px;font-size:12.5px;font-weight:700;color:#334155}\n  .dot{width:9px;height:9px;border-radius:50%}\n  .cnt{margin-left:auto;background:#cbd5e1;color:#334155;border-radius:20px;padding:1px 8px;font-size:11px}\n  .colbody{padding:0 9px 10px;overflow-y:auto;display:flex;flex-direction:column;gap:8px;min-height:40px}\n  .col[data-over]{outline:2px dashed #16c784;outline-offset:-2px}\n  .card{background:#fff;border:1px solid #e2e8f0;border-radius:11px;padding:11px;cursor:grab;box-shadow:0 1px 2px rgba(15,23,42,.04)}\n  .card:hover{border-color:#16c784}\n  .cname{font-weight:700;font-size:13.5px}\n  .cmeta{font-size:12px;color:#64748b;margin-top:2px}\n  .cbadges{display:flex;flex-wrap:wrap;gap:5px;margin-top:8px}\n  .tag{background:#eef2f7;color:#475569;border-radius:6px;padding:2px 7px;font-size:10.5px;font-weight:600}\n  .badge{border-radius:6px;padding:2px 7px;font-size:10.5px;font-weight:700}\n  .badge.red{background:#fee2e2;color:#b91c1c}.badge.amber{background:#fef3c7;color:#b45309}\n  .badge.green{background:#dcfce7;color:#15803d}.badge.gray{background:#e2e8f0;color:#475569}.badge.teal{background:#ccfbf1;color:#0f766e}\n  .empty{color:#94a3b8;font-size:12px;text-align:center;padding:8px}\n  .prospec{flex:1;display:flex;gap:18px;padding:10px 22px 22px;overflow:hidden}\n  .pform{flex:0 0 340px;display:flex;flex-direction:column;gap:8px;background:#fff;border:1px solid #e2e8f0;border-radius:14px;padding:16px;height:fit-content}\n  .plabel{font-size:12px;font-weight:700;color:#334155}\n  .icp{height:120px;resize:vertical}\n  .icp,.query{border:1px solid #cbd5e1;border-radius:9px;padding:10px;font:13px sans-serif;color:#0f172a;width:100%;box-sizing:border-box}\n  .searchbtn{background:linear-gradient(135deg,#16c784,#0ea5e9);color:#fff;border:0;border-radius:10px;padding:12px;cursor:pointer;font-weight:700;font-size:14px;margin-top:4px}\n  .searchbtn:disabled{opacity:.6;cursor:default}\n  .phint{font-size:11.5px;color:#94a3b8;margin-top:2px}\n  .serr{background:#fee2e2;color:#b91c1c;border-radius:8px;padding:8px;font-size:12px}\n  .results{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:12px;padding-right:4px}\n  .loading{color:#64748b;font-size:14px;padding:24px;text-align:center}\n  .scard{background:#fff;border:1px solid #e2e8f0;border-radius:13px;padding:14px}\n  .srow{display:flex;align-items:baseline;justify-content:space-between;gap:8px}\n  .sname{font-weight:800;font-size:15px}.scompany{font-size:13px;color:#16a34a;font-weight:700}\n  .scargo{font-size:12.5px;color:#64748b;margin-top:1px}\n  .smotivo{font-size:13px;margin-top:8px;line-height:1.45}\n  .smsg{font-size:12.5px;background:#f8fafc;border-left:3px solid #16c784;border-radius:0 8px 8px 0;padding:8px 10px;margin-top:8px;color:#334155}\n  .sfoot{display:flex;align-items:center;justify-content:space-between;margin-top:10px}\n  .slink{font-size:12px;color:#0ea5e9;text-decoration:none;font-weight:600}\n  .addbtn{background:#0f172a;color:#fff;border:0;border-radius:8px;padding:8px 13px;cursor:pointer;font-weight:700;font-size:12.5px}\n  .addbtn.done{background:#dcfce7;color:#15803d}\n  .scrim{position:fixed;inset:0;background:rgba(15,23,42,.5);display:flex;align-items:center;justify-content:center;z-index:1000;padding:24px}\n  .modal{background:#fff;border-radius:16px;width:min(720px,96vw);max-height:92vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 70px rgba(0,0,0,.35)}\n  .mhead{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #eef2f7}\n  .mtitle{font-size:17px;font-weight:800}\n  .closebtn{background:transparent;border:0;font-size:18px;cursor:pointer;color:#64748b}\n  .mbody{padding:18px 20px;overflow-y:auto;display:flex;flex-direction:column;gap:12px}\n  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px}\n  .fl{display:flex;flex-direction:column;gap:4px;font-size:11.5px;color:#475569;font-weight:600}\n  .fl.full{grid-column:1/-1}\n  .fl input,.fl select,.fl textarea{border:1px solid #cbd5e1;border-radius:8px;padding:8px 9px;font:13px sans-serif;color:#0f172a}\n  .fl textarea{min-height:60px;resize:vertical}\n  .fuphint{font-size:12px;background:#fef3c7;color:#92400e;border-radius:8px;padding:8px 10px}\n  .mtgnow{background:#ede9fe;color:#6d28d9;border:0;border-radius:9px;padding:10px;cursor:pointer;font-weight:700;font-size:13px}\n  .mfoot{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-top:1px solid #eef2f7}\n  .mfr{display:flex;gap:8px}\n  .delbtn{background:#fff;border:1px solid #fecaca;color:#b91c1c;border-radius:9px;padding:9px 14px;cursor:pointer;font-weight:600;font-size:13px}\n  .savebtn{background:#16c784;color:#fff;border:0;border-radius:9px;padding:9px 20px;cursor:pointer;font-weight:700;font-size:14px}\n  .ghost{background:#f1f5f9;border:0;border-radius:9px;padding:9px 14px;color:#475569;font-size:13px;font-weight:600}\n  `,\n  render(d2) {\n    const ds = d2.data();\n    if (!ds) return `<div class=\"boot\">Conectando à base de dados…</div>`;\n    if (ds.loading) return `<div class=\"boot\">Carregando prospects…</div>`;\n    const rows = ds.records() || [];\n    const tab = d2.state.tab || 'funil';\n    return `<div class=\"app\">\n      ${header(d2, rows)}\n      ${tabsBar(d2)}\n      ${tab === 'funil' ? board(d2, rows) : prospec(d2)}\n      ${d2.state.modalOpen ? modal(d2) : ''}\n    </div>`;\n  },\n  mounted(d2) {\n    const openModal = (rec) => { d2.state.form = rec ? { ...rec } : {}; d2.state.saving = false; d2.state.modalOpen = true; };\n\n    d2.on('.tab', 'click', (e, el) => { d2.state.tab = el.dataset.tab; });\n    d2.on('.logoutbtn', 'click', async () => { try { await d2.auth('crmAuth').logout(); } catch (e) {} d2.router('MainRouter').goTo('/login'); });\n    d2.on('.newbtn', 'click', () => openModal(null));\n\n    d2.on('.card', 'click', (e, el) => {\n      const rec = (d2.data().records() || []).find(r => r._id === el.dataset.id);\n      if (rec) openModal(rec);\n    });\n    d2.on('.card', 'dragstart', (e, el) => { e.dataTransfer.setData('text/plain', el.dataset.id); });\n    d2.on('.col', 'dragover', (e, el) => { e.preventDefault(); el.setAttribute('data-over', ''); });\n    d2.on('.col', 'dragleave', (e, el) => el.removeAttribute('data-over'));\n    d2.on('.col', 'drop', async (e, el) => {\n      e.preventDefault(); el.removeAttribute('data-over');\n      const id = e.dataTransfer.getData('text/plain');\n      const lane = el.dataset.lane;\n      const rec = (d2.data().records() || []).find(r => r._id === id);\n      if (!rec || (rec.stage || 'prospeccao') === lane) return;\n      const upd = { ...rec, stage: lane };\n      const today = todayStr();\n      if (lane === 'reuniao' && !rec.dataReuniao) { upd.dataReuniao = today; upd.dataFup = addDays(today, 7); }\n      if (lane === 'fup' && rec.dataReuniao && !rec.dataFup) { upd.dataFup = addDays(rec.dataReuniao, 7); }\n      if (lane === 'ganho' && !rec.dataFechamento) { upd.dataFechamento = today; }\n      await d2.data().update(upd);\n    });\n\n    // Prospecção IA\n    d2.on('.icp', 'input', (e, el) => { d2.state.icp = el.value; });\n    d2.on('.query', 'input', (e, el) => { d2.state.query = el.value; });\n    d2.on('.searchbtn', 'click', async () => {\n      const icp = (d2.state.icp || '').trim();\n      if (!icp) { d2.state.searchError = 'Descreva o perfil de cliente ideal primeiro.'; return; }\n      d2.state.searchError = ''; d2.state.searching = true; d2.state.suggestions = []; d2.state.added = {};\n      try {\n        const resp = await fetch('https://people-gabi-carmo.data2apis.com/rush/prospect-search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ icp, query: (d2.state.query || '').trim() }) });\n        if (!resp.ok) throw new Error('HTTP ' + resp.status);\n        const res = await resp.json();\n        const list = extractProspects(res);\n        d2.state.suggestions = list;\n        if (!list.length) d2.state.searchError = 'Nenhuma sugestão retornada. Tente refinar o ICP.';\n      } catch (err) {\n        d2.state.searchError = 'Erro ao buscar: ' + (err && err.message ? err.message : err);\n      } finally {\n        d2.state.searching = false;\n      }\n    });\n    d2.on('.addbtn', 'click', async (e, el) => {\n      const i = Number(el.dataset.idx);\n      const s = (d2.state.suggestions || [])[i];\n      if (!s) return;\n      await d2.data().create({\n        nome: s.nome, empresa: s.empresa, cargo: s.cargo, setor: s.setor,\n        linkedinUrl: s.linkedinUrl, motivoAbordagem: s.motivoAbordagem, mensagemRascunho: s.mensagemRascunho,\n        stage: 'prospeccao', statusResposta: 'sem_resposta', origem: 'IA', createdAt: todayStr()\n      });\n      d2.state.added = { ...(d2.state.added || {}), [i]: true };\n    });\n\n    // Modal form\n    d2.on('.f', 'input', (e, el) => { d2.state.form = { ...(d2.state.form || {}), [el.dataset.field]: el.value }; });\n    d2.on('.f', 'change', (e, el) => { d2.state.form = { ...(d2.state.form || {}), [el.dataset.field]: el.value }; });\n    d2.on('.mtgnow', 'click', () => {\n      const t = todayStr();\n      d2.state.form = { ...(d2.state.form || {}), stage: 'reuniao', dataReuniao: t, dataFup: addDays(t, 7) };\n    });\n    d2.on('.closebtn', 'click', () => { d2.state.modalOpen = false; });\n    d2.on('.scrim', 'click', (e, el) => { if (e.target === el) d2.state.modalOpen = false; });\n    d2.on('.savebtn', 'click', async () => {\n      const f = { ...(d2.state.form || {}) };\n      if (!f.nome || !String(f.nome).trim()) { await d2.dialog.alert('Informe ao menos o nome.'); return; }\n      if (f.valorDeal === '' || f.valorDeal == null) delete f.valorDeal; else f.valorDeal = Number(f.valorDeal) || 0;\n      if ((f.stage === 'reuniao' || f.stage === 'fup') && f.dataReuniao && !f.dataFup) f.dataFup = addDays(f.dataReuniao, 7);\n      d2.state.saving = true;\n      try {\n        if (f._id) {\n          const rec = (d2.data().records() || []).find(r => r._id === f._id) || {};\n          await d2.data().update({ ...rec, ...f });\n        } else {\n          f.stage = f.stage || 'prospeccao';\n          f.statusResposta = f.statusResposta || 'sem_resposta';\n          f.origem = f.origem || 'Manual';\n          f.createdAt = todayStr();\n          await d2.data().create(f);\n        }\n        d2.state.modalOpen = false; d2.state.form = {};\n      } catch (err) {\n        await d2.dialog.alert('Erro ao salvar: ' + (err && err.message ? err.message : err));\n      } finally {\n        d2.state.saving = false;\n      }\n    });\n    d2.on('.delbtn', 'click', async () => {\n      const f = d2.state.form || {};\n      if (!f._id) return;\n      const ok = await d2.dialog.confirm('Excluir este prospect?');\n      if (!ok) return;\n      await d2.data().remove(f._id);\n      d2.state.modalOpen = false; d2.state.form = {};\n    });\n  },\n};"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6a3ea760b36fbedbb243ad2b","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"loginScreen","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n  .wrap{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#0f172a,#1e293b);font-family:Inter,system-ui,sans-serif;padding:20px}\n  .card{width:min(400px,94vw);background:#fff;border-radius:18px;padding:32px;box-shadow:0 24px 70px rgba(0,0,0,.4)}\n  .logo{width:52px;height:52px;border-radius:14px;background:linear-gradient(135deg,#16c784,#0ea5e9);color:#fff;font-weight:800;display:flex;align-items:center;justify-content:center;font-size:22px;margin-bottom:16px}\n  .h{font-size:21px;font-weight:800;color:#0f172a}\n  .sub{font-size:13px;color:#64748b;margin:4px 0 20px}\n  .lbl{font-size:12px;font-weight:700;color:#334155;margin-bottom:5px;display:block}\n  .in{width:100%;box-sizing:border-box;border:1px solid #cbd5e1;border-radius:10px;padding:12px;font:14px sans-serif;color:#0f172a;margin-bottom:14px}\n  .in:focus{outline:2px solid #16c784;border-color:#16c784}\n  .btn{width:100%;background:linear-gradient(135deg,#16c784,#0ea5e9);color:#fff;border:0;border-radius:10px;padding:13px;font-weight:700;font-size:15px;cursor:pointer}\n  .btn:disabled{opacity:.6;cursor:default}\n  .err{background:#fee2e2;color:#b91c1c;border-radius:9px;padding:10px;font-size:13px;margin-bottom:14px}\n  .toggle{text-align:center;font-size:13px;color:#64748b;margin-top:16px}\n  .toggle a{color:#0ea5e9;font-weight:700;cursor:pointer}\n  `,\n  render(d2) {\n    const mode = d2.state.mode || 'login';\n    const busy = d2.state.busy;\n    const err = d2.state.error;\n    return `<div class=\"wrap\"><div class=\"card\">\n      <div class=\"logo\">d2</div>\n      <div class=\"h\">${mode === 'login' ? 'Entrar no CRM' : 'Criar sua conta'}</div>\n      <div class=\"sub\">CRM de Prospects da data2 — acesso restrito.</div>\n      ${err ? `<div class=\"err\">${err.replace(/</g, '&lt;')}</div>` : ''}\n      <label class=\"lbl\">E-mail</label>\n      <input class=\"in email\" type=\"email\" placeholder=\"voce@data2.app\" autocomplete=\"username\"/>\n      <label class=\"lbl\">Senha</label>\n      <input class=\"in pass\" type=\"password\" placeholder=\"••••••••\" autocomplete=\"${mode === 'login' ? 'current-password' : 'new-password'}\"/>\n      <button class=\"btn submit\" ${busy ? 'disabled' : ''}>${busy ? 'Aguarde…' : (mode === 'login' ? 'Entrar' : 'Criar conta e entrar')}</button>\n      <div class=\"toggle\">${mode === 'login' ? 'Primeiro acesso? <a class=\"sw\">Criar conta</a>' : 'Já tem conta? <a class=\"sw\">Entrar</a>'}</div>\n    </div></div>`;\n  },\n  mounted(d2) {\n    const submit = async () => {\n      const email = (d2.$('.email')?.value || '').trim();\n      const pass = d2.$('.pass')?.value || '';\n      if (!email || !pass) { d2.state.error = 'Preencha e-mail e senha.'; return; }\n      d2.state.error = ''; d2.state.busy = true;\n      const auth = d2.auth('crmAuth');\n      try {\n        if ((d2.state.mode || 'login') === 'register') {\n          await auth.register({ email, password: pass });\n          await auth.login({ email, password: pass });\n        } else {\n          await auth.login({ email, password: pass });\n        }\n        d2.router('MainRouter').goTo('/');\n      } catch (err) {\n        const msg = (err && err.message ? err.message : String(err)) || 'Falha na autenticação.';\n        d2.state.error = /invalid|credential|password|not found|unauthor/i.test(msg)\n          ? 'E-mail ou senha incorretos.' : msg;\n      } finally {\n        d2.state.busy = false;\n      }\n    };\n    d2.on('.submit', 'click', submit);\n    d2.on('.in', 'keydown', (e) => { if (e.key === 'Enter') submit(); });\n    d2.on('.sw', 'click', () => { d2.state.mode = (d2.state.mode || 'login') === 'login' ? 'register' : 'login'; d2.state.error = ''; });\n  },\n};"}}}}}}}},{"_id":"6a427ac5e75b1a01707b2c16","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"ownerApp","package":"people-gabi-carmo","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 RUSH='https://people-gabi-carmo.data2apis.com/rush';\nconst EMP_BASE='https://tarefa.people-gabi-carmo.next.data2.app';\nasync function api(ref,body){\n  const r=await fetch(RUSH+'/'+ref,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body||{})});\n  let j={}; try{ j=await r.json(); }catch(e){}\n  return (j && j.data!==undefined) ? j.data : j;\n}\nconst esc=(x)=>(x==null?'':String(x)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;');\nconst digits=(x)=>(x==null?'':String(x)).replace(/\\D/g,'');\nconst PRIO={alta:{l:'Alta',c:'#c62828',b:'#ffebee'},media:{l:'Média',c:'#e65100',b:'#fff3e0'},baixa:{l:'Baixa',c:'#2e7d32',b:'#e8f5e9'}};\nconst STAT={pendente:{l:'Pendente',c:'#546e7a',b:'#eceff1'},parcial:{l:'Parcial',c:'#e65100',b:'#fff3e0'},concluida:{l:'Concluída',c:'#2e7d32',b:'#e8f5e9'}};\nconst fmtDate=(d)=>{ if(!d) return '—'; try{ return new Date(d).toLocaleDateString('pt-BR'); }catch(e){ return '—'; } };\nconst newToken=()=>{ try{ if(window.crypto&&crypto.randomUUID) return crypto.randomUUID(); }catch(e){} return 't'+Date.now()+Math.round(Math.random()*1e6); };\n\nexport default {\n  css:`\n.app{display:flex;flex-direction:column;height:100%;background:#eef3ec;color:#1a2e1a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;box-sizing:border-box}\n.top{display:flex;align-items:center;gap:12px;padding:16px 22px;background:#2e7d32;color:#fff}\n.top h1{font-size:19px;margin:0;font-weight:800;flex:1}\n.top .sub{font-size:12px;opacity:.85}\n.tabs{display:flex;gap:6px;padding:12px 22px 0;background:#2e7d32}\n.tab{padding:9px 18px;border-radius:10px 10px 0 0;cursor:pointer;font-size:14px;font-weight:700;color:#dcedc8;background:transparent;border:none}\n.tab[data-on]{background:#eef3ec;color:#2e7d32}\n.body{flex:1;overflow:auto;padding:18px 22px}\n.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:16px;flex-wrap:wrap}\n.btn{border:none;border-radius:9px;padding:10px 15px;font-size:14px;font-weight:700;cursor:pointer}\n.btn-primary{background:#2e7d32;color:#fff}\n.btn-ghost{background:#fff;color:#2e7d32;border:1px solid #cfe0cf}\n.btn-wa{background:#25D366;color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:6px}\n.btn-sm{padding:7px 11px;font-size:13px;border-radius:8px}\n.filters{display:flex;gap:6px;margin-left:auto;flex-wrap:wrap}\n.chip{padding:7px 13px;border-radius:999px;font-size:13px;cursor:pointer;background:#fff;border:1px solid #cfe0cf;color:#557;font-weight:600}\n.chip[data-on]{background:#2e7d32;color:#fff;border-color:#2e7d32}\n.card{background:#fff;border-radius:14px;padding:16px 18px;margin-bottom:12px;box-shadow:0 1px 4px rgba(0,0,0,.07)}\n.card-head{display:flex;align-items:flex-start;gap:10px}\n.card-title{font-size:16px;font-weight:800;flex:1;margin:0}\n.badges{display:flex;gap:6px;flex-wrap:wrap;margin:8px 0}\n.badge{padding:4px 11px;border-radius:999px;font-size:12px;font-weight:700;white-space:nowrap}\n.meta{font-size:13px;color:#555;margin:5px 0;display:flex;gap:16px;flex-wrap:wrap}\n.meta b{color:#333}\n.desc{font-size:14px;color:#444;margin:8px 0;white-space:pre-wrap;line-height:1.45}\n.actions{display:flex;gap:8px;margin-top:13px;flex-wrap:wrap}\n.empty{text-align:center;color:#789;padding:48px 20px;font-size:15px}\n.scrim{position:fixed;inset:0;background:rgba(0,0,0,.45);display:flex;align-items:center;justify-content:center;padding:20px;z-index:50}\n.modal{background:#fff;border-radius:16px;padding:22px;width:min(540px,95vw);max-height:92vh;overflow:auto}\n.modal h2{margin:0 0 16px;font-size:18px;color:#2e7d32}\n.field{margin-bottom:13px;display:flex;flex-direction:column;gap:5px}\n.field label{font-size:13px;font-weight:700;color:#445}\n.field input,.field select,.field textarea{padding:10px 11px;border:1px solid #cfd8cf;border-radius:9px;font-size:14px;font-family:inherit;background:#fff;color:#1a2e1a}\n.field textarea{min-height:76px;resize:vertical}\n.row2{display:flex;gap:10px}\n.row2>.field{flex:1}\n.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:6px}\n.muted{color:#8a9a8a;font-size:13px;align-self:center}\n`,\n  render(d2){\n    const s=d2.state;\n    if(s.loading){ return `<div class=\"app\"><div class=\"top\"><h1>🌱 Gestão da Fazenda</h1></div><div class=\"empty\">Carregando…</div></div>`; }\n    const tab=s.tab||'tarefas';\n    const tarefas=s.tarefas||[];\n    const funcionarios=s.funcionarios||[];\n    const filter=s.filter||'todas';\n\n    const filtered=tarefas.filter(t=> filter==='todas'?true:((t.status||'pendente')===filter));\n    const taskCards = filtered.length ? filtered.map(t=>{\n      const p=PRIO[t.prioridade]||PRIO.media;\n      const st=STAT[t.status||'pendente']||STAT.pendente;\n      const ph=digits(t.funcionarioTelefone);\n      const link=EMP_BASE+'/?t='+encodeURIComponent(t.token||'');\n      const msg='Olá '+(t.funcionarioNome||'')+'! Você tem uma nova tarefa: \"'+(t.titulo||'')+'\". Prazo: '+fmtDate(t.prazo)+'. Abra, confirme e marque o andamento aqui: '+link;\n      const wa = ph ? 'https://wa.me/'+ph+'?text='+encodeURIComponent(msg) : '';\n      return `<div class=\"card\" data-key=\"${t._id}\">\n        <div class=\"card-head\">\n          <h3 class=\"card-title\">${esc(t.titulo)}</h3>\n          <span class=\"badge\" style=\"color:${st.c};background:${st.b}\">${st.l}</span>\n        </div>\n        <div class=\"badges\"><span class=\"badge\" style=\"color:${p.c};background:${p.b}\">⚑ ${p.l}</span></div>\n        <div class=\"meta\"><span>📅 <b>${fmtDate(t.prazo)}</b></span>${t.local?`<span>📍 ${esc(t.local)}</span>`:''}${t.funcionarioNome?`<span>👤 ${esc(t.funcionarioNome)}</span>`:'<span class=\"muted\">sem responsável</span>'}</div>\n        ${t.descricao?`<div class=\"desc\">${esc(t.descricao)}</div>`:''}\n        ${(t.status==='parcial'&&t.motivoParcial)?`<div class=\"meta\" style=\"color:#e65100\">⚠️ Motivo do parcial: ${esc(t.motivoParcial)}</div>`:''}\n        <div class=\"actions\">\n          ${wa?`<a class=\"btn btn-sm btn-wa\" href=\"${wa}\" target=\"_blank\" rel=\"noopener\">📲 Enviar por WhatsApp</a>`:`<span class=\"muted\">cadastre o telefone do funcionário</span>`}\n          <button class=\"btn btn-sm btn-ghost\" data-act=\"copy\" data-link=\"${esc(link)}\">🔗 Copiar link</button>\n          <button class=\"btn btn-sm btn-ghost\" data-act=\"edit\" data-id=\"${t._id}\">✏️ Editar</button>\n          <button class=\"btn btn-sm btn-ghost\" data-act=\"del\" data-id=\"${t._id}\">🗑️ Excluir</button>\n        </div>\n      </div>`;\n    }).join('') : `<div class=\"empty\">Nenhuma tarefa ${filter!=='todas'?'com esse status.':'cadastrada ainda. Clique em “+ Nova tarefa”.'}</div>`;\n\n    const funcCards = funcionarios.length ? funcionarios.map(f=>`<div class=\"card\" data-key=\"${f._id}\">\n        <div class=\"card-head\"><h3 class=\"card-title\">${esc(f.nome)}</h3></div>\n        <div class=\"meta\">${f.funcao?`<span>🛠️ ${esc(f.funcao)}</span>`:''}<span>📞 ${esc(f.telefone||'—')}</span></div>\n      </div>`).join('') : `<div class=\"empty\">Nenhum funcionário cadastrado. Clique em “+ Novo funcionário”.</div>`;\n\n    let bodyHtml='';\n    if(tab==='tarefas'){\n      bodyHtml=`<div class=\"toolbar\">\n          <button class=\"btn btn-primary btn-new-task\">+ Nova tarefa</button>\n          <div class=\"filters\">${['todas','pendente','parcial','concluida'].map(fk=>`<span class=\"chip\" data-filter=\"${fk}\" ${filter===fk?'data-on':''}>${fk==='todas'?'Todas':STAT[fk].l}</span>`).join('')}</div>\n        </div>${taskCards}`;\n    } else {\n      bodyHtml=`<div class=\"toolbar\"><button class=\"btn btn-primary btn-new-func\">+ Novo funcionário</button></div>${funcCards}`;\n    }\n\n    let modalHtml='';\n    if(s.showForm){\n      const e=s.editing||{};\n      const opts=`<option value=\"\">— selecione —</option>`+funcionarios.map(f=>`<option value=\"${f._id}\" ${e.funcionarioId===f._id?'selected':''}>${esc(f.nome)}</option>`).join('');\n      const pr=e.prioridade||'media';\n      modalHtml=`<div class=\"scrim\"><div class=\"modal\">\n        <h2>${e._id?'Editar tarefa':'Nova tarefa'}</h2>\n        <div class=\"field\"><label>Título *</label><input data-f=\"titulo\" value=\"${esc(e.titulo||'')}\" placeholder=\"Ex.: Irrigar a horta\"/></div>\n        <div class=\"field\"><label>Informações detalhadas</label><textarea data-f=\"descricao\" placeholder=\"Descreva o que precisa ser feito...\">${esc(e.descricao||'')}</textarea></div>\n        <div class=\"row2\">\n          <div class=\"field\"><label>Prioridade</label><select data-f=\"prioridade\">\n            <option value=\"alta\" ${pr==='alta'?'selected':''}>Alta</option>\n            <option value=\"media\" ${pr==='media'?'selected':''}>Média</option>\n            <option value=\"baixa\" ${pr==='baixa'?'selected':''}>Baixa</option>\n          </select></div>\n          <div class=\"field\"><label>Prazo</label><input type=\"date\" data-f=\"prazo\" value=\"${e.prazo?String(e.prazo).slice(0,10):''}\"/></div>\n        </div>\n        <div class=\"row2\">\n          <div class=\"field\"><label>Local</label><input data-f=\"local\" value=\"${esc(e.local||'')}\" placeholder=\"Ex.: Setor 3 / Curral\"/></div>\n          <div class=\"field\"><label>Responsável</label><select data-f=\"funcionario\">${opts}</select></div>\n        </div>\n        <div class=\"modal-actions\">\n          <button class=\"btn btn-ghost task-cancel\">Cancelar</button>\n          <button class=\"btn btn-primary task-save\">${s.saving?'Salvando…':'Salvar tarefa'}</button>\n        </div>\n      </div></div>`;\n    }\n    if(s.showFunc){\n      modalHtml+=`<div class=\"scrim\"><div class=\"modal\">\n        <h2>Novo funcionário</h2>\n        <div class=\"field\"><label>Nome *</label><input data-ff=\"nome\" placeholder=\"Nome do funcionário\"/></div>\n        <div class=\"field\"><label>WhatsApp (com DDD)</label><input data-ff=\"telefone\" placeholder=\"Ex.: 11988887777\"/></div>\n        <div class=\"field\"><label>Função</label><input data-ff=\"funcao\" placeholder=\"Ex.: Tratorista\"/></div>\n        <div class=\"modal-actions\">\n          <button class=\"btn btn-ghost func-cancel\">Cancelar</button>\n          <button class=\"btn btn-primary func-save\">Salvar</button>\n        </div>\n      </div></div>`;\n    }\n\n    return `<div class=\"app\">\n      <div class=\"top\"><div><h1>🌱 Gestão da Fazenda</h1><div class=\"sub\">Atividades e tarefas da equipe</div></div></div>\n      <div class=\"tabs\">\n        <button class=\"tab\" data-tab=\"tarefas\" ${tab==='tarefas'?'data-on':''}>Tarefas</button>\n        <button class=\"tab\" data-tab=\"funcionarios\" ${tab==='funcionarios'?'data-on':''}>Funcionários</button>\n      </div>\n      <div class=\"body\">${bodyHtml}</div>\n      ${modalHtml}\n    </div>`;\n  },\n  mounted(d2){\n    const load=async()=>{\n      try{\n        const [tj,fj]=await Promise.all([api('agro-tar-list',{}),api('agro-func-list',{})]);\n        d2.state.tarefas=(((tj&&tj.items)||[]).filter(t=>!t.removida));\n        d2.state.funcionarios=((fj&&fj.items)||[]);\n      }catch(e){ d2.log('load error',e); }\n      d2.state.loading=false;\n    };\n    d2.state.loading=true;\n    load();\n\n    d2.on('.tab','click',(e,el)=>{ d2.state.tab=el.dataset.tab; });\n    d2.on('.chip','click',(e,el)=>{ d2.state.filter=el.dataset.filter; });\n    d2.on('.btn-new-task','click',()=>{ d2.state.editing={}; d2.state.showForm=true; });\n    d2.on('.btn-new-func','click',()=>{ d2.state.showFunc=true; });\n\n    d2.on('[data-act]','click',async (e,el)=>{\n      const act=el.dataset.act;\n      if(act==='edit'){\n        const t=(d2.state.tarefas||[]).find(x=>x._id===el.dataset.id);\n        d2.state.editing=Object.assign({},t); d2.state.showForm=true;\n      } else if(act==='del'){\n        const ok=await d2.dialog.confirm('Excluir esta tarefa?');\n        if(ok){ await api('agro-tar-del',{_id:el.dataset.id}); await load(); }\n      } else if(act==='copy'){\n        try{ await navigator.clipboard.writeText(el.dataset.link); await d2.dialog.alert('Link copiado!'); }\n        catch(_){ await d2.dialog.alert(el.dataset.link); }\n      }\n    });\n\n    d2.on('.task-cancel','click',()=>{ d2.state.showForm=false; });\n    d2.on('.task-save','click',async ()=>{\n      const g=(k)=>{ const el=d2.$('[data-f=\"'+k+'\"]'); return el?el.value:''; };\n      const titulo=(g('titulo')||'').trim();\n      if(!titulo){ await d2.dialog.alert('Informe o título da tarefa.'); return; }\n      const fid=g('funcionario');\n      const f=(d2.state.funcionarios||[]).find(x=>x._id===fid)||{};\n      const payload={ titulo, descricao:g('descricao'), prioridade:g('prioridade')||'media', prazo:g('prazo')||null, local:g('local'), funcionarioId:fid||null, funcionarioNome:f.nome||'', funcionarioTelefone:f.telefone||'' };\n      const ed=d2.state.editing||{};\n      if(ed._id){ payload._id=ed._id; } else { payload.token=newToken(); }\n      d2.state.saving=true;\n      try{ await api('agro-tar-save',payload); }catch(e){ d2.log(e); }\n      d2.state.saving=false; d2.state.showForm=false;\n      await load();\n    });\n\n    d2.on('.func-cancel','click',()=>{ d2.state.showFunc=false; });\n    d2.on('.func-save','click',async ()=>{\n      const g=(k)=>{ const el=d2.$('[data-ff=\"'+k+'\"]'); return el?el.value:''; };\n      const nome=(g('nome')||'').trim();\n      if(!nome){ await d2.dialog.alert('Informe o nome.'); return; }\n      try{ await api('agro-func-save',{ nome, telefone:digits(g('telefone')), funcao:g('funcao') }); }catch(e){ d2.log(e); }\n      d2.state.showFunc=false; await load();\n    });\n  }\n}"}}}}}}}},{"_id":"6a427aefbf38d3828143071c","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"empApp","package":"people-gabi-carmo","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 RUSH='https://people-gabi-carmo.data2apis.com/rush';\nasync function api(ref,body){\n  const r=await fetch(RUSH+'/'+ref,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body||{})});\n  let j={}; try{ j=await r.json(); }catch(e){}\n  return (j && j.data!==undefined) ? j.data : j;\n}\nconst esc=(x)=>(x==null?'':String(x)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;');\nconst PRIO={alta:{l:'Alta',c:'#c62828',b:'#ffebee'},media:{l:'Média',c:'#e65100',b:'#fff3e0'},baixa:{l:'Baixa',c:'#2e7d32',b:'#e8f5e9'}};\nconst STAT={pendente:{l:'Pendente',c:'#546e7a',b:'#eceff1'},parcial:{l:'Feita parcialmente',c:'#e65100',b:'#fff3e0'},concluida:{l:'Concluída',c:'#2e7d32',b:'#e8f5e9'}};\nconst fmtDate=(d)=>{ if(!d) return '—'; try{ return new Date(d).toLocaleDateString('pt-BR'); }catch(e){ return '—'; } };\nfunction getToken(){\n  try{\n    const u=new URL(window.location.href);\n    return u.searchParams.get('t')||u.searchParams.get('token')||'';\n  }catch(e){ return ''; }\n}\n\nexport default {\n  css:`\n.wrap{min-height:100%;background:#eef3ec;color:#1a2e1a;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;display:flex;flex-direction:column;align-items:center;padding:0 0 40px;box-sizing:border-box}\n.bar{width:100%;background:#2e7d32;color:#fff;padding:16px 20px;box-sizing:border-box;text-align:center;font-weight:800;font-size:17px}\n.sheet{width:min(560px,94vw);background:#fff;border-radius:16px;margin-top:18px;padding:22px;box-shadow:0 2px 10px rgba(0,0,0,.08)}\n.title{font-size:22px;font-weight:800;margin:0 0 6px}\n.badges{display:flex;gap:8px;flex-wrap:wrap;margin:10px 0 4px}\n.badge{padding:5px 12px;border-radius:999px;font-size:13px;font-weight:700}\n.meta{font-size:14px;color:#455;margin:10px 0;display:flex;flex-direction:column;gap:8px}\n.meta .row{display:flex;gap:8px;align-items:flex-start}\n.meta .k{width:22px}\n.desc{background:#f6f9f4;border-radius:10px;padding:14px;margin:14px 0;font-size:15px;line-height:1.5;white-space:pre-wrap}\n.sectitle{font-size:15px;font-weight:800;margin:22px 0 10px;color:#2e7d32}\n.opts{display:flex;flex-direction:column;gap:10px}\n.opt{display:flex;align-items:center;gap:12px;border:2px solid #d7e2d5;border-radius:12px;padding:14px;cursor:pointer;font-size:15px;font-weight:700;background:#fff}\n.opt[data-on=\"concluida\"]{border-color:#2e7d32;background:#e8f5e9}\n.opt[data-on=\"parcial\"]{border-color:#e65100;background:#fff3e0}\n.opt[data-on=\"pendente\"]{border-color:#90a4ae;background:#eceff1}\n.dot{width:20px;height:20px;border-radius:50%;border:2px solid #aaa;flex:0 0 auto}\n.opt[data-on] .dot{border-color:currentColor;background:currentColor;box-shadow:inset 0 0 0 3px #fff}\n.opt.c-concluida{color:#2e7d32}.opt.c-parcial{color:#e65100}.opt.c-pendente{color:#546e7a}\n.field{margin-top:12px;display:flex;flex-direction:column;gap:6px}\n.field label{font-size:13px;font-weight:700;color:#445}\n.field textarea{padding:11px;border:1px solid #cfd8cf;border-radius:10px;font-size:15px;font-family:inherit;min-height:90px;resize:vertical}\n.save{width:100%;margin-top:18px;border:none;border-radius:12px;padding:15px;font-size:16px;font-weight:800;background:#2e7d32;color:#fff;cursor:pointer}\n.save[disabled]{opacity:.55;cursor:default}\n.ok{background:#e8f5e9;border:1px solid #a5d6a7;color:#2e7d32;border-radius:12px;padding:14px;margin-top:16px;font-size:15px;font-weight:700;text-align:center}\n.center{text-align:center;padding:60px 20px;color:#789}\n.cur{margin-top:8px;font-size:14px;color:#555}\n.cur b{font-weight:800}\n`,\n  render(d2){\n    const s=d2.state;\n    if(s.loading){ return `<div class=\"wrap\"><div class=\"bar\">🌱 Minha Tarefa</div><div class=\"center\">Carregando tarefa…</div></div>`; }\n    const t=s.task;\n    if(!t){ return `<div class=\"wrap\"><div class=\"bar\">🌱 Minha Tarefa</div><div class=\"sheet\"><div class=\"center\">${esc(s.error||'Tarefa não encontrada. Verifique o link recebido.')}</div></div></div>`; }\n    const p=PRIO[t.prioridade]||PRIO.media;\n    const cur=STAT[t.status||'pendente']||STAT.pendente;\n    const sel=s.sel||t.status||'pendente';\n    const opt=(val,emoji,label)=>`<div class=\"opt c-${val}\" data-opt=\"${val}\" ${sel===val?`data-on=\"${val}\"`:''}><span class=\"dot\"></span><span>${emoji} ${label}</span></div>`;\n    return `<div class=\"wrap\">\n      <div class=\"bar\">🌱 Minha Tarefa</div>\n      <div class=\"sheet\">\n        <h1 class=\"title\">${esc(t.titulo)}</h1>\n        <div class=\"badges\">\n          <span class=\"badge\" style=\"color:${p.c};background:${p.b}\">⚑ Prioridade ${p.l}</span>\n          <span class=\"badge\" style=\"color:${cur.c};background:${cur.b}\">${cur.l}</span>\n        </div>\n        <div class=\"meta\">\n          <div class=\"row\"><span class=\"k\">📅</span><span>Prazo: <b>${fmtDate(t.prazo)}</b></span></div>\n          ${t.local?`<div class=\"row\"><span class=\"k\">📍</span><span>Local: ${esc(t.local)}</span></div>`:''}\n          ${t.funcionarioNome?`<div class=\"row\"><span class=\"k\">👤</span><span>Responsável: ${esc(t.funcionarioNome)}</span></div>`:''}\n        </div>\n        ${t.descricao?`<div class=\"desc\">${esc(t.descricao)}</div>`:''}\n        <div class=\"sectitle\">Você fez esta tarefa?</div>\n        <div class=\"opts\">\n          ${opt('concluida','✅','Fiz por completo')}\n          ${opt('parcial','🟧','Fiz parcialmente')}\n          ${opt('pendente','⬜','Ainda não fiz')}\n        </div>\n        <div class=\"field\" style=\"${sel==='parcial'?'':'display:none'}\">\n          <label>Por que foi feita só parcialmente?</label>\n          <textarea data-f=\"motivo\" placeholder=\"Ex.: faltou material, máquina quebrou, chuva...\">${esc(t.motivoParcial||'')}</textarea>\n        </div>\n        <button class=\"save\" ${s.saving?'disabled':''}>${s.saving?'Salvando…':'Salvar resposta'}</button>\n        ${s.saved?`<div class=\"ok\">✔️ Resposta enviada! Você pode mudar e salvar de novo quando quiser.</div>`:''}\n        <div class=\"cur\">Status atual registrado: <b>${cur.l}</b></div>\n      </div>\n    </div>`;\n  },\n  mounted(d2){\n    const load=async()=>{\n      const token=getToken();\n      if(!token){ d2.state.error='Link sem código da tarefa.'; d2.state.loading=false; return; }\n      try{\n        const res=await api('agro-tar-get',{token});\n        if(res&&res.ok&&res.item){ d2.state.task=res.item; d2.state.sel=res.item.status||'pendente'; }\n        else { d2.state.error=(res&&res.error)||'Tarefa não encontrada.'; }\n      }catch(e){ d2.state.error='Não foi possível carregar a tarefa.'; d2.log(e); }\n      d2.state.loading=false;\n    };\n    d2.state.loading=true;\n    load();\n\n    d2.on('.opt','click',(e,el)=>{ d2.state.sel=el.dataset.opt; d2.state.saved=false; });\n    d2.on('.save','click',async ()=>{\n      const t=d2.state.task; if(!t) return;\n      const sel=d2.state.sel||'pendente';\n      const mEl=d2.$('[data-f=\"motivo\"]');\n      const motivo=(sel==='parcial'&&mEl)?mEl.value:'';\n      if(sel==='parcial' && !motivo.trim()){ await d2.dialog.alert('Conte o motivo de ter feito parcialmente.'); return; }\n      d2.state.saving=true;\n      try{\n        const res=await api('agro-tar-status',{ token:t.token, status:sel, motivoParcial:motivo });\n        if(res&&res.item){ d2.state.task=res.item; }\n        d2.state.saved=true;\n      }catch(e){ await d2.dialog.alert('Erro ao salvar. Tente de novo.'); d2.log(e); }\n      d2.state.saving=false;\n    });\n  }\n}"}}}}}}}},{"_id":"6a46968bc53088741c52d576","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"vivaraApp","package":"people-gabi-carmo","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 CATS = [\n  { label: 'Anéis', term: 'anel' },\n  { label: 'Colares', term: 'colar' },\n  { label: 'Brincos', term: 'brinco' },\n  { label: 'Pulseiras', term: 'pulseira' },\n  { label: 'Relógios', term: 'relógio' }\n];\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;');\nconst fmtPrice = (n) => {\n  const v = Number(n);\n  if (!isFinite(v) || v <= 0) return '';\n  return 'R$ ' + v.toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n};\nconst jewelKind = (p) => {\n  const txt = ((p && p.name) || '') + ' ' + (((p && p.categories) || []).join(' '));\n  const t = txt.toLowerCase();\n  if (/gargantilha|colar|pingente|corrente/.test(t)) return 'necklace';\n  if (/brinco/.test(t)) return 'earring';\n  if (/rel[oó]gio|pulseira|bracelete/.test(t)) return 'wrist';\n  return 'ring';\n};\nconst KIND_NOUN = { ring: 'este anel', necklace: 'este colar', earring: 'estes brincos', wrist: 'esta peça' };\nconst KIND_HINT = {\n  ring: 'Suba uma foto da sua mão com os dedos visíveis',\n  necklace: 'Suba uma foto sua de frente mostrando o pescoço',\n  earring: 'Suba uma foto do seu rosto com a orelha visível',\n  wrist: 'Suba uma foto do seu punho / pulso'\n};\nconst readAsDataURL = (file) => new Promise((resolve, reject) => {\n  const r = new FileReader();\n  r.onload = () => {\n    const img = new Image();\n    img.onload = () => {\n      try {\n        const MAX = 1024;\n        let w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;\n        if (w > h && w > MAX) { h = Math.round(h * MAX / w); w = MAX; }\n        else if (h >= w && h > MAX) { w = Math.round(w * MAX / h); h = MAX; }\n        const canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        canvas.getContext('2d').drawImage(img, 0, 0, w, h);\n        resolve(canvas.toDataURL('image/jpeg', 0.85));\n      } catch (e) { resolve(r.result); }\n    };\n    img.onerror = () => resolve(r.result);\n    img.src = r.result;\n  };\n  r.onerror = reject;\n  r.readAsDataURL(file);\n});\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Jost:wght@300;400;500;600&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .app { display: flex; flex-direction: column; height: 100%; font-family: 'Jost', 'Helvetica Neue', Arial, sans-serif; background: #FFFFFF; overflow: hidden; color: #1A1A1A; }\n    .header { display: flex; align-items: center; padding: 0 40px; height: 80px; background: #FFFFFF; border-bottom: 1px solid #ECECEC; flex-shrink: 0; gap: 24px; }\n    .logo { font-family: 'Cormorant Garamond', serif; font-size: 30px; font-weight: 700; letter-spacing: 4px; color: #0A8C86; text-transform: uppercase; }\n    .search-wrap { margin-left: auto; display: flex; align-items: center; background: #F6F8F8; border: 1px solid #E3E9E9; border-radius: 24px; height: 42px; padding: 0 6px 0 18px; width: 340px; max-width: 38%; }\n    .search-input { flex: 1; background: transparent; border: none; outline: none; color: #1A1A1A; font-size: 14px; font-family: inherit; }\n    .search-input::placeholder { color: #9BA7A7; }\n    .search-btn, .search-clear { width: 32px; height: 32px; border: none; background: transparent; color: #7d8a8a; cursor: pointer; font-size: 15px; border-radius: 50%; transition: all 0.2s; }\n    .search-btn:hover, .search-clear:hover { background: #E6EDED; color: #0A8C86; }\n    .filter-bar { display: flex; align-items: center; padding: 0 40px; gap: 8px; height: 56px; background: #FFFFFF; border-bottom: 1px solid #ECECEC; flex-shrink: 0; overflow-x: auto; }\n    .filter-btn { padding: 6px 20px; border: 1.5px solid transparent; border-radius: 20px; background: transparent; font-size: 13px; color: #5f6b6b; cursor: pointer; transition: all 0.2s; font-family: inherit; letter-spacing: 0.5px; white-space: nowrap; }\n    .filter-btn:hover { background: #F0F5F5; color: #0A8C86; }\n    .filter-btn.active { background: #0A8C86; color: #FFFFFF; border-color: #0A8C86; }\n    .count { margin-left: auto; font-size: 12px; color: #AAB4B4; white-space: nowrap; padding-left: 16px; }\n    .content { display: flex; flex: 1; overflow: hidden; }\n    .grid-wrapper { flex: 1; overflow-y: auto; padding: 22px; background: #FAFBFB; }\n    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); grid-auto-rows: 300px; gap: 16px; }\n    .card { background: #FFFFFF; border: 1px solid #EEF1F1; border-radius: 10px; overflow: hidden; cursor: pointer; transition: transform 0.2s, box-shadow 0.2s; display: flex; flex-direction: column; }\n    .card:hover { transform: translateY(-4px); box-shadow: 0 12px 32px rgba(10,140,134,0.14); }\n    .card-img { width: 100%; height: 208px; object-fit: contain; background: #FFFFFF; display: block; padding: 10px; }\n    .card-body { padding: 12px 16px; flex: 1; }\n    .card-name { font-size: 13px; font-weight: 400; color: #1A1A1A; line-height: 1.4; margin-bottom: 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .card-price { font-size: 15px; font-weight: 600; color: #0A8C86; }\n    .state-msg { grid-column: 1/-1; text-align: center; padding: 80px 0; color: #9BA7A7; font-size: 15px; }\n    .state-full { display: flex; align-items: center; justify-content: center; height: 100%; color: #9BA7A7; font-size: 15px; gap: 12px; }\n    .spinner-sm { width: 22px; height: 22px; border: 3px solid #E6EDED; border-top-color: #0A8C86; border-radius: 50%; animation: spin 0.8s linear infinite; }\n    .detail-panel { width: 560px; background: #FFFFFF; border-left: 1px solid #ECECEC; display: flex; flex-direction: column; overflow-y: auto; flex-shrink: 0; animation: slideIn 0.25s ease; }\n    @keyframes slideIn { from { transform: translateX(40px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }\n    .detail-header { display: flex; align-items: center; justify-content: space-between; padding: 0 24px; height: 56px; border-bottom: 1px solid #ECECEC; flex-shrink: 0; position: sticky; top: 0; background: #FFFFFF; z-index: 2; }\n    .detail-title { font-size: 13px; font-weight: 600; color: #1A1A1A; letter-spacing: 1px; text-transform: uppercase; }\n    .close-btn { width: 32px; height: 32px; border: none; background: #F0F5F5; border-radius: 50%; font-size: 14px; cursor: pointer; color: #5f6b6b; display: flex; align-items: center; justify-content: center; transition: background 0.2s; font-family: inherit; }\n    .close-btn:hover { background: #DCE7E7; }\n    .detail-img { width: 100%; height: 380px; object-fit: contain; background: #FFFFFF; display: block; padding: 20px; }\n    .detail-info { padding: 24px; }\n    .detail-name { font-family: 'Cormorant Garamond', serif; font-size: 24px; font-weight: 600; color: #1A1A1A; line-height: 1.3; margin-bottom: 10px; }\n    .detail-price { font-size: 22px; font-weight: 600; color: #0A8C86; margin-bottom: 24px; }\n    .tryon-section { border-top: 1px solid #ECECEC; padding-top: 24px; }\n    .cta-btn { width: 100%; height: 52px; background: #0A8C86; color: #FFFFFF; border: none; border-radius: 8px; font-size: 15px; font-weight: 500; letter-spacing: 0.5px; cursor: pointer; transition: background 0.2s; font-family: inherit; display: flex; align-items: center; justify-content: center; gap: 8px; }\n    .cta-btn:hover:not(:disabled) { background: #0A736E; }\n    .cta-hint { margin-top: 12px; font-size: 12px; color: #AAB4B4; text-align: center; line-height: 1.5; }\n    .photo-input { display: none; }\n    .gen-box { margin-top: 16px; text-align: center; }\n    .spinner { width: 40px; height: 40px; border: 3px solid #E6EDED; border-top-color: #0A8C86; border-radius: 50%; margin: 24px auto 12px; animation: spin 0.8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gen-status { font-size: 13px; color: #5f6b6b; }\n    .gen-sub { font-size: 11px; color: #AAB4B4; margin-top: 4px; }\n    .result-img { width: 100%; border-radius: 12px; display: block; margin-bottom: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.12); }\n    .result-badge { font-size: 11px; color: #9BA7A7; text-align: center; margin-bottom: 16px; }\n    .ghost-btn { width: 100%; height: 44px; background: #FFFFFF; color: #1A1A1A; border: 1.5px solid #DCE7E7; border-radius: 8px; font-size: 13px; font-weight: 500; cursor: pointer; font-family: inherit; transition: border-color 0.2s; }\n    .ghost-btn:hover { border-color: #0A8C86; }\n    .err-box { margin-top: 16px; padding: 14px 16px; background: #FDECEC; color: #C0392B; font-size: 12px; border-radius: 8px; line-height: 1.5; }\n    .warn-box { margin-bottom: 16px; padding: 14px 16px; background: #FFF6E5; color: #9A6B00; font-size: 13px; border-radius: 8px; line-height: 1.5; }\n    .product-url { display: block; margin-top: 16px; text-align: center; font-size: 12px; color: #9BA7A7; text-decoration: none; border-top: 1px solid #ECECEC; padding-top: 16px; }\n    .product-url:hover { color: #0A8C86; }\n  `,\n\n  render(d2) {\n    const {\n      term = 'anel', search = '', products = [], gridStatus = 'loading', gridError = null, total = 0,\n      detailOpen = false, selectedProduct = null,\n      tryStatus = 'idle', tryImage = null, tryError = null, tryAttempt = 1\n    } = d2.state;\n\n    const filterBtns = CATS.map(c =>\n      `<button class=\"filter-btn${term === c.term && !search ? ' active' : ''}\" data-term=\"${c.term}\">${c.label}</button>`\n    ).join('');\n\n    let gridHTML;\n    if (gridStatus === 'loading') {\n      gridHTML = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando joias...</span></div>`;\n    } else if (gridStatus === 'error') {\n      gridHTML = `<div class=\"state-full\"><span>Erro ao carregar: ${esc(gridError)}</span></div>`;\n    } else if (products.length === 0) {\n      gridHTML = `<div class=\"state-msg\">Nenhuma joia encontrada${search ? ' para \"' + esc(search) + '\"' : ''}.</div>`;\n    } else {\n      const cards = products.map((p, i) =>\n        `<div class=\"card\" data-idx=\"${i}\" data-key=\"${esc(p.productId)}\">\n          <img class=\"card-img\" src=\"${esc(p.image)}\" alt=\"\" loading=\"lazy\" />\n          <div class=\"card-body\">\n            <div class=\"card-name\">${esc(p.name)}</div>\n            <div class=\"card-price\">${fmtPrice(p.price)}</div>\n          </div>\n        </div>`\n      ).join('');\n      gridHTML = `<div class=\"grid\">${cards}</div>`;\n    }\n\n    const countLabel = gridStatus === 'done' && total ? `${total} ${total === 1 ? 'joia' : 'joias'}` : '';\n\n    let detailHTML = '';\n    if (detailOpen && selectedProduct) {\n      const kind = jewelKind(selectedProduct);\n      let tryOnHTML;\n      if (tryStatus === 'checking' || tryStatus === 'uploading' || tryStatus === 'generating') {\n        const msg = tryStatus === 'checking' ? 'Analisando sua foto...' : (tryStatus === 'uploading' ? 'Preparando sua foto...' : 'Provando a joia com IA...');\n        const sub = tryStatus === 'generating' ? ((tryAttempt > 1 ? 'Tentativa ' + tryAttempt + ' de 3 — ' : '') + 'isso pode levar até 30 segundos ✨') : 'Só um instante';\n        tryOnHTML = `<div class=\"gen-box\"><div class=\"spinner\"></div><div class=\"gen-status\">${msg}</div><div class=\"gen-sub\">${sub}</div></div>`;\n      } else if (tryStatus === 'rejected') {\n        tryOnHTML = `<div class=\"warn-box\">📷 ${esc(tryError)}</div><button class=\"cta-btn tryon-btn\">📸 Enviar outra foto</button>`;\n      } else if (tryStatus === 'done' && tryImage) {\n        tryOnHTML = `<div class=\"gen-box\"><img class=\"result-img\" src=\"${tryImage}\" alt=\"Prova virtual\" /><div class=\"result-badge\">✨ Prova virtual gerada por IA</div><button class=\"ghost-btn try-again\">Experimentar com outra foto</button></div>`;\n      } else if (tryStatus === 'error') {\n        tryOnHTML = `<button class=\"cta-btn tryon-btn\">📸 Tentar novamente</button><div class=\"err-box\">Não consegui gerar a prova virtual: ${esc(tryError)}</div>`;\n      } else {\n        tryOnHTML = `<button class=\"cta-btn tryon-btn\">📸 Experimentar com sua foto</button><p class=\"cta-hint\">${esc(KIND_HINT[kind])} e a IA vai te mostrar usando ${esc(KIND_NOUN[kind])}.</p>`;\n      }\n\n      detailHTML = `\n        <div class=\"detail-panel\">\n          <div class=\"detail-header\">\n            <span class=\"detail-title\">Detalhes da Joia</span>\n            <button class=\"close-btn\">✕</button>\n          </div>\n          <img class=\"detail-img\" src=\"${esc(selectedProduct.image)}\" alt=\"\" />\n          <div class=\"detail-info\">\n            <h2 class=\"detail-name\">${esc(selectedProduct.name)}</h2>\n            <div class=\"detail-price\">${fmtPrice(selectedProduct.price)}</div>\n            <div class=\"tryon-section\">\n              ${tryOnHTML}\n              <input type=\"file\" class=\"photo-input\" accept=\"image/*\" />\n            </div>\n            <a class=\"product-url\" href=\"${esc(selectedProduct.link)}\" target=\"_blank\">Ver no site da Vivara →</a>\n          </div>\n        </div>`;\n    }\n\n    return `\n      <div class=\"app\">\n        <header class=\"header\">\n          <span class=\"logo\">Vivara</span>\n          <div class=\"search-wrap\">\n            <input class=\"search-input\" placeholder=\"Buscar joias...\" value=\"${esc(search)}\" />\n            ${search ? '<button class=\"search-clear\" title=\"Limpar\">✕</button>' : ''}\n            <button class=\"search-btn\" title=\"Buscar\">🔍</button>\n          </div>\n        </header>\n        <div class=\"filter-bar\">\n          ${filterBtns}\n          <span class=\"count\">${countLabel}</span>\n        </div>\n        <div class=\"content\">\n          <div class=\"grid-wrapper\">${gridHTML}</div>\n          ${detailHTML}\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    const fetchProducts = async (term, search) => {\n      d2.setState({ gridStatus: 'loading', term, search });\n      try {\n        const r = await d2.rush('vivara-products').invoke({ term: term || 'anel', search: search || '' });\n        const payload = (r && r.data) ? r.data : r;\n        const products = (payload && payload.products) || [];\n        const total = (payload && payload.total) || products.length;\n        d2.setState({ products, total, gridStatus: 'done' });\n      } catch (err) {\n        d2.setState({ gridStatus: 'error', gridError: String((err && err.message) || err) });\n      }\n    };\n\n    d2.on('.filter-btn', 'click', (e) => {\n      const el = e.target.closest('.filter-btn');\n      if (el) fetchProducts(el.dataset.term, '');\n    });\n    d2.on('.search-btn', 'click', () => {\n      const i = d2.$('.search-input');\n      fetchProducts(d2.state.term || 'anel', (i && i.value.trim()) || '');\n    });\n    d2.on('.search-input', 'keydown', (e) => {\n      if (e.key === 'Enter') fetchProducts(d2.state.term || 'anel', e.target.value.trim());\n    });\n    d2.on('.search-clear', 'click', () => {\n      fetchProducts(d2.state.term || 'anel', '');\n    });\n    d2.on('.card', 'click', (e) => {\n      const el = e.target.closest('.card');\n      const idx = el && el.dataset.idx;\n      const prod = (d2.state.products || [])[idx];\n      if (prod) d2.setState({ selectedProduct: prod, detailOpen: true, tryStatus: 'idle', tryImage: null, tryError: null });\n    });\n    d2.on('.close-btn', 'click', () => {\n      d2.setState({ detailOpen: false, selectedProduct: null, tryStatus: 'idle', tryImage: null, tryError: null });\n    });\n    d2.on('.tryon-btn', 'click', () => {\n      const input = d2.$('.photo-input');\n      if (input) { input.value = ''; input.click(); }\n    });\n    d2.on('.try-again', 'click', () => {\n      d2.setState({ tryStatus: 'idle', tryImage: null, tryError: null });\n      const input = d2.$('.photo-input');\n      if (input) input.value = '';\n    });\n\n    d2.on('.photo-input', 'change', async (e) => {\n      const file = e.target.files && e.target.files[0];\n      if (!file) return;\n      const prod = d2.state.selectedProduct;\n      if (!prod) return;\n      const kind = jewelKind(prod);\n\n      d2.setState({ tryStatus: 'checking', tryImage: null, tryError: null });\n      try {\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('vivara-photo-check').invoke({ photoUrl: userPhotoUrl, kind });\n        const cp = (check && check.data) ? check.data : check;\n        const verdict = (cp && cp.verdict) || {};\n        if (verdict.ok === false) {\n          d2.setState({ tryStatus: 'rejected', tryError: verdict.motivo || 'A foto não ficou boa para a prova virtual. Tente outra seguindo a dica.' });\n          return;\n        }\n        const jewelUrl = prod.image;\n        const name = prod.name || '';\n        const prompts = {\n          ring: 'Imagem 1 = uma pessoa. Imagem 2 = um anel da Vivara (\"' + name + '\"). Use APENAS o anel da Imagem 2 (mesmo design, metal, pedras, cor e detalhes); ignore qualquer modelo, mão ou dedo que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesma mão, dedos, pele, unhas, pose, iluminação e fundo), agora usando esse anel em UM dedo da mão. É ESSENCIAL que o anel tenha o TAMANHO CERTO para o dedo: o diâmetro interno do aro deve ser IGUAL à largura do dedo naquele ponto, de modo que o aro ENVOLVA o dedo de forma JUSTA, encostando na pele nas laterais e PASSANDO POR TRÁS do dedo (a parte de trás do aro fica oculta atrás do dedo). NUNCA deixe o anel grande demais, largo demais, solto, flutuando ou com folga/vão entre o aro e a pele. A frente do anel (pedra/detalhe) apoia sobre o topo do dedo, com escala coerente ao tamanho real da mão. Mantenha a espessura e o comprimento do dedo idênticos aos da Imagem 1 — não afine nem engrosse o dedo para caber o anel. Remova qualquer outro anel que já apareça na Imagem 1. Não adicione nenhuma outra joia. Não altere mais nada além de colocar o anel, no tamanho certo, no dedo.',\n          necklace: 'Imagem 1 = uma pessoa. Imagem 2 = um colar ou gargantilha da Vivara (\"' + name + '\"). Use APENAS a joia da Imagem 2 (mesmo design, metal, pingente, pedras e detalhes); ignore qualquer modelo que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesmo rosto, pele, cabelo, roupa, pose, iluminação e fundo), agora usando esse colar ao redor do pescoço, caindo naturalmente sobre o colo/decote, com fecho, comprimento e perspectiva realistas. Remova qualquer outro colar que já apareça na Imagem 1. Não adicione outras joias. Não altere mais nada além de colocar o colar.',\n          earring: 'Imagem 1 = uma pessoa. Imagem 2 = um brinco da Vivara (\"' + name + '\"). Use APENAS o brinco da Imagem 2 (mesmo design, metal, pedras e detalhes); ignore qualquer modelo que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesmo rosto, pele, cabelo, iluminação e fundo), agora usando o MESMO brinco nas DUAS orelhas (se a Imagem 2 mostrar apenas um, replique idêntico na outra orelha), com tamanho e posição realistas na orelha. Remova qualquer outro brinco que já apareça na Imagem 1. Não adicione outras joias. Não altere mais nada além de colocar os brincos.',\n          wrist: 'Imagem 1 = uma pessoa. Imagem 2 = uma pulseira ou relógio da Vivara (\"' + name + '\"). Use APENAS a peça da Imagem 2 (mesmo design, metal, mostrador, pedras e detalhes); ignore qualquer modelo, braço ou punho que apareça na Imagem 2. Gere uma foto realista da MESMA pessoa da Imagem 1 (mesma mão, punho, pele, pose, iluminação e fundo), agora usando essa peça no punho, com ajuste e perspectiva realistas. Remova qualquer outra pulseira ou relógio que já apareça na Imagem 1. Não adicione outras joias. Não altere mais nada além de colocar a peça no punho.'\n        };\n        const prompt = prompts[kind] || prompts.ring;\n\n        let img = null, lastErr = null;\n        for (let attempt = 1; attempt <= 3 && !img; attempt++) {\n          d2.setState({ tryStatus: 'generating', tryAttempt: attempt });\n          try {\n            const res = await d2.rush('vivara-tryon').invoke({ userPhotoUrl, jewelUrl, prompt });\n            const payload = (res && res.data) ? res.data : res;\n            img = (payload && payload.image) || null;\n            if (!img) throw new Error('Resposta sem imagem');\n          } catch (e2) { lastErr = e2; }\n        }\n        if (img) d2.setState({ tryStatus: 'done', tryImage: img });\n        else d2.setState({ tryStatus: 'error', tryError: 'Não consegui gerar após 3 tentativas. ' + String((lastErr && lastErr.message) || lastErr || '') });\n      } catch (err) {\n        d2.setState({ tryStatus: 'error', tryError: String((err && err.message) || err) });\n      }\n    });\n\n    fetchProducts('anel', '');\n  }\n}"}}}}}}}},{"_id":"6a5fd031f687a4f28da01ad5","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"nasp-app","package":"people-gabi-carmo","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 RUSH_BASE = 'https://people-gabi-carmo.data2apis.com/rush/';\nconst esc = s => String(s ?? '').replace(/[&<>\"]/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]));\nconst fmtD = iso => {\n  if (!iso) return '—';\n  const d = new Date(iso);\n  if (isNaN(d)) return String(iso).slice(0, 16);\n  return d.toLocaleDateString('pt-BR', {day:'2-digit', month:'2-digit'}) + ' ' + d.toLocaleTimeString('pt-BR', {hour:'2-digit', minute:'2-digit'});\n};\nconst nowIso = () => new Date().toISOString();\n\nconst NAV = [\n  { id:'painel',        label:'Painel',            icon:'◧', roles:['admin','operador'] },\n  { id:'estoque',       label:'Reconciliação',     icon:'⇄', roles:['admin','operador'] },\n  { id:'entrada',       label:'Entrada',           icon:'⬇', roles:['admin','operador'] },\n  { id:'tablet',        label:'Pré-retirada (QR)', icon:'▣', roles:['admin','operador','tecnico'] },\n  { id:'regularizacao', label:'Regularização',     icon:'✓', roles:['admin','operador'] },\n  { id:'movs',          label:'Movimentações',     icon:'≡', roles:['admin','operador'] },\n  { id:'admin',         label:'Administração',     icon:'⚙', roles:['admin'] },\n];\nconst canSee = (u, id) => { const n = NAV.find(x => x.id === id); return n && u && n.roles.includes(u.role); };\nconst homeFor = u => u.role === 'tecnico' ? 'tablet' : 'painel';\n\nasync function rush(d2, ref, body) {\n  let r;\n  try {\n    if (typeof d2.rush !== 'function') throw new Error('no d2.rush');\n    r = await d2.rush(ref).invoke(body || {});\n    if (r === undefined || r === null) throw new Error('empty rush result');\n  } catch (e) {\n    const res = await fetch(RUSH_BASE + ref, { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body || {}) });\n    r = await res.json();\n  }\n  if (r && typeof r === 'object' && r.data !== undefined && r.ok === undefined && r.items === undefined && r.error === undefined && r.user === undefined) r = r.data;\n  return r || {};\n}\nasync function load(d2, action, force) {\n  const s = d2.state;\n  if (!force && s['ld_' + action]) return;\n  const r = await rush(d2, 'nasp-data', { action });\n  d2.state['ld_' + action] = true;\n  d2.state[action] = r.items || [];\n}\nfunction toast(d2, msg, kind) {\n  d2.state.toast = { msg, kind: kind || 'ok' };\n  clearTimeout(toast._t);\n  toast._t = setTimeout(() => { d2.state.toast = null; }, 3200);\n}\n\n/* ---------- views ---------- */\nfunction vLogin(s) {\n  return `<div class=\"login-wrap\"><div class=\"login-card\">\n    <div class=\"brand\">Data<sup>2</sup> <span class=\"brand-sub\">Piloto NASP · Spare Parts</span></div>\n    <h1>Gestão Autônoma de<br>Estoque de Spare Parts</h1>\n    <p class=\"muted\">CDSP · NASP — São Paulo</p>\n    <label>E-mail</label>\n    <input id=\"lg-email\" type=\"email\" placeholder=\"voce@nasp.com\" value=\"\">\n    <label>Senha</label>\n    <input id=\"lg-pass\" type=\"password\" placeholder=\"••••••••\">\n    ${s.loginErr ? `<div class=\"err\">${esc(s.loginErr)}</div>` : ''}\n    <button class=\"btn primary w100\" data-action=\"login\">${s.busy ? 'Entrando…' : 'Entrar'}</button>\n    <div class=\"hint\">demo: admin@nasp.com · operador@nasp.com · tecnico@nasp.com — senha nasp123</div>\n  </div></div>`;\n}\n\nfunction kpi(label, val, cls) {\n  return `<div class=\"kpi\"><div class=\"kpi-v ${cls || ''}\">${val}</div><div class=\"kpi-l\">${label}</div></div>`;\n}\n\nfunction vPainel(s) {\n  const est = s.estoque || [], movs = s.movimentacoes || [], pres = s.pre_retiradas || [];\n  const okItems = est.filter(e => (e.saldo_natura || 0) === (e.saldo_sap || 0)).length;\n  const acc = est.length ? Math.round(okItems / est.length * 1000) / 10 : 100;\n  const div = est.length - okItems;\n  const pend = pres.filter(p => p.status === 'pendente').length;\n  const now = Date.now(), day = 864e5;\n  const inLast = n => movs.filter(m => m.data && (now - new Date(m.data).getTime()) < n * day).length;\n  const days = [];\n  for (let i = 6; i >= 0; i--) {\n    const d0 = new Date(now - i * day);\n    const key = d0.toISOString().slice(0, 10);\n    const c = movs.filter(m => (m.data || '').slice(0, 10) === key).length;\n    days.push({ label: d0.toLocaleDateString('pt-BR', {day:'2-digit', month:'2-digit'}), c });\n  }\n  const max = Math.max(1, ...days.map(d => d.c));\n  return `<h2>Painel · NASP</h2>\n  <div class=\"kpis\">\n    ${kpi('Acuracidade de inventário', acc + '%', acc >= 99 ? 'ok' : 'warn')}\n    ${kpi('Divergências abertas', div, div > 0 ? 'crit' : 'ok')}\n    ${kpi('Pré-retiradas pendentes', pend, pend > 0 ? 'warn' : 'ok')}\n    ${kpi('Movim. hoje', inLast(1))}\n    ${kpi('Movim. 7 dias', inLast(7))}\n    ${kpi('Movim. 30 dias', inLast(30))}\n  </div>\n  <div class=\"card\">\n    <div class=\"card-head\"><b>Movimentações — últimos 7 dias</b>\n      <button class=\"btn ghost sm\" data-action=\"export-dbx\">${s.busy ? '…' : 'Exportar p/ Databricks'}</button>\n    </div>\n    <div class=\"chart\">${days.map(d => `<div class=\"bar-col\"><div class=\"bar\" style=\"height:${Math.round(d.c / max * 100)}%\"><span>${d.c}</span></div><div class=\"bar-l\">${d.label}</div></div>`).join('')}</div>\n  </div>\n  <div class=\"card\"><div class=\"card-head\"><b>Últimas movimentações</b></div>${tblMovs(movs.slice(0, 6))}</div>`;\n}\n\nfunction tblMovs(list) {\n  if (!list.length) return '<div class=\"empty\">Nenhuma movimentação.</div>';\n  return `<table><thead><tr><th>Data</th><th>Tipo</th><th>Peça</th><th>Qtd</th><th>Origem/Destino</th><th>Usuário</th><th>Ref. SAP</th></tr></thead><tbody>\n  ${list.map(m => `<tr data-key=\"${m._id}\">\n    <td>${fmtD(m.data)}</td>\n    <td><span class=\"pill ${m.tipo === 'entrada' ? 'in' : m.tipo === 'ajuste' ? 'adj' : 'out'}\">${esc(m.tipo)}</span></td>\n    <td><b>${esc(m.codigo_sap)}</b><div class=\"sub\">${esc(m.descricao)}</div></td>\n    <td>${m.quantidade}</td><td>${esc(m.origem || m.destino || '—')}</td>\n    <td>${esc((m.usuario || '').split('@')[0])}</td><td class=\"mono\">${esc(m.sap_ref || '—')}</td>\n  </tr>`).join('')}</tbody></table>`;\n}\n\nfunction vEstoque(s, u) {\n  const est = s.estoque || [];\n  const okItems = est.filter(e => (e.saldo_natura || 0) === (e.saldo_sap || 0)).length;\n  const acc = est.length ? Math.round(okItems / est.length * 1000) / 10 : 100;\n  return `<h2>Reconciliação Natura × SAP</h2>\n  <div class=\"kpis\">${kpi('Acuracidade', acc + '%', acc >= 99 ? 'ok' : 'warn')}${kpi('Itens no estoque', est.length)}${kpi('Divergências', est.length - okItems, est.length - okItems ? 'crit' : 'ok')}</div>\n  <div class=\"card\"><div class=\"card-head\"><b>Saldo por peça</b><button class=\"btn ghost sm\" data-action=\"reload-est\">Atualizar</button></div>\n  <table><thead><tr><th>Peça</th><th>Saldo Natura</th><th>Saldo SAP</th><th>Diferença</th><th>Última reconciliação</th>${u.role === 'admin' ? '<th></th>' : ''}</tr></thead><tbody>\n  ${est.map(e => {\n    const diff = (e.saldo_natura || 0) - (e.saldo_sap || 0);\n    return `<tr data-key=\"${e._id}\" class=\"${diff !== 0 ? 'row-div' : ''}\">\n      <td><b>${esc(e.codigo_sap)}</b><div class=\"sub\">${esc(e.descricao)}</div></td>\n      <td class=\"num\">${e.saldo_natura}</td><td class=\"num\">${e.saldo_sap}</td>\n      <td>${diff === 0 ? '<span class=\"pill in\">OK</span>' : `<span class=\"pill out\">${diff > 0 ? '+' + diff : diff}</span>`}</td>\n      <td>${fmtD(e.ultima_reconciliacao)}</td>\n      ${u.role === 'admin' ? `<td>${diff !== 0 ? `<button class=\"btn sm\" data-action=\"ajustar\" data-cod=\"${esc(e.codigo_sap)}\" data-nat=\"${e.saldo_natura}\" data-sap=\"${e.saldo_sap}\">Resolver</button>` : ''}</td>` : ''}\n    </tr>`;\n  }).join('')}</tbody></table></div>`;\n}\n\nfunction vEntrada(s) {\n  const tab = s.entTab || 'ind';\n  const pecas = s.estoque || [];\n  const origens = (s.origens || []).filter(o => o.ativo !== false);\n  const selOrig = s.entOrigem || (origens[0] && origens[0].nome) || '';\n  const lote = s.lote || [];\n  return `<h2>Entrada de estoque</h2>\n  <div class=\"tabs\">\n    <button class=\"tab ${tab === 'ind' ? 'on' : ''}\" data-action=\"ent-tab\" data-tab=\"ind\">Individual</button>\n    <button class=\"tab ${tab === 'lote' ? 'on' : ''}\" data-action=\"ent-tab\" data-tab=\"lote\">Em lote (CSV / Nota fiscal)</button>\n  </div>\n  ${tab === 'ind' ? `\n  <div class=\"card form\">\n    <label>Peça</label>\n    <select id=\"ent-peca\">${pecas.map(p => `<option value=\"${esc(p.codigo_sap)}\">${esc(p.codigo_sap)} — ${esc(p.descricao)}</option>`).join('')}</select>\n    <div class=\"row2\">\n      <div><label>Quantidade</label><input id=\"ent-qtd\" type=\"number\" min=\"1\" value=\"1\"></div>\n      <div><label>Origem</label><select id=\"ent-origem\" data-action-change=\"ent-origem\">${origens.map(o => `<option ${o.nome === selOrig ? 'selected' : ''}>${esc(o.nome)}</option>`).join('')}</select></div>\n    </div>\n    ${selOrig === 'Compra nova' ? `\n    <div class=\"row2\"><div><label>Ordem de compra (COUPA)</label><input id=\"ent-oc\" placeholder=\"OC-2026-0000\" value=\"${esc(s.entOc || '')}\"></div>\n    <div><label>&nbsp;</label><button class=\"btn ghost\" data-action=\"coupa\">Consultar OC no COUPA</button></div></div>\n    ${s.coupaInfo ? `<div class=\"note\">COUPA: OC <b>${esc(s.coupaInfo.oc)}</b> · ${esc(s.coupaInfo.fornecedor)} · status <b>${esc(s.coupaInfo.status)}</b> <span class=\"pill adj\">mock</span></div>` : ''}` : ''}\n    <button class=\"btn primary\" data-action=\"ent-confirmar\">Confirmar entrada</button>\n    <div class=\"hint\">Toda entrada é espelhada no SAP no momento da confirmação (integração simulada na PoC).</div>\n  </div>` : `\n  <div class=\"card form\">\n    <div class=\"row2\">\n      <div>\n        <label>Colar CSV (codigo;quantidade)</label>\n        <textarea id=\"lote-csv\" rows=\"4\" placeholder=\"SP-100234;10&#10;SP-103150;5\"></textarea>\n        <button class=\"btn ghost sm\" data-action=\"lote-parse\">Carregar itens do CSV</button>\n      </div>\n      <div>\n        <label>Ou foto da nota fiscal (leitura por IA)</label>\n        <input id=\"nf-file\" type=\"file\" accept=\"image/*\">\n        ${s.nfBusy ? '<div class=\"note\">Lendo a nota fiscal…</div>' : ''}\n        <label style=\"margin-top:10px\">Origem do lote</label>\n        <select id=\"lote-origem\">${origens.map(o => `<option>${esc(o.nome)}</option>`).join('')}</select>\n      </div>\n    </div>\n  </div>\n  ${lote.length ? `<div class=\"card\">\n    <div class=\"card-head\"><b>Conferência do lote (${lote.length} itens)</b>\n      <button class=\"btn primary sm\" data-action=\"lote-aprovar-todos\">Aprovar todos</button></div>\n    <table><thead><tr><th>Código SAP</th><th>Descrição</th><th>Qtd</th><th>Status</th><th></th></tr></thead><tbody>\n    ${lote.map((it, i) => `<tr data-key=\"l${i}\">\n      <td><input class=\"cell-in\" data-lote-cod=\"${i}\" value=\"${esc(it.codigo)}\"></td>\n      <td class=\"sub\">${esc(it.desc || '')}</td>\n      <td><input class=\"cell-in num\" type=\"number\" min=\"1\" data-lote-qtd=\"${i}\" value=\"${it.qtd}\"></td>\n      <td>${it.status === 'ok' ? '<span class=\"pill in\">confirmado</span>' : it.status === 'erro' ? `<span class=\"pill out\" title=\"${esc(it.msg)}\">erro</span>` : '<span class=\"pill adj\">pendente</span>'}</td>\n      <td class=\"acts\">\n        ${it.status !== 'ok' ? `<button class=\"btn sm\" data-action=\"lote-aprovar\" data-i=\"${i}\">Aprovar</button>` : ''}\n        <button class=\"btn ghost sm\" data-action=\"lote-remover\" data-i=\"${i}\">✕</button>\n      </td></tr>`).join('')}\n    </tbody></table>\n    ${lote.some(i => i.status === 'erro') ? '<div class=\"hint\">Itens com erro: confira o código SAP e aprove novamente.</div>' : ''}\n  </div>` : ''}`}`;\n}\n\nfunction vTablet(s, u) {\n  const st = s.qr || { step: 'idle' };\n  if (st.step === 'done') {\n    return `<div class=\"tablet\"><div class=\"t-card ok-bg\">\n      <div class=\"t-big\">✓</div><h2>Pré-retirada registrada!</h2>\n      <p>${esc(st.peca.descricao)} · ${st.qtd} un</p>\n      <p class=\"muted\">Regularize depois — a operação não para.</p>\n      <button class=\"btn primary xl\" data-action=\"qr-reset\">Nova retirada</button>\n    </div></div>`;\n  }\n  if (st.step === 'confirm') {\n    return `<div class=\"tablet\"><div class=\"t-card\">\n      <div class=\"t-eyebrow\">Peça identificada</div>\n      <h2>${esc(st.peca.codigo_sap)}</h2>\n      <p class=\"t-desc\">${esc(st.peca.descricao)}</p>\n      <div class=\"stepper\">\n        <button class=\"btn xl\" data-action=\"qr-dec\">−</button>\n        <div class=\"step-v\">${st.qtd}</div>\n        <button class=\"btn xl\" data-action=\"qr-inc\">+</button>\n      </div>\n      <button class=\"btn primary xl w100\" data-action=\"qr-confirm\">${s.busy ? 'Registrando…' : 'Confirmar pré-retirada'}</button>\n      <button class=\"btn ghost\" data-action=\"qr-reset\">Cancelar</button>\n    </div></div>`;\n  }\n  return `<div class=\"tablet\"><div class=\"t-card\">\n    <div class=\"t-eyebrow\">Autoatendimento · ${esc((u.nome || '').split(' ')[0])}</div>\n    <h2>Retirada de peça</h2>\n    <p class=\"muted\">Aponte a câmera para o QR Code da caixa<br>ou digite o código da peça.</p>\n    ${s.scanning ? `\n      <div class=\"cam-wrap\"><video id=\"qr-video\" data-d2-static autoplay playsinline muted></video><div class=\"cam-frame\"></div></div>\n      <button class=\"btn ghost\" data-action=\"qr-stop\">Parar câmera</button>\n    ` : `<button class=\"btn primary xl w100\" data-action=\"qr-scan\">📷 Escanear QR Code</button>`}\n    <div class=\"or\">ou</div>\n    <div class=\"row-inline\">\n      <input id=\"qr-manual\" placeholder=\"QR-SP-000000 ou SP-000000\" class=\"xl-in\">\n      <button class=\"btn xl\" data-action=\"qr-manual\">OK</button>\n    </div>\n    ${st.err ? `<div class=\"err\">${esc(st.err)}</div>` : ''}\n  </div></div>`;\n}\n\nfunction vRegularizacao(s) {\n  const pend = (s.pre_retiradas || []).filter(p => p.status === 'pendente');\n  return `<h2>Regularização de pré-retiradas</h2>\n  ${!pend.length ? '<div class=\"card\"><div class=\"empty\">Nenhuma pré-retirada pendente. 🎉</div></div>' : `\n  <div class=\"reg-grid\">${pend.map(p => `<div class=\"card reg-card\" data-key=\"${p._id}\">\n    <div class=\"reg-head\"><b>${esc(p.codigo_sap)}</b><span class=\"pill adj\">pendente</span></div>\n    <div class=\"sub\">${esc(p.descricao)}</div>\n    <div class=\"reg-meta\">Solicitante: ${esc((p.solicitante || '').split('@')[0])} · ${fmtD(p.data_criacao)}</div>\n    <div class=\"row2\">\n      <div><label>Qtd final</label><input type=\"number\" min=\"1\" value=\"${p.quantidade}\" data-reg-qtd=\"${p._id}\"></div>\n      <div><label>Destino</label><select data-reg-dest=\"${p._id}\"><option>Uso interno</option><option>Transferência</option></select></div>\n    </div>\n    <button class=\"btn primary\" data-action=\"regularizar\" data-id=\"${p._id}\">Finalizar e dar baixa no SAP</button>\n  </div>`).join('')}</div>`}\n  <div class=\"card\"><div class=\"card-head\"><b>Regularizadas recentemente</b></div>\n  ${(s.pre_retiradas || []).filter(p => p.status === 'regularizada').slice(0, 5).map(p =>\n    `<div class=\"line\" data-key=\"${p._id}\"><span class=\"pill in\">regularizada</span> <b>${esc(p.codigo_sap)}</b> · ${p.quantidade} un · ${esc(p.destino)} · ${fmtD(p.data_regularizacao)}</div>`).join('') || '<div class=\"empty\">—</div>'}\n  </div>`;\n}\n\nfunction vMovs(s) {\n  return `<h2>Movimentações</h2>\n  <div class=\"card\"><div class=\"card-head\"><b>Histórico completo</b>\n    <span><button class=\"btn ghost sm\" data-action=\"reload-movs\">Atualizar</button>\n    <button class=\"btn ghost sm\" data-action=\"export-dbx\">Exportar p/ Databricks</button></span></div>\n  ${tblMovs(s.movimentacoes || [])}</div>`;\n}\n\nfunction vAdmin(s) {\n  const sec = s.admSec || 'origens';\n  const tabs = [['origens','Origens'],['unidades','Unidades'],['usuarios','Usuários'],['logs','Log de integração']];\n  let body = '';\n  if (sec === 'origens') {\n    body = `<div class=\"row-inline\"><input id=\"adm-orig-nome\" placeholder=\"Nova origem…\"><button class=\"btn primary sm\" data-action=\"adm-add-origem\">Adicionar</button></div>\n    <table><thead><tr><th>Nome</th><th>Descrição</th><th>Status</th><th></th></tr></thead><tbody>\n    ${(s.origens || []).map(o => `<tr data-key=\"${o._id}\"><td><b>${esc(o.nome)}</b></td><td class=\"sub\">${esc(o.descricao || '')}</td>\n      <td>${o.ativo !== false ? '<span class=\"pill in\">ativa</span>' : '<span class=\"pill out\">inativa</span>'}</td>\n      <td><button class=\"btn ghost sm\" data-action=\"adm-toggle-origem\" data-id=\"${o._id}\" data-on=\"${o.ativo !== false}\">${o.ativo !== false ? 'Desativar' : 'Ativar'}</button></td></tr>`).join('')}</tbody></table>`;\n  } else if (sec === 'unidades') {\n    body = `<div class=\"row-inline\"><input id=\"adm-un-cod\" placeholder=\"Código (ex.: CDRE)\" style=\"max-width:140px\"><input id=\"adm-un-nome\" placeholder=\"Nome da unidade\"><input id=\"adm-un-cid\" placeholder=\"Cidade\"><button class=\"btn primary sm\" data-action=\"adm-add-unidade\">Adicionar</button></div>\n    <table><thead><tr><th>Código</th><th>Nome</th><th>Cidade</th><th>Status</th></tr></thead><tbody>\n    ${(s.unidades || []).map(un => `<tr data-key=\"${un._id}\"><td><b>${esc(un.codigo)}</b></td><td>${esc(un.nome)}</td><td>${esc(un.cidade || '')}</td>\n      <td>${un.ativo !== false ? '<span class=\"pill in\">ativa</span>' : '<span class=\"pill adj\">expansão</span>'}</td></tr>`).join('')}</tbody></table>\n    <div class=\"hint\">A base já nasce multiunidade — novas unidades entram aqui na fase de expansão.</div>`;\n  } else if (sec === 'usuarios') {\n    body = `<div class=\"row-inline\"><input id=\"adm-us-nome\" placeholder=\"Nome\"><input id=\"adm-us-email\" placeholder=\"E-mail\"><input id=\"adm-us-pass\" placeholder=\"Senha\" style=\"max-width:120px\">\n    <select id=\"adm-us-role\" style=\"max-width:140px\"><option value=\"operador\">operador</option><option value=\"tecnico\">técnico</option><option value=\"admin\">admin</option></select>\n    <button class=\"btn primary sm\" data-action=\"adm-add-usuario\">Adicionar</button></div>\n    <table><thead><tr><th>Nome</th><th>E-mail</th><th>Papel</th><th>Unidade</th></tr></thead><tbody>\n    ${(s.usuarios || []).map(us => `<tr data-key=\"${us._id}\"><td><b>${esc(us.nome)}</b></td><td>${esc(us.email)}</td><td><span class=\"pill steel\">${esc(us.role)}</span></td><td>${esc(us.unidade || '')}</td></tr>`).join('')}</tbody></table>`;\n  } else {\n    body = `<table><thead><tr><th>Data</th><th>Sistema</th><th>Operação</th><th>Payload</th><th>Status</th><th>Referência</th></tr></thead><tbody>\n    ${(s.logs || []).map(l => `<tr data-key=\"${l._id}\"><td>${fmtD(l.data)}</td><td><b>${esc(l.sistema)}</b></td><td>${esc(l.operacao)}</td><td class=\"mono sub\">${esc(l.payload || '')}</td>\n      <td>${l.status === 'sucesso' ? '<span class=\"pill in\">sucesso</span>' : '<span class=\"pill out\">' + esc(l.status) + '</span>'}</td><td class=\"mono\">${esc(l.referencia || '')}</td></tr>`).join('')}</tbody></table>\n    <div class=\"hint\">Cada chamada às integrações (SAP, COUPA, Databricks) fica auditada aqui. Na PoC, as integrações são simuladas.</div>`;\n  }\n  return `<h2>Administração</h2>\n  <div class=\"tabs\">${tabs.map(([id, l]) => `<button class=\"tab ${sec === id ? 'on' : ''}\" data-action=\"adm-sec\" data-sec=\"${id}\">${l}</button>`).join('')}</div>\n  <div class=\"card\">${body}</div>`;\n}\n\n/* ---------- shell ---------- */\nfunction shell(s, u, content) {\n  return `<div class=\"app\">\n  <aside>\n    <div class=\"brand\">Data<sup>2</sup><span class=\"brand-sub\">Piloto NASP</span></div>\n    <nav>${NAV.filter(n => n.roles.includes(u.role)).map(n =>\n      `<button class=\"nav-it ${s.view === n.id ? 'on' : ''}\" data-action=\"nav\" data-view=\"${n.id}\"><span class=\"nav-ic\">${n.icon}</span>${n.label}</button>`).join('')}</nav>\n    <div class=\"side-foot\">\n      <div class=\"who\"><b>${esc(u.nome)}</b><span>${esc(u.role)} · ${esc(u.unidade)}</span></div>\n      <button class=\"btn ghost sm w100\" data-action=\"logout\">Sair</button>\n    </div>\n  </aside>\n  <main>${content}</main>\n  ${s.toast ? `<div class=\"toast ${s.toast.kind}\">${esc(s.toast.msg)}</div>` : ''}\n  </div>`;\n}\n\nexport default {\n  css: `\n  :host { all: initial; }\n  * { box-sizing: border-box; margin: 0; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }\n  .app { display: flex; width: 100%; height: 100%; background: #eff1f2; color: #1a2028; font-size: 14px; overflow: hidden; }\n  aside { width: 224px; flex: none; background: #fff; border-right: 1px solid #dde1e5; display: flex; flex-direction: column; padding: 18px 12px; }\n  .brand { font-weight: 800; font-size: 17px; padding: 0 10px 14px; border-bottom: 1px solid #eee; color: #1a2028; }\n  .brand sup { color: #c15a10; }\n  .brand-sub { display: block; font-size: 10px; color: #888; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; margin-top: 2px; }\n  nav { flex: 1; padding-top: 12px; display: flex; flex-direction: column; gap: 2px; }\n  .nav-it { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 9px 10px; border: 0; background: none; border-radius: 8px; color: #626d7a; font-size: 13.5px; cursor: pointer; }\n  .nav-it:hover { background: #f4f5f6; color: #1a2028; }\n  .nav-it.on { background: #f4e2d3; color: #c15a10; font-weight: 700; }\n  .nav-ic { width: 18px; text-align: center; }\n  .side-foot { border-top: 1px solid #eee; padding-top: 10px; }\n  .who { padding: 0 10px 8px; font-size: 12px; } .who b { display:block; font-size: 13px; } .who span { color:#888; }\n  main { flex: 1; overflow-y: auto; padding: 26px 30px 60px; }\n  h1, h2 { color: #1a2028; }\n  h2 { font-size: 21px; letter-spacing: -.02em; margin-bottom: 16px; font-weight: 800; }\n  .kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }\n  .kpi { background: #fff; border: 1px solid #dde1e5; border-radius: 10px; padding: 14px 16px; box-shadow: 0 1px 2px rgba(20,28,38,.05); }\n  .kpi-v { font-size: 22px; font-weight: 800; font-variant-numeric: tabular-nums; }\n  .kpi-v.ok { color: #2f7a4d; } .kpi-v.warn { color: #9a6b12; } .kpi-v.crit { color: #a5341f; }\n  .kpi-l { font-size: 12px; color: #626d7a; margin-top: 3px; }\n  .card { background: #fff; border: 1px solid #dde1e5; border-radius: 12px; padding: 16px 18px; margin-bottom: 16px; box-shadow: 0 1px 2px rgba(20,28,38,.05); }\n  .card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; gap: 8px; flex-wrap: wrap; }\n  table { width: 100%; border-collapse: collapse; font-size: 13px; }\n  th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; color: #888; padding: 7px 10px; border-bottom: 1px solid #dde1e5; background: #fafbfb; }\n  td { padding: 9px 10px; border-bottom: 1px solid #f0f1f2; vertical-align: middle; }\n  tr:last-child td { border-bottom: 0; }\n  td.num { font-variant-numeric: tabular-nums; font-weight: 600; }\n  .sub { color: #888; font-size: 12px; }\n  .mono { font-family: ui-monospace, monospace; font-size: 11.5px; color: #2f6a8d; }\n  .row-div { background: #fdf6f4; }\n  .pill { display: inline-block; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 2px 8px; border-radius: 20px; }\n  .pill.in { background: #dcefe2; color: #2f7a4d; } .pill.out { background: #f3ddd7; color: #a5341f; }\n  .pill.adj { background: #fbeedc; color: #9a6b12; } .pill.steel { background: #e2ecf2; color: #2f6a8d; }\n  .btn { border: 1px solid #c7ccd2; background: #fff; color: #1a2028; border-radius: 8px; padding: 9px 16px; font-size: 13.5px; font-weight: 600; cursor: pointer; }\n  .btn:hover { border-color: #999; }\n  .btn.primary { background: #c15a10; border-color: #c15a10; color: #fff; }\n  .btn.primary:hover { background: #a84d0c; }\n  .btn.ghost { border-color: transparent; background: #f4f5f6; }\n  .btn.sm { padding: 5px 10px; font-size: 12px; border-radius: 6px; }\n  .btn.xl { padding: 16px 22px; font-size: 18px; border-radius: 12px; }\n  .w100 { width: 100%; }\n  label { display: block; font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: #626d7a; margin: 12px 0 5px; }\n  input, select, textarea { width: 100%; border: 1px solid #c7ccd2; border-radius: 8px; padding: 9px 11px; font-size: 14px; background: #fff; color: #1a2028; }\n  input:focus, select:focus, textarea:focus { outline: 2px solid #f4e2d3; border-color: #c15a10; }\n  .cell-in { padding: 5px 8px; font-size: 13px; } .cell-in.num { width: 70px; }\n  .form { max-width: 620px; } .form .btn.primary { margin-top: 16px; }\n  .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: end; }\n  .row-inline { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }\n  .tabs { display: flex; gap: 6px; margin-bottom: 14px; flex-wrap: wrap; }\n  .tab { border: 1px solid #dde1e5; background: #fff; border-radius: 20px; padding: 7px 16px; font-size: 13px; font-weight: 600; color: #626d7a; cursor: pointer; }\n  .tab.on { background: #1a2028; border-color: #1a2028; color: #fff; }\n  .chart { display: flex; gap: 10px; align-items: flex-end; height: 150px; padding: 8px 4px 0; }\n  .bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; justify-content: flex-end; }\n  .bar { width: 100%; max-width: 54px; min-height: 4px; background: linear-gradient(180deg, #c15a10, #d97a35); border-radius: 6px 6px 2px 2px; position: relative; }\n  .bar span { position: absolute; top: -18px; left: 0; right: 0; text-align: center; font-size: 11px; font-weight: 700; color: #626d7a; }\n  .bar-l { font-size: 10.5px; color: #888; }\n  .empty { color: #888; padding: 18px 4px; text-align: center; }\n  .line { padding: 7px 0; border-bottom: 1px solid #f0f1f2; font-size: 13px; } .line:last-child { border: 0; }\n  .err { background: #f3ddd7; color: #a5341f; border-radius: 8px; padding: 9px 12px; font-size: 13px; margin-top: 10px; }\n  .note { background: #e2ecf2; color: #2f6a8d; border-radius: 8px; padding: 9px 12px; font-size: 13px; margin-top: 10px; }\n  .hint { color: #888; font-size: 12px; margin-top: 12px; }\n  .acts { white-space: nowrap; }\n  .toast { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); background: #1a2028; color: #fff; padding: 11px 20px; border-radius: 10px; font-size: 13.5px; font-weight: 600; box-shadow: 0 8px 24px rgba(0,0,0,.25); z-index: 50; }\n  .toast.err { background: #a5341f; }\n  /* login */\n  .login-wrap { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: radial-gradient(120% 150% at 100% 0%, #f4e2d3 0%, #eff1f2 55%); }\n  .login-card { width: 380px; max-width: 92vw; background: #fff; border: 1px solid #dde1e5; border-radius: 16px; padding: 30px; box-shadow: 0 12px 40px rgba(20,28,38,.10); }\n  .login-card h1 { font-size: 19px; letter-spacing: -.01em; margin: 16px 0 4px; }\n  .login-card .btn { margin-top: 18px; }\n  .muted { color: #626d7a; font-size: 13px; }\n  /* tablet */\n  .tablet { min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 10px; }\n  .t-card { width: 520px; max-width: 96vw; background: #fff; border: 1px solid #dde1e5; border-radius: 20px; padding: 34px; text-align: center; box-shadow: 0 12px 40px rgba(20,28,38,.08); }\n  .t-card h2 { font-size: 26px; margin-bottom: 8px; color: #1a2028; }\n  .t-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #c15a10; margin-bottom: 10px; }\n  .t-desc { font-size: 16px; color: #626d7a; margin-bottom: 18px; }\n  .t-big { font-size: 52px; color: #2f7a4d; }\n  .ok-bg h2 { color: #2f7a4d; }\n  .t-card .btn { margin-top: 14px; }\n  .or { color: #aaa; margin: 16px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: .1em; }\n  .xl-in { font-size: 17px; padding: 14px; text-align: center; }\n  .stepper { display: flex; gap: 16px; align-items: center; justify-content: center; margin: 18px 0; }\n  .step-v { font-size: 40px; font-weight: 800; min-width: 70px; font-variant-numeric: tabular-nums; }\n  .cam-wrap { position: relative; width: 100%; height: 300px; border-radius: 14px; overflow: hidden; background: #000; margin-top: 14px; }\n  .cam-wrap video { width: 100%; height: 100%; object-fit: cover; }\n  .cam-frame { position: absolute; inset: 12%; border: 3px solid rgba(255,255,255,.75); border-radius: 12px; pointer-events: none; }\n  .reg-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; margin-bottom: 16px; }\n  .reg-card .btn { margin-top: 14px; width: 100%; }\n  .reg-head { display: flex; justify-content: space-between; align-items: center; font-size: 15px; }\n  .reg-meta { color: #888; font-size: 12px; margin: 6px 0 2px; }\n  @media (max-width: 760px) {\n    .app { flex-direction: column; overflow: auto; }\n    aside { width: 100%; flex-direction: row; align-items: center; padding: 10px; gap: 8px; border-right: 0; border-bottom: 1px solid #dde1e5; }\n    .brand { border: 0; padding: 0 8px; } .brand-sub { display: none; }\n    nav { flex-direction: row; overflow-x: auto; padding: 0; }\n    .nav-it { white-space: nowrap; padding: 8px 10px; }\n    .side-foot { border: 0; padding: 0; } .who { display: none; }\n    main { padding: 16px 14px 50px; }\n    .row2 { grid-template-columns: 1fr; }\n  }\n  `,\n  render(d2) {\n    const s = d2.state;\n    if (!s.user) return vLogin(s);\n    const u = s.user;\n    const v = s.view || homeFor(u);\n    let content = '';\n    if (v === 'painel') content = vPainel(s);\n    else if (v === 'estoque') content = vEstoque(s, u);\n    else if (v === 'entrada') content = vEntrada(s);\n    else if (v === 'tablet') content = vTablet(s, u);\n    else if (v === 'regularizacao') content = vRegularizacao(s);\n    else if (v === 'movs') content = vMovs(s);\n    else if (v === 'admin') content = vAdmin(s);\n    return shell(s, u, content);\n  },\n  mounted(d2, root) {\n    try {\n    /* ---- compat layer: o player publicado pode rodar um SDK d2 mais antigo ---- */\n    const $ = sel => { let el = null; try { el = d2.$ ? d2.$(sel) : null; } catch (e) {} return el || root.querySelector(sel); };\n    const on = (sel, evt, fn) => root.addEventListener(evt, e => { const t = e.target; if (t && t.closest && t.closest(sel)) fn(e); });\n    const raf = (fn) => {\n      if (typeof d2.raf === 'function') return d2.raf(fn);\n      let go = true;\n      const loop = (t) => { if (!go) return; if (fn(t) === false) { go = false; return; } requestAnimationFrame(loop); };\n      requestAnimationFrame(loop);\n      return () => { go = false; };\n    };\n    let jsqrMod = null;\n    const getJsQR = async () => {\n      if (jsqrMod) return jsqrMod;\n      try { if (typeof d2.lib === 'function') { const m = d2.lib('jsqr@1.4.0'); if (m) { jsqrMod = m.default || m; return jsqrMod; } } } catch (e) {}\n      try { const m = await import('https://esm.sh/jsqr@1.4.0'); jsqrMod = m.default || m; } catch (e) {}\n      return jsqrMod;\n    };\n    getJsQR(); // pre-warm QR lib\n    const ask = (msg, def) => (d2.dialog && d2.dialog.prompt) ? d2.dialog.prompt(msg, def) : Promise.resolve(window.prompt(msg, def));\n    try {\n      const saved = localStorage.getItem('nasp_user');\n      if (saved) { d2.state.user = JSON.parse(saved); d2.state.view = homeFor(d2.state.user); }\n    } catch (e) {}\n\n    const loadForView = async (v) => {\n      if (v === 'painel') { await Promise.all([load(d2, 'estoque'), load(d2, 'movimentacoes'), load(d2, 'pre_retiradas')]); }\n      if (v === 'estoque') await load(d2, 'estoque');\n      if (v === 'entrada') { await Promise.all([load(d2, 'estoque'), load(d2, 'origens')]); }\n      if (v === 'regularizacao') await load(d2, 'pre_retiradas');\n      if (v === 'movs') await load(d2, 'movimentacoes');\n      if (v === 'admin') { await Promise.all([load(d2, 'origens'), load(d2, 'unidades'), load(d2, 'usuarios'), load(d2, 'logs')]); }\n    };\n    if (d2.state.user) loadForView(d2.state.view || homeFor(d2.state.user));\n\n    let stopRaf = null, stream = null;\n    const stopCam = () => {\n      if (stopRaf) { stopRaf(); stopRaf = null; }\n      if (stream) { stream.getTracks().forEach(t => t.stop()); stream = null; }\n      d2.state.scanning = false;\n    };\n    if (typeof d2.onUnmount === 'function') d2.onUnmount(stopCam);\n\n    const foundCode = async (code) => {\n      stopCam();\n      const r = await rush(d2, 'nasp-data', { action: 'pecas' });\n      const pecas = r.items || [];\n      const p = pecas.find(x => x.qrcode === code || x.codigo_sap === code || x.qrcode === code.toUpperCase() || x.codigo_sap === code.toUpperCase());\n      if (!p) { d2.state.qr = { step: 'idle', err: 'Peça não encontrada para \"' + code + '\". Tente novamente.' }; return; }\n      d2.state.qr = { step: 'confirm', peca: p, qtd: 1 };\n    };\n\n    const startCam = async () => {\n      const jsQR = await getJsQR();\n      if (!jsQR) { toast(d2, 'Não foi possível carregar o leitor de QR — digite o código.', 'err'); return; }\n      try {\n        stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } });\n      } catch (e) { toast(d2, 'Câmera indisponível — digite o código.', 'err'); return; }\n      d2.state.scanning = true;\n      setTimeout(() => {\n        const video = $('#qr-video');\n        if (!video) { stopCam(); return; }\n        video.srcObject = stream;\n        const cv = document.createElement('canvas');\n        const ctx = cv.getContext('2d', { willReadFrequently: true });\n        stopRaf = raf(() => {\n          if (!video.videoWidth) return;\n          cv.width = video.videoWidth; cv.height = video.videoHeight;\n          ctx.drawImage(video, 0, 0);\n          const img = ctx.getImageData(0, 0, cv.width, cv.height);\n          const hit = jsQR(img.data, img.width, img.height);\n          if (hit && hit.data) { foundCode(hit.data.trim()); return false; }\n        });\n      }, 60);\n    };\n\n    const readInput = sel => { const el = $(sel); return el ? el.value.trim() : ''; };\n\n    on('select#ent-origem', 'change', e => {\n      d2.state.entOrigem = e.target.value; d2.state.coupaInfo = null;\n    });\n    on('input[data-lote-cod]', 'change', e => {\n      const i = +e.target.dataset.loteCod; const l = [...d2.state.lote]; l[i] = { ...l[i], codigo: e.target.value.trim(), status: 'pendente' }; d2.state.lote = l;\n    });\n    on('input[data-lote-qtd]', 'change', e => {\n      const i = +e.target.dataset.loteQtd; const l = [...d2.state.lote]; l[i] = { ...l[i], qtd: +e.target.value || 1, status: l[i].status === 'ok' ? 'ok' : 'pendente' }; d2.state.lote = l;\n    });\n    on('#nf-file', 'change', async e => {\n      const f = e.target.files && e.target.files[0];\n      if (!f) return;\n      d2.state.nfBusy = true;\n      try {\n        const dataUrl = await new Promise((res, rej) => { const r = new FileReader(); r.onload = () => res(r.result); r.onerror = rej; r.readAsDataURL(f); });\n        if (typeof d2.assistant !== 'function') throw new Error('assistant indisponivel');\n        const ans = await d2.assistant('nasp-nf-reader').ask('Extraia os itens desta nota fiscal. Responda apenas o JSON.', { images: [dataUrl], json: true });\n        const obj = typeof ans === 'string' ? JSON.parse(ans) : ans;\n        const itens = (obj && obj.itens) || [];\n        if (!itens.length) { toast(d2, 'Nenhum item identificado na nota.', 'err'); }\n        d2.state.lote = [...(d2.state.lote || []), ...itens.map(it => ({ codigo: it.codigo_sap || '', desc: it.descricao || '', qtd: +it.quantidade || 1, status: 'pendente' }))];\n      } catch (err) {\n        toast(d2, 'Não foi possível ler a nota — use o CSV ou a entrada individual.', 'err');\n      }\n      d2.state.nfBusy = false;\n      e.target.value = '';\n    });\n\n    const aprovarItem = async (i) => {\n      const it = d2.state.lote[i];\n      if (!it || !it.codigo) { return { error: 'código vazio' }; }\n      const origem = readInput('#lote-origem') || 'Compra nova';\n      const r = await rush(d2, 'nasp-mov', { tipo: 'entrada', codigo_sap: it.codigo, quantidade: it.qtd, origem, usuario: d2.state.user.email, data: nowIso() });\n      const l = [...d2.state.lote];\n      l[i] = { ...l[i], status: r.ok ? 'ok' : 'erro', msg: r.error || '' };\n      d2.state.lote = l;\n      return r;\n    };\n\n    on('[data-action]', 'click', async e => {\n      const el = e.target.closest('[data-action]');\n      if (!el) return;\n      const a = el.dataset.action;\n      const s = d2.state;\n\n      if (a === 'login') {\n        s.busy = true; s.loginErr = '';\n        const r = await rush(d2, 'nasp-login', { email: readInput('#lg-email'), password: readInput('#lg-pass') });\n        s.busy = false;\n        if (r.user) {\n          s.user = r.user; s.view = homeFor(r.user);\n          try { localStorage.setItem('nasp_user', JSON.stringify(r.user)); } catch (_) {}\n          loadForView(s.view);\n        } else s.loginErr = r.error || 'Falha no login';\n      }\n      else if (a === 'logout') { stopCam(); try { localStorage.removeItem('nasp_user'); } catch (_) {} s.user = null; s.view = null; s.qr = null; }\n      else if (a === 'nav') { stopCam(); s.view = el.dataset.view; s.qr = null; loadForView(s.view); }\n      else if (a === 'reload-est') { await load(d2, 'estoque', true); toast(d2, 'Estoque atualizado'); }\n      else if (a === 'reload-movs') { await load(d2, 'movimentacoes', true); toast(d2, 'Histórico atualizado'); }\n      else if (a === 'export-dbx') {\n        s.busy = true;\n        const r = await rush(d2, 'nasp-export-databricks', { data: nowIso() });\n        s.busy = false;\n        toast(d2, r.ok ? `${r.exportadas} movimentações exportadas para o Databricks (mock)` : (r.error || 'Falha no export'), r.ok ? 'ok' : 'err');\n      }\n      else if (a === 'ajustar') {\n        const cod = el.dataset.cod;\n        const sap = el.dataset.sap;\n        const novo = await ask(`Resolver divergência de ${cod}.\\nSaldo Natura: ${el.dataset.nat} · Saldo SAP: ${sap}\\n\\nInforme o saldo correto (contagem física):`, sap);\n        if (novo === null || novo === undefined || novo === '') return;\n        const r = await rush(d2, 'nasp-ajuste', { codigo_sap: cod, novo_saldo: +novo, usuario: s.user.email, data: nowIso() });\n        if (r.ok) { toast(d2, `Saldo ajustado e espelhado no SAP (${r.sap_ref})`); await load(d2, 'estoque', true); await load(d2, 'movimentacoes', true); }\n        else toast(d2, r.error || 'Falha no ajuste', 'err');\n      }\n      else if (a === 'ent-tab') { s.entTab = el.dataset.tab; }\n      else if (a === 'coupa') {\n        const oc = readInput('#ent-oc');\n        s.entOc = oc;\n        const r = await rush(d2, 'nasp-coupa-po', { oc });\n        if (r.oc) s.coupaInfo = r; else toast(d2, r.error || 'OC não encontrada', 'err');\n      }\n      else if (a === 'ent-confirmar') {\n        const codigo = readInput('#ent-peca'), qtd = +readInput('#ent-qtd'), origem = readInput('#ent-origem');\n        s.entOc = readInput('#ent-oc');\n        const r = await rush(d2, 'nasp-mov', { tipo: 'entrada', codigo_sap: codigo, quantidade: qtd, origem, usuario: s.user.email, data: nowIso() });\n        if (r.ok) { toast(d2, `Entrada confirmada — SAP ${r.sap_ref} · novo saldo ${r.saldo_natura}`); await load(d2, 'estoque', true); await load(d2, 'movimentacoes', true); }\n        else toast(d2, r.error || 'Falha na entrada', 'err');\n      }\n      else if (a === 'lote-parse') {\n        const txt = readInput('#lote-csv');\n        const rows = txt.split(/\\n+/).map(l => l.trim()).filter(Boolean).map(l => {\n          const [c, q] = l.split(/[;,\\t]/);\n          return { codigo: (c || '').trim().toUpperCase(), desc: '', qtd: +(q || '').trim() || 1, status: 'pendente' };\n        }).filter(r => r.codigo);\n        if (!rows.length) { toast(d2, 'Nenhuma linha válida no CSV', 'err'); return; }\n        s.lote = [...(s.lote || []), ...rows];\n      }\n      else if (a === 'lote-aprovar') { await aprovarItem(+el.dataset.i); await load(d2, 'estoque', true); }\n      else if (a === 'lote-aprovar-todos') {\n        for (let i = 0; i < d2.state.lote.length; i++) {\n          if (d2.state.lote[i].status !== 'ok') await aprovarItem(i);\n        }\n        await load(d2, 'estoque', true); await load(d2, 'movimentacoes', true);\n        const okN = d2.state.lote.filter(x => x.status === 'ok').length;\n        toast(d2, `${okN}/${d2.state.lote.length} itens do lote confirmados no estoque + SAP`);\n      }\n      else if (a === 'lote-remover') { const l = [...s.lote]; l.splice(+el.dataset.i, 1); s.lote = l; }\n      else if (a === 'qr-scan') startCam();\n      else if (a === 'qr-stop') stopCam();\n      else if (a === 'qr-manual') { const c = readInput('#qr-manual'); if (c) foundCode(c); }\n      else if (a === 'qr-inc') { s.qr = { ...s.qr, qtd: s.qr.qtd + 1 }; }\n      else if (a === 'qr-dec') { s.qr = { ...s.qr, qtd: Math.max(1, s.qr.qtd - 1) }; }\n      else if (a === 'qr-reset') { stopCam(); s.qr = { step: 'idle' }; }\n      else if (a === 'qr-confirm') {\n        s.busy = true;\n        const r = await rush(d2, 'nasp-pre-retirada', { codigo_sap: s.qr.peca.codigo_sap, quantidade: s.qr.qtd, solicitante: s.user.email, data: nowIso() });\n        s.busy = false;\n        if (r.ok) { d2.state['ld_pre_retiradas'] = false; s.qr = { step: 'done', peca: s.qr.peca, qtd: s.qr.qtd }; }\n        else toast(d2, r.error || 'Falha ao registrar', 'err');\n      }\n      else if (a === 'regularizar') {\n        const id = el.dataset.id;\n        const qEl = $(`input[data-reg-qtd=\"${id}\"]`), dEl = $(`select[data-reg-dest=\"${id}\"]`);\n        const r = await rush(d2, 'nasp-regularizar', { id, quantidade: qEl ? +qEl.value : 0, destino: dEl ? dEl.value : 'Uso interno', usuario: s.user.email, data: nowIso() });\n        if (r.ok) { toast(d2, `Baixa registrada no SAP — ${r.sap_ref}`); await load(d2, 'pre_retiradas', true); await load(d2, 'estoque', true); await load(d2, 'movimentacoes', true); }\n        else toast(d2, r.error || 'Falha na regularização', 'err');\n      }\n      else if (a === 'adm-sec') { s.admSec = el.dataset.sec; }\n      else if (a === 'adm-add-origem') {\n        const nome = readInput('#adm-orig-nome');\n        if (!nome) return;\n        const r = await rush(d2, 'nasp-admin', { entity: 'origens', record: { nome, descricao: '', ativo: true } });\n        if (r.ok) { toast(d2, 'Origem adicionada'); await load(d2, 'origens', true); } else toast(d2, r.error || 'Falha', 'err');\n      }\n      else if (a === 'adm-toggle-origem') {\n        const r = await rush(d2, 'nasp-admin', { entity: 'origens', record: { _id: el.dataset.id, ativo: el.dataset.on !== 'true' } });\n        if (r.ok) { await load(d2, 'origens', true); } else toast(d2, r.error || 'Falha', 'err');\n      }\n      else if (a === 'adm-add-unidade') {\n        const codigo = readInput('#adm-un-cod').toUpperCase(), nome = readInput('#adm-un-nome'), cidade = readInput('#adm-un-cid');\n        if (!codigo || !nome) return;\n        const r = await rush(d2, 'nasp-admin', { entity: 'unidades', record: { codigo, nome, cidade, ativo: false } });\n        if (r.ok) { toast(d2, 'Unidade cadastrada (expansão)'); await load(d2, 'unidades', true); } else toast(d2, r.error || 'Falha', 'err');\n      }\n      else if (a === 'adm-add-usuario') {\n        const nome = readInput('#adm-us-nome'), email = readInput('#adm-us-email'), password = readInput('#adm-us-pass'), role = readInput('#adm-us-role');\n        if (!nome || !email || !password) return;\n        const r = await rush(d2, 'nasp-admin', { entity: 'usuarios', record: { nome, email, password, role, unidade: 'NASP', ativo: true } });\n        if (r.ok) { toast(d2, 'Usuário criado'); await load(d2, 'usuarios', true); } else toast(d2, r.error || 'Falha', 'err');\n      }\n    });\n    console.log('[nasp] wired');\n    } catch (e) { console.error('[nasp] mounted error', e && (e.stack || e.message || e)); }\n  }\n};"}}}}}}}},{"_id":"6a71f3c910437424105b8110","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"lelisApp","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Le Lis — Provador Virtual (v2: home → lançamentos → provador)\nconst fmtPrice = (v) => (v == null || isNaN(v)) ? '' : 'R$ ' + Number(v).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n\nconst classify = (prod) => {\n  const path = String((prod && prod.categoryPath) || '').toLowerCase();\n  if (/bolsa|acess/.test(path)) return 'bag';\n  if (/vestido|macac/.test(path)) return 'dress';\n  if (/cal[çc]a|saia|short|bermuda/.test(path)) return 'bottom';\n  return 'top';\n};\nconst isClothing = (p) => {\n  const path = String((p && p.categoryPath) || '').toLowerCase();\n  if (/acess|sapato|cal[çc]ado|t[êe]nis|bolsa|clutch|mochila|carteira|\\bhome\\b|casa|decora|aroma|vela|len[çc]|xale|poncho|cinto|meia|[óo]culos|joia|colar|brinco|pulseira|\\banel\\b|broche|rel[óo]gio|necess|bon[ée]|chap[ée]u|viseira|fragr|perfume/.test(path)) return false;\n  return /roupa|moda/.test(path);\n};\nconst kindLabel = (k) => k === 'bag' ? 'Acessório' : (k === 'dress' ? 'Vestido' : (k === 'bottom' ? 'Peça de baixo' : 'Peça de cima'));\n\n// ---- Prompts: strict framing + hard replacement (no layering over existing clothes) ----\nconst FRAME = 'ENQUADRAMENTO (regra absoluta): a imagem final deve ter EXATAMENTE o mesmo enquadramento, proporção, ângulo e distância da câmera da Imagem 1. Mostre a pessoa na MESMA medida em que ela aparece na Imagem 1 — se a foto for de corpo inteiro, mantenha da cabeça aos pés. NUNCA corte a cabeça, os pés, as mãos ou as laterais do corpo, NUNCA aproxime o zoom e NUNCA reenquadre. Mantenha o mesmo fundo, o mesmo cenário e as mesmas margens da Imagem 1.';\nconst FIDELITY = 'FIDELIDADE: preserve 100% o rosto, o cabelo, o tom de pele, o corpo, a pose e a iluminação da pessoa da Imagem 1. A Imagem 2 pode mostrar a peça sozinha OU vestida em uma modelo — use SOMENTE a peça de roupa, ignore por completo qualquer modelo, rosto, corpo, pose ou cenário que apareça na Imagem 2. Copie da Imagem 2 exatamente a cor, a estampa, a textura, o comprimento, a modelagem, a gola, as mangas e os detalhes.';\nconst buildPrompt = (kind, name, styleInstr) => {\n  const head = 'Imagem 1 = foto de uma pessoa. Imagem 2 = uma peça da Le Lis (\"' + (name || '') + '\"). ' + FRAME + ' ' + FIDELITY + ' ';\n  if (kind !== 'bag' && styleInstr && String(styleInstr).trim()) {\n    return head + 'DECISÃO DE VESTIMENTA (siga à risca, definida para este caso): ' + styleInstr + ' É PROIBIDO deixar qualquer peça que deveria ser removida aparecendo por baixo, nas mangas, no decote, nas alças ou nas bordas. Se a peça deixa ombros ou braços à mostra, mostre a pele nua da pessoa nessas áreas, sem nenhum tecido por baixo.';\n  }\n  if (kind === 'bag') {\n    return head + 'TAREFA: mantenha a roupa da pessoa exatamente como está e faça ela carregar esta bolsa de forma natural e condizente com o tipo (transversal atravessada no corpo, de ombro, de mão pela alça, ou clutch na mão). Mantenha formato, tamanho proporcional, cor, alça e ferragens idênticos aos da Imagem 2. Remova qualquer outra bolsa que já apareça na Imagem 1. Não altere mais nada na pessoa nem no fundo.';\n  }\n  if (kind === 'dress') {\n    return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE toda a roupa que a pessoa já veste (tanto a parte de cima quanto a de baixo) ANTES de colocar a peça nova. Vista a pessoa APENAS com este vestido/macacão da Imagem 2. É PROIBIDO deixar a peça nova por cima da roupa antiga: não pode sobrar nenhuma gola, manga, barra, alça, camiseta, calça ou tecido da roupa anterior aparecendo por baixo, nas bordas, no decote ou nas pernas. O resultado deve mostrar a pessoa usando somente a peça da Le Lis, com caimento natural.';\n  }\n  if (kind === 'bottom') {\n    return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE a calça/saia/short que a pessoa já usa na parte de baixo ANTES de vestir a peça nova, e coloque esta peça de baixo da Imagem 2 no lugar. É PROIBIDO vestir a peça nova por cima da antiga — não pode sobrar barra, tecido ou peça anterior aparecendo. Mantenha INTACTAS a parte de cima do corpo (blusa/camisa), o rosto, o cabelo e o fundo. Caimento natural e cintura na posição correta.';\n  }\n  // top\n  return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE a blusa/camisa/casaco que a pessoa já veste na parte de cima ANTES de colocar a peça nova, e vista esta peça de cima da Imagem 2 no lugar. É PROIBIDO colocar a peça nova por cima da roupa existente — não pode sobrar gola, manga, barra ou tecido da peça antiga aparecendo por baixo, nas bordas ou no decote. Mantenha INTACTAS a parte de baixo (calça/saia/short), o rosto, o cabelo e o fundo. Caimento natural.';\n};\n\nconst readAsDataURL = (file) => new Promise((resolve, reject) => {\n  const r = new FileReader();\n  r.onload = () => {\n    const img = new Image();\n    img.onload = () => {\n      try {\n        const MAX = 1152;\n        let w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;\n        if (w > h && w > MAX) { h = Math.round(h * MAX / w); w = MAX; }\n        else if (h >= w && h > MAX) { w = Math.round(w * MAX / h); h = MAX; }\n        const canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        canvas.getContext('2d').drawImage(img, 0, 0, w, h);\n        resolve(canvas.toDataURL('image/jpeg', 0.9));\n      } catch (e) { resolve(r.result); }\n    };\n    img.onerror = () => resolve(r.result);\n    img.src = r.result;\n  };\n  r.onerror = reject;\n  r.readAsDataURL(file);\n});\n\nconst urlToDataUrl = (url) => fetch(url).then(r => r.blob()).then(b => new Promise((res, rej) => { const fr = new FileReader(); fr.onload = () => res(fr.result); fr.onerror = rej; fr.readAsDataURL(b); }));\n\nconst BRANDS = [\n  { id: 'lelis', name: 'Le Lis', base: 'https://www.lelis.com.br', accent: '#8C6E5D', cats: [\n    { label: 'Lançamentos', path: '' }, { label: 'Vestidos', path: 'Feminino/Roupas/Vestidos' }, { label: 'Blusas', path: 'Feminino/Roupas/Blusas' }, { label: 'Tricots', path: 'Feminino/Roupas/Tricots' }, { label: 'Calças', path: 'Feminino/Roupas/Calças' }, { label: 'Saias', path: 'Feminino/Roupas/Saias' }, { label: 'Blazers', path: 'Feminino/Roupas/Blazers' } ] },\n  { id: 'johnjohn', name: 'John John', base: 'https://www.johnjohndenim.com.br', accent: '#1F2937', cats: [\n    { label: 'Lançamentos', path: '' }, { label: 'Vestidos', path: 'Feminino/Roupas/Vestidos' }, { label: 'Blusas', path: 'Feminino/Roupas/Blusas e Camisetas' }, { label: 'Feminino', path: 'Feminino' }, { label: 'Masculino', path: 'Masculino' } ] },\n  { id: 'bobo', name: 'Bo.Bô', base: 'https://www.bobo.com.br', accent: '#7A2E3B', cats: [\n    { label: 'Lançamentos', path: '' }, { label: 'Vestidos', path: 'Roupas/Vestidos' }, { label: 'Blusas', path: 'Roupas/Blusas' }, { label: 'Calças', path: 'Roupas/Calças' }, { label: 'Saias', path: 'Roupas/Saias' }, { label: 'Blazers', path: 'Roupas/Blazers' } ] },\n  { id: 'individual', name: 'Individual', base: 'https://www.individual.com.br', accent: '#33463B', cats: [\n    { label: 'Lançamentos', path: '' }, { label: 'Camisas', path: 'Moda/Camisas' }, { label: 'Polos', path: 'Moda/Polos e Camisetas/Polos' }, { label: 'Camisetas', path: 'Moda/Polos e Camisetas/Camisetas' } ] },\n  { id: 'dudalina', name: 'Dudalina', base: 'https://www.dudalina.com.br', accent: '#2C4A6E', cats: [\n    { label: 'Lançamentos', path: '' }, { label: 'Camisas', path: 'Masculino/Roupas/Camisas' }, { label: 'Camisetas', path: 'Masculino/Roupas/Camisetas' }, { label: 'Polos', path: 'Masculino/Roupas/Polos' }, { label: 'Feminino', path: 'Feminino' } ] }\n];\nconst brandById = (id) => BRANDS.find(b => b.id === id) || null;\n\nconst topbar = (extra, looksCount, lookCount, back, brand) => `\n  <header class=\"bar\">\n    ${back ? '<button class=\"go-back\">← Voltar</button>' : ''}\n    <button class=\"nav-home wordmark-sm\"><svg class=\"home-ico\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 10.5 12 3l9 7.5\"/><path d=\"M5 9.5V20h14V9.5\"/></svg>${brand || 'Grupo Veste'}</button>\n    <span class=\"bar-tag\">Provador Virtual</span>\n    <div class=\"bar-right\">\n      ${lookCount ? `<button class=\"go-look\">Meu look <span class=\"looks-badge\">${lookCount}</span></button>` : ''}\n      <button class=\"go-looks\">Minhas provas${looksCount ? ' <span class=\"looks-badge\">' + looksCount + '</span>' : ''}</button>\n      ${extra || ''}\n    </div>\n  </header>`;\n\nconst cardHTML = (p, lookCodes) => {\n  const hasDisc = p.listPrice && p.price && p.listPrice > p.price;\n  const inLook = lookCodes && lookCodes.has(String(p.code));\n  return `<div class=\"card\" data-code=\"${esc(p.code)}\" data-key=\"${esc(p.code)}\">\n    <div class=\"card-img-wrap\">\n      <img class=\"card-img\" src=\"${esc(p.image)}\" alt=\"\" loading=\"lazy\" />\n      <button class=\"card-add${inLook ? ' in' : ''}\" data-code=\"${esc(p.code)}\" title=\"Adicionar ao look\">${inLook ? '✓ no look' : '+ look'}</button>\n      <div class=\"card-cta\">Experimentar</div>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"card-name\">${esc(p.name)}</div>\n      <div class=\"card-price\">${hasDisc ? '<span class=\"card-old\">' + fmtPrice(p.listPrice) + '</span>' : ''}${fmtPrice(p.price)}</div>\n    </div>\n  </div>`;\n};\n\nconst renderHome = (st) => {\n  const hero = st.heroImage || '';\n  const bg = hero ? `style=\"background-image:linear-gradient(90deg, rgba(20,16,14,0.55) 0%, rgba(20,16,14,0.15) 45%, rgba(20,16,14,0.05) 100%), url('${esc(hero)}')\"` : '';\n  return `\n    <div class=\"app home\">\n      <div class=\"hero ${hero ? '' : 'hero-plain'}\">\n        ${hero ? `<img class=\"hero-img\" src=\"${esc(hero)}\" alt=\"\" />` : ''}\n        <div class=\"hero-shade\"></div>\n        <div class=\"hero-top\"><span class=\"wordmark\">Le Lis</span>${st.looksCount ? `<button class=\"go-looks home-looks\">Minhas provas <span class=\"looks-badge\">${st.looksCount}</span></button>` : ''}</div>\n        <div class=\"hero-content\">\n          <div class=\"kicker light\">Provador Virtual</div>\n          <h1 class=\"hero-title\">Vista as novas peças<br/>da Le Lis com a sua foto.</h1>\n          <p class=\"hero-sub\">Escolha entre os lançamentos e veja na hora como cada peça fica em você — sem sair de casa.</p>\n          <button class=\"btn-primary go-lancamentos\">Ver os lançamentos</button>\n          <div class=\"hero-tag\">moda feminina sofisticada e atemporal</div>\n        </div>\n      </div>\n    </div>`;\n};\n\nconst renderGrid = (st) => {\n  const { products = [], gridStatus = 'loading', gridError = null, total = 0, category = '', search = '' } = st;\n  const lookCodes = new Set((st.lookItems || []).map(x => String(x.code)));\n  const isNew = category === '' && !search;\n  const brandCats = (st.brand && st.brand.cats) || [];\n  const cats = brandCats.map(c => `<button class=\"cat-link${category === c.path && !search ? ' active' : ''}\" data-path=\"${esc(c.path)}\">${c.label}</button>`).join('');\n\n  let grid;\n  if (gridStatus === 'loading') grid = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando peças...</span></div>`;\n  else if (gridStatus === 'error') grid = `<div class=\"state-full\"><span>Erro ao carregar: ${esc(gridError)}</span></div>`;\n  else if (!products.length) grid = `<div class=\"state-msg\">Nenhuma peça encontrada${search ? ' para \"' + esc(search) + '\"' : ''}.</div>`;\n  else grid = `<div class=\"grid\">${products.map(p => cardHTML(p, lookCodes)).join('')}</div>`;\n\n  const title = search ? `Resultados para “${esc(search)}”` : (isNew ? 'As chegadas mais recentes' : (brandCats.find(c => c.path === category) || {}).label || 'Seleção');\n\n  return `\n    <div class=\"app grid-screen\">\n      ${topbar(`\n        <div class=\"search-wrap\">\n          <input class=\"search-input\" placeholder=\"Buscar peças...\" value=\"${esc(search)}\" />\n          ${search ? '<button class=\"search-clear\" title=\"Limpar\">✕</button>' : ''}\n          <button class=\"search-btn\" title=\"Buscar\">🔍</button>\n        </div>`, st.looksCount, st.lookCount, true, (st.brand ? st.brand.name : 'Grupo Veste'))}\n      <div class=\"editorial\">\n        <div class=\"kicker\">${st.brand ? esc(st.brand.name) + ' · ' : ''}${isNew ? 'Lançamentos' : 'Coleção'}</div>\n        <h2 class=\"ed-title\">${title}</h2>\n        <p class=\"ed-sub\">Escolha uma peça e experimente com a sua foto.${gridStatus === 'done' && total ? ' <span class=\"count\">· ' + total + ' peças</span>' : ''}</p>\n      </div>\n      <div class=\"cat-row\">${cats}</div>\n      <div class=\"grid-wrapper\">${grid}</div>\n    </div>`;\n};\n\nconst renderDetail = (st) => {\n  const sp = st.selectedProduct;\n  if (!sp) return renderGrid(st);\n  const { selectedSize = null, tryStatus = 'idle', tryImage = null, tryError = null, tryAttempt = 1, gateError = null, gateSubmitting = false } = st;\n  const kind = classify(sp);\n  const inLook = (st.lookItems || []).some(x => String(x.code) === String(sp.code));\n  const hasDisc = sp.listPrice && sp.price && sp.listPrice > sp.price;\n  const sizes = (sp.sizes || []).map(v =>\n    `<button class=\"size-btn${selectedSize === v.id ? ' active' : ''}${v.available === false ? ' off' : ''}\" data-size=\"${esc(v.id)}\" data-key=\"${esc(v.id)}\"${v.available === false ? ' disabled' : ''}>${esc(v.label)}</button>`\n  ).join('');\n\n  let tryOnHTML;\n  if (tryStatus === 'gate') {\n    tryOnHTML = `\n      <div class=\"gate-inline\">\n        <div class=\"gate-inline-kicker\">Quase lá</div>\n        <div class=\"gate-inline-title\">Diga quem é você para provar</div>\n        <input class=\"gate-name\" type=\"text\" placeholder=\"Seu nome\" />\n        <input class=\"gate-email\" type=\"email\" placeholder=\"seu@email.com\" />\n        <button class=\"cta-btn gate-submit\"${gateSubmitting ? ' disabled' : ''}>${gateSubmitting ? 'Um instante...' : 'Continuar para a foto'}</button>\n        ${gateError ? '<div class=\"err-inline\">' + esc(gateError) + '</div>' : ''}\n        <div class=\"gate-note\">Usamos seus dados apenas para esta experiência.</div>\n      </div>`;\n  } else if (tryStatus === 'checking' || tryStatus === 'uploading' || tryStatus === 'generating') {\n    const msg = tryStatus === 'checking' ? 'Analisando sua foto...' : (tryStatus === 'uploading' ? 'Preparando sua foto...' : 'Criando sua prova virtual...');\n    const sub = tryStatus === 'generating' ? ((tryAttempt > 1 ? 'Tentativa ' + tryAttempt + ' de 3 — ' : '') + 'pode levar até 30 segundos') : 'Só um instante';\n    tryOnHTML = `<div class=\"gen-box\"><div class=\"spinner\"></div><div class=\"gen-status\">${msg}</div><div class=\"gen-sub\">${sub}</div></div>`;\n  } else if (tryStatus === 'rejected') {\n    tryOnHTML = `<div class=\"warn-box\">${esc(tryError)}</div><button class=\"cta-btn tryon-btn\">Enviar outra foto</button>`;\n  } else if (tryStatus === 'done' && tryImage) {\n    tryOnHTML = `<div class=\"gen-box\"><img class=\"result-img\" src=\"${tryImage}\" alt=\"Prova virtual\" /><div class=\"result-badge\">✓ Salvo em Minhas provas</div><button class=\"cta-btn go-looks\">Ver minhas provas</button><button class=\"ghost-btn try-again\" style=\"margin-top:10px\">Experimentar com outra foto</button></div>`;\n  } else if (tryStatus === 'error') {\n    tryOnHTML = `<button class=\"cta-btn tryon-btn\">Tentar novamente</button><div class=\"err-box\">Não consegui gerar a prova: ${esc(tryError)}</div>`;\n  } else {\n    const hint = kind === 'bag'\n      ? 'Envie uma foto sua (da cintura para cima já funciona) e a IA mostra você com esta bolsa.'\n      : 'Envie uma foto sua de corpo inteiro, com boa luz, e a IA mostra você vestindo esta peça.';\n    tryOnHTML = `<button class=\"cta-btn tryon-btn\">Experimentar com a minha foto</button><p class=\"cta-hint\">${hint}</p>`;\n  }\n\n  return `\n    <div class=\"app detail-screen\">\n      ${topbar(`<button class=\"back-grid\">← Voltar</button>`, st.looksCount, st.lookCount, false, (st.brand ? st.brand.name : 'Grupo Veste'))}\n      <div class=\"detail-wrap\">\n        <div class=\"detail-media\"><img class=\"detail-img\" src=\"${esc(sp.image)}\" alt=\"\" /></div>\n        <div class=\"detail-side\">\n          <div class=\"kicker\">${kindLabel(kind)}</div>\n          <h2 class=\"detail-name\">${esc(sp.name)}</h2>\n          <div class=\"detail-price\">${hasDisc ? '<span class=\"detail-old\">' + fmtPrice(sp.listPrice) + '</span>' : ''}${fmtPrice(sp.price)}</div>\n          <div class=\"size-label\">Tamanhos</div>\n          <div class=\"sizes-row\">${sizes || '<span class=\"gen-sub\">Tamanho único</span>'}</div>\n          <button class=\"add-to-look${inLook ? ' in' : ''}\" data-code=\"${esc(sp.code)}\">${inLook ? '✓ No meu look' : '+ Adicionar ao meu look'}</button>\n          <div class=\"tryon-section\">${tryOnHTML}</div>\n          <input type=\"file\" class=\"photo-input\" accept=\"image/*\" />\n          <a class=\"product-url\" href=\"${esc(sp.url)}\" target=\"_blank\">Ver no site da Le Lis →</a>\n        </div>\n      </div>\n    </div>`;\n};\n\nconst renderLooks = (st) => {\n  const { looks = [], looksStatus = 'loading', lead = null } = st;\n  let body;\n  if (!lead || !lead.email) body = `<div class=\"state-msg\">Experimente uma peça primeiro — suas provas ficam salvas aqui.</div>`;\n  else if (looksStatus === 'loading') body = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando suas provas...</span></div>`;\n  else if (!looks.length) body = `<div class=\"state-msg\">Você ainda não salvou nenhuma prova. Experimente uma peça e ela aparece aqui.</div>`;\n  else body = `<div class=\"grid looks-grid\">` + looks.map(l => `\n      <div class=\"look-card\">\n        <div class=\"look-img-wrap\"><img class=\"look-img\" src=\"${esc(l.imageUrl)}\" alt=\"\" loading=\"lazy\" /></div>\n        <div class=\"look-body\">\n          <div class=\"look-name\">${esc(l.productName)}</div>\n          <div class=\"look-price\">${fmtPrice(l.price)}</div>\n          <div class=\"look-actions\">\n            <a class=\"look-buy\" href=\"${esc(l.productUrl)}\" target=\"_blank\">Comprar</a>\n            <button class=\"look-remove\" data-id=\"${esc(l._id)}\">Remover</button>\n          </div>\n        </div>\n      </div>`).join('') + `</div>`;\n  return `\n    <div class=\"app grid-screen\">\n      ${topbar('', st.looksCount, st.lookCount, true, 'Grupo Veste')}\n      <div class=\"editorial\">\n        <div class=\"kicker\">Minhas provas</div>\n        <h2 class=\"ed-title\">Suas provas virtuais</h2>\n        <p class=\"ed-sub\">Reveja tudo o que você experimentou e escolha o que vai comprar.</p>\n      </div>\n      <div class=\"grid-wrapper\">${body}</div>\n    </div>`;\n};\n\nconst renderLookBuilder = (st) => {\n  const { lookItems = [], lookStatus = 'idle', lookImage = null, lookError = null, lookStep = 1, lookTotal = 1, gateError = null, gateSubmitting = false } = st;\n  const total = lookItems.reduce((s, p) => s + (p.price || 0), 0);\n  const sel = Array.isArray(st.lookSel) ? st.lookSel : lookItems.map(x => x.code);\n  const selCount = lookItems.filter(p => sel.indexOf(p.code) >= 0).length;\n  let tryHTML;\n  if (!lookItems.length) tryHTML = '';\n  else if (lookStatus === 'gate') tryHTML = `<div class=\"gate-inline\"><div class=\"gate-inline-kicker\">Quase lá</div><div class=\"gate-inline-title\">Diga quem é você para provar</div><input class=\"gate-name\" type=\"text\" placeholder=\"Seu nome\" /><input class=\"gate-email\" type=\"email\" placeholder=\"seu@email.com\" /><button class=\"cta-btn gate-submit\"${gateSubmitting ? ' disabled' : ''}>${gateSubmitting ? 'Um instante...' : 'Continuar para a foto'}</button>${gateError ? '<div class=\"err-inline\">' + esc(gateError) + '</div>' : ''}</div>`;\n  else if (lookStatus === 'checking' || lookStatus === 'generating') tryHTML = `<div class=\"gen-box\"><div class=\"spinner\"></div><div class=\"gen-status\">${lookStatus === 'checking' ? 'Analisando sua foto...' : ('Vestindo peça ' + lookStep + ' de ' + lookTotal + '...')}</div><div class=\"gen-sub\">montar o look pode levar 1 a 2 minutos</div></div>`;\n  else if (lookStatus === 'rejected') tryHTML = `<div class=\"warn-box\">${esc(lookError)}</div><button class=\"cta-btn provar-look-btn\">Enviar outra foto</button>`;\n  else if (lookStatus === 'done' && lookImage) tryHTML = `<div class=\"gen-box\"><img class=\"result-img\" src=\"${lookImage}\" alt=\"Look\" /><div class=\"result-badge\">✓ Look salvo em Minhas provas</div><button class=\"cta-btn go-looks\">Ver minhas provas</button><button class=\"ghost-btn provar-look-btn\" style=\"margin-top:10px\">Provar com outra foto</button></div>`;\n  else if (lookStatus === 'error') tryHTML = `<button class=\"cta-btn provar-look-btn\">Tentar novamente</button><div class=\"err-box\">Não consegui montar o look: ${esc(lookError)}</div>`;\n  else tryHTML = `<button class=\"cta-btn provar-look-btn\"${selCount ? '' : ' disabled'}>${selCount ? 'Provar ' + selCount + (selCount === 1 ? ' peça' : ' peças') : 'Selecione as peças'}</button><p class=\"cta-hint\">Marque acima as peças que quer provar juntas — combine partes diferentes (ex.: uma de cima + uma de baixo).</p>${lookError ? '<div class=\"err-inline\">' + esc(lookError) + '</div>' : ''}`;\n  let body;\n  if (!lookItems.length) body = `<div class=\"state-msg\">Seu look está vazio. No catálogo, toque em <b>+ look</b> nas peças para montar um look e provar tudo junto.</div>`;\n  else body = `<div class=\"look-items\">${lookItems.map(p => `<div class=\"look-item${sel.indexOf(p.code) >= 0 ? ' sel' : ''}\"><button class=\"look-sel${sel.indexOf(p.code) >= 0 ? ' on' : ''}\" data-code=\"${esc(p.code)}\" title=\"Provar esta peça\">${sel.indexOf(p.code) >= 0 ? '✓' : ''}</button><img class=\"look-item-img\" src=\"${esc(p.image)}\" alt=\"\" /><div class=\"look-item-info\"><div class=\"look-item-name\">${esc(p.name)}</div><div class=\"look-item-price\">${fmtPrice(p.price)}</div></div><button class=\"look-item-remove\" data-code=\"${esc(p.code)}\">Remover</button></div>`).join('')}<div class=\"look-total\">Total do look <span>${fmtPrice(total)}</span></div></div><div class=\"look-tryon\">${tryHTML}<input type=\"file\" class=\"photo-input\" accept=\"image/*\" /></div>`;\n  return `<div class=\"app grid-screen\">${topbar('', st.looksCount, st.lookCount, true, 'Grupo Veste')}<div class=\"editorial\"><div class=\"kicker\">Meu look</div><h2 class=\"ed-title\">Monte seu look</h2><p class=\"ed-sub\">Marque as peças que quer provar juntas e veja na sua foto.</p></div><div class=\"grid-wrapper\"><div class=\"look-wrap\">${body}</div></div></div>`;\n};\n\nconst renderBrands = (st) => {\n  const tiles = BRANDS.map(b => `<button class=\"brand-tile\" data-brand=\"${b.id}\" style=\"--bt:${b.accent}\"><span class=\"brand-tile-name\">${esc(b.name)}</span><span class=\"brand-tile-go\">Experimentar →</span></button>`).join('');\n  return `\n    <div class=\"app brands-screen\">\n      <header class=\"bar\">\n        <span class=\"wordmark-sm hub-mark\"><svg class=\"home-ico\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 10.5 12 3l9 7.5\"/><path d=\"M5 9.5V20h14V9.5\"/></svg>Grupo Veste</span>\n        <div class=\"bar-right\">\n          ${st.lookCount ? `<button class=\"go-look\">Meu look <span class=\"looks-badge\">${st.lookCount}</span></button>` : ''}\n          <button class=\"go-looks\">Minhas provas${st.looksCount ? ' <span class=\"looks-badge\">' + st.looksCount + '</span>' : ''}</button>\n        </div>\n      </header>\n      <div class=\"brands-hero\">\n        <div class=\"kicker\">Provador Virtual</div>\n        <h1 class=\"brands-title\">Experimente as marcas<br/>do Grupo Veste</h1>\n        <p class=\"brands-sub\">Escolha uma marca, prove as peças com a sua foto e monte seu look — tudo em um só lugar.</p>\n        <div class=\"brands-grid\">${tiles}</div>\n      </div>\n    </div>`;\n};\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Jost:wght@300;400;500&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .app { height: 100%; font-family: 'Jost','Helvetica Neue',Arial,sans-serif; background: #FFFFFF; color: #1A1A1A; display: flex; flex-direction: column; overflow: hidden; }\n    .wordmark { font-family: 'Cormorant Garamond', serif; font-weight: 600; letter-spacing: 6px; text-transform: uppercase; }\n    .kicker { font-size: 11px; letter-spacing: 3px; text-transform: uppercase; color: #A98C7D; margin-bottom: 14px; }\n    .kicker.light { color: rgba(255,255,255,0.85); }\n    .btn-primary { height: 54px; padding: 0 40px; background: #1A1A1A; color: #fff; border: none; font-family: inherit; font-size: 12px; letter-spacing: 2px; text-transform: uppercase; cursor: pointer; transition: background .25s; }\n    .btn-primary:hover { background: #3a332e; }\n\n    /* ---------- HOME ---------- */\n    .home { }\n    .hero { flex: 1; position: relative; overflow: hidden; display: flex; flex-direction: column; }\n    .hero-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 16%; }\n    .hero-shade { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(20,16,14,0.60) 0%, rgba(20,16,14,0.18) 55%, rgba(20,16,14,0.34) 100%); }\n    .hero-plain { background: linear-gradient(140deg, #EFE3DC 0%, #F6F0EA 55%, #EDE6DE 100%); }\n    .hero-top { padding: 30px 48px; position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n    .hero-top .wordmark { font-size: 26px; color: #fff; letter-spacing: 9px; }\n    .hero-plain .hero-top .wordmark { color: #1A1A1A; }\n    .hero-content { margin-top: auto; padding: 0 48px 66px; max-width: 680px; position: relative; z-index: 1; }\n    .hero-plain .hero-content { color: #1A1A1A; }\n    .hero-title { font-family: 'Cormorant Garamond', serif; font-weight: 500; font-size: 54px; line-height: 1.05; color: #fff; margin-bottom: 20px; letter-spacing: 0.5px; }\n    .hero-plain .hero-title { color: #1A1A1A; }\n    .hero-sub { font-size: 15px; line-height: 1.6; color: rgba(255,255,255,0.88); max-width: 460px; margin-bottom: 30px; font-weight: 300; }\n    .hero-plain .hero-sub { color: #6B6259; }\n    .hero-tag { font-family: 'Cormorant Garamond', serif; font-style: italic; font-size: 16px; color: rgba(255,255,255,0.8); margin-top: 22px; }\n    .hero-plain .hero-tag { color: #A98C7D; }\n\n    /* ---------- BAR ---------- */\n    .bar { display: flex; align-items: center; gap: 20px; padding: 0 44px; height: 74px; border-bottom: 1px solid #ECE7E1; flex-shrink: 0; background: #fff; }\n    .wordmark-sm { font-family: 'Cormorant Garamond', serif; font-weight: 600; font-size: 21px; letter-spacing: 6px; text-transform: uppercase; background: none; border: none; cursor: pointer; color: #1A1A1A; display: inline-flex; align-items: center; gap: 9px; transition: opacity .2s; }\n    .wordmark-sm:hover { opacity: 0.6; }\n    .home-ico { opacity: 0.8; flex-shrink: 0; }\n    .bar-tag { font-size: 10px; letter-spacing: 2.5px; text-transform: uppercase; color: #B4A99C; border-left: 1px solid #E6DED4; padding-left: 20px; }\n    .bar-right { margin-left: auto; display: flex; align-items: center; gap: 14px; }\n    .back-grid { background: none; border: none; font-family: inherit; font-size: 12px; letter-spacing: 1.5px; text-transform: uppercase; color: #6B6259; cursor: pointer; }\n    .back-grid:hover { color: #1A1A1A; }\n    .go-back { background: none; border: none; font-family: inherit; font-size: 12px; letter-spacing: 1.5px; text-transform: uppercase; color: #6B6259; cursor: pointer; margin-right: 6px; }\n    .go-back:hover { color: #1A1A1A; }\n    .search-wrap { display: flex; align-items: center; background: #F6F3EF; border: 1px solid #E7E0D8; border-radius: 22px; height: 40px; padding: 0 6px 0 16px; width: 260px; }\n    .search-input { flex: 1; background: transparent; border: none; outline: none; font-size: 13px; font-family: inherit; color: #1A1A1A; }\n    .search-input::placeholder { color: #A79E93; }\n    .search-btn,.search-clear { width: 30px; height: 30px; border: none; background: transparent; color: #8C8073; cursor: pointer; border-radius: 50%; }\n    .search-btn:hover,.search-clear:hover { background: #EAE3DA; }\n\n    /* ---------- GRID / LANÇAMENTOS ---------- */\n    .grid-screen { background: #F6F3EF; }\n    .editorial { padding: 40px 48px 8px; background: #FFFFFF; }\n    .ed-title { font-family: 'Cormorant Garamond', serif; font-weight: 500; font-size: 40px; line-height: 1.05; margin-bottom: 10px; }\n    .ed-sub { font-size: 14px; color: #8C8073; font-weight: 300; }\n    .ed-sub .count { color: #B4A99C; }\n    .cat-row { display: flex; gap: 6px; padding: 20px 48px; background: #fff; border-bottom: 1px solid #ECE7E1; overflow-x: auto; flex-shrink: 0; }\n    .cat-link { padding: 7px 16px; border: none; background: transparent; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: #7A7167; cursor: pointer; white-space: nowrap; border-radius: 20px; transition: all .2s; }\n    .cat-link:hover { background: #F3EEE8; color: #1A1A1A; }\n    .cat-link.active { background: #1A1A1A; color: #fff; }\n    .grid-wrapper { flex: 1; overflow-y: auto; padding: 26px 48px 40px; }\n    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); grid-auto-rows: 460px; gap: 22px; }\n    .card { background: #fff; cursor: pointer; display: flex; flex-direction: column; transition: transform .25s; }\n    .card:hover { transform: translateY(-4px); }\n    .card-img-wrap { position: relative; overflow: hidden; height: 380px; background: #EFEAE3; }\n    .card-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; transition: transform .5s ease; }\n    .card:hover .card-img { transform: scale(1.04); }\n    .card-cta { position: absolute; left: 0; right: 0; bottom: 0; background: rgba(26,26,26,0.9); color: #fff; text-align: center; padding: 12px; font-size: 11px; letter-spacing: 2px; text-transform: uppercase; transform: translateY(100%); transition: transform .3s ease; }\n    .card:hover .card-cta { transform: translateY(0); }\n    .card-body { padding: 14px 4px 4px; }\n    .card-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #4A443D; line-height: 1.5; margin-bottom: 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .card-price { font-family: 'Cormorant Garamond', serif; font-size: 19px; font-weight: 600; color: #1A1A1A; }\n    .card-old { font-family: 'Jost'; font-size: 12px; color: #B4A99C; text-decoration: line-through; margin-right: 7px; }\n    .state-full,.state-msg { grid-column: 1/-1; display: flex; align-items: center; justify-content: center; gap: 12px; padding: 90px 0; color: #A79E93; font-size: 15px; }\n    .spinner-sm { width: 22px; height: 22px; border: 3px solid #E7E0D8; border-top-color: #A98C7D; border-radius: 50%; animation: spin .8s linear infinite; }\n\n    /* ---------- DETAIL / PROVADOR ---------- */\n    .detail-screen { background: #fff; }\n    .detail-wrap { flex: 1; display: flex; overflow: hidden; }\n    .detail-media { width: 50%; background: #EFEAE3; overflow: hidden; }\n    .detail-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }\n    .detail-side { width: 50%; overflow-y: auto; padding: 48px 56px; }\n    .detail-name { font-family: 'Cormorant Garamond', serif; font-weight: 500; font-size: 30px; line-height: 1.15; margin-bottom: 14px; }\n    .detail-price { font-family: 'Cormorant Garamond', serif; font-size: 26px; font-weight: 600; margin-bottom: 30px; }\n    .detail-old { font-family: 'Jost'; font-size: 16px; color: #B4A99C; text-decoration: line-through; margin-right: 10px; }\n    .size-label { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #A79E93; margin-bottom: 12px; }\n    .sizes-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 34px; }\n    .size-btn { min-width: 46px; height: 44px; padding: 0 12px; border: 1px solid #DDD5CA; background: #fff; font-size: 13px; font-family: inherit; cursor: pointer; transition: all .15s; color: #1A1A1A; }\n    .size-btn:hover { border-color: #1A1A1A; }\n    .size-btn.active { background: #1A1A1A; color: #fff; border-color: #1A1A1A; }\n    .size-btn.off { opacity: .35; cursor: not-allowed; text-decoration: line-through; }\n    .tryon-section { border-top: 1px solid #ECE7E1; padding-top: 30px; }\n    .cta-btn { width: 100%; height: 54px; background: #1A1A1A; color: #fff; border: none; font-family: inherit; font-size: 12px; letter-spacing: 2px; text-transform: uppercase; cursor: pointer; transition: background .2s; }\n    .cta-btn:hover:not(:disabled) { background: #3a332e; }\n    .cta-btn:disabled { opacity: .6; }\n    .cta-hint { margin-top: 14px; font-size: 12.5px; color: #A79E93; line-height: 1.6; font-weight: 300; }\n    .photo-input { display: none; }\n    .gen-box { text-align: center; }\n    .spinner { width: 40px; height: 40px; border: 3px solid #E7E0D8; border-top-color: #A98C7D; border-radius: 50%; margin: 10px auto 14px; animation: spin .8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gen-status { font-size: 14px; color: #4A443D; }\n    .gen-sub { font-size: 11px; color: #B4A99C; margin-top: 5px; }\n    .result-img { width: 100%; display: block; margin-bottom: 12px; box-shadow: 0 10px 30px rgba(60,40,25,.16); }\n    .result-badge { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #A79E93; text-align: center; margin-bottom: 18px; }\n    .ghost-btn { width: 100%; height: 46px; background: #fff; border: 1px solid #DDD5CA; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; }\n    .ghost-btn:hover { border-color: #1A1A1A; }\n    .err-box { margin-top: 14px; padding: 14px; background: #FBECEC; color: #A5453C; font-size: 12px; border-radius: 4px; line-height: 1.5; }\n    .warn-box { margin-bottom: 16px; padding: 14px; background: #FBF2E4; color: #8A6420; font-size: 13px; border-radius: 4px; line-height: 1.5; }\n    .product-url { display: block; margin-top: 22px; padding-top: 20px; border-top: 1px solid #ECE7E1; text-align: center; font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #A79E93; text-decoration: none; }\n    .product-url:hover { color: #1A1A1A; }\n    .gate-inline { }\n    .gate-inline-kicker { font-size: 10px; letter-spacing: 2px; text-transform: uppercase; color: #A98C7D; margin-bottom: 8px; }\n    .gate-inline-title { font-family: 'Cormorant Garamond', serif; font-size: 22px; font-weight: 500; margin-bottom: 18px; }\n    .gate-inline input { width: 100%; height: 48px; border: 1px solid #DDD5CA; padding: 0 16px; font-size: 14px; font-family: inherit; outline: none; margin-bottom: 12px; }\n    .gate-inline input:focus { border-color: #1A1A1A; }\n    .gate-note { margin-top: 12px; font-size: 11px; color: #C3BAAD; }\n    .err-inline { margin-top: 10px; font-size: 12px; color: #A5453C; }\n\n    .go-looks { background: none; border: 1px solid #DDD5CA; height: 36px; padding: 0 16px; font-family: inherit; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #1A1A1A; cursor: pointer; border-radius: 20px; display: inline-flex; align-items: center; gap: 7px; }\n    .go-looks:hover { border-color: #1A1A1A; background: #F6F3EF; }\n    .looks-badge { background: #1A1A1A; color: #fff; border-radius: 10px; padding: 1px 7px; font-size: 10px; }\n    .home-looks { color: #fff; border-color: rgba(255,255,255,0.5); background: rgba(255,255,255,0.12); flex-shrink: 0; }\n    .looks-grid { grid-auto-rows: 540px; }\n    .look-card { background: #fff; display: flex; flex-direction: column; }\n    .look-img-wrap { height: 440px; background: #EFEAE3; overflow: hidden; }\n    .look-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }\n    .look-body { padding: 14px 4px; }\n    .look-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #4A443D; margin-bottom: 6px; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .look-price { font-family: 'Cormorant Garamond', serif; font-size: 19px; font-weight: 600; margin-bottom: 12px; }\n    .look-actions { display: flex; gap: 8px; }\n    .look-buy { flex: 1; text-align: center; background: #1A1A1A; color: #fff; text-decoration: none; height: 40px; line-height: 40px; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; }\n    .look-buy:hover { background: #3a332e; }\n    .look-remove { background: none; border: 1px solid #DDD5CA; color: #8C8073; height: 40px; padding: 0 14px; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; }\n    .look-remove:hover { border-color: #A5453C; color: #A5453C; }\n    .card-add { position: absolute; top: 10px; right: 10px; z-index: 2; background: rgba(255,255,255,0.92); border: 1px solid #E7DFD6; color: #1A1A1A; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; padding: 6px 10px; border-radius: 16px; cursor: pointer; transition: all .2s; }\n    .card-add:hover { background: #fff; }\n    .card-add.in { background: #1A1A1A; color: #fff; border-color: #1A1A1A; }\n    .add-to-look { width: 100%; height: 46px; background: #fff; border: 1px solid #DDD5CA; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; margin-bottom: 20px; color: #1A1A1A; }\n    .add-to-look:hover { border-color: #1A1A1A; }\n    .add-to-look.in { background: #F3EEE8; border-color: #1A1A1A; }\n    .go-look { background: #1A1A1A; border: 1px solid #1A1A1A; height: 36px; padding: 0 16px; font-family: inherit; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #fff; cursor: pointer; border-radius: 20px; display: inline-flex; align-items: center; gap: 7px; }\n    .go-look .looks-badge { background: #fff; color: #1A1A1A; }\n    .look-wrap { max-width: 760px; }\n    .look-item { display: flex; align-items: center; gap: 16px; padding: 14px 0; border-bottom: 1px solid #ECE7E1; }\n    .look-item-img { width: 64px; height: 84px; object-fit: cover; object-position: center top; background: #EFEAE3; flex-shrink: 0; }\n    .look-item-info { flex: 1; }\n    .look-item-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #1A1A1A; margin-bottom: 4px; }\n    .look-item-price { font-family: 'Cormorant Garamond', serif; font-size: 17px; font-weight: 600; }\n    .look-item-remove { background: none; border: none; color: #A79E93; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; }\n    .look-item-remove:hover { color: #A5453C; }\n    .look-total { display: flex; justify-content: space-between; align-items: baseline; padding: 18px 0; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: #8C8073; }\n    .look-total span { font-family: 'Cormorant Garamond', serif; font-size: 22px; font-weight: 600; color: #1A1A1A; }\n    .look-tryon { margin-top: 14px; border-top: 1px solid #ECE7E1; padding-top: 26px; max-width: 460px; }\n    .brands-screen { background: #F6F3EF; }\n    .brands-hero { flex: 1; overflow-y: auto; padding: 56px 48px; width: 100%; max-width: 1080px; margin: 0 auto; }\n    .brands-title { font-family: 'Cormorant Garamond', serif; font-weight: 500; font-size: 46px; line-height: 1.05; margin-bottom: 14px; }\n    .brands-sub { font-size: 15px; color: #8C8073; margin-bottom: 40px; font-weight: 300; max-width: 520px; }\n    .brands-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 18px; }\n    .brand-tile { position: relative; height: 240px; border: none; border-radius: 6px; cursor: pointer; overflow: hidden; background: var(--bt, #1A1A1A); color: #fff; display: flex; flex-direction: column; align-items: flex-start; justify-content: flex-end; padding: 28px; font-family: inherit; text-align: left; transition: transform .25s, box-shadow .25s; }\n    .brand-tile:hover { transform: translateY(-5px); box-shadow: 0 18px 44px rgba(60,40,25,0.22); }\n    .brand-tile-name { font-family: 'Cormorant Garamond', serif; font-weight: 600; font-size: 30px; letter-spacing: 1px; margin-bottom: 8px; }\n    .brand-tile-go { font-size: 10px; letter-spacing: 2px; text-transform: uppercase; opacity: 0.85; }\n    .hub-mark { color: #1A1A1A; }\n    .look-sel { width: 26px; height: 26px; min-width: 26px; border: 1.5px solid #C3BAAD; border-radius: 6px; background: #fff; color: #fff; cursor: pointer; font-size: 13px; line-height: 1; display: flex; align-items: center; justify-content: center; margin-right: 6px; flex-shrink: 0; transition: all .15s; }\n    .look-sel.on { background: #1A1A1A; border-color: #1A1A1A; }\n    @media (max-width: 860px) {\n      .hero-title { font-size: 38px; }\n      .hero-content, .hero-top { padding-left: 26px; padding-right: 26px; }\n      .bar, .editorial, .cat-row, .grid-wrapper { padding-left: 22px; padding-right: 22px; }\n      .bar-tag { display: none; }\n      .detail-wrap { flex-direction: column; overflow-y: auto; }\n      .detail-media { width: 100%; height: 60vh; }\n      .detail-side { width: 100%; padding: 30px 24px; }\n      .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); grid-auto-rows: 330px; gap: 14px; }\n      .card-img-wrap { height: 250px; }\n    }\n  `,\n\n  render(d2) {\n    const st = d2.state || {};\n    const screen = st.screen || 'brands';\n    if (screen === 'brands' || screen === 'home') return renderBrands(st);\n    if (screen === 'looks') return renderLooks(st);\n    if (screen === 'lookbuilder') return renderLookBuilder(st);\n    if (screen === 'detail') return renderDetail(st);\n    return renderGrid(st);\n  },\n\n  mounted(d2) {\n    const track = (event, prod) => {\n      try {\n        const kind = prod ? classify(prod) : '';\n        const p = d2.rush('lelis-track').invoke({ event, productCode: (prod && prod.code) || '', productName: (prod && prod.name) || '', kind });\n        if (p && p.catch) p.catch(() => {});\n      } catch (e) {}\n    };\n\n    let savedLead = null;\n    try { savedLead = JSON.parse(window.localStorage.getItem('lelis_lead') || 'null'); } catch (e) {}\n    let savedLook = [];\n    try { savedLook = JSON.parse(window.localStorage.getItem('lelis_look') || '[]') || []; } catch (e) {}\n    d2.setState({ screen: 'brands', leadCaptured: !!(savedLead && savedLead.email), lead: savedLead || null, lookItems: savedLook, lookCount: savedLook.length, navStack: [], gridStatus: 'loading' });\n\n    const fetchProducts = async (path, search) => {\n      const brand = d2.state.brand;\n      if (!brand) return;\n      d2.setState({ gridStatus: 'loading', category: path || '', search: search || '' });\n      try {\n        const enc = path ? path.split('/').map(encodeURIComponent).join('/') : '';\n        const body = search ? { base: brand.base, search: search } : (enc ? { base: brand.base, category: enc } : { base: brand.base, order: 'new' });\n        const r = await d2.rush('lelis-products').invoke(body);\n        const payload = (r && r.data) ? r.data : r;\n        const products = ((payload && payload.products) || []).filter(isClothing);\n        const total = products.length;\n        d2.setState({ products, total, gridStatus: 'done' });\n      } catch (err) {\n        d2.setState({ gridStatus: 'error', gridError: String((err && err.message) || err) });\n      }\n    };\n\n    const loadLooks = async () => {\n      const lead = d2.state.lead;\n      if (!lead || !lead.email) return;\n      d2.setState({ looksStatus: 'loading' });\n      try {\n        const r = await d2.rush('lelis-my-tryons').invoke({ email: lead.email });\n        const payload = (r && r.data) ? r.data : r;\n        const looks = (payload && payload.tryons) || [];\n        d2.setState({ looks, looksStatus: 'done', looksCount: looks.length });\n      } catch (e) { d2.setState({ looksStatus: 'done' }); }\n    };\n    if (savedLead && savedLead.email) loadLooks();\n\n    const navTo = (screen, patch) => { const cur = d2.state.screen || 'home'; const stack = (d2.state.navStack || []).slice(); if (cur !== screen) stack.push(cur); d2.setState(Object.assign({ screen: screen, navStack: stack }, patch || {})); };\n    const goBack = () => { const stack = (d2.state.navStack || []).slice(); const prev = stack.pop() || 'home'; d2.setState({ screen: prev, navStack: stack }); };\n    const setLook = (items) => { try { window.localStorage.setItem('lelis_look', JSON.stringify(items)); } catch (e) {} d2.setState({ lookItems: items, lookCount: items.length }); };\n    const toggleLook = (code) => {\n      const items = (d2.state.lookItems || []).slice();\n      const idx = items.findIndex(x => String(x.code) === String(code));\n      if (idx >= 0) { items.splice(idx, 1); setLook(items); return; }\n      const selp = d2.state.selectedProduct;\n      const prod = (d2.state.products || []).find(p => String(p.code) === String(code)) || (selp && String(selp.code) === String(code) ? selp : null);\n      if (prod) { items.push(prod); setLook(items); }\n    };\n    const handleLookPhoto = async (file) => {\n      const all = (d2.state.lookItems || []).slice();\n      const sel = Array.isArray(d2.state.lookSel) ? d2.state.lookSel : all.map(x => x.code);\n      const items = all.filter(x => sel.indexOf(x.code) >= 0);\n      if (!items.length) return;\n      const lead = d2.state.lead || {};\n      d2.setState({ lookStatus: 'checking', lookImage: null, lookError: null });\n      try {\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('lelis-photo-check').invoke({ photoUrl: userPhotoUrl, kind: 'dress' });\n        const cp = (check && check.data) ? check.data : check;\n        let verdict = (cp && cp.verdict) || {};\n        if (typeof verdict === 'string') { try { verdict = JSON.parse(verdict.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e) { verdict = {}; } }\n        if (verdict.ok === false) { d2.setState({ lookStatus: 'rejected', lookError: verdict.motivo || 'A foto não ficou boa. Tente outra de corpo inteiro com boa luz.' }); return; }\n        let current = userPhotoUrl;\n        for (let i = 0; i < items.length; i++) {\n          d2.setState({ lookStatus: 'generating', lookStep: i + 1, lookTotal: items.length });\n          const p = items[i]; const k = classify(p);\n          let instr = '';\n          if (k !== 'bag') {\n            try {\n              const itemData = await urlToDataUrl(p.image).catch(() => p.image);\n              const kpt = k === 'dress' ? 'vestido ou macacão' : (k === 'bottom' ? 'peça de baixo (calça/saia/short)' : 'peça de cima (blusa/camisa/casaco)');\n              const sres = await d2.rush('lelis-stylist').invoke({ photoUrl: userPhotoUrl, itemUrl: itemData, instr: 'Peça nova que a pessoa quer vestir: \"' + (p.name || '') + '\" (categoria: ' + kpt + '). A pessoa vai usar esta peça combinada com outras. Analise a Imagem 1 e a peça na Imagem 2 e retorne o JSON com o que remover e a instrução.' });\n              const sp2 = (sres && sres.data) ? sres.data : sres;\n              let sv = (sp2 && sp2.verdict) || sp2;\n              if (typeof sv === 'string') { try { sv = JSON.parse(sv.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e2) { sv = {}; } }\n              instr = (sv && sv.instrucao) || '';\n            } catch (e3) {}\n          }\n          const prompt = buildPrompt(k, p.name, instr);\n          let img = null, lastErr = null;\n          for (let a = 1; a <= 2 && !img; a++) {\n            try {\n              const res = await d2.rush('lelis-tryon').invoke({ userPhotoUrl: current, itemUrl: p.image, prompt });\n              const pay = (res && res.data) ? res.data : res;\n              img = (pay && pay.image) || null;\n              if (!img) throw new Error('sem imagem');\n            } catch (e4) { lastErr = e4; }\n          }\n          if (!img) { d2.setState({ lookStatus: 'error', lookError: 'Falhou ao vestir \"' + (p.name || '') + '\". ' + String((lastErr && lastErr.message) || '') }); return; }\n          current = img;\n        }\n        d2.setState({ lookStatus: 'done', lookImage: current });\n        track('look_success');\n        const totalPrice = items.reduce((s, x) => s + (x.price || 0), 0);\n        d2.rush('lelis-save-tryon').invoke({ email: lead.email || '', name: lead.name || '', productCode: 'look', productName: 'Look • ' + items.map(x => x.name).join(' + '), price: totalPrice, productUrl: (items[0] && items[0].url) || '', kind: 'look', size: '', imageUrl: current }).then(() => loadLooks()).catch(() => {});\n      } catch (err) { d2.setState({ lookStatus: 'error', lookError: String((err && err.message) || err) }); }\n    };\n\n    track('app_open');\n\n    d2.on('.go-lancamentos', 'click', () => {\n      navTo('grid');\n      if (!(d2.state.products || []).length) fetchProducts('', '');\n    });\n    d2.on('.nav-home', 'click', () => d2.setState({ screen: 'brands', navStack: [] }));\n    d2.on('.go-back', 'click', goBack);\n    d2.on('.brand-tile', 'click', (e) => { const el = e.target.closest('.brand-tile'); if (!el) return; const b = brandById(el.dataset.brand); if (!b) return; d2.setState({ brand: b, products: [], category: '', search: '' }); navTo('grid'); fetchProducts('', ''); });\n    d2.on('.go-looks', 'click', () => { navTo('looks'); loadLooks(); });\n    d2.on('.card-add', 'click', (e) => { e.stopPropagation(); const el = e.target.closest('.card-add'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.add-to-look', 'click', (e) => { const el = e.target.closest('.add-to-look'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.go-look', 'click', () => navTo('lookbuilder', { lookStatus: 'idle', lookImage: null, lookError: null, lookSel: (d2.state.lookItems || []).map(x => x.code) }));\n    d2.on('.look-item-remove', 'click', (e) => { const el = e.target.closest('.look-item-remove'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.look-sel', 'click', (e) => { const el = e.target.closest('.look-sel'); if (!el) return; const code = el.dataset.code; const items = d2.state.lookItems || []; const cur = Array.isArray(d2.state.lookSel) ? d2.state.lookSel.slice() : items.map(x => x.code); const i = cur.indexOf(code); if (i >= 0) cur.splice(i, 1); else cur.push(code); d2.setState({ lookSel: cur, lookError: null }); });\n    d2.on('.provar-look-btn', 'click', () => { const items = d2.state.lookItems || []; const sel = Array.isArray(d2.state.lookSel) ? d2.state.lookSel : items.map(x => x.code); const chosen = items.filter(x => sel.indexOf(x.code) >= 0); if (!chosen.length) { d2.setState({ lookError: 'Selecione ao menos uma peça para provar.' }); return; } d2.setState({ photoTarget: 'look', lookError: null }); if (d2.state.leadCaptured) openPicker(); else d2.setState({ lookStatus: 'gate', gateError: null }); });\n    d2.on('.look-remove', 'click', async (e) => {\n      const el = e.target.closest('.look-remove'); if (!el) return;\n      const id = el.dataset.id;\n      const looks = (d2.state.looks || []).filter(l => l._id !== id);\n      d2.setState({ looks, looksCount: looks.length });\n      try { await d2.rush('lelis-delete-tryon').invoke({ id }); } catch (e2) {}\n    });\n\n    d2.on('.cat-link', 'click', (e) => {\n      const el = e.target.closest('.cat-link');\n      if (el) { navTo('grid'); fetchProducts(el.dataset.path, ''); }\n    });\n    d2.on('.search-btn', 'click', () => { const i = d2.$('.search-input'); fetchProducts('', (i && i.value.trim()) || ''); });\n    d2.on('.search-input', 'keydown', (e) => { if (e.key === 'Enter') fetchProducts('', e.target.value.trim()); });\n    d2.on('.search-clear', 'click', () => fetchProducts('', ''));\n\n    d2.on('.card', 'click', (e) => {\n      if (e.target.closest('.card-add')) return;\n      const el = e.target.closest('.card');\n      const code = el && el.dataset.code;\n      const prod = (d2.state.products || []).find(p => String(p.code) === String(code));\n      if (prod) { navTo('detail', { selectedProduct: prod, selectedSize: null, tryStatus: 'idle', tryImage: null, tryError: null }); track('product_view', prod); }\n    });\n    d2.on('.back-grid', 'click', goBack);\n\n    d2.on('.size-btn', 'click', (e) => {\n      const el = e.target.closest('.size-btn');\n      if (el && !el.classList.contains('off')) d2.setState({ selectedSize: el.dataset.size });\n    });\n\n    const openPicker = () => { const input = d2.$('.photo-input'); if (input) { input.value = ''; input.click(); } };\n\n    d2.on('.tryon-btn', 'click', () => {\n      d2.setState({ photoTarget: 'single' });\n      track('tryon_click', d2.state.selectedProduct);\n      if (d2.state.leadCaptured) openPicker();\n      else d2.setState({ tryStatus: 'gate', gateError: null });\n    });\n\n    d2.on('.gate-submit', 'click', async () => {\n      const name = ((d2.$('.gate-name') || {}).value || '').trim();\n      const email = ((d2.$('.gate-email') || {}).value || '').trim();\n      if (!name) { d2.setState({ gateError: 'Por favor, preencha seu nome.' }); return; }\n      if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(email)) { d2.setState({ gateError: 'Digite um e-mail válido.' }); return; }\n      d2.setState({ gateSubmitting: true, gateError: null });\n      try { await d2.rush('lelis-lead').invoke({ name, email }); } catch (e) {}\n      try { window.localStorage.setItem('lelis_lead', JSON.stringify({ name, email })); } catch (e) {}\n      d2.setState({ leadCaptured: true, lead: { name, email }, gateSubmitting: false, tryStatus: 'idle', lookStatus: 'idle' });\n      setTimeout(openPicker, 60);\n    });\n    d2.on('.gate-email', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.gate-submit'); if (b) b.click(); } });\n\n    d2.on('.try-again', 'click', () => { d2.setState({ tryStatus: 'idle', tryImage: null, tryError: null }); openPicker(); });\n\n    d2.on('.photo-input', 'change', async (e) => {\n      const file = e.target.files && e.target.files[0];\n      if (!file) return;\n      if (d2.state.photoTarget === 'look') { handleLookPhoto(file); return; }\n      const prod = d2.state.selectedProduct;\n      if (!prod) return;\n      const kind = classify(prod);\n      d2.setState({ tryStatus: 'checking', tryImage: null, tryError: null });\n      try {\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('lelis-photo-check').invoke({ photoUrl: userPhotoUrl, kind });\n        const cp = (check && check.data) ? check.data : check;\n        let verdict = (cp && cp.verdict) || {};\n        if (typeof verdict === 'string') { try { verdict = JSON.parse(verdict.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e2) { verdict = {}; } }\n        if (verdict.ok === false) {\n          d2.setState({ tryStatus: 'rejected', tryError: verdict.motivo || 'A foto não ficou boa para a prova. Tente outra com o corpo bem visível e boa iluminação.' });\n          return;\n        }\n        const itemUrl = prod.image;\n        let styleInstr = '';\n        if (kind !== 'bag') {\n          try {\n            const kpt = kind === 'dress' ? 'vestido ou macacão' : (kind === 'bottom' ? 'peça de baixo (calça/saia/short)' : 'peça de cima (blusa/camisa/casaco)');\n            const itemData = await urlToDataUrl(itemUrl).catch(() => itemUrl);\n            const sres = await d2.rush('lelis-stylist').invoke({ photoUrl: userPhotoUrl, itemUrl: itemData, instr: 'Peça nova que a pessoa quer vestir: \"' + (prod.name || '') + '\" (categoria: ' + kpt + '). Analise a roupa atual da pessoa na Imagem 1 e a peça na Imagem 2 e retorne o JSON com o que remover e a instrução.' });\n            const sp = (sres && sres.data) ? sres.data : sres;\n            let sv = (sp && sp.verdict) || sp;\n            if (typeof sv === 'string') { try { sv = JSON.parse(sv.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e4) { sv = {}; } }\n            styleInstr = (sv && sv.instrucao) || '';\n          } catch (e5) {}\n        }\n        const prompt = buildPrompt(kind, prod.name, styleInstr);\n        let img = null, lastErr = null;\n        for (let attempt = 1; attempt <= 3 && !img; attempt++) {\n          d2.setState({ tryStatus: 'generating', tryAttempt: attempt });\n          try {\n            const res = await d2.rush('lelis-tryon').invoke({ userPhotoUrl, itemUrl, prompt });\n            const payload = (res && res.data) ? res.data : res;\n            img = (payload && payload.image) || null;\n            if (!img) throw new Error('Resposta sem imagem');\n          } catch (e3) { lastErr = e3; }\n        }\n        if (img) {\n          d2.setState({ tryStatus: 'done', tryImage: img });\n          track('tryon_success', prod);\n          const lead = d2.state.lead || {};\n          const sizeLabel = ((prod.sizes || []).find(s => String(s.id) === String(d2.state.selectedSize)) || {}).label || '';\n          d2.rush('lelis-save-tryon').invoke({ email: lead.email || '', name: lead.name || '', productCode: prod.code, productName: prod.name, price: prod.price, productUrl: prod.url, kind, size: sizeLabel, imageUrl: img }).then(() => loadLooks()).catch(() => {});\n        }\n        else d2.setState({ tryStatus: 'error', tryError: 'Não consegui gerar após 3 tentativas. ' + String((lastErr && lastErr.message) || lastErr || '') });\n      } catch (err) {\n        d2.setState({ tryStatus: 'error', tryError: String((err && err.message) || err) });\n      }\n    });\n  }\n}\n\n"}}}}}}}},{"_id":"6a720250c8e65f51f8365a1d","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"lelisStats","package":"people-gabi-carmo","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 KIND = { dress: 'Vestido', top: 'Parte de cima', bottom: 'Parte de baixo', bag: 'Bolsa' };\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst fmtTime = (iso) => { try { return new Date(iso).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Jost:wght@300;400;500;600&family=Cormorant+Garamond:wght@500;600&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .dash { height: 100%; overflow-y: auto; font-family: 'Jost', 'Helvetica Neue', Arial, sans-serif; background: #F6F3EF; color: #1A1A1A; }\n    .top { display: flex; align-items: center; padding: 24px 44px; background: #FFFFFF; border-bottom: 1px solid #ECE7E1; position: sticky; top: 0; z-index: 2; }\n    .title { font-size: 15px; font-weight: 500; letter-spacing: 2px; text-transform: uppercase; display: flex; align-items: center; }\n    .title .logo { letter-spacing: 9px; text-transform: uppercase; margin-right: 12px; font-weight: 500; }\n    .right { margin-left: auto; display: flex; align-items: center; gap: 14px; }\n    .updated { font-size: 12px; color: #B4A99C; }\n    .refresh { height: 38px; padding: 0 18px; border: 1px solid #DDD5CA; border-radius: 20px; background: #FFF; font-family: inherit; font-size: 12px; font-weight: 500; letter-spacing: 0.5px; cursor: pointer; transition: all 0.2s; }\n    .refresh:hover { border-color: #1A1A1A; }\n    .wrap { padding: 30px 44px; }\n    .section-label { font-size: 10px; font-weight: 600; color: #A79E93; text-transform: uppercase; letter-spacing: 1.5px; margin: 30px 0 14px; }\n    .section-label:first-child { margin-top: 0; }\n    .kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }\n    .kpi { background: #FFFFFF; border: 1px solid #EFEAE3; border-radius: 8px; padding: 22px 24px; }\n    .kpi .label { font-size: 12px; color: #8C8073; margin-bottom: 10px; letter-spacing: 0.3px; }\n    .kpi .val { font-size: 38px; font-weight: 600; line-height: 1; }\n    .kpi .val.accent { color: #A98C7D; }\n    .kpi .sub { font-size: 12px; color: #B4A99C; margin-top: 8px; }\n    .mini { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }\n    .mini .kpi .val { font-size: 28px; }\n    .panel { background: #FFFFFF; border: 1px solid #EFEAE3; border-radius: 8px; overflow: hidden; }\n    .row { display: flex; align-items: center; padding: 14px 22px; border-bottom: 1px solid #F3EEE8; font-size: 14px; }\n    .row:last-child { border-bottom: none; }\n    .row .rank { width: 28px; color: #C3BAAD; font-weight: 600; font-size: 13px; }\n    .row .name { flex: 1; font-weight: 500; }\n    .row .count { font-weight: 600; color: #A98C7D; }\n    .row .badge { font-size: 11px; font-weight: 500; padding: 3px 10px; border-radius: 12px; background: #F3EEE8; color: #7A7167; margin: 0 12px; letter-spacing: 0.3px; }\n    .row .time { color: #B4A99C; font-size: 12px; min-width: 96px; text-align: right; }\n    .row .email { flex: 1; text-align: right; color: #8C8073; font-size: 13px; padding-right: 16px; }\n    .empty { padding: 40px; text-align: center; color: #B4A99C; font-size: 14px; }\n    .state { display: flex; align-items: center; justify-content: center; height: 100%; gap: 12px; color: #A79E93; font-size: 15px; }\n    .spinner { width: 24px; height: 24px; border: 3px solid #E7E0D8; border-top-color: #A98C7D; border-radius: 50%; animation: spin 0.8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gate { min-height: 100%; height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px; background: linear-gradient(140deg, #F3E9E4 0%, #FBF9F6 60%, #F6F0EA 100%); }\n    .gate-card { width: 100%; max-width: 390px; background: #FFFFFF; border: 1px solid #EFEAE3; border-radius: 8px; padding: 40px 34px; box-shadow: 0 24px 70px rgba(90,60,40,0.10); text-align: center; }\n    .gate-logo { font-size: 25px; font-weight: 500; letter-spacing: 11px; text-transform: uppercase; margin-bottom: 4px; padding-left: 6px; }\n    .gate-kicker { font-family: 'Cormorant Garamond', serif; font-size: 14px; font-style: italic; color: #A98C7D; margin-bottom: 20px; }\n    .gate-title { font-size: 16px; font-weight: 500; margin-bottom: 6px; }\n    .gate-sub { font-size: 13px; color: #A79E93; margin-bottom: 24px; }\n    .gate-input { width: 100%; height: 48px; border: 1px solid #DDD5CA; border-radius: 4px; padding: 0 16px; font-size: 14px; font-family: inherit; outline: none; margin-bottom: 14px; }\n    .gate-input:focus { border-color: #1A1A1A; }\n    .gate-btn { width: 100%; height: 50px; background: #1A1A1A; color: #FFF; border: none; border-radius: 4px; font-size: 13px; font-weight: 500; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; font-family: inherit; }\n    .gate-btn:disabled { opacity: 0.6; }\n    .gate-err { margin-top: 12px; font-size: 12px; color: #A5453C; }\n  `,\n\n  render(d2) {\n    const { status = 'loading', s = null, error = null, updatedAt = '', pwError = null, pwSubmitting = false } = d2.state;\n\n    if (status === 'locked') {\n      return `\n        <div class=\"dash\"><div class=\"gate\"><div class=\"gate-card\">\n          <div class=\"gate-logo\">Le Lis</div>\n          <div class=\"gate-kicker\">painel do try-on</div>\n          <div class=\"gate-title\">Painel protegido</div>\n          <div class=\"gate-sub\">Digite a senha para ver os números.</div>\n          <input class=\"gate-input\" type=\"password\" placeholder=\"Senha\" />\n          <button class=\"gate-btn\"${pwSubmitting ? ' disabled' : ''}>${pwSubmitting ? 'Entrando...' : 'Entrar'}</button>\n          ${pwError ? '<div class=\"gate-err\">' + esc(pwError) + '</div>' : ''}\n        </div></div></div>`;\n    }\n    if (status === 'loading') {\n      return `<div class=\"dash\"><div class=\"state\"><div class=\"spinner\"></div><span>Carregando dados...</span></div></div>`;\n    }\n    if (status === 'error') {\n      return `<div class=\"dash\"><div class=\"state\"><span>Erro ao carregar: ${esc(error)}</span></div></div>`;\n    }\n\n    const opens = (s && s.opens && s.opens.total) || 0;\n    const clicks = (s && s.clicks && s.clicks.total) || 0;\n    const generated = (s && s.generated && s.generated.total) || 0;\n    const dress = (s && s.dress && s.dress.total) || 0;\n    const top = (s && s.top && s.top.total) || 0;\n    const bottom = (s && s.bottom && s.bottom.total) || 0;\n    const bag = (s && s.bag && s.bag.total) || 0;\n    const rate = opens > 0 ? Math.round((clicks / opens) * 100) : 0;\n    const recent = (s && s.recent && s.recent.data) || [];\n    const leads = (s && s.leads && s.leads.total) || 0;\n    const recentLeads = (s && s.recentLeads && s.recentLeads.data) || [];\n\n    const byProduct = {};\n    recent.forEach(r => { const n = r.productName || '—'; byProduct[n] = (byProduct[n] || 0) + 1; });\n    const topList = Object.keys(byProduct).map(n => ({ name: n, count: byProduct[n] })).sort((a, b) => b.count - a.count).slice(0, 8);\n\n    const topHTML = topList.length\n      ? topList.map((p, i) => `<div class=\"row\"><span class=\"rank\">${i + 1}</span><span class=\"name\">${esc(p.name)}</span><span class=\"count\">${p.count}</span></div>`).join('')\n      : `<div class=\"empty\">Ainda não houve cliques no try-on.</div>`;\n\n    const recentHTML = recent.length\n      ? recent.slice(0, 20).map(r => `<div class=\"row\"><span class=\"name\">${esc(r.productName || '—')}</span><span class=\"badge\">${esc(KIND[r.kind] || r.kind || '—')}</span><span class=\"time\">${esc(fmtTime(r.createdAt))}</span></div>`).join('')\n      : `<div class=\"empty\">Sem atividade recente.</div>`;\n\n    const leadsHTML = recentLeads.length\n      ? recentLeads.map(l => `<div class=\"row\"><span class=\"name\">${esc(l.name || '—')}</span><span class=\"email\">${esc(l.email || '')}</span><span class=\"time\">${esc(fmtTime(l.createdAt))}</span></div>`).join('')\n      : `<div class=\"empty\">Nenhum cadastro ainda.</div>`;\n\n    return `\n      <div class=\"dash\">\n        <div class=\"top\">\n          <div class=\"title\"><span class=\"logo\">Le Lis</span> Painel do Try-On</div>\n          <div class=\"right\">\n            <span class=\"updated\">${updatedAt ? 'Atualizado às ' + esc(updatedAt) : ''}</span>\n            <button class=\"refresh\">↻ Atualizar</button>\n          </div>\n        </div>\n        <div class=\"wrap\">\n          <div class=\"section-label\">Visão geral</div>\n          <div class=\"kpis\">\n            <div class=\"kpi\"><div class=\"label\">Cadastros</div><div class=\"val accent\">${leads}</div><div class=\"sub\">preencheram o formulário</div></div>\n            <div class=\"kpi\"><div class=\"label\">Aberturas do app</div><div class=\"val\">${opens}</div><div class=\"sub\">cada vez que abriram o link</div></div>\n            <div class=\"kpi\"><div class=\"label\">Cliques no try-on</div><div class=\"val accent\">${clicks}</div><div class=\"sub\">${rate}% das aberturas</div></div>\n            <div class=\"kpi\"><div class=\"label\">Provas geradas</div><div class=\"val\">${generated}</div><div class=\"sub\">imagens criadas pela IA</div></div>\n          </div>\n\n          <div class=\"section-label\">Cliques por tipo de peça</div>\n          <div class=\"mini\">\n            <div class=\"kpi\"><div class=\"label\">Vestidos</div><div class=\"val\">${dress}</div></div>\n            <div class=\"kpi\"><div class=\"label\">Parte de cima</div><div class=\"val\">${top}</div></div>\n            <div class=\"kpi\"><div class=\"label\">Parte de baixo</div><div class=\"val\">${bottom}</div></div>\n            <div class=\"kpi\"><div class=\"label\">Bolsas</div><div class=\"val\">${bag}</div></div>\n          </div>\n\n          <div class=\"section-label\">Mais experimentadas (últimos 50 cliques)</div>\n          <div class=\"panel\">${topHTML}</div>\n\n          <div class=\"section-label\">Atividade recente</div>\n          <div class=\"panel\">${recentHTML}</div>\n\n          <div class=\"section-label\">Cadastros recentes</div>\n          <div class=\"panel\">${leadsHTML}</div>\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    const PW_KEY = 'lelis_stats_pw';\n    const getPw = () => { try { return localStorage.getItem(PW_KEY) || ''; } catch (e) { return ''; } };\n\n    const load = async (pw) => {\n      d2.setState({ status: 'loading', error: null });\n      try {\n        const r = await d2.rush('lelis-stats').invoke({ password: pw });\n        const s = (r && r.data) ? r.data : r;\n        if (!s || s.ok !== true) {\n          try { localStorage.removeItem(PW_KEY); } catch (e) {}\n          d2.setState({ status: 'locked', pwSubmitting: false, pwError: pw ? 'Senha incorreta.' : null });\n          return;\n        }\n        try { localStorage.setItem(PW_KEY, pw); } catch (e) {}\n        let now = '';\n        try { now = new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } catch (e) {}\n        d2.setState({ status: 'done', s, updatedAt: now, pwSubmitting: false, pwError: null });\n      } catch (err) {\n        d2.setState({ status: 'error', error: String((err && err.message) || err) });\n      }\n    };\n\n    d2.on('.refresh', 'click', () => load(getPw()));\n    d2.on('.gate-btn', 'click', () => {\n      const el = d2.$('.gate-input');\n      const pw = (el && el.value) || '';\n      if (!pw) { d2.setState({ pwError: 'Digite a senha.' }); return; }\n      d2.setState({ pwSubmitting: true, pwError: null });\n      load(pw);\n    });\n    d2.on('.gate-input', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.gate-btn'); if (b) b.click(); } });\n\n    const saved = getPw();\n    if (saved) { load(saved); } else { d2.setState({ status: 'locked' }); }\n  }\n}\n"}}}}}}}},{"_id":"6a74c0584b986519b1625d41","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"hyperaChartWidget","package":"people-gabi-carmo","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 CHART_LIB = { url: 'https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js', global: 'Chart' }\n\nfunction esc(s) { return String(s == null ? '' : s).replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c])) }\n\nfunction drawChart(d2, root) {\n  if (root._chartDrawn) return\n  const Chart = d2.lib(CHART_LIB)\n  if (!Chart) return\n  const cv = root.querySelector('canvas')\n  if (!cv) return\n  const type = d2.param('type', 'bar')\n  const labels = d2.param('labels', []) || []\n  const data = (d2.param('data', []) || []).map((v) => Number(v) || 0)\n  const accent = d2.param('accent', '#0062AA')\n  const isDoughnut = type === 'doughnut'\n  const isLine = type === 'line'\n  const palette = ['#0062AA', '#3B82C4', '#8FBEE0', '#C9E0F0', '#0B2545', '#64748B', '#94A3B8']\n\n  new Chart(cv.getContext('2d'), {\n    type: isDoughnut ? 'doughnut' : (isLine ? 'line' : 'bar'),\n    data: {\n      labels: labels,\n      datasets: [{\n        data: data,\n        backgroundColor: isDoughnut ? palette : accent,\n        borderColor: isLine ? accent : (isDoughnut ? '#FFFFFF' : accent),\n        borderWidth: isLine ? 2 : (isDoughnut ? 2 : 0),\n        borderRadius: isDoughnut ? 0 : 6,\n        tension: 0.35,\n        fill: isLine ? false : undefined,\n        pointRadius: isLine ? 3 : undefined,\n        pointBackgroundColor: isLine ? accent : undefined,\n      }]\n    },\n    options: {\n      responsive: true,\n      maintainAspectRatio: false,\n      animation: { duration: 420 },\n      plugins: {\n        legend: { display: isDoughnut, position: 'bottom', labels: { boxWidth: 10, font: { size: 11 } } },\n        tooltip: { padding: 8 },\n      },\n      scales: isDoughnut ? {} : {\n        x: { grid: { display: false }, ticks: { font: { size: 10 }, maxRotation: 40, minRotation: 0 } },\n        y: { beginAtZero: true, grace: '10%', ticks: { font: { size: 10 } } },\n      },\n    },\n  })\n  root._chartDrawn = true\n}\n\nexport default {\n  css: `\n    .card{background:#fff;border:1px solid #E2E8F0;border-radius:12px;padding:14px 16px;box-sizing:border-box;height:100%;display:flex;flex-direction:column;font-family:-apple-system,'Segoe UI',Roboto,sans-serif}\n    .ttl{font:600 13px/1.3 inherit;color:#0B2545;margin:0 0 10px 0}\n    .cvwrap{flex:1;position:relative;min-height:0}\n    canvas{width:100% !important;height:100% !important}\n    .skel{flex:1;display:flex;align-items:center;justify-content:center;color:#94A3B8;font-size:12px}\n  `,\n  render(d2) {\n    const Chart = d2.lib(CHART_LIB)\n    const title = d2.param('title', '')\n    if (!Chart) {\n      return `<div class=\"card\"><div class=\"ttl\">${esc(title)}</div><div class=\"skel\">Carregando gráfico…</div></div>`\n    }\n    return `<div class=\"card\"><div class=\"ttl\">${esc(title)}</div><div class=\"cvwrap\"><canvas data-d2-static></canvas></div></div>`\n  },\n  mounted(d2, root) { drawChart(d2, root) },\n  updated(d2, root) { drawChart(d2, root) },\n}"}},"type":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"type"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"labels":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"labels"}}}},"data":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"data"}}}},"accent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accent"}}}}}}}}}},{"_id":"6a74c085c5a2e39683a6c0f6","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"hyperaPeopleAnalyticsApp","package":"people-gabi-carmo","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) { return String(s == null ? '' : s).replace(/[&<>\"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[c])) }\n\nconst CHIPS = [\n  'Qual o turnover da minha área nos últimos 6 meses?',\n  'Como está a distribuição de performance do meu time?',\n  'Qual o headcount atual da minha área?',\n  'Como está o absenteísmo comparado ao período anterior?',\n  'Quais áreas têm maior risco de saída?',\n]\n\nconst LOGO_COLOR = 'https://people-gabi-carmo.data2.media/file/JEYD4mcTcXnVhmxZYupjx'\nconst LOGO_WHITE = 'https://people-gabi-carmo.data2.media/file/MesXmgugNAnnGIgdYMXpm'\n\nasync function sendMessage(d2) {\n  const text = (d2.state.input || '').trim()\n  if (!text || d2.state.sending) return\n  const session = d2.state.session || {}\n  const priorMessages = d2.state.messages || []\n  const history = priorMessages.slice(-6).map((m) => ({ role: m.role, text: m.text }))\n  const userMsg = { id: 'u' + Date.now(), role: 'user', text }\n  d2.state.messages = [...priorMessages, userMsg]\n  d2.state.input = ''\n  d2.state.sending = true\n  try {\n    const res = await d2.rush(d2.params.askRush).invoke({\n      message: text,\n      history: history,\n      nome: session.nome,\n      perfil: session.perfil,\n      diretoria: session.diretoria,\n      gerencia: session.gerencia,\n      diretoriasAtendidas: session.diretoriasAtendidas,\n      escopoDescricao: session.escopoDescricao,\n    })\n    const data = (res && res.data) || {}\n    const aiMsg = {\n      id: 'a' + Date.now(),\n      role: 'assistant',\n      text: data.reply || 'Não consegui gerar uma resposta.',\n      kpis: data.kpis || [],\n      chart: data.chart || null,\n    }\n    d2.state.messages = [...d2.state.messages, aiMsg]\n  } catch (err) {\n    d2.state.messages = [...(d2.state.messages || []), {\n      id: 'e' + Date.now(), role: 'assistant',\n      text: 'Não foi possível consultar os dados agora. Tente novamente em alguns instantes.',\n      kpis: [], chart: null,\n    }]\n  } finally {\n    d2.state.sending = false\n  }\n}\n\nfunction renderLogin(d2) {\n  const personas = d2.param('personas', []) || []\n  const cards = personas.map((p, i) => `\n    <div class=\"persona-card\" data-idx=\"${i}\">\n      <div class=\"avatar\">${esc((p.nome || '?').charAt(0))}</div>\n      <div class=\"persona-body\">\n        <div class=\"persona-top\">\n          <span class=\"persona-name\">${esc(p.nome)}</span>\n          <span class=\"badge\">${esc(p.perfil)}</span>\n        </div>\n        <div class=\"persona-desc\">${esc(p.escopoDescricao)}</div>\n      </div>\n    </div>\n  `).join('')\n\n  return `\n    <div class=\"login\">\n      <div class=\"login-inner\">\n        <img class=\"logo\" src=\"${LOGO_COLOR}\" alt=\"Hypera Pharma\"/>\n        <h1 class=\"title\">Agente de IA · People Analytics</h1>\n        <p class=\"subtitle\">Simulação de acesso — escolha um perfil para ver como o Row-Level Security filtra os dados por diretoria e gerência.</p>\n        <div class=\"info-banner\">\n          <div class=\"info-icon\">i</div>\n          <div class=\"info-text\">Cada perfil abaixo enxerga apenas os dados aos quais tem direito de acesso — exatamente como aconteceria em produção com Microsoft Entra ID e Row-Level Security no Power BI.</div>\n        </div>\n        <div class=\"persona-list\">${cards}</div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderKpis(kpis) {\n  if (!kpis || !kpis.length) return ''\n  return `<div class=\"kpi-row\">${kpis.map((k) => `\n    <div class=\"kpi\">\n      <div class=\"kpi-val\">${esc(k.value)}${k.suffix ? esc(k.suffix) : ''}</div>\n      <div class=\"kpi-label\">${esc(k.label)}</div>\n    </div>\n  `).join('')}</div>`\n}\n\nfunction renderMessage(d2, m) {\n  if (m.role === 'user') {\n    return `<div class=\"msg-row user\"><div class=\"bubble user\">${esc(m.text)}</div></div>`\n  }\n  const chartWidget = d2.param('chartWidget')\n  const chartHtml = (m.chart && chartWidget)\n    ? `<div class=\"chart-slot\">${d2.render(chartWidget, {\n        type: m.chart.type, title: m.chart.title, labels: m.chart.labels, data: m.chart.data, accent: '#0062AA',\n      }, { key: m.id + '-chart', height: 250 })}</div>`\n    : ''\n  return `\n    <div class=\"msg-row ai\">\n      <div class=\"bubble ai\">\n        <div class=\"ai-text\">${esc(m.text)}</div>\n        ${renderKpis(m.kpis)}\n        ${chartHtml}\n      </div>\n    </div>\n  `\n}\n\nfunction renderChat(d2) {\n  const session = d2.state.session || {}\n  const messages = d2.state.messages || []\n  const sending = !!d2.state.sending\n  const input = d2.state.input || ''\n\n  const chips = messages.length === 0\n    ? `<div class=\"chips\">${CHIPS.map((c) => `<div class=\"chip\" data-text=\"${esc(c)}\">${esc(c)}</div>`).join('')}</div>`\n    : ''\n\n  const typing = sending\n    ? `<div class=\"msg-row ai\"><div class=\"bubble ai typing\"><span></span><span></span><span></span></div></div>`\n    : ''\n\n  return `\n    <div class=\"chat\">\n      <div class=\"topbar\">\n        <img class=\"logo-sm\" src=\"${LOGO_WHITE}\" alt=\"Hypera\"/>\n        <div class=\"topbar-title\">Agente People Analytics <span class=\"dot\">·</span> Hypera Pharma</div>\n        <div class=\"topbar-right\">\n          <span class=\"persona-pill\">${esc(session.nome)} · ${esc(session.perfil)}</span>\n          <button class=\"logout-btn\">Trocar perfil</button>\n        </div>\n      </div>\n      ${chips}\n      <div class=\"msg-list\">\n        ${messages.map((m) => renderMessage(d2, m)).join('')}\n        ${typing}\n      </div>\n      <div class=\"input-bar\">\n        <input class=\"chat-input\" placeholder=\"Digite sua pergunta sobre pessoas...\" value=\"${esc(input)}\" ${sending ? 'disabled' : ''}/>\n        <button class=\"send-btn\" ${sending ? 'disabled' : ''}>Enviar</button>\n      </div>\n    </div>\n  `\n}\n\nexport default {\n  css: `\n    :host, .app { font-family: -apple-system, 'Segoe UI', Roboto, sans-serif; }\n    .app { width:100%; height:100%; background:#F4F6F9; box-sizing:border-box; overflow:hidden; }\n\n    .login { width:100%; height:100%; overflow-y:auto; background:#FFFFFF; }\n    .login-inner { max-width:720px; margin:0 auto; padding:56px 24px 40px; box-sizing:border-box; }\n    .logo { height:70px; display:block; margin-bottom:20px; }\n    .title { font-size:28px; font-weight:700; color:#0B2545; margin:0 0 12px 0; }\n    .subtitle { font-size:14px; color:#64748B; line-height:1.5; margin:0 0 20px 0; }\n    .info-banner { display:flex; gap:12px; align-items:flex-start; background:#EAF2FB; border-radius:10px; padding:14px 16px; margin-bottom:24px; }\n    .info-icon { flex:none; width:26px; height:26px; border-radius:999px; background:#0062AA; color:#fff; font-size:14px; font-weight:700; display:flex; align-items:center; justify-content:center; }\n    .info-text { font-size:13px; color:#0B2545; line-height:1.5; }\n    .persona-list { display:flex; flex-direction:column; gap:14px; }\n    .persona-card { display:flex; gap:16px; align-items:flex-start; background:#fff; border:1px solid #E2E8F0; border-radius:14px; padding:18px 20px; cursor:pointer; transition:box-shadow .15s ease, border-color .15s ease; }\n    .persona-card:hover { box-shadow:0 4px 16px rgba(11,37,69,0.08); border-color:#0062AA; }\n    .avatar { flex:none; width:52px; height:52px; border-radius:999px; background:#0062AA; color:#fff; font-size:20px; font-weight:700; display:flex; align-items:center; justify-content:center; }\n    .persona-body { flex:1; min-width:0; }\n    .persona-top { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:6px; }\n    .persona-name { font-size:16px; font-weight:600; color:#0B2545; }\n    .badge { font-size:12px; font-weight:600; color:#0062AA; background:#EAF2FB; border-radius:999px; padding:4px 12px; white-space:nowrap; }\n    .persona-desc { font-size:13px; color:#64748B; line-height:1.4; }\n\n    .chat { width:100%; height:100%; display:flex; flex-direction:column; background:#F4F6F9; }\n    .topbar { flex:none; height:64px; background:#0B2545; display:flex; align-items:center; gap:16px; padding:0 24px; box-sizing:border-box; }\n    .logo-sm { height:32px; }\n    .topbar-title { color:#fff; font-size:15px; font-weight:600; flex:1; }\n    .topbar-title .dot { color:#8FA3C4; margin:0 4px; }\n    .topbar-right { display:flex; align-items:center; gap:14px; }\n    .persona-pill { color:#CBD9EC; font-size:13px; }\n    .logout-btn { background:rgba(255,255,255,0.14); color:#fff; border:0; border-radius:8px; padding:9px 16px; font-size:13px; cursor:pointer; }\n    .logout-btn:hover { background:rgba(255,255,255,0.22); }\n\n    .chips { flex:none; display:flex; gap:10px; padding:14px 24px; background:#EAF0F7; overflow-x:auto; }\n    .chip { flex:none; background:#fff; border:1px solid #CBD5E1; border-radius:999px; padding:8px 16px; font-size:12px; color:#0B2545; cursor:pointer; white-space:nowrap; }\n    .chip:hover { border-color:#0062AA; color:#0062AA; }\n\n    .msg-list { flex:1; overflow-y:auto; padding:20px 24px; display:flex; flex-direction:column; gap:14px; }\n    .msg-row { display:flex; }\n    .msg-row.user { justify-content:flex-end; }\n    .msg-row.ai { justify-content:flex-start; }\n    .bubble { max-width:640px; border-radius:14px; padding:14px 18px; font-size:14px; line-height:1.5; }\n    .bubble.user { background:#0062AA; color:#fff; }\n    .bubble.ai { background:#fff; border:1px solid #E2E8F0; color:#0B2545; }\n    .ai-text { white-space:pre-wrap; }\n    .bubble.typing { display:flex; gap:4px; align-items:center; padding:16px 18px; }\n    .bubble.typing span { width:6px; height:6px; border-radius:999px; background:#94A3B8; animation:blink 1.2s infinite ease-in-out; }\n    .bubble.typing span:nth-child(2) { animation-delay:.2s; }\n    .bubble.typing span:nth-child(3) { animation-delay:.4s; }\n    @keyframes blink { 0%,80%,100%{opacity:.25} 40%{opacity:1} }\n\n    .kpi-row { display:flex; gap:10px; margin-top:14px; flex-wrap:wrap; }\n    .kpi { flex:1; min-width:110px; background:#F4F6F9; border-radius:10px; padding:10px 14px; }\n    .kpi-val { font-size:20px; font-weight:700; color:#0062AA; }\n    .kpi-label { font-size:11px; color:#64748B; margin-top:2px; }\n\n    .chart-slot { margin-top:14px; height:250px; }\n\n    .input-bar { flex:none; display:flex; gap:12px; padding:16px 24px; background:#fff; border-top:1px solid #E2E8F0; }\n    .chat-input { flex:1; border:none; background:#F4F6F9; border-radius:10px; padding:14px 16px; font-size:14px; color:#0B2545; outline:none; }\n    .chat-input::placeholder { color:#94A3B8; }\n    .send-btn { background:#0062AA; color:#fff; border:0; border-radius:10px; padding:0 26px; font-size:14px; font-weight:600; cursor:pointer; }\n    .send-btn:disabled { opacity:.6; cursor:default; }\n    .send-btn:not(:disabled):hover { background:#004F8A; }\n  `,\n\n  render(d2) {\n    const view = d2.state.view || 'login'\n    return `<div class=\"app\">${view === 'login' ? renderLogin(d2) : renderChat(d2)}</div>`\n  },\n\n  mounted(d2) {\n    d2.on('.persona-card', 'click', (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const personas = d2.param('personas', []) || []\n      const p = personas[idx]\n      if (!p) return\n      d2.state.session = {\n        nome: p.nome, perfil: p.perfil, diretoria: p.diretoria || '', gerencia: p.gerencia || '',\n        diretoriasAtendidas: p.diretoriasAtendidas || [], escopoDescricao: p.escopoDescricao || '',\n      }\n      d2.state.view = 'chat'\n      d2.state.messages = []\n      d2.state.input = ''\n    })\n\n    d2.on('.logout-btn', 'click', () => {\n      d2.state.view = 'login'\n      d2.state.session = null\n      d2.state.messages = []\n      d2.state.input = ''\n    })\n\n    d2.on('.chip', 'click', (e, el) => { d2.state.input = el.dataset.text || '' })\n    d2.on('.chat-input', 'input', (e, el) => { d2.state.input = el.value })\n    d2.on('.chat-input', 'keydown', (e) => { if (e.key === 'Enter') sendMessage(d2) })\n    d2.on('.send-btn', 'click', () => sendMessage(d2))\n  },\n\n  updated(d2, root) {\n    const log = root.querySelector('.msg-list')\n    if (log) log.scrollTop = log.scrollHeight\n  },\n}"}},"personas":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"personas"}}}},"askRush":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"askRush"}}}},"chartWidget":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chartWidget"}}}}}}}}}},{"_id":"6a75db6ee8875522e2da037d","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"propostaVeste","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Data² — Capa Proposta Grupo Veste (identidade oficial: logo anel + wordmark)\nconst POC_URL = 'https://veste.people-gabi-carmo.next.data2.app/';\nconst AUTH_SENHA = 'veste2026';\nconst AUTH_KEY = 'veste_proposta_auth';\n\nconst LOGO_MARK = `<svg class=\"logo-mark\" viewBox=\"0 0 104 104\" aria-hidden=\"true\">\n  <circle cx=\"52\" cy=\"19\" r=\"11\" fill=\"#3D6DF0\"/>\n  <circle cx=\"75.33\" cy=\"28.67\" r=\"11\" fill=\"#E9632B\"/>\n  <circle cx=\"85\" cy=\"52\" r=\"11\" fill=\"#EFD94F\"/>\n  <circle cx=\"75.33\" cy=\"75.33\" r=\"11\" fill=\"#5FE0A6\"/>\n  <circle cx=\"52\" cy=\"85\" r=\"11\" fill=\"#58C8EE\"/>\n  <circle cx=\"28.67\" cy=\"75.33\" r=\"11\" fill=\"#EF478F\"/>\n  <circle cx=\"19\" cy=\"52\" r=\"11\" fill=\"#9B3FD7\"/>\n  <circle cx=\"28.67\" cy=\"28.67\" r=\"11\" fill=\"#8FD24C\"/>\n</svg>`;\nconst LOGO = `<span class=\"logo\">${LOGO_MARK}<span class=\"logo-word\">Data<sup>2</sup></span></span>`;\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst fmtDT = (iso) => { try { return new Date(iso).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\nconst shortDev = (ua) => { ua = String(ua || ''); const os = /iphone|ipad/i.test(ua) ? 'iOS' : /android/i.test(ua) ? 'Android' : /windows/i.test(ua) ? 'Windows' : /macintosh|mac os x/i.test(ua) ? 'Mac' : /linux/i.test(ua) ? 'Linux' : '—'; const br = /edg/i.test(ua) ? 'Edge' : /opr|opera/i.test(ua) ? 'Opera' : /chrome/i.test(ua) ? 'Chrome' : /firefox/i.test(ua) ? 'Firefox' : /safari/i.test(ua) ? 'Safari' : ''; return os + (br ? ' · ' + br : ''); };\nconst renderAcessos = (st) => {\n  const status = st.acStatus || 'locked';\n  let inner;\n  if (status === 'loading') { inner = `<div class=\"ac-msg\">Carregando...</div>`; }\n  else if (status !== 'done') { inner = `<div class=\"login-card\" style=\"max-width:360px\"><div class=\"login-title\">Painel de acessos</div><input class=\"login-input ac-pass\" type=\"password\" placeholder=\"Senha do painel\" /><button class=\"btn btn-grad ac-btn\" style=\"width:100%;justify-content:center\">Entrar</button>${st.acError ? '<div class=\"login-err\">' + esc(st.acError) + '</div>' : ''}</div>`; }\n  else {\n    const ac = st.ac || {}; const recent = ac.recent || [];\n    const cset = {}; recent.forEach(r => { if (r.clientId) cset[r.clientId] = 1; });\n    const distinct = Object.keys(cset).length;\n    const rows = recent.slice(0, 60).map(r => `<tr><td>${fmtDT(r.createdAt)}</td><td>${r.event === 'login' ? 'Entrou (senha)' : 'Abriu'}</td><td>${esc(shortDev(r.device))}</td><td class=\"ac-cid\">${esc((r.clientId || '').slice(0, 10))}</td></tr>`).join('');\n    inner = `<div class=\"ac-kpis\"><div class=\"ac-kpi\"><div class=\"ac-k-label\">Aberturas</div><div class=\"ac-k-val\">${ac.opens || 0}</div></div><div class=\"ac-kpi\"><div class=\"ac-k-label\">Entradas (senha)</div><div class=\"ac-k-val\">${ac.logins || 0}</div></div><div class=\"ac-kpi\"><div class=\"ac-k-label\">Dispositivos distintos</div><div class=\"ac-k-val\">${distinct}</div></div></div><div class=\"p-subhead\" style=\"margin-top:26px\">Acessos recentes</div><div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Quando</th><th>Evento</th><th>Dispositivo</th><th>ID navegador</th></tr></thead><tbody>${rows || '<tr><td>Sem acessos ainda.</td></tr>'}</tbody></table></div><button class=\"ghost ac-refresh\" style=\"margin-top:20px\">↻ Atualizar</button>`;\n  }\n  return `<div class=\"stage\"><div class=\"proposal\"><div class=\"p-top\">${LOGO}<span class=\"ac-tag\">Painel de acessos</span></div><div class=\"p-head\" style=\"border:0;padding-bottom:8px\"><div class=\"eyebrow\">Monitoramento · Proposta Grupo Veste</div><h2>Quem acessou</h2></div>${inner}</div></div>`;\n};\n\nexport default {\n  css: `\n@import url('https://fonts.googleapis.com/css2?family=Encode+Sans+Expanded:wght@300;400&display=swap');\n@font-face{font-family:'Circular Air Light';src:url('https://www.data2.media/file/DWcXnLLnDWwlhRgYpsi1-') format('woff2'),url('https://www.data2.media/file/TDX5oH7NTx-vDAtRBcNeY') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Medium';src:url('https://www.data2.media/file/EByR5T2BNUoKCejgqK1JQ') format('woff2'),url('https://www.data2.media/file/tnIrecxS9FEqh9vh_Go-8') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Bold';src:url('https://www.data2.media/file/lALU-3wyCL2kksdactSyo') format('woff2'),url('https://www.data2.media/file/R3eunIkdQ9ors671taObT') format('woff');font-display:swap}\n*{box-sizing:border-box;margin:0;padding:0}\n:host{display:block;width:100%;height:100%}\n.stage{height:100%;width:100%;overflow-y:auto;font-family:'Circular Air Light','Circular Air Medium',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;-webkit-font-smoothing:antialiased;color:#f5f6f8;background:#000000}\n@keyframes gflow{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}\n@keyframes floatUp{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}\n@keyframes glowpulse{0%,100%{opacity:.42;transform:translate(-50%,-50%) scale(1)}50%{opacity:.6;transform:translate(-50%,-50%) scale(1.08)}}\n.hero{position:relative;min-height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;padding:56px 28px;background:#000000}\n.glow{display:none}\n.inner{position:relative;z-index:2;text-align:center;max-width:860px;width:100%;animation:floatUp .8s cubic-bezier(.2,.7,.2,1) both}\n.logo{display:inline-flex;align-items:center;gap:20px;color:#fff}\n.logo-mark{width:66px;height:66px;flex-shrink:0}\n.logo-word{font-family:'Encode Sans Expanded','Circular Air Light',sans-serif;font-weight:300;font-size:58px;line-height:1;display:inline-flex;align-items:flex-start;letter-spacing:0}\n.logo-word sup{font-family:'Encode Sans Expanded',sans-serif;font-weight:400;font-size:.5em;margin-left:3px;margin-top:.1em}\n.tagline{margin-top:16px;font-family:'Circular Air Light',sans-serif;font-size:15px;letter-spacing:.04em;color:rgba(255,255,255,.62)}\n.divider{width:54px;height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.4),transparent);margin:40px auto 34px}\n.kicker{display:inline-block;font-family:'Circular Air Bold',sans-serif;font-size:12.5px;letter-spacing:.26em;text-transform:uppercase;color:#fff;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.24);border-radius:999px;padding:9px 20px;margin-bottom:24px}\nh1{font-family:'Circular Air Bold',sans-serif;font-size:clamp(46px,8.5vw,96px);line-height:1.02;letter-spacing:-.01em;color:#fff;margin-bottom:24px}\nh1 .grad{color:#fff}\n.lede{font-size:clamp(16px,2vw,20px);color:rgba(255,255,255,.78);max-width:580px;margin:0 auto 46px;line-height:1.55}\n.cta{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}\n.btn{cursor:pointer;border:0;border-radius:999px;font-family:'Circular Air Bold',sans-serif;font-size:16px;padding:17px 34px;transition:transform .15s,box-shadow .25s,background .2s;color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:10px}\n.btn:active{transform:translateY(1px)}\n.btn-grad{background:#fff;color:#000;box-shadow:none;border:1px solid #fff}\n.btn-grad:hover{background:#e6e6e6;border-color:#e6e6e6;transform:translateY(-2px)}\n.btn-glass{background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.32);backdrop-filter:blur(6px)}\n.btn-glass:hover{background:rgba(255,255,255,.16);transform:translateY(-2px)}\n.login-card{margin-top:6px;text-align:left}\n.login-title{font-family:'Circular Air Bold',sans-serif;font-size:15px;color:#fff;text-align:center;margin-bottom:18px}\n.login-input{width:100%;height:50px;background:#111216;border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:0 16px;color:#fff;font-size:15px;font-family:inherit;outline:none;margin-bottom:12px;transition:border-color .15s}\n.login-input:focus{border-color:#fff}\n.login-input::placeholder{color:rgba(255,255,255,.4)}\n.login-err{margin-top:12px;font-size:13px;color:#ff9b9b;text-align:center}\n.brands-line{margin-top:50px;font-family:'Circular Air Medium',sans-serif;font-size:12.5px;letter-spacing:.18em;text-transform:uppercase;color:rgba(255,255,255,.5)}\n/* proposta placeholder */\n.prop{min-height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:60px 28px;background:#000000;position:relative;overflow:hidden}\n.prop .logo{margin-bottom:30px}\n.prop .logo-mark{width:48px;height:48px}\n.prop .logo-word{font-size:46px}\n.eyebrow{font-family:'Circular Air Bold',sans-serif;font-size:13px;letter-spacing:.22em;text-transform:uppercase;color:rgba(255,255,255,.55);margin-bottom:12px}\n.prop h2{font-family:'Circular Air Bold',sans-serif;font-size:clamp(30px,5vw,52px);letter-spacing:-.01em;color:#fff;margin-bottom:16px}\n.prop p{font-size:17px;color:rgba(255,255,255,.66);max-width:520px;line-height:1.6;margin-bottom:34px}\n.dash{display:inline-flex;align-items:center;gap:10px;color:rgba(255,255,255,.5);font-size:14px;margin-bottom:40px}\n.pulse{width:9px;height:9px;border-radius:50%;background:rgba(255,255,255,.8);animation:pl 1.8s infinite}\n@keyframes pl{0%{box-shadow:0 0 0 0 rgba(255,255,255,.4)}70%{box-shadow:0 0 0 12px rgba(255,255,255,0)}100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}}\n.ghost{cursor:pointer;background:none;border:1px solid rgba(255,255,255,.3);color:#fff;border-radius:999px;font-family:'Circular Air Medium',sans-serif;font-size:14px;padding:12px 24px;transition:border-color .2s,background .2s}\n.ghost:hover{border-color:#fff;background:rgba(255,255,255,.08)}\n.proposal{max-width:920px;margin:0 auto;padding:40px 32px 80px;min-height:100%}\n.p-top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:44px}\n.p-top .logo{gap:11px}.p-top .logo-word{font-size:26px}.p-top .logo-mark{width:30px;height:30px}\n.p-head{margin-bottom:20px;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:34px}\n.p-head h2{font-family:'Circular Air Bold',sans-serif;font-size:clamp(30px,5vw,48px);color:#fff;letter-spacing:-.01em;margin:10px 0 14px}\n.p-lede{font-size:16px;color:rgba(255,255,255,.62);line-height:1.6;max-width:640px}\n.p-sec{display:flex;gap:24px;padding:38px 0;border-bottom:1px solid rgba(255,255,255,.08)}\n.p-secnum{font-family:'Circular Air Bold',sans-serif;font-size:15px;color:rgba(255,255,255,.35);width:36px;flex-shrink:0;padding-top:4px}\n.p-secbody{flex:1;min-width:0}\n.p-secbody h3{font-family:'Circular Air Bold',sans-serif;font-size:22px;color:#fff;margin-bottom:10px}\n.p-desc{font-size:15px;color:rgba(255,255,255,.6);line-height:1.6;margin-bottom:22px;max-width:640px}\n.p-price-card{display:flex;flex-wrap:wrap;gap:20px;align-items:center;justify-content:space-between;background:#0f1013;border:1px solid rgba(255,255,255,.12);border-radius:16px;padding:24px 28px}\n.ppc-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:6px}\n.ppc-val{font-family:'Circular Air Bold',sans-serif;font-size:22px;color:#fff}\n.ppc-big{font-family:'Circular Air Bold',sans-serif;font-size:34px;color:#fff}\n.ppc-right{text-align:right}\n.tbl-wrap{overflow-x:auto;border:1px solid rgba(255,255,255,.12);border-radius:14px}\n.tbl{width:100%;border-collapse:collapse;font-size:14px;min-width:540px}\n.tbl th{font-family:'Circular Air Bold',sans-serif;text-align:left;font-size:12px;letter-spacing:.03em;color:rgba(255,255,255,.75);padding:14px 16px;border-bottom:1px solid rgba(255,255,255,.14);background:#0f1013;white-space:nowrap}\n.tbl td{padding:13px 16px;color:rgba(255,255,255,.82);border-bottom:1px solid rgba(255,255,255,.07);white-space:nowrap}\n.tbl td strong{color:#fff}\n.tbl tbody tr:last-child td{border-bottom:0}\n.tbl tbody tr:hover td{background:rgba(255,255,255,.03)}\n.tbl td:first-child{font-family:'Circular Air Medium',sans-serif;color:#fff}\n.p-link{display:inline-block;margin-top:18px;color:#fff;font-size:14px;text-decoration:none;border-bottom:1px solid rgba(255,255,255,.4);padding-bottom:2px}\n.p-link:hover{border-color:#fff}\n.ppc-sub{font-size:12px;color:rgba(255,255,255,.45);margin-top:6px}\n.p-note{font-size:12.5px;color:rgba(255,255,255,.45);line-height:1.6;margin-top:16px}\n.p-subhead{font-family:'Circular Air Bold',sans-serif;font-size:14px;color:#fff;margin:30px 0 14px;letter-spacing:.02em}\n.ac-tag{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4)}\n.ac-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:10px}\n.ac-kpi{background:#0f1013;border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:20px}\n.ac-k-label{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:8px}\n.ac-k-val{font-family:'Circular Air Bold',sans-serif;font-size:32px;color:#fff}\n.ac-cid{color:rgba(255,255,255,.4);font-size:12px}\n.ac-msg{color:rgba(255,255,255,.6);padding:30px 0}\n@media(max-width:600px){.ac-kpis{grid-template-columns:1fr}}\n.p-contact{margin-top:56px;border-top:1px solid rgba(255,255,255,.1);padding-top:34px}\n.pc-brand{margin-bottom:24px}.pc-brand .logo{gap:11px}.pc-brand .logo-word{font-size:26px}.pc-brand .logo-mark{width:30px;height:30px}\n.pc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:20px;margin-bottom:26px}\n.pc-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:6px}\n.pc-val{font-size:15px;color:#fff}\n.pc-tag{font-family:'Circular Air Light',sans-serif;font-size:14px;color:rgba(255,255,255,.5)}\n.p-foot{margin-top:50px;text-align:center;font-size:13px;color:rgba(255,255,255,.4)}\n@media(max-width:600px){.cta{flex-direction:column}.btn{width:100%;justify-content:center}.logo-word{font-size:48px}.logo-mark{width:50px;height:50px}.p-sec{flex-direction:column;gap:8px}.p-secnum{padding-top:0}}\n  `,\n\n  render(d2) {\n    const screen = (d2.state && d2.state.screen) || 'login';\n    if (screen === 'acessos') return renderAcessos(d2.state || {});\n    if (screen === 'login') {\n      return `\n        <div class=\"stage\">\n          <div class=\"hero\">\n            <div class=\"inner\" style=\"max-width:420px\">\n              ${LOGO}\n              <div class=\"tagline\">Express yourself at scale</div>\n              <div class=\"divider\"></div>\n              <div class=\"kicker\">Proposta · Grupo Veste</div>\n              <div class=\"login-card\">\n                <div class=\"login-title\">Digite a senha para acessar</div>\n                <input class=\"login-input login-pass\" type=\"password\" placeholder=\"Senha\" autocomplete=\"current-password\" />\n                <button class=\"btn btn-grad login-btn\" style=\"width:100%;justify-content:center\">Entrar</button>\n                ${(d2.state && d2.state.loginError) ? '<div class=\"login-err\">' + d2.state.loginError + '</div>' : ''}\n              </div>\n            </div>\n          </div>\n        </div>`;\n    }\n    if (screen === 'proposta') {\n      return `\n        <div class=\"stage\">\n          <div class=\"proposal\">\n            <div class=\"p-top\"><button class=\"ghost go-capa\">← Voltar</button>${LOGO}</div>\n            <div class=\"p-head\">\n              <div class=\"eyebrow\">Proposta Comercial · Grupo Veste</div>\n              <h2>Provador Virtual com IA</h2>\n              <p class=\"p-lede\">Desenvolvimento, implementação e operação do provador virtual para as marcas do Grupo Veste — Le Lis, John John, Bo.Bô, Individual e Dudalina.</p>\n            </div>\n            <section class=\"p-sec\"><div class=\"p-secnum\">01</div><div class=\"p-secbody\">\n              <h3>Desenvolvimento e implementação</h3>\n              <p class=\"p-desc\">Virtual Try-On para as marcas da Veste — catálogo por marca, prova virtual com IA, montagem de looks e galeria de provas.</p>\n              <div class=\"p-price-card\"><div><div class=\"ppc-label\">Escopo</div><div class=\"ppc-val\">1 sprint</div></div><div class=\"ppc-right\"><div class=\"ppc-label\">Investimento</div><div class=\"ppc-big\">R$ 50.000</div></div></div>\n            </div></section>\n            <section class=\"p-sec\"><div class=\"p-secnum\">02</div><div class=\"p-secbody\">\n              <h3>Custos de geração de imagem</h3>\n              <p class=\"p-desc\">Cada prova virtual gera uma imagem. Quanto maior o volume mensal contratado, menor o preço por imagem.</p>\n              <div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Faixa</th><th>Mínimo mensal</th><th>Desconto</th><th>Preço/imagem</th><th>Spend mínimo/mês</th><th>Preço cheio equivalente</th></tr></thead><tbody>\n                <tr><td>Sem compromisso</td><td>—</td><td>0%</td><td><strong>$0,090</strong></td><td>—</td><td>—</td></tr>\n                <tr><td>Tier 1</td><td>5.000 img</td><td>50%</td><td><strong>$0,045</strong></td><td>$225/mês</td><td>$450</td></tr>\n                <tr><td>Tier 2</td><td>15.000 img</td><td>60%</td><td><strong>$0,036</strong></td><td>$540/mês</td><td>$1.350</td></tr>\n                <tr><td>Tier 3</td><td>50.000 img</td><td>70%</td><td><strong>$0,027</strong></td><td>$1.350/mês</td><td>$4.500</td></tr>\n              </tbody></table></div>\n            </div></section>\n            <section class=\"p-sec\"><div class=\"p-secnum\">03</div><div class=\"p-secbody\">\n              <h3>Utilização da plataforma Data²</h3>\n              <p class=\"p-desc\">Custos de uso da plataforma para operar o provador (valores em US$, conforme data2.app/pricing). Abaixo, os itens que o provador consome:</p>\n              <div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Item</th><th>Preço</th></tr></thead><tbody>\n                <tr><td>Tokens de IA (análise da foto)</td><td>US$ 0,0001 / token</td></tr>\n                <tr><td>Escrita de dados</td><td>US$ 0,0005 / registro</td></tr>\n                <tr><td>Leitura de dados</td><td>US$ 0,00001 / registro</td></tr>\n                <tr><td>Rush · listener (backend)</td><td>US$ 0,0001 / invocação</td></tr>\n                <tr><td>Connect · request (catálogo)</td><td>US$ 0,0001 / request</td></tr>\n                <tr><td>Media · armazenamento das provas</td><td>US$ 1,00 / GB</td></tr>\n              </tbody></table></div>\n              <a class=\"p-link\" href=\"https://data2.app/pricing\" target=\"_blank\" rel=\"noopener\">Ver tabela completa em data2.app/pricing →</a>\n            </div></section>\n            <section class=\"p-sec\"><div class=\"p-secnum\">04</div><div class=\"p-secbody\">\n              <h3>Resumo do investimento</h3>\n              <div class=\"p-price-card\" style=\"margin-bottom:22px\"><div><div class=\"ppc-label\">Setup (único)</div><div class=\"ppc-big\">R$ 50.000</div><div class=\"ppc-sub\">Desenvolvimento e implementação · 1 sprint</div></div><div class=\"ppc-right\"><div class=\"ppc-label\">Recorrente (mensal)</div><div class=\"ppc-val\">a partir de US$ 225/mês</div><div class=\"ppc-sub\">conforme o volume de imagens</div></div></div>\n              <div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Cenário</th><th>Provas/mês</th><th>Preço/imagem</th><th>Custo de imagem/mês</th></tr></thead><tbody>\n                <tr><td>Leve · Tier 1</td><td>5.000</td><td>$0,045</td><td><strong>$225/mês</strong></td></tr>\n                <tr><td>Moderado · Tier 2</td><td>15.000</td><td>$0,036</td><td><strong>$540/mês</strong></td></tr>\n                <tr><td>Pesado · Tier 3</td><td>50.000</td><td>$0,027</td><td><strong>$1.350/mês</strong></td></tr>\n              </tbody></table></div>\n              <p class=\"p-note\">* Custo de imagem conforme os tiers (seção 02). O uso da plataforma Data² (seção 03) é cobrado à parte, conforme o volume real. Setup em R$; valores de uso em US$.</p>\n              <div class=\"p-subhead\">Custo de operação estimado por tier · mensal</div>\n              <div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Tier</th><th>Provas/mês</th><th>Imagem</th><th>Plataforma Data²</th><th>Total/mês</th></tr></thead><tbody>\n                <tr><td>Tier 1</td><td>5.000</td><td>$225</td><td>~$549</td><td><strong>~$774</strong></td></tr>\n                <tr><td>Tier 2</td><td>15.000</td><td>$540</td><td>~$1.648</td><td><strong>~$2.188</strong></td></tr>\n                <tr><td>Tier 3</td><td>50.000</td><td>$1.350</td><td>~$5.493</td><td><strong>~$6.843</strong></td></tr>\n              </tbody></table></div>\n              <p class=\"p-note\">Plataforma estimada com base em ~1.000 tokens de IA por prova (análise da foto) — cerca de 90% do custo de plataforma e principal alavanca. Enviando a foto de análise reduzida (~500 tokens/prova), a plataforma cai aproximadamente pela metade. Valores em US$, conforme data2.app/pricing.</p>\n            </div></section>\n            <div class=\"p-contact\">\n              <div class=\"pc-brand\">${LOGO}</div>\n              <div class=\"pc-grid\">\n                <div><div class=\"pc-label\">E-mail</div><div class=\"pc-val\">gabi.carmo@data2.app</div></div>\n                <div><div class=\"pc-label\">Telefone</div><div class=\"pc-val\">+55 (11) 98962-6170</div></div>\n                <div><div class=\"pc-label\">Social</div><div class=\"pc-val\">@data2.app</div></div>\n                <div><div class=\"pc-label\">Local</div><div class=\"pc-val\">São Paulo, Brasil</div></div>\n              </div>\n              <div class=\"pc-tag\">Express yourself at scale</div>\n            </div>\n          </div>\n        </div>`;\n    }\n    return `\n      <div class=\"stage\">\n        <div class=\"hero\">\n          <div class=\"glow\"></div>\n          <div class=\"inner\">\n            ${LOGO}\n            <div class=\"tagline\">Express yourself at scale</div>\n            <div class=\"divider\"></div>\n            <div class=\"kicker\">Proposta Comercial</div>\n            <h1>Grupo <span class=\"grad\">Veste</span></h1>\n            <p class=\"lede\">Provador virtual com Inteligência Artificial para as marcas do grupo — experimente as peças com a sua própria foto.</p>\n            <div class=\"cta\">\n              <a class=\"btn btn-grad\" href=\"${POC_URL}\" target=\"_blank\" rel=\"noopener\">✨ Acessar o Provador Virtual</a>\n              <button class=\"btn btn-glass go-proposta\">Ver a proposta →</button>\n            </div>\n            <div class=\"brands-line\">Le Lis · John John · Bo.Bô · Individual · Dudalina</div>\n          </div>\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    let cid = ''; try { cid = window.localStorage.getItem('veste_cid') || ''; if (!cid) { cid = 'c' + Date.now().toString(36) + Math.floor(Math.random() * 1e6).toString(36); window.localStorage.setItem('veste_cid', cid); } } catch (e) {}\n    const track = (event) => { try { const dev = (navigator.userAgent || '').slice(0, 180); const p = d2.rush('veste-track').invoke({ event: event, clientId: cid, device: dev, ref: (document.referrer || '') }); if (p && p.catch) p.catch(() => {}); } catch (e) {} };\n    let isAdmin = false; try { isAdmin = ((window.location.search || '') + (window.location.hash || '')).indexOf('acessos') >= 0; } catch (e) {}\n    let authed = false; try { authed = window.localStorage.getItem(AUTH_KEY) === '1'; } catch (e) {}\n    d2.setState({ screen: isAdmin ? 'acessos' : (authed ? 'capa' : 'login') });\n    if (!isAdmin) track('open');\n    const loadAcessos = async (pw) => {\n      d2.setState({ acStatus: 'loading', acError: null });\n      try {\n        const r = await d2.rush('veste-acessos').invoke({ password: pw });\n        const s = (r && r.data) ? r.data : r;\n        if (!s || s.ok !== true) { try { window.localStorage.removeItem('veste_admin'); } catch (e) {} d2.setState({ acStatus: 'locked', acError: pw ? 'Senha incorreta.' : null }); return; }\n        try { window.localStorage.setItem('veste_admin', pw); } catch (e) {}\n        d2.setState({ acStatus: 'done', ac: s });\n      } catch (err) { d2.setState({ acStatus: 'error', acError: String((err && err.message) || err) }); }\n    };\n    d2.on('.login-btn', 'click', () => {\n      const p = ((d2.$('.login-pass') || {}).value || '');\n      if (p === AUTH_SENHA) { try { window.localStorage.setItem(AUTH_KEY, '1'); } catch (e) {} track('login'); d2.setState({ screen: 'capa', loginError: null }); }\n      else { d2.setState({ loginError: 'Senha incorreta.' }); }\n    });\n    d2.on('.login-pass', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.login-btn'); if (b) b.click(); } });\n    d2.on('.go-proposta', 'click', () => d2.setState({ screen: 'proposta' }));\n    d2.on('.go-capa', 'click', () => d2.setState({ screen: 'capa' }));\n    d2.on('.ac-btn', 'click', () => { const el = d2.$('.ac-pass'); const pw = (el && el.value) || ''; if (!pw) { d2.setState({ acError: 'Digite a senha.' }); return; } loadAcessos(pw); });\n    d2.on('.ac-pass', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.ac-btn'); if (b) b.click(); } });\n    d2.on('.ac-refresh', 'click', () => { let pw = ''; try { pw = window.localStorage.getItem('veste_admin') || ''; } catch (e) {} loadAcessos(pw); });\n    if (isAdmin) { let saved = ''; try { saved = window.localStorage.getItem('veste_admin') || ''; } catch (e) {} if (saved) loadAcessos(saved); else d2.setState({ acStatus: 'locked' }); }\n  }\n}\n\n"}}}}}}}},{"_id":"6a7a24d2a0a162269e7ed5fc","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"dresstoApp","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Dress To — Provador Virtual (home → lançamentos → provador)\nconst fmtPrice = (v) => (v == null || isNaN(v)) ? '' : 'R$ ' + Number(v).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;');\n\nconst classify = (prod) => {\n  const path = String((prod && prod.categoryPath) || '').toLowerCase();\n  if (/bolsa|acess/.test(path)) return 'bag';\n  if (/vestido|macac/.test(path)) return 'dress';\n  if (/cal[çc]a|saia|short|bermuda/.test(path)) return 'bottom';\n  return 'top';\n};\nconst isClothing = (p) => {\n  const path = String((p && p.categoryPath) || '').toLowerCase();\n  if (/acess|sapato|cal[çc]ado|t[êe]nis|bolsa|clutch|mochila|carteira|\\bhome\\b|casa|decora|aroma|vela|len[çc]|xale|poncho|cinto|meia|[óo]culos|joia|colar|brinco|pulseira|\\banel\\b|broche|rel[óo]gio|necess|bon[ée]|chap[ée]u|viseira|fragr|perfume|underwear|praia|beachwear/.test(path)) return false;\n  return true;\n};\nconst kindLabel = (k) => k === 'bag' ? 'Acessório' : (k === 'dress' ? 'Vestido' : (k === 'bottom' ? 'Peça de baixo' : 'Peça de cima'));\n\n// ---- Prompts: strict framing + hard replacement (no layering over existing clothes) ----\nconst FRAME = 'ENQUADRAMENTO (regra absoluta): a imagem final deve ter EXATAMENTE o mesmo enquadramento, proporção, ângulo e distância da câmera da Imagem 1. Mostre a pessoa na MESMA medida em que ela aparece na Imagem 1 — se a foto for de corpo inteiro, mantenha da cabeça aos pés. NUNCA corte a cabeça, os pés, as mãos ou as laterais do corpo, NUNCA aproxime o zoom e NUNCA reenquadre. Mantenha o mesmo fundo, o mesmo cenário e as mesmas margens da Imagem 1.';\nconst FIDELITY = 'FIDELIDADE: preserve 100% o rosto, o cabelo, o tom de pele, o corpo, a pose e a iluminação da pessoa da Imagem 1. A Imagem 2 pode mostrar a peça sozinha OU vestida em uma modelo — use SOMENTE a peça de roupa, ignore por completo qualquer modelo, rosto, corpo, pose ou cenário que apareça na Imagem 2. Copie da Imagem 2 exatamente a cor, a estampa, a textura, o comprimento, a modelagem, a gola, as mangas e os detalhes.';\nconst buildPrompt = (kind, name, styleInstr) => {\n  const head = 'Imagem 1 = foto de uma pessoa. Imagem 2 = uma peça da Dress To (\"' + (name || '') + '\"). ' + FRAME + ' ' + FIDELITY + ' ';\n  if (kind !== 'bag' && styleInstr && String(styleInstr).trim()) {\n    return head + 'DECISÃO DE VESTIMENTA (siga à risca, definida para este caso): ' + styleInstr + ' É PROIBIDO deixar qualquer peça que deveria ser removida aparecendo por baixo, nas mangas, no decote, nas alças ou nas bordas. Se a peça deixa ombros ou braços à mostra, mostre a pele nua da pessoa nessas áreas, sem nenhum tecido por baixo.';\n  }\n  if (kind === 'bag') {\n    return head + 'TAREFA: mantenha a roupa da pessoa exatamente como está e faça ela carregar esta bolsa de forma natural e condizente com o tipo (transversal atravessada no corpo, de ombro, de mão pela alça, ou clutch na mão). Mantenha formato, tamanho proporcional, cor, alça e ferragens idênticos aos da Imagem 2. Remova qualquer outra bolsa que já apareça na Imagem 1. Não altere mais nada na pessoa nem no fundo.';\n  }\n  if (kind === 'dress') {\n    return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE toda a roupa que a pessoa já veste (tanto a parte de cima quanto a de baixo) ANTES de colocar a peça nova. Vista a pessoa APENAS com este vestido/macacão da Imagem 2. É PROIBIDO deixar a peça nova por cima da roupa antiga: não pode sobrar nenhuma gola, manga, barra, alça, camiseta, calça ou tecido da roupa anterior aparecendo por baixo, nas bordas, no decote ou nas pernas. O resultado deve mostrar a pessoa usando somente a peça da Dress To, com caimento natural.';\n  }\n  if (kind === 'bottom') {\n    return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE a calça/saia/short que a pessoa já usa na parte de baixo ANTES de vestir a peça nova, e coloque esta peça de baixo da Imagem 2 no lugar. É PROIBIDO vestir a peça nova por cima da antiga — não pode sobrar barra, tecido ou peça anterior aparecendo. Mantenha INTACTAS a parte de cima do corpo (blusa/camisa), o rosto, o cabelo e o fundo. Caimento natural e cintura na posição correta.';\n  }\n  // top\n  return head + 'SUBSTITUIÇÃO (crítico): REMOVA COMPLETAMENTE a blusa/camisa/casaco que a pessoa já veste na parte de cima ANTES de colocar a peça nova, e vista esta peça de cima da Imagem 2 no lugar. É PROIBIDO colocar a peça nova por cima da roupa existente — não pode sobrar gola, manga, barra ou tecido da peça antiga aparecendo por baixo, nas bordas ou no decote. Mantenha INTACTAS a parte de baixo (calça/saia/short), o rosto, o cabelo e o fundo. Caimento natural.';\n};\n\nconst readAsDataURL = (file) => new Promise((resolve, reject) => {\n  const r = new FileReader();\n  r.onload = () => {\n    const img = new Image();\n    img.onload = () => {\n      try {\n        const MAX = 1152;\n        let w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;\n        if (w > h && w > MAX) { h = Math.round(h * MAX / w); w = MAX; }\n        else if (h >= w && h > MAX) { w = Math.round(w * MAX / h); h = MAX; }\n        const canvas = document.createElement('canvas');\n        canvas.width = w; canvas.height = h;\n        canvas.getContext('2d').drawImage(img, 0, 0, w, h);\n        resolve(canvas.toDataURL('image/jpeg', 0.9));\n      } catch (e) { resolve(r.result); }\n    };\n    img.onerror = () => resolve(r.result);\n    img.src = r.result;\n  };\n  r.onerror = reject;\n  r.readAsDataURL(file);\n});\n\nconst urlToDataUrl = (url) => fetch(url).then(r => r.blob()).then(b => new Promise((res, rej) => { const fr = new FileReader(); fr.onload = () => res(fr.result); fr.onerror = rej; fr.readAsDataURL(b); }));\n\nconst DRESSTO = { id: 'dressto', name: 'Dress To', base: 'https://www.dressto.com.br', accent: '#B5622E', cats: [\n  { label: 'Lançamentos', path: '' },\n  { label: 'Vestidos', path: 'dress-to/shop/vestidos' },\n  { label: 'Blusas', path: 'dress-to/shop/blusas' },\n  { label: 'Calças', path: 'dress-to/shop/calcas' },\n  { label: 'Saias', path: 'dress-to/shop/saias' },\n  { label: 'Shorts', path: 'dress-to/shop/shorts' },\n  { label: 'Casacos', path: 'dress-to/shop/casacos' },\n  { label: 'Macacão', path: 'dress-to/shop/macacao' }\n] };\n\nconst topbar = (extra, looksCount, lookCount, back, brandName) => `\n  <header class=\"bar\">\n    ${back ? '<button class=\"go-back\">← Voltar</button>' : ''}\n    <button class=\"nav-home wordmark-sm\"><svg class=\"home-ico\" viewBox=\"0 0 24 24\" width=\"15\" height=\"15\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 10.5 12 3l9 7.5\"/><path d=\"M5 9.5V20h14V9.5\"/></svg>${brandName || 'Dress To'}</button>\n    <span class=\"bar-tag\">Provador Virtual</span>\n    <div class=\"bar-right\">\n      ${lookCount ? `<button class=\"go-look\">Meu look <span class=\"looks-badge\">${lookCount}</span></button>` : ''}\n      <button class=\"go-looks\">Minhas provas${looksCount ? ' <span class=\"looks-badge\">' + looksCount + '</span>' : ''}</button>\n      ${extra || ''}\n    </div>\n  </header>`;\n\nconst cardHTML = (p, lookCodes) => {\n  const hasDisc = p.listPrice && p.price && p.listPrice > p.price;\n  const inLook = lookCodes && lookCodes.has(String(p.code));\n  return `<div class=\"card\" data-code=\"${esc(p.code)}\" data-key=\"${esc(p.code)}\">\n    <div class=\"card-img-wrap\">\n      <img class=\"card-img\" src=\"${esc(p.image)}\" alt=\"\" loading=\"lazy\" />\n      <button class=\"card-add${inLook ? ' in' : ''}\" data-code=\"${esc(p.code)}\" title=\"Adicionar ao look\">${inLook ? '✓ no look' : '+ look'}</button>\n      <div class=\"card-cta\">Experimentar</div>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"card-name\">${esc(p.name)}</div>\n      <div class=\"card-price\">${hasDisc ? '<span class=\"card-old\">' + fmtPrice(p.listPrice) + '</span>' : ''}${fmtPrice(p.price)}</div>\n    </div>\n  </div>`;\n};\n\nconst renderHome = (st) => {\n  const hero = st.heroImage || '';\n  return `\n    <div class=\"app home\">\n      <div class=\"hero ${hero ? '' : 'hero-plain'}\">\n        ${hero ? `<img class=\"hero-img\" src=\"${esc(hero)}\" alt=\"\" />` : ''}\n        <div class=\"hero-shade\"></div>\n        <div class=\"hero-top\"><span class=\"wordmark\">Dress To</span>${st.looksCount ? `<button class=\"go-looks home-looks\">Minhas provas <span class=\"looks-badge\">${st.looksCount}</span></button>` : ''}</div>\n        <div class=\"hero-content\">\n          <div class=\"kicker light\">Provador Virtual</div>\n          <h1 class=\"hero-title\">Vista as novas peças<br/>da Dress To com a sua foto.</h1>\n          <p class=\"hero-sub\">Escolha entre os lançamentos e veja na hora como cada peça fica em você — sem sair de casa.</p>\n          <button class=\"btn-primary go-lancamentos\">Ver os lançamentos</button>\n          <div class=\"hero-tag\">moda feminina colorida, leve e cheia de estilo</div>\n        </div>\n      </div>\n    </div>`;\n};\n\nconst renderGrid = (st) => {\n  const { products = [], gridStatus = 'loading', gridError = null, total = 0, category = '', search = '' } = st;\n  const lookCodes = new Set((st.lookItems || []).map(x => String(x.code)));\n  const isNew = category === '' && !search;\n  const brandCats = (st.brand && st.brand.cats) || [];\n  const cats = brandCats.map(c => `<button class=\"cat-link${category === c.path && !search ? ' active' : ''}\" data-path=\"${esc(c.path)}\">${c.label}</button>`).join('');\n\n  let grid;\n  if (gridStatus === 'loading') grid = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando peças...</span></div>`;\n  else if (gridStatus === 'error') grid = `<div class=\"state-full\"><span>Erro ao carregar: ${esc(gridError)}</span></div>`;\n  else if (!products.length) grid = `<div class=\"state-msg\">Nenhuma peça encontrada${search ? ' para \"' + esc(search) + '\"' : ''}.</div>`;\n  else grid = `<div class=\"grid\">${products.map(p => cardHTML(p, lookCodes)).join('')}</div>`;\n\n  const title = search ? `Resultados para “${esc(search)}”` : (isNew ? 'As chegadas mais recentes' : (brandCats.find(c => c.path === category) || {}).label || 'Seleção');\n\n  return `\n    <div class=\"app grid-screen\">\n      ${topbar(`\n        <div class=\"search-wrap\">\n          <input class=\"search-input\" placeholder=\"Buscar peças...\" value=\"${esc(search)}\" />\n          ${search ? '<button class=\"search-clear\" title=\"Limpar\">✕</button>' : ''}\n          <button class=\"search-btn\" title=\"Buscar\">🔍</button>\n        </div>`, st.looksCount, st.lookCount, true, (st.brand ? st.brand.name : 'Dress To'))}\n      <div class=\"editorial\">\n        <div class=\"kicker\">${st.brand ? esc(st.brand.name) + ' · ' : ''}${isNew ? 'Lançamentos' : 'Coleção'}</div>\n        <h2 class=\"ed-title\">${title}</h2>\n        <p class=\"ed-sub\">Escolha uma peça e experimente com a sua foto.${gridStatus === 'done' && total ? ' <span class=\"count\">· ' + total + ' peças</span>' : ''}</p>\n      </div>\n      <div class=\"cat-row\">${cats}</div>\n      <div class=\"grid-wrapper\">${grid}</div>\n    </div>`;\n};\n\nconst renderDetail = (st) => {\n  const sp = st.selectedProduct;\n  if (!sp) return renderGrid(st);\n  const { selectedSize = null, tryStatus = 'idle', tryImage = null, tryError = null, tryAttempt = 1, gateError = null, gateSubmitting = false } = st;\n  const kind = classify(sp);\n  const inLook = (st.lookItems || []).some(x => String(x.code) === String(sp.code));\n  const hasDisc = sp.listPrice && sp.price && sp.listPrice > sp.price;\n  const sizes = (sp.sizes || []).map(v =>\n    `<button class=\"size-btn${selectedSize === v.id ? ' active' : ''}${v.available === false ? ' off' : ''}\" data-size=\"${esc(v.id)}\" data-key=\"${esc(v.id)}\"${v.available === false ? ' disabled' : ''}>${esc(v.label)}</button>`\n  ).join('');\n\n  let tryOnHTML;\n  if (tryStatus === 'gate') {\n    tryOnHTML = `\n      <div class=\"gate-inline\">\n        <div class=\"gate-inline-kicker\">Quase lá</div>\n        <div class=\"gate-inline-title\">Diga quem é você para provar</div>\n        <input class=\"gate-name\" type=\"text\" placeholder=\"Seu nome\" />\n        <input class=\"gate-email\" type=\"email\" placeholder=\"seu@email.com\" />\n        <button class=\"cta-btn gate-submit\"${gateSubmitting ? ' disabled' : ''}>${gateSubmitting ? 'Um instante...' : 'Continuar para a foto'}</button>\n        ${gateError ? '<div class=\"err-inline\">' + esc(gateError) + '</div>' : ''}\n        <div class=\"gate-note\">Usamos seus dados apenas para esta experiência.</div>\n      </div>`;\n  } else if (tryStatus === 'checking' || tryStatus === 'uploading' || tryStatus === 'generating') {\n    const msg = tryStatus === 'checking' ? 'Analisando sua foto...' : (tryStatus === 'uploading' ? 'Preparando sua foto...' : 'Criando sua prova virtual...');\n    const sub = tryStatus === 'generating' ? ((tryAttempt > 1 ? 'Tentativa ' + tryAttempt + ' de 3 — ' : '') + 'pode levar até 30 segundos') : 'Só um instante';\n    tryOnHTML = `<div class=\"gen-box\"><div class=\"spinner\"></div><div class=\"gen-status\">${msg}</div><div class=\"gen-sub\">${sub}</div></div>`;\n  } else if (tryStatus === 'rejected') {\n    tryOnHTML = `<div class=\"warn-box\">${esc(tryError)}</div><button class=\"cta-btn tryon-btn\">Enviar outra foto</button>`;\n  } else if (tryStatus === 'done' && tryImage) {\n    tryOnHTML = `<div class=\"gen-box\"><img class=\"result-img\" src=\"${tryImage}\" alt=\"Prova virtual\" /><div class=\"result-badge\">✓ Salvo em Minhas provas</div><button class=\"cta-btn go-looks\">Ver minhas provas</button><button class=\"ghost-btn try-again\" style=\"margin-top:10px\">Experimentar com outra foto</button></div>`;\n  } else if (tryStatus === 'error') {\n    tryOnHTML = `<button class=\"cta-btn tryon-btn\">Tentar novamente</button><div class=\"err-box\">Não consegui gerar a prova: ${esc(tryError)}</div>`;\n  } else {\n    const hint = kind === 'bag'\n      ? 'Envie uma foto sua (da cintura para cima já funciona) e a IA mostra você com esta bolsa.'\n      : 'Envie uma foto sua de corpo inteiro, com boa luz, e a IA mostra você vestindo esta peça.';\n    tryOnHTML = `<button class=\"cta-btn tryon-btn\">Experimentar com a minha foto</button><p class=\"cta-hint\">${hint}</p>`;\n  }\n\n  return `\n    <div class=\"app detail-screen\">\n      ${topbar(`<button class=\"back-grid\">← Voltar</button>`, st.looksCount, st.lookCount, false, (st.brand ? st.brand.name : 'Dress To'))}\n      <div class=\"detail-wrap\">\n        <div class=\"detail-media\"><img class=\"detail-img\" src=\"${esc(sp.image)}\" alt=\"\" /></div>\n        <div class=\"detail-side\">\n          <div class=\"kicker\">${kindLabel(kind)}</div>\n          <h2 class=\"detail-name\">${esc(sp.name)}</h2>\n          <div class=\"detail-price\">${hasDisc ? '<span class=\"detail-old\">' + fmtPrice(sp.listPrice) + '</span>' : ''}${fmtPrice(sp.price)}</div>\n          <div class=\"size-label\">Tamanhos</div>\n          <div class=\"sizes-row\">${sizes || '<span class=\"gen-sub\">Tamanho único</span>'}</div>\n          <button class=\"add-to-look${inLook ? ' in' : ''}\" data-code=\"${esc(sp.code)}\">${inLook ? '✓ No meu look' : '+ Adicionar ao meu look'}</button>\n          <div class=\"tryon-section\">${tryOnHTML}</div>\n          <input type=\"file\" class=\"photo-input\" accept=\"image/*\" />\n          <a class=\"product-url\" href=\"${esc(sp.url)}\" target=\"_blank\">Ver no site da Dress To →</a>\n        </div>\n      </div>\n    </div>`;\n};\n\nconst renderLooks = (st) => {\n  const { looks = [], looksStatus = 'loading', lead = null } = st;\n  let body;\n  if (!lead || !lead.email) body = `<div class=\"state-msg\">Experimente uma peça primeiro — suas provas ficam salvas aqui.</div>`;\n  else if (looksStatus === 'loading') body = `<div class=\"state-full\"><div class=\"spinner-sm\"></div><span>Carregando suas provas...</span></div>`;\n  else if (!looks.length) body = `<div class=\"state-msg\">Você ainda não salvou nenhuma prova. Experimente uma peça e ela aparece aqui.</div>`;\n  else body = `<div class=\"grid looks-grid\">` + looks.map(l => `\n      <div class=\"look-card\">\n        <div class=\"look-img-wrap\"><img class=\"look-img\" src=\"${esc(l.imageUrl)}\" alt=\"\" loading=\"lazy\" /></div>\n        <div class=\"look-body\">\n          <div class=\"look-name\">${esc(l.productName)}</div>\n          <div class=\"look-price\">${fmtPrice(l.price)}</div>\n          <div class=\"look-actions\">\n            <a class=\"look-buy\" href=\"${esc(l.productUrl)}\" target=\"_blank\">Comprar</a>\n            <button class=\"look-remove\" data-id=\"${esc(l._id)}\">Remover</button>\n          </div>\n        </div>\n      </div>`).join('') + `</div>`;\n  return `\n    <div class=\"app grid-screen\">\n      ${topbar('', st.looksCount, st.lookCount, true, 'Dress To')}\n      <div class=\"editorial\">\n        <div class=\"kicker\">Minhas provas</div>\n        <h2 class=\"ed-title\">Suas provas virtuais</h2>\n        <p class=\"ed-sub\">Reveja tudo o que você experimentou e escolha o que vai comprar.</p>\n      </div>\n      <div class=\"grid-wrapper\">${body}</div>\n    </div>`;\n};\n\nconst renderLookBuilder = (st) => {\n  const { lookItems = [], lookStatus = 'idle', lookImage = null, lookError = null, lookStep = 1, lookTotal = 1, gateError = null, gateSubmitting = false } = st;\n  const total = lookItems.reduce((s, p) => s + (p.price || 0), 0);\n  const sel = Array.isArray(st.lookSel) ? st.lookSel : lookItems.map(x => x.code);\n  const selCount = lookItems.filter(p => sel.indexOf(p.code) >= 0).length;\n  let tryHTML;\n  if (!lookItems.length) tryHTML = '';\n  else if (lookStatus === 'gate') tryHTML = `<div class=\"gate-inline\"><div class=\"gate-inline-kicker\">Quase lá</div><div class=\"gate-inline-title\">Diga quem é você para provar</div><input class=\"gate-name\" type=\"text\" placeholder=\"Seu nome\" /><input class=\"gate-email\" type=\"email\" placeholder=\"seu@email.com\" /><button class=\"cta-btn gate-submit\"${gateSubmitting ? ' disabled' : ''}>${gateSubmitting ? 'Um instante...' : 'Continuar para a foto'}</button>${gateError ? '<div class=\"err-inline\">' + esc(gateError) + '</div>' : ''}</div>`;\n  else if (lookStatus === 'checking' || lookStatus === 'generating') tryHTML = `<div class=\"gen-box\"><div class=\"spinner\"></div><div class=\"gen-status\">${lookStatus === 'checking' ? 'Analisando sua foto...' : ('Vestindo peça ' + lookStep + ' de ' + lookTotal + '...')}</div><div class=\"gen-sub\">montar o look pode levar 1 a 2 minutos</div></div>`;\n  else if (lookStatus === 'rejected') tryHTML = `<div class=\"warn-box\">${esc(lookError)}</div><button class=\"cta-btn provar-look-btn\">Enviar outra foto</button>`;\n  else if (lookStatus === 'done' && lookImage) tryHTML = `<div class=\"gen-box\"><img class=\"result-img\" src=\"${lookImage}\" alt=\"Look\" /><div class=\"result-badge\">✓ Look salvo em Minhas provas</div><button class=\"cta-btn go-looks\">Ver minhas provas</button><button class=\"ghost-btn provar-look-btn\" style=\"margin-top:10px\">Provar com outra foto</button></div>`;\n  else if (lookStatus === 'error') tryHTML = `<button class=\"cta-btn provar-look-btn\">Tentar novamente</button><div class=\"err-box\">Não consegui montar o look: ${esc(lookError)}</div>`;\n  else tryHTML = `<button class=\"cta-btn provar-look-btn\"${selCount ? '' : ' disabled'}>${selCount ? 'Provar ' + selCount + (selCount === 1 ? ' peça' : ' peças') : 'Selecione as peças'}</button><p class=\"cta-hint\">Marque acima as peças que quer provar juntas — combine partes diferentes (ex.: uma de cima + uma de baixo).</p>${lookError ? '<div class=\"err-inline\">' + esc(lookError) + '</div>' : ''}`;\n  let body;\n  if (!lookItems.length) body = `<div class=\"state-msg\">Seu look está vazio. No catálogo, toque em <b>+ look</b> nas peças para montar um look e provar tudo junto.</div>`;\n  else body = `<div class=\"look-items\">${lookItems.map(p => `<div class=\"look-item${sel.indexOf(p.code) >= 0 ? ' sel' : ''}\"><button class=\"look-sel${sel.indexOf(p.code) >= 0 ? ' on' : ''}\" data-code=\"${esc(p.code)}\" title=\"Provar esta peça\">${sel.indexOf(p.code) >= 0 ? '✓' : ''}</button><img class=\"look-item-img\" src=\"${esc(p.image)}\" alt=\"\" /><div class=\"look-item-info\"><div class=\"look-item-name\">${esc(p.name)}</div><div class=\"look-item-price\">${fmtPrice(p.price)}</div></div><button class=\"look-item-remove\" data-code=\"${esc(p.code)}\">Remover</button></div>`).join('')}<div class=\"look-total\">Total do look <span>${fmtPrice(total)}</span></div></div><div class=\"look-tryon\">${tryHTML}<input type=\"file\" class=\"photo-input\" accept=\"image/*\" /></div>`;\n  return `<div class=\"app grid-screen\">${topbar('', st.looksCount, st.lookCount, true, 'Dress To')}<div class=\"editorial\"><div class=\"kicker\">Meu look</div><h2 class=\"ed-title\">Monte seu look</h2><p class=\"ed-sub\">Marque as peças que quer provar juntas e veja na sua foto.</p></div><div class=\"grid-wrapper\"><div class=\"look-wrap\">${body}</div></div></div>`;\n};\n\nexport default {\n  css: `\n    @import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Jost:wght@300;400;500&display=swap');\n    :host { display: block; width: 100%; height: 100%; }\n    * { box-sizing: border-box; margin: 0; padding: 0; }\n    .app { height: 100%; font-family: 'Jost','Helvetica Neue',Arial,sans-serif; background: #FFFFFF; color: #1A1A1A; display: flex; flex-direction: column; overflow: hidden; }\n    .wordmark { font-family: 'Fraunces', serif; font-weight: 600; letter-spacing: 4px; text-transform: uppercase; }\n    .kicker { font-size: 11px; letter-spacing: 3px; text-transform: uppercase; color: #B5622E; margin-bottom: 14px; }\n    .kicker.light { color: rgba(255,255,255,0.85); }\n    .btn-primary { height: 54px; padding: 0 40px; background: #1A1A1A; color: #fff; border: none; font-family: inherit; font-size: 12px; letter-spacing: 2px; text-transform: uppercase; cursor: pointer; transition: background .25s; }\n    .btn-primary:hover { background: #3a332e; }\n\n    /* ---------- HOME ---------- */\n    .home { }\n    .hero { flex: 1; position: relative; overflow: hidden; display: flex; flex-direction: column; }\n    .hero-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 16%; }\n    .hero-shade { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(30,18,10,0.60) 0%, rgba(30,18,10,0.18) 55%, rgba(30,18,10,0.34) 100%); }\n    .hero-plain { background: linear-gradient(140deg, #F3DFC8 0%, #FBF1E6 55%, #F0E2D2 100%); }\n    .hero-top { padding: 30px 48px; position: relative; z-index: 1; display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n    .hero-top .wordmark { font-size: 24px; color: #fff; letter-spacing: 6px; }\n    .hero-plain .hero-top .wordmark { color: #1A1A1A; }\n    .hero-content { margin-top: auto; padding: 0 48px 66px; max-width: 680px; position: relative; z-index: 1; }\n    .hero-plain .hero-content { color: #1A1A1A; }\n    .hero-title { font-family: 'Fraunces', serif; font-weight: 500; font-size: 54px; line-height: 1.05; color: #fff; margin-bottom: 20px; letter-spacing: 0.5px; }\n    .hero-plain .hero-title { color: #1A1A1A; }\n    .hero-sub { font-size: 15px; line-height: 1.6; color: rgba(255,255,255,0.88); max-width: 460px; margin-bottom: 30px; font-weight: 300; }\n    .hero-plain .hero-sub { color: #6B5E52; }\n    .hero-tag { font-family: 'Fraunces', serif; font-style: italic; font-size: 16px; color: rgba(255,255,255,0.8); margin-top: 22px; }\n    .hero-plain .hero-tag { color: #B5622E; }\n\n    /* ---------- BAR ---------- */\n    .bar { display: flex; align-items: center; gap: 20px; padding: 0 44px; height: 74px; border-bottom: 1px solid #ECE7E1; flex-shrink: 0; background: #fff; }\n    .wordmark-sm { font-family: 'Fraunces', serif; font-weight: 600; font-size: 19px; letter-spacing: 4px; text-transform: uppercase; background: none; border: none; cursor: pointer; color: #1A1A1A; display: inline-flex; align-items: center; gap: 9px; transition: opacity .2s; }\n    .wordmark-sm:hover { opacity: 0.6; }\n    .home-ico { opacity: 0.8; flex-shrink: 0; }\n    .bar-tag { font-size: 10px; letter-spacing: 2.5px; text-transform: uppercase; color: #C79B7B; border-left: 1px solid #E6DED4; padding-left: 20px; }\n    .bar-right { margin-left: auto; display: flex; align-items: center; gap: 14px; }\n    .back-grid { background: none; border: none; font-family: inherit; font-size: 12px; letter-spacing: 1.5px; text-transform: uppercase; color: #6B5E52; cursor: pointer; }\n    .back-grid:hover { color: #1A1A1A; }\n    .go-back { background: none; border: none; font-family: inherit; font-size: 12px; letter-spacing: 1.5px; text-transform: uppercase; color: #6B5E52; cursor: pointer; margin-right: 6px; }\n    .go-back:hover { color: #1A1A1A; }\n    .search-wrap { display: flex; align-items: center; background: #F6F3EF; border: 1px solid #E7E0D8; border-radius: 22px; height: 40px; padding: 0 6px 0 16px; width: 260px; }\n    .search-input { flex: 1; background: transparent; border: none; outline: none; font-size: 13px; font-family: inherit; color: #1A1A1A; }\n    .search-input::placeholder { color: #A79E93; }\n    .search-btn,.search-clear { width: 30px; height: 30px; border: none; background: transparent; color: #8C8073; cursor: pointer; border-radius: 50%; }\n    .search-btn:hover,.search-clear:hover { background: #EAE3DA; }\n\n    /* ---------- GRID / LANÇAMENTOS ---------- */\n    .grid-screen { background: #F6F3EF; }\n    .editorial { padding: 40px 48px 8px; background: #FFFFFF; }\n    .ed-title { font-family: 'Fraunces', serif; font-weight: 500; font-size: 40px; line-height: 1.05; margin-bottom: 10px; }\n    .ed-sub { font-size: 14px; color: #8C8073; font-weight: 300; }\n    .ed-sub .count { color: #D9A98C; }\n    .cat-row { display: flex; gap: 6px; padding: 20px 48px; background: #fff; border-bottom: 1px solid #ECE7E1; overflow-x: auto; flex-shrink: 0; }\n    .cat-link { padding: 7px 16px; border: none; background: transparent; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: #7A7167; cursor: pointer; white-space: nowrap; border-radius: 20px; transition: all .2s; }\n    .cat-link:hover { background: #F3EEE8; color: #1A1A1A; }\n    .cat-link.active { background: #1A1A1A; color: #fff; }\n    .grid-wrapper { flex: 1; overflow-y: auto; padding: 26px 48px 40px; }\n    .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); grid-auto-rows: 460px; gap: 22px; }\n    .card { background: #fff; cursor: pointer; display: flex; flex-direction: column; transition: transform .25s; }\n    .card:hover { transform: translateY(-4px); }\n    .card-img-wrap { position: relative; overflow: hidden; height: 380px; background: #F1E7DA; }\n    .card-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; transition: transform .5s ease; }\n    .card:hover .card-img { transform: scale(1.04); }\n    .card-cta { position: absolute; left: 0; right: 0; bottom: 0; background: rgba(26,26,26,0.9); color: #fff; text-align: center; padding: 12px; font-size: 11px; letter-spacing: 2px; text-transform: uppercase; transform: translateY(100%); transition: transform .3s ease; }\n    .card:hover .card-cta { transform: translateY(0); }\n    .card-body { padding: 14px 4px 4px; }\n    .card-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #4A443D; line-height: 1.5; margin-bottom: 6px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .card-price { font-family: 'Fraunces', serif; font-size: 19px; font-weight: 600; color: #1A1A1A; }\n    .card-old { font-family: 'Jost'; font-size: 12px; color: #D9A98C; text-decoration: line-through; margin-right: 7px; }\n    .state-full,.state-msg { grid-column: 1/-1; display: flex; align-items: center; justify-content: center; gap: 12px; padding: 90px 0; color: #A79E93; font-size: 15px; }\n    .spinner-sm { width: 22px; height: 22px; border: 3px solid #E7E0D8; border-top-color: #B5622E; border-radius: 50%; animation: spin .8s linear infinite; }\n\n    /* ---------- DETAIL / PROVADOR ---------- */\n    .detail-screen { background: #fff; }\n    .detail-wrap { flex: 1; display: flex; overflow: hidden; }\n    .detail-media { width: 50%; background: #F1E7DA; overflow: hidden; }\n    .detail-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }\n    .detail-side { width: 50%; overflow-y: auto; padding: 48px 56px; }\n    .detail-name { font-family: 'Fraunces', serif; font-weight: 500; font-size: 30px; line-height: 1.15; margin-bottom: 14px; }\n    .detail-price { font-family: 'Fraunces', serif; font-size: 26px; font-weight: 600; margin-bottom: 30px; }\n    .detail-old { font-family: 'Jost'; font-size: 16px; color: #D9A98C; text-decoration: line-through; margin-right: 10px; }\n    .size-label { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #A79E93; margin-bottom: 12px; }\n    .sizes-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 34px; }\n    .size-btn { min-width: 46px; height: 44px; padding: 0 12px; border: 1px solid #DDD5CA; background: #fff; font-size: 13px; font-family: inherit; cursor: pointer; transition: all .15s; color: #1A1A1A; }\n    .size-btn:hover { border-color: #1A1A1A; }\n    .size-btn.active { background: #1A1A1A; color: #fff; border-color: #1A1A1A; }\n    .size-btn.off { opacity: .35; cursor: not-allowed; text-decoration: line-through; }\n    .tryon-section { border-top: 1px solid #ECE7E1; padding-top: 30px; }\n    .cta-btn { width: 100%; height: 54px; background: #1A1A1A; color: #fff; border: none; font-family: inherit; font-size: 12px; letter-spacing: 2px; text-transform: uppercase; cursor: pointer; transition: background .2s; }\n    .cta-btn:hover:not(:disabled) { background: #3a332e; }\n    .cta-btn:disabled { opacity: .6; }\n    .cta-hint { margin-top: 14px; font-size: 12.5px; color: #A79E93; line-height: 1.6; font-weight: 300; }\n    .photo-input { display: none; }\n    .gen-box { text-align: center; }\n    .spinner { width: 40px; height: 40px; border: 3px solid #E7E0D8; border-top-color: #B5622E; border-radius: 50%; margin: 10px auto 14px; animation: spin .8s linear infinite; }\n    @keyframes spin { to { transform: rotate(360deg); } }\n    .gen-status { font-size: 14px; color: #4A443D; }\n    .gen-sub { font-size: 11px; color: #D9A98C; margin-top: 5px; }\n    .result-img { width: 100%; display: block; margin-bottom: 12px; box-shadow: 0 10px 30px rgba(120,60,20,.16); }\n    .result-badge { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #A79E93; text-align: center; margin-bottom: 18px; }\n    .ghost-btn { width: 100%; height: 46px; background: #fff; border: 1px solid #DDD5CA; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; }\n    .ghost-btn:hover { border-color: #1A1A1A; }\n    .err-box { margin-top: 14px; padding: 14px; background: #FBECEC; color: #A5453C; font-size: 12px; border-radius: 4px; line-height: 1.5; }\n    .warn-box { margin-bottom: 16px; padding: 14px; background: #FBF2E4; color: #8A6420; font-size: 13px; border-radius: 4px; line-height: 1.5; }\n    .product-url { display: block; margin-top: 22px; padding-top: 20px; border-top: 1px solid #ECE7E1; text-align: center; font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #A79E93; text-decoration: none; }\n    .product-url:hover { color: #1A1A1A; }\n    .gate-inline { }\n    .gate-inline-kicker { font-size: 10px; letter-spacing: 2px; text-transform: uppercase; color: #B5622E; margin-bottom: 8px; }\n    .gate-inline-title { font-family: 'Fraunces', serif; font-size: 22px; font-weight: 500; margin-bottom: 18px; }\n    .gate-inline input { width: 100%; height: 48px; border: 1px solid #DDD5CA; padding: 0 16px; font-size: 14px; font-family: inherit; outline: none; margin-bottom: 12px; }\n    .gate-inline input:focus { border-color: #1A1A1A; }\n    .gate-note { margin-top: 12px; font-size: 11px; color: #C3BAAD; }\n    .err-inline { margin-top: 10px; font-size: 12px; color: #A5453C; }\n\n    .go-looks { background: none; border: 1px solid #DDD5CA; height: 36px; padding: 0 16px; font-family: inherit; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #1A1A1A; cursor: pointer; border-radius: 20px; display: inline-flex; align-items: center; gap: 7px; }\n    .go-looks:hover { border-color: #1A1A1A; background: #F6F3EF; }\n    .looks-badge { background: #1A1A1A; color: #fff; border-radius: 10px; padding: 1px 7px; font-size: 10px; }\n    .home-looks { color: #fff; border-color: rgba(255,255,255,0.5); background: rgba(255,255,255,0.12); flex-shrink: 0; }\n    .looks-grid { grid-auto-rows: 540px; }\n    .look-card { background: #fff; display: flex; flex-direction: column; }\n    .look-img-wrap { height: 440px; background: #F1E7DA; overflow: hidden; }\n    .look-img { width: 100%; height: 100%; object-fit: cover; object-position: center top; display: block; }\n    .look-body { padding: 14px 4px; }\n    .look-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #4A443D; margin-bottom: 6px; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n    .look-price { font-family: 'Fraunces', serif; font-size: 19px; font-weight: 600; margin-bottom: 12px; }\n    .look-actions { display: flex; gap: 8px; }\n    .look-buy { flex: 1; text-align: center; background: #1A1A1A; color: #fff; text-decoration: none; height: 40px; line-height: 40px; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; }\n    .look-buy:hover { background: #3a332e; }\n    .look-remove { background: none; border: 1px solid #DDD5CA; color: #8C8073; height: 40px; padding: 0 14px; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; }\n    .look-remove:hover { border-color: #A5453C; color: #A5453C; }\n    .card-add { position: absolute; top: 10px; right: 10px; z-index: 2; background: rgba(255,255,255,0.92); border: 1px solid #E7DFD6; color: #1A1A1A; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; padding: 6px 10px; border-radius: 16px; cursor: pointer; transition: all .2s; }\n    .card-add:hover { background: #fff; }\n    .card-add.in { background: #1A1A1A; color: #fff; border-color: #1A1A1A; }\n    .add-to-look { width: 100%; height: 46px; background: #fff; border: 1px solid #DDD5CA; font-family: inherit; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; margin-bottom: 20px; color: #1A1A1A; }\n    .add-to-look:hover { border-color: #1A1A1A; }\n    .add-to-look.in { background: #F3EEE8; border-color: #1A1A1A; }\n    .go-look { background: #1A1A1A; border: 1px solid #1A1A1A; height: 36px; padding: 0 16px; font-family: inherit; font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; color: #fff; cursor: pointer; border-radius: 20px; display: inline-flex; align-items: center; gap: 7px; }\n    .go-look .looks-badge { background: #fff; color: #1A1A1A; }\n    .look-wrap { max-width: 760px; }\n    .look-item { display: flex; align-items: center; gap: 16px; padding: 14px 0; border-bottom: 1px solid #ECE7E1; }\n    .look-item-img { width: 64px; height: 84px; object-fit: cover; object-position: center top; background: #F1E7DA; flex-shrink: 0; }\n    .look-item-info { flex: 1; }\n    .look-item-name { font-size: 11px; letter-spacing: 0.6px; text-transform: uppercase; color: #1A1A1A; margin-bottom: 4px; }\n    .look-item-price { font-family: 'Fraunces', serif; font-size: 17px; font-weight: 600; }\n    .look-item-remove { background: none; border: none; color: #A79E93; font-family: inherit; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; }\n    .look-item-remove:hover { color: #A5453C; }\n    .look-total { display: flex; justify-content: space-between; align-items: baseline; padding: 18px 0; font-size: 11px; letter-spacing: 1.5px; text-transform: uppercase; color: #8C8073; }\n    .look-total span { font-family: 'Fraunces', serif; font-size: 22px; font-weight: 600; color: #1A1A1A; }\n    .look-tryon { margin-top: 14px; border-top: 1px solid #ECE7E1; padding-top: 26px; max-width: 460px; }\n    .look-sel { width: 26px; height: 26px; min-width: 26px; border: 1.5px solid #C3BAAD; border-radius: 6px; background: #fff; color: #fff; cursor: pointer; font-size: 13px; line-height: 1; display: flex; align-items: center; justify-content: center; margin-right: 6px; flex-shrink: 0; transition: all .15s; }\n    .look-sel.on { background: #1A1A1A; border-color: #1A1A1A; }\n    @media (max-width: 860px) {\n      .hero-title { font-size: 38px; }\n      .hero-content, .hero-top { padding-left: 26px; padding-right: 26px; }\n      .bar, .editorial, .cat-row, .grid-wrapper { padding-left: 22px; padding-right: 22px; }\n      .bar-tag { display: none; }\n      .detail-wrap { flex-direction: column; overflow-y: auto; }\n      .detail-media { width: 100%; height: 60vh; }\n      .detail-side { width: 100%; padding: 30px 24px; }\n      .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); grid-auto-rows: 330px; gap: 14px; }\n      .card-img-wrap { height: 250px; }\n    }\n  `,\n\n  render(d2) {\n    const st = d2.state || {};\n    const screen = st.screen || 'home';\n    if (screen === 'looks') return renderLooks(st);\n    if (screen === 'lookbuilder') return renderLookBuilder(st);\n    if (screen === 'detail') return renderDetail(st);\n    if (screen === 'home') return renderHome(st);\n    return renderGrid(st);\n  },\n\n  mounted(d2) {\n    const track = (event, prod) => {\n      try {\n        const kind = prod ? classify(prod) : '';\n        const p = d2.rush('dressto-track').invoke({ event, productCode: (prod && prod.code) || '', productName: (prod && prod.name) || '', kind });\n        if (p && p.catch) p.catch(() => {});\n      } catch (e) {}\n    };\n\n    let savedLead = null;\n    try { savedLead = JSON.parse(window.localStorage.getItem('dressto_lead') || 'null'); } catch (e) {}\n    let savedLook = [];\n    try { savedLook = JSON.parse(window.localStorage.getItem('dressto_look') || '[]') || []; } catch (e) {}\n    d2.setState({ screen: 'home', brand: DRESSTO, leadCaptured: !!(savedLead && savedLead.email), lead: savedLead || null, lookItems: savedLook, lookCount: savedLook.length, navStack: [], gridStatus: 'loading' });\n\n    const fetchProducts = async (path, search) => {\n      const brand = d2.state.brand || DRESSTO;\n      d2.setState({ gridStatus: 'loading', category: path || '', search: search || '' });\n      try {\n        const enc = path ? path.split('/').map(encodeURIComponent).join('/') : '';\n        const body = search ? { base: brand.base, search: search } : (enc ? { base: brand.base, category: enc } : { base: brand.base, order: 'new' });\n        const r = await d2.rush('dressto-products').invoke(body);\n        const payload = (r && r.data) ? r.data : r;\n        const products = ((payload && payload.products) || []).filter(isClothing);\n        const total = products.length;\n        d2.setState({ products, total, gridStatus: 'done' });\n      } catch (err) {\n        d2.setState({ gridStatus: 'error', gridError: String((err && err.message) || err) });\n      }\n    };\n\n    const loadLooks = async () => {\n      const lead = d2.state.lead;\n      if (!lead || !lead.email) return;\n      d2.setState({ looksStatus: 'loading' });\n      try {\n        const r = await d2.rush('dressto-my-tryons').invoke({ email: lead.email });\n        const payload = (r && r.data) ? r.data : r;\n        const looks = (payload && payload.tryons) || [];\n        d2.setState({ looks, looksStatus: 'done', looksCount: looks.length });\n      } catch (e) { d2.setState({ looksStatus: 'done' }); }\n    };\n    if (savedLead && savedLead.email) loadLooks();\n\n    const navTo = (screen, patch) => { const cur = d2.state.screen || 'home'; const stack = (d2.state.navStack || []).slice(); if (cur !== screen) stack.push(cur); d2.setState(Object.assign({ screen: screen, navStack: stack }, patch || {})); };\n    const goBack = () => { const stack = (d2.state.navStack || []).slice(); const prev = stack.pop() || 'home'; d2.setState({ screen: prev, navStack: stack }); };\n    const setLook = (items) => { try { window.localStorage.setItem('dressto_look', JSON.stringify(items)); } catch (e) {} d2.setState({ lookItems: items, lookCount: items.length }); };\n    const toggleLook = (code) => {\n      const items = (d2.state.lookItems || []).slice();\n      const idx = items.findIndex(x => String(x.code) === String(code));\n      if (idx >= 0) { items.splice(idx, 1); setLook(items); return; }\n      const selp = d2.state.selectedProduct;\n      const prod = (d2.state.products || []).find(p => String(p.code) === String(code)) || (selp && String(selp.code) === String(code) ? selp : null);\n      if (prod) { items.push(prod); setLook(items); }\n    };\n    const handleLookPhoto = async (file) => {\n      const all = (d2.state.lookItems || []).slice();\n      const sel = Array.isArray(d2.state.lookSel) ? d2.state.lookSel : all.map(x => x.code);\n      const items = all.filter(x => sel.indexOf(x.code) >= 0);\n      if (!items.length) return;\n      const lead = d2.state.lead || {};\n      d2.setState({ lookStatus: 'checking', lookImage: null, lookError: null });\n      try {\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('dressto-photo-check').invoke({ photoUrl: userPhotoUrl, kind: 'dress' });\n        const cp = (check && check.data) ? check.data : check;\n        let verdict = (cp && cp.verdict) || {};\n        if (typeof verdict === 'string') { try { verdict = JSON.parse(verdict.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e) { verdict = {}; } }\n        if (verdict.ok === false) { d2.setState({ lookStatus: 'rejected', lookError: verdict.motivo || 'A foto não ficou boa. Tente outra de corpo inteiro com boa luz.' }); return; }\n        let current = userPhotoUrl;\n        for (let i = 0; i < items.length; i++) {\n          d2.setState({ lookStatus: 'generating', lookStep: i + 1, lookTotal: items.length });\n          const p = items[i]; const k = classify(p);\n          let instr = '';\n          if (k !== 'bag') {\n            try {\n              const itemData = await urlToDataUrl(p.image).catch(() => p.image);\n              const kpt = k === 'dress' ? 'vestido ou macacão' : (k === 'bottom' ? 'peça de baixo (calça/saia/short)' : 'peça de cima (blusa/camisa/casaco)');\n              const sres = await d2.rush('dressto-stylist').invoke({ photoUrl: userPhotoUrl, itemUrl: itemData, instr: 'Peça nova que a pessoa quer vestir: \"' + (p.name || '') + '\" (categoria: ' + kpt + '). A pessoa vai usar esta peça combinada com outras. Analise a Imagem 1 e a peça na Imagem 2 e retorne o JSON com o que remover e a instrução.' });\n              const sp2 = (sres && sres.data) ? sres.data : sres;\n              let sv = (sp2 && sp2.verdict) || sp2;\n              if (typeof sv === 'string') { try { sv = JSON.parse(sv.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e2) { sv = {}; } }\n              instr = (sv && sv.instrucao) || '';\n            } catch (e3) {}\n          }\n          const prompt = buildPrompt(k, p.name, instr);\n          let img = null, lastErr = null;\n          for (let a = 1; a <= 2 && !img; a++) {\n            try {\n              const res = await d2.rush('dressto-tryon').invoke({ userPhotoUrl: current, itemUrl: p.image, prompt });\n              const pay = (res && res.data) ? res.data : res;\n              img = (pay && pay.image) || null;\n              if (!img) throw new Error('sem imagem');\n            } catch (e4) { lastErr = e4; }\n          }\n          if (!img) { d2.setState({ lookStatus: 'error', lookError: 'Falhou ao vestir \"' + (p.name || '') + '\". ' + String((lastErr && lastErr.message) || '') }); return; }\n          current = img;\n        }\n        d2.setState({ lookStatus: 'done', lookImage: current });\n        track('look_success');\n        const totalPrice = items.reduce((s, x) => s + (x.price || 0), 0);\n        d2.rush('dressto-save-tryon').invoke({ email: lead.email || '', name: lead.name || '', productCode: 'look', productName: 'Look • ' + items.map(x => x.name).join(' + '), price: totalPrice, productUrl: (items[0] && items[0].url) || '', kind: 'look', size: '', imageUrl: current }).then(() => loadLooks()).catch(() => {});\n      } catch (err) { d2.setState({ lookStatus: 'error', lookError: String((err && err.message) || err) }); }\n    };\n\n    track('app_open');\n\n    d2.on('.go-lancamentos', 'click', () => {\n      navTo('grid');\n      if (!(d2.state.products || []).length) fetchProducts('', '');\n    });\n    d2.on('.nav-home', 'click', () => d2.setState({ screen: 'home', navStack: [] }));\n    d2.on('.go-back', 'click', goBack);\n    d2.on('.go-looks', 'click', () => { navTo('looks'); loadLooks(); });\n    d2.on('.card-add', 'click', (e) => { e.stopPropagation(); const el = e.target.closest('.card-add'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.add-to-look', 'click', (e) => { const el = e.target.closest('.add-to-look'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.go-look', 'click', () => navTo('lookbuilder', { lookStatus: 'idle', lookImage: null, lookError: null, lookSel: (d2.state.lookItems || []).map(x => x.code) }));\n    d2.on('.look-item-remove', 'click', (e) => { const el = e.target.closest('.look-item-remove'); if (el) toggleLook(el.dataset.code); });\n    d2.on('.look-sel', 'click', (e) => { const el = e.target.closest('.look-sel'); if (!el) return; const code = el.dataset.code; const items = d2.state.lookItems || []; const cur = Array.isArray(d2.state.lookSel) ? d2.state.lookSel.slice() : items.map(x => x.code); const i = cur.indexOf(code); if (i >= 0) cur.splice(i, 1); else cur.push(code); d2.setState({ lookSel: cur, lookError: null }); });\n    d2.on('.provar-look-btn', 'click', () => { const items = d2.state.lookItems || []; const sel = Array.isArray(d2.state.lookSel) ? d2.state.lookSel : items.map(x => x.code); const chosen = items.filter(x => sel.indexOf(x.code) >= 0); if (!chosen.length) { d2.setState({ lookError: 'Selecione ao menos uma peça para provar.' }); return; } d2.setState({ photoTarget: 'look', lookError: null }); if (d2.state.leadCaptured) openPicker(); else d2.setState({ lookStatus: 'gate', gateError: null }); });\n    d2.on('.look-remove', 'click', async (e) => {\n      const el = e.target.closest('.look-remove'); if (!el) return;\n      const id = el.dataset.id;\n      const looks = (d2.state.looks || []).filter(l => l._id !== id);\n      d2.setState({ looks, looksCount: looks.length });\n      try { await d2.rush('dressto-delete-tryon').invoke({ id }); } catch (e2) {}\n    });\n\n    d2.on('.cat-link', 'click', (e) => {\n      const el = e.target.closest('.cat-link');\n      if (el) { navTo('grid'); fetchProducts(el.dataset.path, ''); }\n    });\n    d2.on('.search-btn', 'click', () => { const i = d2.$('.search-input'); fetchProducts('', (i && i.value.trim()) || ''); });\n    d2.on('.search-input', 'keydown', (e) => { if (e.key === 'Enter') fetchProducts('', e.target.value.trim()); });\n    d2.on('.search-clear', 'click', () => fetchProducts('', ''));\n\n    d2.on('.card', 'click', (e) => {\n      if (e.target.closest('.card-add')) return;\n      const el = e.target.closest('.card');\n      const code = el && el.dataset.code;\n      const prod = (d2.state.products || []).find(p => String(p.code) === String(code));\n      if (prod) { navTo('detail', { selectedProduct: prod, selectedSize: null, tryStatus: 'idle', tryImage: null, tryError: null }); track('product_view', prod); }\n    });\n    d2.on('.back-grid', 'click', goBack);\n\n    d2.on('.size-btn', 'click', (e) => {\n      const el = e.target.closest('.size-btn');\n      if (el && !el.classList.contains('off')) d2.setState({ selectedSize: el.dataset.size });\n    });\n\n    const openPicker = () => { const input = d2.$('.photo-input'); if (input) { input.value = ''; input.click(); } };\n\n    d2.on('.tryon-btn', 'click', () => {\n      d2.setState({ photoTarget: 'single' });\n      track('tryon_click', d2.state.selectedProduct);\n      if (d2.state.leadCaptured) openPicker();\n      else d2.setState({ tryStatus: 'gate', gateError: null });\n    });\n\n    d2.on('.gate-submit', 'click', async () => {\n      const name = ((d2.$('.gate-name') || {}).value || '').trim();\n      const email = ((d2.$('.gate-email') || {}).value || '').trim();\n      if (!name) { d2.setState({ gateError: 'Por favor, preencha seu nome.' }); return; }\n      if (!/^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$/.test(email)) { d2.setState({ gateError: 'Digite um e-mail válido.' }); return; }\n      d2.setState({ gateSubmitting: true, gateError: null });\n      try { await d2.rush('dressto-lead').invoke({ name, email }); } catch (e) {}\n      try { window.localStorage.setItem('dressto_lead', JSON.stringify({ name, email })); } catch (e) {}\n      d2.setState({ leadCaptured: true, lead: { name, email }, gateSubmitting: false, tryStatus: 'idle', lookStatus: 'idle' });\n      setTimeout(openPicker, 60);\n    });\n    d2.on('.gate-email', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.gate-submit'); if (b) b.click(); } });\n\n    d2.on('.try-again', 'click', () => { d2.setState({ tryStatus: 'idle', tryImage: null, tryError: null }); openPicker(); });\n\n    d2.on('.photo-input', 'change', async (e) => {\n      const file = e.target.files && e.target.files[0];\n      if (!file) return;\n      if (d2.state.photoTarget === 'look') { handleLookPhoto(file); return; }\n      const prod = d2.state.selectedProduct;\n      if (!prod) return;\n      const kind = classify(prod);\n      d2.setState({ tryStatus: 'checking', tryImage: null, tryError: null });\n      try {\n        const userPhotoUrl = await readAsDataURL(file);\n        const check = await d2.rush('dressto-photo-check').invoke({ photoUrl: userPhotoUrl, kind });\n        const cp = (check && check.data) ? check.data : check;\n        let verdict = (cp && cp.verdict) || {};\n        if (typeof verdict === 'string') { try { verdict = JSON.parse(verdict.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e2) { verdict = {}; } }\n        if (verdict.ok === false) {\n          d2.setState({ tryStatus: 'rejected', tryError: verdict.motivo || 'A foto não ficou boa para a prova. Tente outra com o corpo bem visível e boa iluminação.' });\n          return;\n        }\n        const itemUrl = prod.image;\n        let styleInstr = '';\n        if (kind !== 'bag') {\n          try {\n            const kpt = kind === 'dress' ? 'vestido ou macacão' : (kind === 'bottom' ? 'peça de baixo (calça/saia/short)' : 'peça de cima (blusa/camisa/casaco)');\n            const itemData = await urlToDataUrl(itemUrl).catch(() => itemUrl);\n            const sres = await d2.rush('dressto-stylist').invoke({ photoUrl: userPhotoUrl, itemUrl: itemData, instr: 'Peça nova que a pessoa quer vestir: \"' + (prod.name || '') + '\" (categoria: ' + kpt + '). Analise a roupa atual da pessoa na Imagem 1 e a peça na Imagem 2 e retorne o JSON com o que remover e a instrução.' });\n            const sp = (sres && sres.data) ? sres.data : sres;\n            let sv = (sp && sp.verdict) || sp;\n            if (typeof sv === 'string') { try { sv = JSON.parse(sv.replace(/```json/gi, '').replace(/```/g, '').trim()); } catch (e4) { sv = {}; } }\n            styleInstr = (sv && sv.instrucao) || '';\n          } catch (e5) {}\n        }\n        const prompt = buildPrompt(kind, prod.name, styleInstr);\n        let img = null, lastErr = null;\n        for (let attempt = 1; attempt <= 3 && !img; attempt++) {\n          d2.setState({ tryStatus: 'generating', tryAttempt: attempt });\n          try {\n            const res = await d2.rush('dressto-tryon').invoke({ userPhotoUrl, itemUrl, prompt });\n            const payload = (res && res.data) ? res.data : res;\n            img = (payload && payload.image) || null;\n            if (!img) throw new Error('Resposta sem imagem');\n          } catch (e3) { lastErr = e3; }\n        }\n        if (img) {\n          d2.setState({ tryStatus: 'done', tryImage: img });\n          track('tryon_success', prod);\n          const lead = d2.state.lead || {};\n          const sizeLabel = ((prod.sizes || []).find(s => String(s.id) === String(d2.state.selectedSize)) || {}).label || '';\n          d2.rush('dressto-save-tryon').invoke({ email: lead.email || '', name: lead.name || '', productCode: prod.code, productName: prod.name, price: prod.price, productUrl: prod.url, kind, size: sizeLabel, imageUrl: img }).then(() => loadLooks()).catch(() => {});\n        }\n        else d2.setState({ tryStatus: 'error', tryError: 'Não consegui gerar após 3 tentativas. ' + String((lastErr && lastErr.message) || lastErr || '') });\n      } catch (err) {\n        d2.setState({ tryStatus: 'error', tryError: String((err && err.message) || err) });\n      }\n    });\n  }\n}\n"}}}}}}}},{"_id":"6a7a257ddcefe1ca2a30930a","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"dresstoProposta","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Data² — Capa Proposta Dress To (identidade oficial: logo anel + wordmark)\nconst POC_URL = 'https://dressto.people-gabi-carmo.next.data2.app/';\nconst AUTH_SENHA = 'dressto2026';\nconst AUTH_KEY = 'dressto_proposta_auth';\n\nconst LOGO_MARK = `<svg class=\"logo-mark\" viewBox=\"0 0 104 104\" aria-hidden=\"true\">\n  <circle cx=\"52\" cy=\"19\" r=\"11\" fill=\"#3D6DF0\"/>\n  <circle cx=\"75.33\" cy=\"28.67\" r=\"11\" fill=\"#E9632B\"/>\n  <circle cx=\"85\" cy=\"52\" r=\"11\" fill=\"#EFD94F\"/>\n  <circle cx=\"75.33\" cy=\"75.33\" r=\"11\" fill=\"#5FE0A6\"/>\n  <circle cx=\"52\" cy=\"85\" r=\"11\" fill=\"#58C8EE\"/>\n  <circle cx=\"28.67\" cy=\"75.33\" r=\"11\" fill=\"#EF478F\"/>\n  <circle cx=\"19\" cy=\"52\" r=\"11\" fill=\"#9B3FD7\"/>\n  <circle cx=\"28.67\" cy=\"28.67\" r=\"11\" fill=\"#8FD24C\"/>\n</svg>`;\nconst LOGO = `<span class=\"logo\">${LOGO_MARK}<span class=\"logo-word\">Data<sup>2</sup></span></span>`;\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\nconst fmtDT = (iso) => { try { return new Date(iso).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\nconst shortDev = (ua) => { ua = String(ua || ''); const os = /iphone|ipad/i.test(ua) ? 'iOS' : /android/i.test(ua) ? 'Android' : /windows/i.test(ua) ? 'Windows' : /macintosh|mac os x/i.test(ua) ? 'Mac' : /linux/i.test(ua) ? 'Linux' : '—'; const br = /edg/i.test(ua) ? 'Edge' : /opr|opera/i.test(ua) ? 'Opera' : /chrome/i.test(ua) ? 'Chrome' : /firefox/i.test(ua) ? 'Firefox' : /safari/i.test(ua) ? 'Safari' : ''; return os + (br ? ' · ' + br : ''); };\nconst renderAcessos = (st) => {\n  const status = st.acStatus || 'locked';\n  let inner;\n  if (status === 'loading') { inner = `<div class=\"ac-msg\">Carregando...</div>`; }\n  else if (status !== 'done') { inner = `<div class=\"login-card\" style=\"max-width:360px\"><div class=\"login-title\">Painel de acessos</div><input class=\"login-input ac-pass\" type=\"password\" placeholder=\"Senha do painel\" /><button class=\"btn btn-grad ac-btn\" style=\"width:100%;justify-content:center\">Entrar</button>${st.acError ? '<div class=\"login-err\">' + esc(st.acError) + '</div>' : ''}</div>`; }\n  else {\n    const ac = st.ac || {}; const recent = ac.recent || [];\n    const cset = {}; recent.forEach(r => { if (r.clientId) cset[r.clientId] = 1; });\n    const distinct = Object.keys(cset).length;\n    const rows = recent.slice(0, 60).map(r => `<tr><td>${fmtDT(r.createdAt)}</td><td>${r.event === 'login' ? 'Entrou (senha)' : 'Abriu'}</td><td>${esc(shortDev(r.device))}</td><td class=\"ac-cid\">${esc((r.clientId || '').slice(0, 10))}</td></tr>`).join('');\n    inner = `<div class=\"ac-kpis\"><div class=\"ac-kpi\"><div class=\"ac-k-label\">Aberturas</div><div class=\"ac-k-val\">${ac.opens || 0}</div></div><div class=\"ac-kpi\"><div class=\"ac-k-label\">Entradas (senha)</div><div class=\"ac-k-val\">${ac.logins || 0}</div></div><div class=\"ac-kpi\"><div class=\"ac-k-label\">Dispositivos distintos</div><div class=\"ac-k-val\">${distinct}</div></div></div><div class=\"p-subhead\" style=\"margin-top:26px\">Acessos recentes</div><div class=\"tbl-wrap\"><table class=\"tbl\"><thead><tr><th>Quando</th><th>Evento</th><th>Dispositivo</th><th>ID navegador</th></tr></thead><tbody>${rows || '<tr><td>Sem acessos ainda.</td></tr>'}</tbody></table></div><button class=\"ghost ac-refresh\" style=\"margin-top:20px\">↻ Atualizar</button>`;\n  }\n  return `<div class=\"stage\"><div class=\"proposal\"><div class=\"p-top\">${LOGO}<span class=\"ac-tag\">Painel de acessos</span></div><div class=\"p-head\" style=\"border:0;padding-bottom:8px\"><div class=\"eyebrow\">Monitoramento · Proposta Dress To</div><h2>Quem acessou</h2></div>${inner}</div></div>`;\n};\n\nexport default {\n  css: `\n@import url('https://fonts.googleapis.com/css2?family=Encode+Sans+Expanded:wght@300;400&display=swap');\n@font-face{font-family:'Circular Air Light';src:url('https://www.data2.media/file/DWcXnLLnDWwlhRgYpsi1-') format('woff2'),url('https://www.data2.media/file/TDX5oH7NTx-vDAtRBcNeY') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Medium';src:url('https://www.data2.media/file/EByR5T2BNUoKCejgqK1JQ') format('woff2'),url('https://www.data2.media/file/tnIrecxS9FEqh9vh_Go-8') format('woff');font-display:swap}\n@font-face{font-family:'Circular Air Bold';src:url('https://www.data2.media/file/lALU-3wyCL2kksdactSyo') format('woff2'),url('https://www.data2.media/file/R3eunIkdQ9ors671taObT') format('woff');font-display:swap}\n*{box-sizing:border-box;margin:0;padding:0}\n:host{display:block;width:100%;height:100%}\n.stage{height:100%;width:100%;overflow-y:auto;font-family:'Circular Air Light','Circular Air Medium',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;-webkit-font-smoothing:antialiased;color:#f5f6f8;background:#000000}\n@keyframes gflow{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}\n@keyframes floatUp{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}\n@keyframes glowpulse{0%,100%{opacity:.42;transform:translate(-50%,-50%) scale(1)}50%{opacity:.6;transform:translate(-50%,-50%) scale(1.08)}}\n.hero{position:relative;min-height:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;padding:56px 28px;background:#000000}\n.glow{display:none}\n.inner{position:relative;z-index:2;text-align:center;max-width:860px;width:100%;animation:floatUp .8s cubic-bezier(.2,.7,.2,1) both}\n.logo{display:inline-flex;align-items:center;gap:20px;color:#fff}\n.logo-mark{width:66px;height:66px;flex-shrink:0}\n.logo-word{font-family:'Encode Sans Expanded','Circular Air Light',sans-serif;font-weight:300;font-size:58px;line-height:1;display:inline-flex;align-items:flex-start;letter-spacing:0}\n.logo-word sup{font-family:'Encode Sans Expanded',sans-serif;font-weight:400;font-size:.5em;margin-left:3px;margin-top:.1em}\n.tagline{margin-top:16px;font-family:'Circular Air Light',sans-serif;font-size:15px;letter-spacing:.04em;color:rgba(255,255,255,.62)}\n.divider{width:54px;height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.4),transparent);margin:40px auto 34px}\n.kicker{display:inline-block;font-family:'Circular Air Bold',sans-serif;font-size:12.5px;letter-spacing:.26em;text-transform:uppercase;color:#fff;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.24);border-radius:999px;padding:9px 20px;margin-bottom:24px}\nh1{font-family:'Circular Air Bold',sans-serif;font-size:clamp(46px,8.5vw,96px);line-height:1.02;letter-spacing:-.01em;color:#fff;margin-bottom:24px}\nh1 .grad{color:#fff}\n.lede{font-size:clamp(16px,2vw,20px);color:rgba(255,255,255,.78);max-width:580px;margin:0 auto 46px;line-height:1.55}\n.cta{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}\n.btn{cursor:pointer;border:0;border-radius:999px;font-family:'Circular Air Bold',sans-serif;font-size:16px;padding:17px 34px;transition:transform .15s,box-shadow .25s,background .2s;color:#fff;text-decoration:none;display:inline-flex;align-items:center;gap:10px}\n.btn:active{transform:translateY(1px)}\n.btn-grad{background:#fff;color:#000;box-shadow:none;border:1px solid #fff}\n.btn-grad:hover{background:#e6e6e6;border-color:#e6e6e6;transform:translateY(-2px)}\n.btn-glass{background:rgba(255,255,255,.09);border:1px solid rgba(255,255,255,.32);backdrop-filter:blur(6px)}\n.btn-glass:hover{background:rgba(255,255,255,.16);transform:translateY(-2px)}\n.login-card{margin-top:6px;text-align:left}\n.login-title{font-family:'Circular Air Bold',sans-serif;font-size:15px;color:#fff;text-align:center;margin-bottom:18px}\n.login-input{width:100%;height:50px;background:#111216;border:1px solid rgba(255,255,255,.16);border-radius:12px;padding:0 16px;color:#fff;font-size:15px;font-family:inherit;outline:none;margin-bottom:12px;transition:border-color .15s}\n.login-input:focus{border-color:#fff}\n.login-input::placeholder{color:rgba(255,255,255,.4)}\n.login-err{margin-top:12px;font-size:13px;color:#ff9b9b;text-align:center}\n.brands-line{margin-top:50px;font-family:'Circular Air Medium',sans-serif;font-size:12.5px;letter-spacing:.18em;text-transform:uppercase;color:rgba(255,255,255,.5)}\n/* proposta placeholder */\n.prop{min-height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:60px 28px;background:#000000;position:relative;overflow:hidden}\n.prop .logo{margin-bottom:30px}\n.prop .logo-mark{width:48px;height:48px}\n.prop .logo-word{font-size:46px}\n.eyebrow{font-family:'Circular Air Bold',sans-serif;font-size:13px;letter-spacing:.22em;text-transform:uppercase;color:rgba(255,255,255,.55);margin-bottom:12px}\n.prop h2{font-family:'Circular Air Bold',sans-serif;font-size:clamp(30px,5vw,52px);letter-spacing:-.01em;color:#fff;margin-bottom:16px}\n.prop p{font-size:17px;color:rgba(255,255,255,.66);max-width:520px;line-height:1.6;margin-bottom:34px}\n.dash{display:inline-flex;align-items:center;gap:10px;color:rgba(255,255,255,.5);font-size:14px;margin-bottom:40px}\n.pulse{width:9px;height:9px;border-radius:50%;background:rgba(255,255,255,.8);animation:pl 1.8s infinite}\n@keyframes pl{0%{box-shadow:0 0 0 0 rgba(255,255,255,.4)}70%{box-shadow:0 0 0 12px rgba(255,255,255,0)}100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}}\n.ghost{cursor:pointer;background:none;border:1px solid rgba(255,255,255,.3);color:#fff;border-radius:999px;font-family:'Circular Air Medium',sans-serif;font-size:14px;padding:12px 24px;transition:border-color .2s,background .2s}\n.ghost:hover{border-color:#fff;background:rgba(255,255,255,.08)}\n.proposal{max-width:920px;margin:0 auto;padding:40px 32px 80px;min-height:100%}\n.p-top{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:44px}\n.p-top .logo{gap:11px}.p-top .logo-word{font-size:26px}.p-top .logo-mark{width:30px;height:30px}\n.p-head{margin-bottom:20px;border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:34px}\n.p-head h2{font-family:'Circular Air Bold',sans-serif;font-size:clamp(30px,5vw,48px);color:#fff;letter-spacing:-.01em;margin:10px 0 14px}\n.p-lede{font-size:16px;color:rgba(255,255,255,.62);line-height:1.6;max-width:640px}\n.p-sec{display:flex;gap:24px;padding:38px 0;border-bottom:1px solid rgba(255,255,255,.08)}\n.p-secnum{font-family:'Circular Air Bold',sans-serif;font-size:15px;color:rgba(255,255,255,.35);width:36px;flex-shrink:0;padding-top:4px}\n.p-secbody{flex:1;min-width:0}\n.p-secbody h3{font-family:'Circular Air Bold',sans-serif;font-size:22px;color:#fff;margin-bottom:10px}\n.p-desc{font-size:15px;color:rgba(255,255,255,.6);line-height:1.6;margin-bottom:22px;max-width:640px}\n.p-price-card{display:flex;flex-wrap:wrap;gap:20px;align-items:center;justify-content:space-between;background:#0f1013;border:1px solid rgba(255,255,255,.12);border-radius:16px;padding:24px 28px}\n.ppc-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:6px}\n.ppc-val{font-family:'Circular Air Bold',sans-serif;font-size:22px;color:#fff}\n.ppc-big{font-family:'Circular Air Bold',sans-serif;font-size:34px;color:#fff}\n.ppc-right{text-align:right}\n.tbl-wrap{overflow-x:auto;border:1px solid rgba(255,255,255,.12);border-radius:14px}\n.tbl{width:100%;border-collapse:collapse;font-size:14px;min-width:540px}\n.tbl th{font-family:'Circular Air Bold',sans-serif;text-align:left;font-size:12px;letter-spacing:.03em;color:rgba(255,255,255,.75);padding:14px 16px;border-bottom:1px solid rgba(255,255,255,.14);background:#0f1013;white-space:nowrap}\n.tbl td{padding:13px 16px;color:rgba(255,255,255,.82);border-bottom:1px solid rgba(255,255,255,.07);white-space:nowrap}\n.tbl td strong{color:#fff}\n.tbl tbody tr:last-child td{border-bottom:0}\n.tbl tbody tr:hover td{background:rgba(255,255,255,.03)}\n.tbl td:first-child{font-family:'Circular Air Medium',sans-serif;color:#fff}\n.p-link{display:inline-block;margin-top:18px;color:#fff;font-size:14px;text-decoration:none;border-bottom:1px solid rgba(255,255,255,.4);padding-bottom:2px}\n.p-link:hover{border-color:#fff}\n.ppc-sub{font-size:12px;color:rgba(255,255,255,.45);margin-top:6px}\n.p-note{font-size:12.5px;color:rgba(255,255,255,.45);line-height:1.6;margin-top:16px}\n.p-subhead{font-family:'Circular Air Bold',sans-serif;font-size:14px;color:#fff;margin:30px 0 14px;letter-spacing:.02em}\n.ac-tag{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4)}\n.ac-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:10px}\n.ac-kpi{background:#0f1013;border:1px solid rgba(255,255,255,.12);border-radius:14px;padding:20px}\n.ac-k-label{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:8px}\n.ac-k-val{font-family:'Circular Air Bold',sans-serif;font-size:32px;color:#fff}\n.ac-cid{color:rgba(255,255,255,.4);font-size:12px}\n.ac-msg{color:rgba(255,255,255,.6);padding:30px 0}\n@media(max-width:600px){.ac-kpis{grid-template-columns:1fr}}\n.p-contact{margin-top:56px;border-top:1px solid rgba(255,255,255,.1);padding-top:34px}\n.pc-brand{margin-bottom:24px}.pc-brand .logo{gap:11px}.pc-brand .logo-word{font-size:26px}.pc-brand .logo-mark{width:30px;height:30px}\n.pc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:20px;margin-bottom:26px}\n.pc-label{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.4);margin-bottom:6px}\n.pc-val{font-size:15px;color:#fff}\n.pc-tag{font-family:'Circular Air Light',sans-serif;font-size:14px;color:rgba(255,255,255,.5)}\n.p-foot{margin-top:50px;text-align:center;font-size:13px;color:rgba(255,255,255,.4)}\n@media(max-width:600px){.cta{flex-direction:column}.btn{width:100%;justify-content:center}.logo-word{font-size:48px}.logo-mark{width:50px;height:50px}.p-sec{flex-direction:column;gap:8px}.p-secnum{padding-top:0}}\n  `,\n\n  render(d2) {\n    const screen = (d2.state && d2.state.screen) || 'login';\n    if (screen === 'acessos') return renderAcessos(d2.state || {});\n    if (screen === 'login') {\n      return `\n        <div class=\"stage\">\n          <div class=\"hero\">\n            <div class=\"inner\" style=\"max-width:420px\">\n              ${LOGO}\n              <div class=\"tagline\">Express yourself at scale</div>\n              <div class=\"divider\"></div>\n              <div class=\"kicker\">Proposta · Dress To</div>\n              <div class=\"login-card\">\n                <div class=\"login-title\">Digite a senha para acessar</div>\n                <input class=\"login-input login-pass\" type=\"password\" placeholder=\"Senha\" autocomplete=\"current-password\" />\n                <button class=\"btn btn-grad login-btn\" style=\"width:100%;justify-content:center\">Entrar</button>\n                ${(d2.state && d2.state.loginError) ? '<div class=\"login-err\">' + d2.state.loginError + '</div>' : ''}\n              </div>\n            </div>\n          </div>\n        </div>`;\n    }\n    if (screen === 'proposta') {\n      return `\n        <div class=\"stage\">\n          <div class=\"prop\">\n            ${LOGO}\n            <div class=\"eyebrow\">Proposta Comercial · Dress To</div>\n            <h2>Em construção</h2>\n            <p>A proposta comercial para a Dress To está sendo preparada. Volte em breve para ver o escopo, os prazos e o investimento.</p>\n            <div class=\"dash\"><span class=\"pulse\"></span> Em preparação</div>\n            <button class=\"ghost go-capa\">← Voltar</button>\n          </div>\n        </div>`;\n    }\n    return `\n      <div class=\"stage\">\n        <div class=\"hero\">\n          <div class=\"glow\"></div>\n          <div class=\"inner\">\n            ${LOGO}\n            <div class=\"tagline\">Express yourself at scale</div>\n            <div class=\"divider\"></div>\n            <div class=\"kicker\">Proposta Comercial</div>\n            <h1>Dress To</h1>\n            <p class=\"lede\">Provador virtual com Inteligência Artificial para a Dress To — experimente as peças com a sua própria foto.</p>\n            <div class=\"cta\">\n              <a class=\"btn btn-grad\" href=\"${POC_URL}\" target=\"_blank\" rel=\"noopener\">✨ Acessar o Provador Virtual</a>\n              <button class=\"btn btn-glass go-proposta\">Ver a proposta →</button>\n            </div>\n            <div class=\"brands-line\">Provador Virtual com IA</div>\n          </div>\n        </div>\n      </div>`;\n  },\n\n  mounted(d2) {\n    let cid = ''; try { cid = window.localStorage.getItem('dressto_prop_cid') || ''; if (!cid) { cid = 'c' + Date.now().toString(36) + Math.floor(Math.random() * 1e6).toString(36); window.localStorage.setItem('dressto_prop_cid', cid); } } catch (e) {}\n    const track = (event) => { try { const dev = (navigator.userAgent || '').slice(0, 180); const p = d2.rush('dressto-prop-track').invoke({ event: event, clientId: cid, device: dev, ref: (document.referrer || '') }); if (p && p.catch) p.catch(() => {}); } catch (e) {} };\n    let isAdmin = false; try { isAdmin = ((window.location.search || '') + (window.location.hash || '')).indexOf('acessos') >= 0; } catch (e) {}\n    let authed = false; try { authed = window.localStorage.getItem(AUTH_KEY) === '1'; } catch (e) {}\n    d2.setState({ screen: isAdmin ? 'acessos' : (authed ? 'capa' : 'login') });\n    if (!isAdmin) track('open');\n    const loadAcessos = async (pw) => {\n      d2.setState({ acStatus: 'loading', acError: null });\n      try {\n        const r = await d2.rush('dressto-prop-acessos').invoke({ password: pw });\n        const s = (r && r.data) ? r.data : r;\n        if (!s || s.ok !== true) { try { window.localStorage.removeItem('dressto_prop_admin'); } catch (e) {} d2.setState({ acStatus: 'locked', acError: pw ? 'Senha incorreta.' : null }); return; }\n        try { window.localStorage.setItem('dressto_prop_admin', pw); } catch (e) {}\n        d2.setState({ acStatus: 'done', ac: s });\n      } catch (err) { d2.setState({ acStatus: 'error', acError: String((err && err.message) || err) }); }\n    };\n    d2.on('.login-btn', 'click', () => {\n      const p = ((d2.$('.login-pass') || {}).value || '');\n      if (p === AUTH_SENHA) { try { window.localStorage.setItem(AUTH_KEY, '1'); } catch (e) {} track('login'); d2.setState({ screen: 'capa', loginError: null }); }\n      else { d2.setState({ loginError: 'Senha incorreta.' }); }\n    });\n    d2.on('.login-pass', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.login-btn'); if (b) b.click(); } });\n    d2.on('.go-proposta', 'click', () => d2.setState({ screen: 'proposta' }));\n    d2.on('.go-capa', 'click', () => d2.setState({ screen: 'capa' }));\n    d2.on('.ac-btn', 'click', () => { const el = d2.$('.ac-pass'); const pw = (el && el.value) || ''; if (!pw) { d2.setState({ acError: 'Digite a senha.' }); return; } loadAcessos(pw); });\n    d2.on('.ac-pass', 'keydown', (e) => { if (e.key === 'Enter') { const b = d2.$('.ac-btn'); if (b) b.click(); } });\n    d2.on('.ac-refresh', 'click', () => { let pw = ''; try { pw = window.localStorage.getItem('dressto_prop_admin') || ''; } catch (e) {} loadAcessos(pw); });\n    if (isAdmin) { let saved = ''; try { saved = window.localStorage.getItem('dressto_prop_admin') || ''; } catch (e) {} if (saved) loadAcessos(saved); else d2.setState({ acStatus: 'locked' }); }\n  }\n}\n"}}}}}}}},{"_id":"6aad9a9c01be65c4a08b5651","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"scr-app","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Social CRM · Estímulo — POC (dados reais do Instagram @estimulo.br)\nconst AVATAR = 'https://people-gabi-carmo.data2.media/file/Dfiyxvk5W9HQEY3hg5NYi';\nconst PERIODOS = [{ k: '30', l: '30 dias', d: 30 }, { k: '90', l: '90 dias', d: 90 }, { k: '365', l: '12 meses', d: 365 }];\n\nfunction kf(n){ n = Number(n) || 0; if (n >= 1e6) return (n / 1e6).toFixed(n >= 1e7 ? 0 : 1).replace('.', ',') + 'M'; if (n >= 1e3) return (n / 1e3).toFixed(n >= 1e4 ? 0 : 1).replace('.', ',') + 'k'; return String(n); }\nfunction cap(t){ return (t || '').charAt(0).toUpperCase() + (t || '').slice(1); }\nfunction catLabel(c){\n  const m = { 'historia de cliente': 'História de cliente', 'credito e produto': 'Crédito e produto', 'capacitacao': 'Capacitação', 'parceria': 'Parceria', 'impacto e numeros': 'Impacto e números', 'evento': 'Evento', 'institucional': 'Institucional' };\n  return m[c] || cap(c || '—');\n}\nfunction tipoLabel(t){ return t === 'reel' ? 'Reel' : (t === 'carrossel' ? 'Carrossel' : 'Foto'); }\nfunction periodoLabel(k){ const p = PERIODOS.find(x => x.k === k); return p ? p.l : '12 meses'; }\nfunction dtBR(s){ if (!s) return '—'; const d = new Date(s); if (isNaN(d.getTime())) return '—'; return String(d.getUTCDate()).padStart(2,'0') + '/' + String(d.getUTCMonth()+1).padStart(2,'0') + '/' + d.getUTCFullYear(); }\n// Instagram devolve tudo em UTC; a leitura do time é em horário de Brasília (UTC-3).\nfunction horaBR(s){ const d = new Date(s); if (isNaN(d.getTime())) return null; return (d.getUTCHours() + 21) % 24; }\nfunction diaBR(s){ const d = new Date(s); if (isNaN(d.getTime())) return null; const h = d.getUTCHours(); return h < 3 ? (d.getUTCDay() + 6) % 7 : d.getUTCDay(); }\n\nfunction view(d2){\n  const st = d2.store('scr-ui');\n  const D = st.get('data');\n  if (!D) return `<div class=\"loading\">Carregando dados do CRM…</div>`;\n  const per = st.get('per') || '365';\n  const dias = (PERIODOS.find(p => p.k === per) || PERIODOS[2]).d;\n  const todosPosts = (D.posts || []).slice();\n  const corte = Date.now() - dias * 86400000;\n  const posts = todosPosts.filter(p => { const t = new Date(p.publicadoEm).getTime(); return !isNaN(t) && t >= corte; });\n  const idsPeriodo = new Set(posts.map(p => p.postId));\n  const todosComentarios = D.comentarios || [];\n  const comentarios = todosComentarios.filter(c => idsPeriodo.has(c.postId));\n  const alertas = (D.alertas || []).slice();\n  const insight = st.get('insight') || (D.insights && D.insights[0]) || null;\n  const tab = st.get('tab') || 'dashboard';\n  const sel = st.get('sel');\n  const replies = st.get('replies') || {};\n  const replyLoading = st.get('replyLoading') || {};\n  const insightLoading = st.get('insightLoading');\n  const conta = (D.contas || [])[0] || { handle: '@estimulo.br', seguidores: 20500 };\n  const alertAtivos = alertas.filter(a => a.status !== 'resolvido');\n  const sum = (k) => posts.reduce((a, p) => a + (Number(p[k]) || 0), 0);\n  const avg = (k) => posts.length ? (posts.reduce((a, p) => a + (Number(p[k]) || 0), 0) / posts.length) : 0;\n  const nPos = comentarios.filter(c => c.sentimento === 'positivo').length;\n  const pctPos = comentarios.length ? Math.round((nPos / comentarios.length) * 100) : 0;\n  const kpis = [\n    { l: 'Posts', v: posts.length, s: periodoLabel(per) },\n    { l: 'Curtidas', v: kf(sum('curtidas')), s: 'total no período' },\n    { l: 'Comentários', v: kf(sum('comentarios')), s: `${comentarios.length} analisados por IA` },\n    { l: 'Engaj. médio', v: avg('taxaEngajamento').toFixed(2).replace('.', ',') + '%', s: 'por post' },\n    { l: 'Sentimento +', v: pctPos + '%', s: 'dos comentários' },\n    { l: 'Alertas', v: alertAtivos.length, s: 'ativos agora' },\n  ];\n  const sideNav = `\n    <div class=\"side\">\n      <div class=\"brand\">\n        <div class=\"logo\"><img src=\"${AVATAR}\" alt=\"\"/></div>\n        <div><b>Social CRM</b><small>Estímulo · IA</small></div>\n      </div>\n      <div class=\"nav\">\n        <button class=\"nv ${tab==='dashboard'?'on':''}\" data-tab=\"dashboard\">📊 Dashboard</button>\n        <button class=\"nv ${tab==='alertas'?'on':''}\" data-tab=\"alertas\">🚨 Alertas ${alertAtivos.length?`<span class=\"badge\">${alertAtivos.length}</span>`:''}</button>\n        <button class=\"nv ${tab==='conectar'?'on':''}\" data-tab=\"conectar\">🔗 Conectar redes</button>\n        <button class=\"nv ${tab==='agenda'?'on':''}\" data-tab=\"agenda\">🗓️ Agenda</button>\n        <button class=\"nv ${tab==='comunidade'?'on':''}\" data-tab=\"comunidade\">🧑‍🤝‍🧑 Comunidade</button>\n        <button class=\"nv ${tab==='horario'?'on':''}\" data-tab=\"horario\">⏰ Melhor Horário</button>\n      </div>\n      <div class=\"foot\">POC · dados reais do Instagram<br/>Análise por IA da Data²<br/><button class=\"sair\">Sair</button></div>\n    </div>`;\n  const filtros = `<div class=\"filters\">${PERIODOS.map(p=>`<button class=\"flt ${per===p.k?'on':''}\" data-per=\"${p.k}\">${p.l}</button>`).join('')}</div>`;\n  let content = '';\n  if (tab === 'dashboard') {\n    const list = posts.slice().sort((a, b) => (b.scoreDesempenho || 0) - (a.scoreDesempenho || 0));\n    const postCards = list.map(p => `\n      <div class=\"post op\" data-post=\"${p.postId}\" data-key=\"${p.postId}\">\n        <div class=\"thumb\" style=\"background-image:url('${p.thumbnailUrl}')\"><span class=\"rk\">#${p.rankPosicao}</span></div>\n        <div class=\"p-body\">\n          <div class=\"p-top\"><span class=\"tag ig\">${tipoLabel(p.tipo)}</span><span class=\"tag\">${catLabel(p.categoriaConteudo)}</span><span class=\"score\">${p.scoreDesempenho}<small>/100</small></span></div>\n          <div class=\"cap\">${p.legenda}</div>\n          <div class=\"mets\">❤ ${kf(p.curtidas)} · 💬 ${kf(p.comentarios)} · 📅 ${dtBR(p.publicadoEm)} · ${String(p.taxaEngajamento).replace('.',',')}%</div>\n          <div class=\"sbar\"><i class=\"pos\" style=\"width:${p.sentimentoPositivoPct}%\"></i><i class=\"neu\" style=\"width:${p.sentimentoNeutroPct}%\"></i><i class=\"neg\" style=\"width:${p.sentimentoNegativoPct}%\"></i></div>\n        </div>\n      </div>`).join('');\n    const catMax = insight && insight.topCategorias ? Math.max(...insight.topCategorias.map(c => c.scoreMedio || 0)) : 100;\n    const insightHtml = insight ? `\n      <div class=\"panel insight\">\n        <h2>💡 O que performa melhor <span class=\"muted\">${insight.periodo || 'últimos 12 meses'}</span></h2>\n        <div class=\"resumo\">${insight.resumo}</div>\n        <ul class=\"recs\">${(insight.recomendacoes||[]).map((r,i)=>`<li><span class=\"n\">${i+1}</span><span>${r}</span></li>`).join('')}</ul>\n        <div class=\"cats\">${(insight.topCategorias||[]).map(c=>`<div class=\"cat\"><div class=\"cl2\"><span>${catLabel(c.categoria)}</span><span>${c.scoreMedio}</span></div><div class=\"track\"><div class=\"fill\" style=\"width:${Math.round((c.scoreMedio/catMax)*100)}%\"></div></div></div>`).join('')}</div>\n        <div style=\"margin-top:16px\"><button class=\"btn ghost rgi\" ${insightLoading?'disabled':''}>${insightLoading?'<span class=\"spin\"></span> Gerando…':'✨ Regenerar com IA'}</button></div>\n      </div>` : '';\n    const alertHtml = `\n      <div class=\"panel\" style=\"margin-top:20px\">\n        <h2>🚨 Alertas de sentimento <span class=\"muted\">${alertAtivos.length} ativos</span></h2>\n        ${alertas.length?alertas.map(a=>`<div class=\"alert op ${a.severidade==='alta'?'':'media'}\" data-post=\"${a.postId}\"><div class=\"sev\"></div><div><div class=\"msg\">${a.mensagem}</div><div class=\"mt\">${a.severidade==='alta'?'Severidade alta':'Severidade média'} · ${a.qtdNegativos} comentários negativos</div></div></div>`).join(''):'<div style=\"color:#6b7280;font-size:13px\">Nenhum alerta ativo 🎉</div>'}\n      </div>`;\n    content = `\n      <div class=\"head\"><div><h1>Dashboard</h1><p>Desempenho e sentimento do Instagram do Estímulo, analisados por IA</p></div>\n        <div class=\"accts\"><div class=\"acct\"><span class=\"dot\"></span>📸 ${conta.handle} · ${kf(conta.seguidores)} seguidores</div></div></div>\n      <div class=\"kpis\">${kpis.map(k=>`<div class=\"kpi\"><div class=\"l\">${k.l}</div><div class=\"v\">${k.v}</div><div class=\"s\">${k.s}</div></div>`).join('')}</div>\n      <div class=\"cols\">\n        <div><div class=\"panel\"><h2>🏆 Ranking de posts</h2>\n          ${filtros}\n          ${postCards || '<div style=\"color:#6b7280;font-size:13px\">Nenhum post no período.</div>'}</div></div>\n        <div>${insightHtml}${alertHtml}</div>\n      </div>`;\n  } else if (tab === 'alertas') {\n    content = `\n      <div class=\"head\"><div><h1>Alertas de sentimento</h1><p>Picos de comentários negativos detectados automaticamente pela IA</p></div></div>\n      <div class=\"panel\">${alertas.length?alertas.map(a=>{const p=todosPosts.find(x=>x.postId===a.postId)||{};return `<div class=\"alert op ${a.severidade==='alta'?'':'media'}\" data-post=\"${a.postId}\"><div class=\"sev\"></div><div style=\"flex:1\"><div class=\"msg\"><b>${(p.legenda||a.postId).slice(0,120)}</b></div><div class=\"msg\" style=\"margin-top:4px\">${a.mensagem}</div><div class=\"mt\">${a.severidade==='alta'?'Severidade alta':'Severidade média'} · ${a.qtdNegativos} negativos · ${a.status}</div></div></div>`;}).join(''):'<div style=\"color:#6b7280;font-size:14px\">Nenhum alerta ativo 🎉</div>'}</div>`;\n  } else if (tab === 'conectar') {\n    const conn = (D.contas||[]);\n    const others = [{n:'LinkedIn',i:'in',c:'#eaf2fb'},{n:'TikTok',i:'🎵',c:'#e9f7f8'},{n:'Facebook',i:'👍',c:'#eef3ff'},{n:'YouTube',i:'▶',c:'#fdeeee'}];\n    content = `\n      <div class=\"head\"><div><h1>Conectar redes</h1><p>Conecte os perfis do Estímulo para a IA analisar posts e comentários</p></div></div>\n      <div class=\"panel conectar\"><div class=\"grid\">\n        ${conn.map(c=>`<div class=\"cc\"><div class=\"ico\" style=\"background:#fdecf3\">📸</div><div class=\"st on\">● Conectado</div><div><b style=\"font-size:15px\">Instagram</b><div style=\"color:#6b7280;font-size:13px\">${c.handle}</div></div><div style=\"font-size:13px;color:#374151\">${kf(c.seguidores)} seguidores</div></div>`).join('')}\n        ${others.map(o=>`<div class=\"cc\" style=\"opacity:.85\"><div class=\"ico\" style=\"background:${o.c}\">${o.i}</div><div class=\"st off\">Não conectado</div><div><b style=\"font-size:15px\">${o.n}</b><div style=\"color:#6b7280;font-size:13px\">Disponível na POC</div></div><button class=\"btn ghost mk\" data-net=\"${o.n}\">Conectar</button></div>`).join('')}\n      </div></div>`;\n  } else if (tab === 'agenda') {\n    const planejados = (D.planejados || []).slice().sort((a, b) => {\n      const pa = Number(a.prioridade), pb = Number(b.prioridade);\n      if (!isNaN(pa) && !isNaN(pb) && pa !== pb) return pa - pb;\n      return String(a.dataSugerida||'').localeCompare(String(b.dataSugerida||''));\n    });\n    const planLoading = st.get('planLoading');\n    const planCards = planejados.map(p => `\n      <div class=\"panel\">\n        <div class=\"p-top\"><span class=\"tag\">${catLabel(p.categoriaConteudo)}</span><span class=\"tag\">${cap(p.formato)}</span>${p.prioridade!=null?`<span class=\"score\" style=\"margin-left:auto;font-size:13px\">P${p.prioridade}</span>`:''}</div>\n        <div class=\"cap\" style=\"white-space:normal;font-size:14.5px;font-weight:600;margin:8px 0 6px\">${p.ideia||''}</div>\n        <div style=\"font-size:12.5px;color:#6b7280;line-height:1.5;margin-bottom:10px\">${p.justificativa||''}</div>\n        <div class=\"mets\">📅 ${p.dataSugerida?String(p.dataSugerida).slice(0,10).split('-').reverse().join('/'):'—'}${p.horarioSugerido?` · 🕒 ${p.horarioSugerido}`:''}</div>\n      </div>`).join('');\n    content = `\n      <div class=\"head\"><div><h1>Agenda de conteúdo</h1><p>Sugestões de posts geradas por IA a partir do desempenho real dos últimos 12 meses</p></div>\n        <button class=\"btn gsp\" ${planLoading?'disabled':''}>${planLoading?'<span class=\"spin\"></span> Gerando…':'✨ Gerar sugestões com IA'}</button></div>\n      ${planejados.length ? `<div class=\"plans\">${planCards}</div>` : `<div class=\"panel\" style=\"text-align:center;padding:44px 20px;color:#6b7280\"><div style=\"font-size:34px;margin-bottom:10px\">🗓️</div><div style=\"font-weight:700;color:#0b0b0c;margin-bottom:4px\">Nenhuma sugestão gerada ainda</div><div style=\"font-size:13px\">Clique em \"Gerar sugestões com IA\" para criar um plano de conteúdo.</div></div>`}`;\n  } else if (tab === 'comunidade') {\n    const proprio = String(conta.handle || '').replace(/^@/, '').toLowerCase();\n    const byAutor = {};\n    comentarios.forEach(c => {\n      const a = c.autor || '—';\n      if (String(a).toLowerCase() === proprio) return;\n      if (!byAutor[a]) byAutor[a] = { autor: a, curtidas: 0, count: 0, posts: new Set(), sent: { positivo: 0, negativo: 0, neutro: 0 } };\n      const e = byAutor[a];\n      e.curtidas += Number(c.curtidas) || 0;\n      e.count += 1;\n      if (c.postId) e.posts.add(c.postId);\n      const s = c.sentimento || 'neutro';\n      e.sent[s] = (e.sent[s] || 0) + 1;\n    });\n    const authors = Object.values(byAutor).map(e => {\n      const predominante = Object.entries(e.sent).sort((x, y) => y[1] - x[1])[0][0];\n      return { autor: e.autor, curtidas: e.curtidas, count: e.count, postsCount: e.posts.size, predominante };\n    });\n    const formadores = authors.slice().sort((a, b) => (b.count - a.count) || (b.curtidas - a.curtidas)).slice(0, 6);\n    const promotores = authors.filter(a => a.predominante === 'positivo' && a.postsCount >= 2).sort((a, b) => b.curtidas - a.curtidas).slice(0, 6);\n    const detratores = authors.filter(a => a.predominante === 'negativo').sort((a, b) => b.count - a.count).slice(0, 6);\n    const renderCommList = (list, emptyMsg) => list.length ? list.map(a => `\n      <div class=\"cu-item\">\n        <div class=\"cu-top\"><span class=\"au\">@${String(a.autor||'').replace(/^@/,'')}</span><span class=\"stag ${a.predominante}\">${a.predominante}</span></div>\n        <div class=\"mets\">💬 ${a.count} coment. · ❤ ${kf(a.curtidas)} · 📌 ${a.postsCount} post${a.postsCount===1?'':'s'}</div>\n      </div>`).join('') : `<div style=\"color:#6b7280;font-size:13px\">${emptyMsg}</div>`;\n    content = `\n      <div class=\"head\"><div><h1>Comunidade</h1><p>Vozes que mais engajam nos comentários (${comentarios.length} comentários analisados em ${periodoLabel(per).toLowerCase()})</p></div></div>\n      ${filtros}\n      <div class=\"cols3\">\n        <div class=\"panel\"><h2>💡 Formadores de opinião</h2>${renderCommList(formadores, 'Sem dados suficientes ainda.')}</div>\n        <div class=\"panel\"><h2>✅ Promotores</h2>${renderCommList(promotores, 'Nenhum promotor recorrente identificado no período.')}</div>\n        <div class=\"panel\"><h2>🚨 Detratores</h2>${renderCommList(detratores, 'Nenhum detrator identificado no período.')}</div>\n      </div>`;\n  } else if (tab === 'horario') {\n    const byHour = {};\n    posts.forEach(p => {\n      const h = horaBR(p.publicadoEm);\n      if (h === null) return;\n      if (!byHour[h]) byHour[h] = { hour: h, sumEng: 0, count: 0 };\n      byHour[h].sumEng += Number(p.taxaEngajamento) || 0;\n      byHour[h].count += 1;\n    });\n    const hours = Object.values(byHour).filter(x => x.count >= 2).map(x => ({ hour: x.hour, avg: x.sumEng / x.count, count: x.count })).sort((a, b) => b.avg - a.avg);\n    const maxAvg = hours.length ? hours[0].avg : 1;\n    const top = hours.slice(0, 3).map(h => `${String(h.hour).padStart(2,'0')}h`).join(', ');\n    const barsHtml = hours.length ? hours.map(h => `\n      <div class=\"cat\"><div class=\"cl2\"><span>${String(h.hour).padStart(2,'0')}h–${String((h.hour+1)%24).padStart(2,'0')}h <small style=\"color:#9aa0a6;font-weight:500\">(${h.count} post${h.count===1?'':'s'})</small></span><span>${h.avg.toFixed(2).replace('.',',')}%</span></div><div class=\"track\"><div class=\"fill\" style=\"width:${Math.round((h.avg/maxAvg)*100)}%\"></div></div></div>`).join('') : '<div style=\"color:#6b7280;font-size:13px\">Sem dados de horário suficientes no período.</div>';\n    const dayNames = ['Domingo','Segunda','Terça','Quarta','Quinta','Sexta','Sábado'];\n    const byDay = {};\n    posts.forEach(p => {\n      const w = diaBR(p.publicadoEm);\n      if (w === null) return;\n      if (!byDay[w]) byDay[w] = { day: w, sumEng: 0, count: 0 };\n      byDay[w].sumEng += Number(p.taxaEngajamento) || 0;\n      byDay[w].count += 1;\n    });\n    const days = Object.values(byDay).map(x => ({ day: x.day, avg: x.sumEng / x.count, count: x.count })).sort((a, b) => b.avg - a.avg);\n    const maxDayAvg = days.length ? days[0].avg : 1;\n    const daysHtml = days.map(d => `\n      <div class=\"cat\"><div class=\"cl2\"><span>${dayNames[d.day]} <small style=\"color:#9aa0a6;font-weight:500\">(${d.count})</small></span><span>${d.avg.toFixed(2).replace('.',',')}%</span></div><div class=\"track\"><div class=\"fill\" style=\"width:${Math.round((d.avg/maxDayAvg)*100)}%\"></div></div></div>`).join('');\n    content = `\n      <div class=\"head\"><div><h1>Melhor horário</h1><p>Engajamento médio por hora de publicação (horário de Brasília)</p></div></div>\n      ${filtros}\n      ${top ? `<div class=\"callout\">🔥 Melhor horário para postar: <b>${top}</b></div>` : ''}\n      <div class=\"panel\"><h2>⏰ Engajamento médio por hora</h2><div class=\"cats\">${barsHtml}</div></div>\n      ${days.length ? `<div class=\"panel\" style=\"margin-top:20px\"><h2>📅 Melhor dia da semana</h2><div class=\"cats\">${daysHtml}</div></div>` : ''}`;\n  }\n  let overlay = '';\n  if (sel) {\n    const p = todosPosts.find(x => x.postId === sel);\n    if (p) {\n      const cms = todosComentarios.filter(c => c.postId === p.postId);\n      const cmtHtml = cms.length ? cms.map(c => {\n        const reply = (replies && replies[c.comentarioId]) || c.respostaSugerida || '';\n        const loading = replyLoading && replyLoading[c.comentarioId];\n        return `<div class=\"cmt\" data-key=\"${c.comentarioId}\">\n          <div class=\"ct\"><span class=\"au\">@${String(c.autor||'').replace(/^@/,'')}</span><span class=\"stag ${c.sentimento}\">${c.sentimento}</span></div>\n          <div class=\"tx\">${c.texto}</div>\n          <div class=\"cf\"><span class=\"lk\">❤ ${kf(c.curtidas)}</span><button class=\"btn ghost gr\" style=\"padding:6px 11px;font-size:11.5px\" data-cid=\"${c.comentarioId}\" ${loading?'disabled':''}>${loading?'<span class=\"spin\"></span> Gerando…':(reply?'↻ Nova sugestão':'✨ Gerar resposta')}</button></div>\n          ${reply?`<div class=\"reply\"><div class=\"rl\">✨ Resposta sugerida pela IA</div>${reply}</div>`:''}\n        </div>`;\n      }).join('') : '<div style=\"color:#6b7280;font-size:13px\">Sem comentários carregados para este post.</div>';\n      overlay = `\n        <div class=\"ov\"><div class=\"draw\">\n          <div class=\"dh\"><span class=\"tag ig\">${tipoLabel(p.tipo)}</span><b>Detalhe do post · #${p.rankPosicao}</b><button class=\"x cl\">✕</button></div>\n          <div class=\"db\">\n            <div class=\"hero\"><div class=\"thumb\" style=\"background-image:url('${p.thumbnailUrl}')\"></div><div><span class=\"tag\">${catLabel(p.categoriaConteudo)}</span><div class=\"cap2\">${p.legenda}</div><div style=\"font-size:13px;color:#6b7280\">Publicado em ${dtBR(p.publicadoEm)} · Score <b style=\"color:#0b0b0c;font-size:16px\">${p.scoreDesempenho}/100</b></div></div></div>\n            <div class=\"mgrid\"><div class=\"m\"><div class=\"l\">Curtidas</div><div class=\"v\">${kf(p.curtidas)}</div></div><div class=\"m\"><div class=\"l\">Comentários</div><div class=\"v\">${kf(p.comentarios)}</div></div><div class=\"m\"><div class=\"l\">Analisados</div><div class=\"v\">${cms.length}</div></div><div class=\"m\"><div class=\"l\">Engajamento</div><div class=\"v\">${String(p.taxaEngajamento).replace('.',',')}%</div></div></div>\n            <div class=\"panel\" style=\"margin-bottom:18px\"><h2>😊 Sentimento dos comentários</h2><div class=\"sent-big\"><div class=\"b pos\">${p.sentimentoPositivoPct}%<span>Positivo</span></div><div class=\"b neu\">${p.sentimentoNeutroPct}%<span>Neutro</span></div><div class=\"b neg\">${p.sentimentoNegativoPct}%<span>Negativo</span></div></div></div>\n            <h2 style=\"font-size:15px;margin:0 0 12px\">💬 Comentários <span style=\"color:#9aa0a6;font-weight:500;font-size:12px\">— gere respostas com 1 clique</span></h2>\n            ${cmtHtml}\n          </div>\n        </div></div>`;\n    }\n  }\n  return sideNav + `<div class=\"main\">${content}</div>` + overlay;\n}\n\nexport default {\n  css: `\n  :host, .wrap { font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }\n  * { box-sizing: border-box; }\n  .wrap { display:flex; height:100%; min-height:100%; background:#f4f5f8; color:#0d0d2b; position:relative; }\n  .side { width:236px; flex:0 0 236px; background:#141450; color:#fff; display:flex; flex-direction:column; padding:20px 14px; gap:6px; }\n  .brand { display:flex; align-items:center; gap:10px; padding:6px 8px 16px; }\n  .brand .logo { width:38px; height:38px; border-radius:50%; background:#fff; overflow:hidden; display:flex; align-items:center; justify-content:center; flex:0 0 38px; }\n  .brand .logo img { width:100%; height:100%; object-fit:cover; }\n  .brand b { font-size:15px; line-height:1.1; } .brand small{ display:block; color:#a7aede; font-weight:500; font-size:11px; }\n  .nav { display:flex; flex-direction:column; gap:2px; margin-top:8px; }\n  .nav button { all:unset; cursor:pointer; display:flex; align-items:center; gap:10px; padding:11px 12px; border-radius:10px; color:#c6cbf0; font-size:14px; font-weight:500; }\n  .nav button:hover { background:#1e1e6b; color:#fff; }\n  .nav button.on { background:#fff; color:#141450; font-weight:600; }\n  .nav .badge { margin-left:auto; background:#e11d48; color:#fff; font-size:11px; font-weight:700; border-radius:999px; padding:1px 7px; }\n  .side .foot { margin-top:auto; font-size:11px; color:#7f87c4; padding:8px; }\n  .side .foot .sair { all:unset; cursor:pointer; display:inline-block; margin-top:10px; font-size:11px; font-weight:700; color:#c6cbf0; border:1px solid #33339a; border-radius:8px; padding:5px 11px; }\n  .side .foot .sair:hover { background:#1e1e6b; color:#fff; }\n  .main { flex:1; overflow:auto; padding:26px 30px; }\n  .head { display:flex; align-items:center; justify-content:space-between; margin-bottom:20px; flex-wrap:wrap; gap:12px; }\n  .head h1 { font-size:22px; margin:0; letter-spacing:-0.02em; } .head p{ margin:3px 0 0; color:#6b7280; font-size:13px; }\n  .accts { display:flex; gap:8px; }\n  .acct { display:flex; align-items:center; gap:8px; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:7px 12px; font-size:12px; font-weight:600; }\n  .acct .dot{ width:8px;height:8px;border-radius:50%;background:#4fd68a; }\n  .kpis { display:grid; grid-template-columns:repeat(6,1fr); gap:12px; margin-bottom:22px; }\n  @media (max-width:1100px){ .kpis{ grid-template-columns:repeat(3,1fr);} .cols{ grid-template-columns:1fr !important; } }\n  .kpi { background:#fff; border:1px solid #e9eaef; border-radius:14px; padding:14px 16px; }\n  .kpi .l { color:#6b7280; font-size:12px; font-weight:600; } .kpi .v{ font-size:24px; font-weight:750; letter-spacing:-0.02em; margin-top:4px; } .kpi .s{ font-size:11px; color:#9aa0a6; margin-top:2px; }\n  .cols { display:grid; grid-template-columns:minmax(0,1.6fr) minmax(0,1fr); gap:20px; align-items:start; }\n  .cols > div { min-width:0; }\n  .panel { background:#fff; border:1px solid #e9eaef; border-radius:16px; padding:18px; min-width:0; overflow:hidden; }\n  .panel h2 { font-size:15px; margin:0 0 14px; display:flex; align-items:center; gap:8px; }\n  .panel h2 .muted { color:#9aa0a6; font-weight:500; font-size:12px; margin-left:auto; }\n  .filters { display:flex; gap:6px; margin:0 0 14px; }\n  .filters button{ all:unset; cursor:pointer; font-size:12px; font-weight:600; color:#6b7280; padding:6px 12px; border-radius:999px; border:1px solid #e5e7eb; background:#fff; }\n  .filters button.on{ background:#141450; color:#fff; border-color:#141450; }\n  .post { display:flex; gap:14px; padding:12px; border-radius:14px; cursor:pointer; border:1px solid transparent; }\n  .post:hover { background:#fafbfd; border-color:#eceef3; }\n  .thumb { position:relative; width:74px; height:74px; flex:0 0 74px; border-radius:12px; background:#141450 center/cover no-repeat; overflow:hidden; }\n  .thumb .rk{ position:absolute; top:0; left:0; background:#141450; color:#fff; font-size:11px; font-weight:700; padding:2px 7px; border-bottom-right-radius:8px; }\n  .p-body { flex:1; min-width:0; }\n  .p-top { display:flex; align-items:center; gap:7px; margin-bottom:3px; }\n  .tag { font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:.03em; padding:2px 7px; border-radius:6px; background:#f0f1f5; color:#4b5563; }\n  .tag.ig{ background:#efe9fd; color:#6b3fd4; }\n  .score { margin-left:auto; font-weight:750; font-size:15px; }\n  .score small{ font-weight:500; color:#9aa0a6; font-size:10px; }\n  .cap { font-size:13px; font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; margin-bottom:6px; }\n  .mets { font-size:12px; color:#6b7280; display:flex; gap:10px; flex-wrap:wrap; margin-bottom:7px; }\n  .sbar { display:flex; height:6px; border-radius:999px; overflow:hidden; background:#eee; }\n  .sbar i{ display:block; height:100%; } .sbar .pos{ background:#25b36b; } .sbar .neu{ background:#cbd0d6; } .sbar .neg{ background:#e11d48; }\n  .insight .resumo{ font-size:13.5px; line-height:1.55; color:#22262b; }\n  .recs{ list-style:none; padding:0; margin:14px 0 0; display:flex; flex-direction:column; gap:9px; }\n  .recs li{ display:flex; gap:9px; font-size:13px; line-height:1.45; }\n  .recs li .n{ flex:0 0 20px; height:20px; border-radius:6px; background:#141450; color:#fff; font-size:11px; font-weight:700; display:flex; align-items:center; justify-content:center; }\n  .cats{ margin-top:16px; display:flex; flex-direction:column; gap:9px; }\n  .cat{ font-size:12px; } .cat .cl2{ display:flex; justify-content:space-between; margin-bottom:3px; font-weight:600; }\n  .cat .track{ height:8px; background:#eef0f4; border-radius:999px; overflow:hidden; } .cat .fill{ height:100%; background:linear-gradient(90deg,#141450,#4a4ad6); border-radius:999px; }\n  .btn{ all:unset; cursor:pointer; background:#141450; color:#fff; font-size:12.5px; font-weight:600; padding:9px 14px; border-radius:10px; display:inline-flex; align-items:center; gap:7px; }\n  .btn.ghost{ background:#fff; color:#141450; border:1px solid #d8dae4; }\n  .btn:disabled{ opacity:.55; cursor:default; }\n  .alert{ display:flex; gap:11px; padding:12px; border-radius:12px; border:1px solid #f0d9dd; background:#fdf5f6; cursor:pointer; margin-bottom:10px; }\n  .alert.media{ border-color:#f3e6c8; background:#fdfaf1; }\n  .alert .sev{ width:9px; height:9px; border-radius:50%; background:#e11d48; margin-top:5px; flex:0 0 9px; } .alert.media .sev{ background:#f59e0b; }\n  .alert .msg{ font-size:12.5px; line-height:1.45; color:#3a2026; } .alert.media .msg{ color:#3a3020; }\n  .alert .mt{ font-size:11px; color:#9aa0a6; margin-top:4px; }\n  .ov{ position:absolute; inset:0; background:rgba(13,13,43,.45); display:flex; justify-content:flex-end; z-index:20; }\n  .draw{ width:min(680px,94%); height:100%; background:#f4f5f8; overflow:auto; box-shadow:-20px 0 60px rgba(0,0,0,.2); }\n  .draw .dh{ position:sticky; top:0; background:#141450; color:#fff; padding:16px 22px; display:flex; align-items:center; gap:12px; z-index:2; }\n  .draw .dh .x{ all:unset; cursor:pointer; margin-left:auto; font-size:20px; line-height:1; padding:4px 10px; border-radius:8px; } .draw .dh .x:hover{ background:#252580; }\n  .draw .db{ padding:22px; }\n  .hero{ display:flex; gap:16px; margin-bottom:18px; }\n  .hero .thumb{ width:120px; height:120px; flex:0 0 120px; border-radius:16px; }\n  .hero .cap2{ font-size:14px; font-weight:500; line-height:1.45; margin:6px 0 8px; max-height:150px; overflow:auto; white-space:pre-wrap; }\n  .mgrid{ display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-bottom:18px; }\n  .m{ background:#fff; border:1px solid #e9eaef; border-radius:12px; padding:11px 12px; } .m .l{ font-size:11px; color:#6b7280; font-weight:600; } .m .v{ font-size:18px; font-weight:750; margin-top:2px; }\n  .sent-big{ display:flex; gap:10px; margin-bottom:8px; }\n  .sent-big .b{ flex:1; text-align:center; border-radius:12px; padding:12px; color:#fff; font-weight:700; }\n  .sent-big .b span{ display:block; font-size:11px; font-weight:600; opacity:.9; margin-top:2px; }\n  .sent-big .pos{ background:#25b36b; } .sent-big .neu{ background:#94a3b8; } .sent-big .neg{ background:#e11d48; }\n  .cmt{ background:#fff; border:1px solid #e9eaef; border-radius:12px; padding:13px 14px; margin-bottom:10px; }\n  .cmt .ct{ display:flex; align-items:center; gap:8px; margin-bottom:5px; } .cmt .au{ font-weight:700; font-size:13px; }\n  .stag{ font-size:10px; font-weight:700; text-transform:uppercase; padding:2px 7px; border-radius:6px; }\n  .stag.positivo{ background:#e4f6ec; color:#25b36b; } .stag.negativo{ background:#fbe7ea; color:#e11d48; } .stag.neutro{ background:#eef0f4; color:#64748b; }\n  .cmt .tx{ font-size:13.5px; line-height:1.5; white-space:pre-wrap; } .cmt .cf{ display:flex; align-items:center; gap:10px; margin-top:9px; }\n  .cmt .lk{ font-size:12px; color:#9aa0a6; }\n  .reply{ margin-top:10px; background:#f4fbf7; border:1px solid #d3ebde; border-left:3px solid #25b36b; border-radius:10px; padding:11px 12px; font-size:13px; line-height:1.5; }\n  .reply .rl{ font-size:10.5px; font-weight:700; text-transform:uppercase; color:#25b36b; margin-bottom:4px; letter-spacing:.03em; display:flex; align-items:center; gap:6px; }\n  .conectar .grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:14px; }\n  .cc{ background:#fff; border:1px solid #e9eaef; border-radius:16px; padding:18px; display:flex; flex-direction:column; gap:10px; }\n  .cc .ico{ width:44px; height:44px; border-radius:12px; display:flex; align-items:center; justify-content:center; font-size:22px; font-weight:700; color:#3b4a6b; }\n  .cc .st{ font-size:12px; font-weight:700; padding:3px 9px; border-radius:999px; align-self:flex-start; }\n  .cc .st.on{ background:#e4f6ec; color:#25b36b; } .cc .st.off{ background:#f0f1f5; color:#9aa0a6; }\n  .plans{ display:grid; grid-template-columns:repeat(auto-fill,minmax(280px,1fr)); gap:14px; }\n  .cols3{ display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:16px; align-items:start; }\n  .cols3 > div { min-width:0; }\n  @media (max-width:1100px){ .cols3{ grid-template-columns:1fr !important; } }\n  .cu-item{ padding:10px 0; border-bottom:1px solid #f0f1f5; } .cu-item:last-child{ border-bottom:none; }\n  .cu-top{ display:flex; justify-content:space-between; align-items:center; margin-bottom:4px; gap:8px; } .cu-top .au{ font-weight:700; font-size:13.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n  .callout{ background:#141450; color:#fff; border-radius:14px; padding:14px 18px; font-size:14px; font-weight:600; margin-bottom:18px; }\n  .loading{ display:flex; height:100%; width:100%; align-items:center; justify-content:center; color:#6b7280; font-size:14px; }\n  .spin{ width:15px;height:15px;border:2px solid #ffffff55;border-top-color:#fff;border-radius:50%;display:inline-block;animation:sp .7s linear infinite; }\n  .btn.ghost .spin{ border:2px solid #14145033; border-top-color:#141450; }\n  @keyframes sp{ to{ transform:rotate(360deg);} }\n  `,\n  render(d2){ return `<div class=\"wrap\">${view(d2)}</div>`; },\n  mounted(d2, root){\n    const S = () => d2.store('scr-ui');\n    const rerender = () => { const w = d2.$('.wrap'); if (w) w.innerHTML = view(d2); };\n    const load = async () => {\n      try { const res = await d2.rush('scr-dados').invoke({}); S().set('data', (res && res.data) ? res.data : res); }\n      catch (e) { d2.log('load err', e); }\n      rerender();\n    };\n    load();\n    const pick = (e, sel) => (e && e.target && e.target.closest) ? e.target.closest(sel) : null;\n    d2.on('.sair', 'click', () => { try { localStorage.removeItem('__data2-session-people-gabi-carmo'); } catch (err) {} window.location.replace('/'); });\n    d2.on('.nv', 'click', (e) => { const el = pick(e, '.nv'); if (!el) return; S().set('tab', el.dataset.tab); S().set('sel', null); rerender(); });\n    d2.on('.flt', 'click', (e) => { const el = pick(e, '.flt'); if (!el) return; S().set('per', el.dataset.per); rerender(); });\n    d2.on('.op', 'click', (e) => { const el = pick(e, '.op'); if (!el) return; S().set('sel', el.dataset.post); rerender(); });\n    d2.on('.cl', 'click', () => { S().set('sel', null); rerender(); });\n    d2.on('.ov', 'click', (e) => { if (e.target && e.target.classList && e.target.classList.contains('ov')) { S().set('sel', null); rerender(); } });\n    d2.on('.mk', 'click', (e) => { const el = pick(e, '.mk'); if (!el) return; d2.dialog.alert('Na POC, ' + el.dataset.net + ' entra pelo mesmo fluxo do Instagram — basta plugar as credenciais do Estímulo.'); });\n    d2.on('.rgi', 'click', async () => {\n      S().set('insightLoading', true); rerender();\n      try { const res = await d2.rush('scr-gerar-insight').invoke({}); S().set('insight', (res && res.data) ? res.data.insight : null); }\n      catch (err) { d2.dialog.alert('Erro ao gerar insight: ' + ((err && err.message) || err)); }\n      S().set('insightLoading', false); rerender();\n    });\n    d2.on('.gsp', 'click', async () => {\n      S().set('planLoading', true); rerender();\n      try {\n        await d2.rush('scr-planejar-posts').invoke({});\n        const res = await d2.rush('scr-dados').invoke({});\n        S().set('data', (res && res.data) ? res.data : res);\n      } catch (err) { d2.dialog.alert('Erro ao gerar sugestões: ' + ((err && err.message) || err)); }\n      S().set('planLoading', false); rerender();\n    });\n    d2.on('.gr', 'click', async (e) => {\n      const el = pick(e, '.gr'); if (!el) return;\n      const cid = el.dataset.cid;\n      const D = S().get('data');\n      const c = ((D && D.comentarios) || []).find(x => x.comentarioId === cid);\n      if (!c) return;\n      S().set('replyLoading', Object.assign({}, S().get('replyLoading'), { [cid]: true })); rerender();\n      try {\n        const res = await d2.rush('scr-sugerir-resposta').invoke({ texto: c.texto, autor: c.autor, sentimento: c.sentimento });\n        const r = (res && res.data) ? res.data.resposta : '';\n        S().set('replies', Object.assign({}, S().get('replies'), { [cid]: r }));\n      } catch (err) { S().set('replies', Object.assign({}, S().get('replies'), { [cid]: 'Erro ao gerar resposta.' })); }\n      S().set('replyLoading', Object.assign({}, S().get('replyLoading'), { [cid]: false })); rerender();\n    });\n  }\n}"}}}}}}}},{"_id":"6aad9d974f22c65a0c67cbc3","component":"data2-core-types@Component","system":"69a704a047c6ef5f14b2dd47","params":{"extends":"data2-core-primitives@Block","component":"scr-login","package":"people-gabi-carmo","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Tela de login do Social CRM · Estímulo (POC gated).\nconst AVATAR = 'https://people-gabi-carmo.data2.media/file/Dfiyxvk5W9HQEY3hg5NYi';\nconst LOGIN_URL = 'https://people-gabi-carmo.data2apis.com/id/ln/scr-auth';\nconst SESSION_KEY = '__data2-session-people-gabi-carmo';\n\nexport default {\n  css: `\n  :host, .wrap { font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }\n  * { box-sizing: border-box; }\n  .wrap { display:flex; align-items:center; justify-content:center; height:100%; min-height:100%; padding:24px;\n          background:radial-gradient(1200px 600px at 20% -10%, #2a2ab0 0%, #141450 45%, #0c0c33 100%); color:#fff; }\n  .card { width:100%; max-width:400px; background:#fff; color:#0d0d2b; border-radius:20px; padding:30px 28px;\n          box-shadow:0 30px 70px rgba(0,0,0,.35); }\n  .top { display:flex; align-items:center; gap:12px; margin-bottom:22px; }\n  .top .logo { width:44px; height:44px; border-radius:50%; overflow:hidden; flex:0 0 44px; background:#f0f1f5; }\n  .top .logo img { width:100%; height:100%; object-fit:cover; }\n  .top b { font-size:16px; display:block; line-height:1.15; }\n  .top small { color:#6b7280; font-size:12px; }\n  label { display:block; font-size:12px; font-weight:700; color:#4b5563; margin:0 0 6px; }\n  input { width:100%; font-size:14px; font-family:inherit; color:#0d0d2b; background:#fff; padding:11px 13px;\n          border:1px solid #d8dae4; border-radius:11px; outline:none; margin-bottom:14px; }\n  input:focus { border-color:#4a4ad6; box-shadow:0 0 0 3px rgba(74,74,214,.15); }\n  button { all:unset; box-sizing:border-box; cursor:pointer; width:100%; text-align:center; background:#141450; color:#fff;\n           font-size:14px; font-weight:700; padding:12px 16px; border-radius:11px; margin-top:4px; }\n  button:disabled { opacity:.6; cursor:default; }\n  .err { background:#fbe7ea; color:#b91c3c; border-radius:10px; padding:9px 11px; font-size:12.5px; margin-bottom:12px; }\n  .hint { margin-top:16px; font-size:11.5px; color:#9aa0a6; line-height:1.5; text-align:center; }\n  .ok { text-align:center; font-size:14px; color:#25b36b; font-weight:600; }\n  .spin { width:14px;height:14px;border:2px solid #ffffff55;border-top-color:#fff;border-radius:50%;\n          display:inline-block;animation:sp .7s linear infinite;vertical-align:-2px;margin-right:6px; }\n  @keyframes sp{ to{ transform:rotate(360deg);} }\n  `,\n  render(d2){\n    const auth = d2.auth(d2.param('auth'));\n    const user = auth && auth.user;\n    if (user && user.email) {\n      return `<div class=\"wrap\"><div class=\"card\"><div class=\"ok\">Sessão ativa como ${user.email} — redirecionando…</div></div></div>`;\n    }\n    const erro = d2.state.erro;\n    const busy = d2.state.busy;\n    return `<div class=\"wrap\"><div class=\"card\">\n      <div class=\"top\">\n        <div class=\"logo\"><img src=\"${AVATAR}\" alt=\"\"/></div>\n        <div><b>Social CRM · Estímulo</b><small>Acesso restrito — POC Data²</small></div>\n      </div>\n      ${erro ? `<div class=\"err\">${erro}</div>` : ''}\n      <label for=\"em\">E-mail</label>\n      <input id=\"em\" type=\"email\" autocomplete=\"username\" placeholder=\"voce@estimulo.org\"/>\n      <label for=\"pw\">Senha</label>\n      <input id=\"pw\" type=\"password\" autocomplete=\"current-password\" placeholder=\"••••••••\"/>\n      <button class=\"go\" ${busy?'disabled':''}>${busy?'<span class=\"spin\"></span>Entrando…':'Entrar'}</button>\n      <div class=\"hint\">Os dados desta POC incluem comentários públicos de terceiros e por isso o acesso é autenticado.</div>\n    </div></div>`;\n  },\n  mounted(d2, root){\n    const entrar = async () => {\n      if (d2.state.busy) return;\n      const em = d2.$('#em'), pw = d2.$('#pw');\n      const email = (em && em.value || '').trim();\n      const password = (pw && pw.value || '');\n      if (!email || !password) { d2.state.erro = 'Preencha e-mail e senha.'; return; }\n      d2.state.erro = ''; d2.state.busy = true;\n      try {\n        // O wrapper do SDK monta o corpo sem `credentials` e o /id/ln devolve 400,\n        // então postamos direto no endpoint da estratégia e gravamos o token de sessão\n        // na mesma chave de localStorage que o player lê.\n        const atual = localStorage.getItem(SESSION_KEY);\n        const r = await fetch(LOGIN_URL, {\n          method: 'POST',\n          credentials: 'include',\n          headers: Object.assign(\n            { 'content-type': 'application/json' },\n            atual ? { authorization: 'Bearer ' + atual } : {}\n          ),\n          body: JSON.stringify({\n            authenticationStrategyReference: 'scr-auth',\n            credentials: { email, password }\n          })\n        });\n        if (!r.ok) throw new Error(String(r.status));\n        const j = await r.json();\n        if (!j || !j.token) throw new Error('sem token');\n        localStorage.setItem(SESSION_KEY, j.token);\n        window.location.replace('/');\n      } catch (e) {\n        d2.state.busy = false;\n        d2.state.erro = 'E-mail ou senha inválidos.';\n      }\n    };\n    d2.on('.go', 'click', entrar);\n    d2.on('#pw', 'keydown', (e) => { if (e && e.key === 'Enter') entrar(); });\n    d2.on('#em', 'keydown', (e) => { if (e && e.key === 'Enter') { const pw = d2.$('#pw'); if (pw) pw.focus(); } });\n  }\n}"}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}}}}}}}},{"_id":"5ed9bed7b9e0891ce913e884","component":"data2-core-types@Component","params":{"package":"data2-core","component":"DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}},{"_id":"5f472ae463c680de4ff296e9","component":"data2-core-types@Component","params":{"package":"data2-core","component":"Data2TableDataSource","extends":"data2-core@DataSource","renderable":{"dimensions":{"disabled":false,"component":"data2-core-types@Dimensions","params":{"width":1366,"exactWidth":false,"exactHeight":false,"height":768}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a3c0488714f8f149eb75ea3","component":"data2-rush@HTTPListener","reference":"arezzo-track","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a3c1e4b284331618318810b","component":"data2-rush@HTTPListener","reference":"arezzo-lead","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a398b3c0b5619f162ec53f9","component":"data2-rush@HTTPListener","reference":"arezzo-products","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a39956990d1934c02e3cfb2","component":"data2-rush@HTTPListener","reference":"arezzo-photo-check","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a3989bda8bfa43cfb3de74a","component":"data2-rush@HTTPListener","reference":"arezzo-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a3c0aad1d4eb2815c947458","component":"data2-rush@HTTPListener","reference":"arezzo-stats","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a3c2be11ad89b4af9e903e7","component":"data2-rush@HTTPListener","reference":"contato-landing","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a3c2e281d4eb2815c950177","component":"data2-rush@HTTPListener","reference":"leads-list","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a427991b8c8927d7b5ebf65","component":"data2-rush@HTTPListener","reference":"agro-tar-list","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a42798ccd977ef7f94ec817","component":"data2-rush@HTTPListener","reference":"agro-func-list","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a42799ffd3c05f659593470","component":"data2-rush@HTTPListener","reference":"agro-tar-del","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a42799db8c8927d7b5ebf9e","component":"data2-rush@HTTPListener","reference":"agro-tar-save","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a42798f5de232c667b5aadd","component":"data2-rush@HTTPListener","reference":"agro-func-save","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a4279a2cd977ef7f94ec8ba","component":"data2-rush@HTTPListener","reference":"agro-tar-get","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a4279a6097719b83c25311e","component":"data2-rush@HTTPListener","reference":"agro-tar-status","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a4695a5d17c9b3315ac82db","component":"data2-rush@HTTPListener","reference":"vivara-products","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a469616d17c9b3315ac84ec","component":"data2-rush@HTTPListener","reference":"vivara-photo-check","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a46960fc53088741c52d419","component":"data2-rush@HTTPListener","reference":"vivara-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce2ec6a63234417fc8b9","component":"data2-rush@HTTPListener","reference":"nasp-data","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fcf49d7643e7690f0a4ed","component":"data2-core-ai@Assistant","reference":"nasp-nf-reader","system":"69a704a047c6ef5f14b2dd47","params":{"model":"cerebras","_context":"Você extrai itens de notas fiscais brasileiras a partir de fotos. Retorne APENAS JSON válido no formato {\"itens\":[{\"codigo_sap\":\"\",\"descricao\":\"\",\"quantidade\":0}]}. Se o código SAP não estiver visível na nota, deixe codigo_sap como string vazia e preencha a descricao. Quantidades sempre numéricas.","behaviour":"Responda somente com JSON válido, sem markdown, sem explicações, sem prefixos.","json":true,"answerPrefix":false}},{"_id":"6a5fce42f442f0ec308bed25","component":"data2-rush@HTTPListener","reference":"nasp-mov","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce29f687a4f28da01184","component":"data2-rush@HTTPListener","reference":"nasp-login","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce54f442f0ec308bee6e","component":"data2-rush@HTTPListener","reference":"nasp-export-databricks","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce4ca6a582b2aa1a18d0","component":"data2-rush@HTTPListener","reference":"nasp-ajuste","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce4fa6a582b2aa1a18fb","component":"data2-rush@HTTPListener","reference":"nasp-coupa-po","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce46c6a63234417fc94a","component":"data2-rush@HTTPListener","reference":"nasp-pre-retirada","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce49d7643e7690f09f59","component":"data2-rush@HTTPListener","reference":"nasp-regularizar","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a5fce61e94b8d0f69515da3","component":"data2-rush@HTTPListener","reference":"nasp-admin","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a71f23249d5d1309404433d","component":"data2-rush@HTTPListener","reference":"lelis-track","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a71f22710437424105b792e","component":"data2-rush@HTTPListener","reference":"lelis-products","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a74dd48b56e0bf3babb9e07","component":"data2-rush@HTTPListener","reference":"lelis-my-tryons","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a71f22fe3e23473fe6d60cd","component":"data2-rush@HTTPListener","reference":"lelis-photo-check","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a74fb9d9f1ece2343b43843","component":"data2-rush@HTTPListener","reference":"lelis-stylist","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a71f22beedd35ec5f2547eb","component":"data2-rush@HTTPListener","reference":"lelis-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a74dd3ff183062aef264589","component":"data2-rush@HTTPListener","reference":"lelis-save-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a74dd5394661ca7af2f367a","component":"data2-rush@HTTPListener","reference":"lelis-delete-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a71f237d074528343bca57f","component":"data2-rush@HTTPListener","reference":"lelis-lead","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7201d7034c99fe6b934d7d","component":"data2-rush@HTTPListener","reference":"lelis-stats","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a76357659c18a7c2a9a9e7c","component":"data2-rush@HTTPListener","reference":"veste-track","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a76357abca296db36c62b5f","component":"data2-rush@HTTPListener","reference":"veste-acessos","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1aa43f9e9defad370378","component":"data2-rush@HTTPListener","reference":"dressto-track","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1a8dc2af0d881104b2c7","component":"data2-rush@HTTPListener","reference":"dressto-products","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1ab33f9e9defad3703b9","component":"data2-rush@HTTPListener","reference":"dressto-my-tryons","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1a99a0a162269e7eb1dc","component":"data2-rush@HTTPListener","reference":"dressto-photo-check","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1a9fc2af0d881104b2f2","component":"data2-rush@HTTPListener","reference":"dressto-stylist","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1a923f9e9defad37034c","component":"data2-rush@HTTPListener","reference":"dressto-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1aad0c3e9170c50abf9f","component":"data2-rush@HTTPListener","reference":"dressto-save-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1aba7f8b5ad6c2a161d8","component":"data2-rush@HTTPListener","reference":"dressto-delete-tryon","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1aa8250a72cb55c96f42","component":"data2-rush@HTTPListener","reference":"dressto-lead","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1abffa877bd209c7b7c7","component":"data2-rush@HTTPListener","reference":"dressto-prop-track","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6a7a1ac46a616cb8d4798f19","component":"data2-rush@HTTPListener","reference":"dressto-prop-acessos","system":"69a704a047c6ef5f14b2dd47","params":{}},{"_id":"6aad8fa2200679cd58b19c83","component":"data2-rush@HTTPListener","reference":"scr-dados","system":"69a704a047c6ef5f14b2dd47","params":{},"deleteMetadata":{}},{"_id":"6aad901c2086c2e4340bcf00","component":"data2-rush@HTTPListener","reference":"scr-gerar-insight","system":"69a704a047c6ef5f14b2dd47","params":{},"deleteMetadata":{}},{"_id":"6aad99cd1f4ee81c56f51abd","component":"data2-rush@HTTPListener","reference":"scr-planejar-posts","system":"69a704a047c6ef5f14b2dd47","params":{},"deleteMetadata":{}},{"_id":"6aad8fb7850d506c9bd49937","component":"data2-rush@HTTPListener","reference":"scr-sugerir-resposta","system":"69a704a047c6ef5f14b2dd47","params":{},"deleteMetadata":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a3ea780dbb7889ef4de91c1","component":"data2-core-primitives@Router","system":"69a704a047c6ef5f14b2dd47","displayName":"CRM Router","params":{"routerType":{"component":"data2-core-primitives@String","params":{"content":"browser"}},"routes":[{"component":"data2-core-primitives@Route","params":{"path":"","name":"CRM de Prospects","content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"m2DxuebWBaf2YK8cDiKZ8"}},"policies":[{"component":"data2-core-types@PolicyReference","params":{"authenticationStrategy":{"reference":"crmAuth"}}}]}},{"component":"data2-core-primitives@Route","params":{"path":"/login","name":"Entrar","content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"ZxVuW25CFl0HRNZScF7UU"}}}}],"login":{"component":"data2-core-types@ArtboardReference","params":{"reference":"ZxVuW25CFl0HRNZScF7UU"}}},"reference":"MainRouter"},{"_id":"6aad9da89b443687a1c64075","component":"data2-core-primitives@Router","system":"69a704a047c6ef5f14b2dd47","displayName":"scr_router","params":{"routerType":{"component":"data2-core-primitives@String","params":{"content":"browser"}},"routes":[{"component":"data2-core-primitives@Route","params":{"path":"","name":"Social CRM · Estímulo","content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"0v-IzaX4jmss2OHrw-VSw"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"scr-auth"}}}}]}}],"login":{"component":"data2-core-types@ArtboardReference","params":{"reference":"Aa4ulh1aTLUhcDx3B3LHn"}}},"reference":"scr-router","deleteMetadata":{}},{"_id":"6a3ea76b9936088eede46a6d","component":"data2-core-primitives@Artboard","system":"69a704a047c6ef5f14b2dd47","displayName":"Login CRM","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Login CRM"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"ZxVuW25CFl0HRNZScF7UU"},{"_id":"6a3ea76aa989393a6a12e4c9","component":"data2-core-primitives@Artboard","system":"69a704a047c6ef5f14b2dd47","displayName":"Painel CRM","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Painel CRM"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"m2DxuebWBaf2YK8cDiKZ8"},{"_id":"6aad9d991f4ee81c56f521b9","component":"data2-core-primitives@Artboard","system":"69a704a047c6ef5f14b2dd47","displayName":"Login Estímulo","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Login Estímulo"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"Aa4ulh1aTLUhcDx3B3LHn","deleteMetadata":{}},{"_id":"6aad9aa001be65c4a08b565c","component":"data2-core-primitives@Artboard","system":"69a704a047c6ef5f14b2dd47","displayName":"Social CRM Estímulo","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Social CRM Estímulo"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"0v-IzaX4jmss2OHrw-VSw","deleteMetadata":{}},{"_id":"9ad34e5d5b2204ea41c6cfa7","parent":"6a3ea76b9936088eede46a6d","component":"people-gabi-carmo@loginScreen","system":"69a704a047c6ef5f14b2dd47","displayName":"Login Screen","params":{"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"top":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}}}},"reference":"cBTpwwFe0XbHmLNZAugiu"},{"_id":"349c7dc902905da89a98912e","parent":"6a3ea76aa989393a6a12e4c9","component":"people-gabi-carmo@crmApp","system":"69a704a047c6ef5f14b2dd47","displayName":"CRM App","params":{"dataset":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"prospectsDataSet"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"top":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}}}},"reference":"6l2rvRT7NzBfjHKH5dC6r"},{"_id":"2cc39ce4a2c742c40c54891a","parent":"6aad9d991f4ee81c56f521b9","component":"people-gabi-carmo@scr-login","system":"69a704a047c6ef5f14b2dd47","displayName":"Login","params":{"auth":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"scr-auth"}},"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":1440,"height":900}}}},"reference":"NwRDiedemvdntsPQ050p3","deleteMetadata":{}},{"_id":"e13ad70f1d9b9c97ef1ff699","parent":"6aad9aa001be65c4a08b565c","component":"people-gabi-carmo@scr-app","system":"69a704a047c6ef5f14b2dd47","displayName":"CRM App","params":{"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":1440,"height":900}}}},"reference":"3vHX26JjExJK0BN8_i1x4","deleteMetadata":{}}]}},{"_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":"6aad9aca4ef0bfdfc9a64740","displayName":"Social CRM Estímulo","reference":"TriGIxUAvz7hwnPbol2PZ","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"social-crm"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Router","reference":"scr-router"}}},"system":"69a704a047c6ef5f14b2dd47","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a3ea619e44ca0e8495cbcf1","component":"data2-core@AuthenticationStrategy","system":"69a704a047c6ef5f14b2dd47","params":{"credentials":[{"key":"email"},{"key":"password"}]},"reference":"crmAuth"},{"_id":"6aad8f1f53f9b2dd51bcaddb","component":"data2-core@Store","system":"69a704a047c6ef5f14b2dd47","params":{"initialData":{"tab":"dashboard","plat":"todas","per":"365","sel":null}},"reference":"scr-ui","deleteMetadata":{}},{"_id":"6aad9f3c850d506c9bd4bb37","component":"data2-core@AuthenticationStrategy","system":"69a704a047c6ef5f14b2dd47","params":{"credentials":[{"key":"email"},{"key":"password"}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"scr-acessoDataSet"}},"email":"email","password":"password","role":"role","roleDefault":"membro"},"reference":"scr-auth","deleteMetadata":{}},{"_id":"6aad9f0b850d506c9bd4ba9a","component":"data2-core@DataSet","system":"69a704a047c6ef5f14b2dd47","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"scr-acesso"}}},"reference":"scr-acessoDataSet","deleteMetadata":{}},{"_id":"6aad9f0b8896e6d9b55b48ca","component":"data2-core@Data2TableDataSource","system":"69a704a047c6ef5f14b2dd47","params":{"table":"scr-acesso","schema":[{"component":"data2-core-types@Field","params":{"key":"email","title":"E-mail","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"password","title":"Senha","component":"data2-core-types@Password"}},{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"role","title":"Papel","component":"data2-core-primitives@String"}}]},"reference":"scr-acesso","deleteMetadata":{}},{"_id":"6a3ea602913fdfe1432d744e","component":"data2-core@DataSet","system":"69a704a047c6ef5f14b2dd47","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"prospects"}}},"reference":"prospectsDataSet"},{"_id":"6a3ea602e44ca0e8495cbc61","component":"data2-core@Data2TableDataSource","system":"69a704a047c6ef5f14b2dd47","params":{"table":"prospects","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":"empresa","title":"Empresa","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"cargo","title":"Cargo","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"setor","title":"Setor","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"linkedinUrl","title":"LinkedIn","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"email","title":"E-mail","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"origem","title":"Origem","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"motivoAbordagem","title":"Motivo da abordagem","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mensagemRascunho","title":"Rascunho mensagem","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"stage","title":"Etapa","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"statusResposta","title":"Status resposta","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"dataMensagem","title":"Data mensagem","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"dataReuniao","title":"Data reunião","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"acordadoPosReuniao","title":"Acordado pós-reunião","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"dataFup","title":"Data follow-up","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"dataDemo","title":"Data demo","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"dataFechamento","title":"Data fechamento","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"valorDeal","title":"Valor do deal","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"notas","title":"Notas","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"ownerId","title":"Owner","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Criado em","component":"data2-core-primitives@Date"}}]},"reference":"prospects"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}