{"context":{"domain":"veste-app"},"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":"698a0c028a310c894be98bc8","name":"app","domain":"veste-app","visibility":"public","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T20:00:20.536Z","createdAt":"2026-02-09T16:32:02.402Z"}]}},{"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":"6a99849b7848d0950257e0a0","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"johnjohn-deck-proposta","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"/* ───────────────────────────────────────────────────────────────────────────\n   Deck — Proposta de formalização · Veste S/A · John John\n   Provador virtual: POC e teste de 30 dias\n   Kit: apresentacao-data2 · arco de formalização · 11 slides\n   ─────────────────────────────────────────────────────────────────────────── */\n\n/* Assets no Drive do system veste-app */\nconst SYS   = 'veste-app'\nconst F_EXT = `https://${SYS}.data2.media/file/iNSYotTKA1cv_fJAa1K4E`\nconst F_LT  = `https://${SYS}.data2.media/file/6bERTVll1aRDU3Xo9NghW`\nconst LD2   = `https://${SYS}.data2.media/file/WLXAyS4HOHvTFytXu7S4P`\nconst RING  = `https://${SYS}.data2.media/file/TA2eFNVhnB7dlI9EPAEWb`\n\n/* Link da POC do John John — entra quando a demo do Gui estiver no ar */\nconst DEMO_URL = ''\n\nconst FONT_CSS = `@font-face{font-family:'AkzExt';src:url(${F_EXT}) format('opentype');font-weight:400;font-display:block}@font-face{font-family:'AkzExtLight';src:url(${F_LT}) format('opentype');font-weight:300;font-display:block}`\n\nconst PASS = 'johnjohn2026'\nconst OPEN = false\n\n/* Wordmark oficial John John — SVG do site, com currentColor para servir\n   claro sobre o --paper e branco sobre o fundo escuro */\nconst JJ_PATHS = `<path d=\"M1056.61,220.1c5.67,0,10.35-.71,14.02-2.13,3.67-1.41,6.34-3.71,8.01-6.88,1.67-3.17,2.5-7.59,2.5-13.27v-107.42h45.32v105.92c0,15.36-2.13,27.75-6.38,37.18-4.26,9.43-10.6,16.28-19.03,20.53-8.43,4.26-19.41,6.39-32.93,6.39-6.68,0-13.06-.42-19.15-1.25-6.09-.84-10.89-1.58-14.4-2.25v-38.06l1-1c7.18,1.67,14.19,2.42,21.03,2.25Z\"/><path d=\"M1151.76,126.95c6.84-12.93,16.19-22.49,28.04-28.67,11.85-6.17,25.37-9.26,40.56-9.26s29.46,3.09,41.31,9.26c11.85,6.18,21.11,15.74,27.79,28.67,6.68,12.94,10.02,29.26,10.02,48.95s-3.05,34.39-9.14,47.07c-6.09,12.69-15.02,22.33-26.79,28.92-11.77,6.6-26.17,9.89-43.19,9.89s-30.88-3.34-42.57-10.02c-11.69-6.68-20.66-16.4-26.92-29.17-6.26-12.77-9.39-28.33-9.39-46.7,0-19.7,3.42-36.01,10.27-48.95ZM1190.2,205.2c2.42,5.51,6.13,9.68,11.14,12.52,5.01,2.84,11.35,4.26,19.03,4.26s14.02-1.46,19.03-4.38c5.01-2.92,8.72-7.09,11.14-12.52,2.42-5.42,3.63-12.31,3.63-20.66v-18.53c0-7.84-1.17-14.48-3.51-19.91-2.34-5.42-6.01-9.64-11.02-12.65s-11.44-4.51-19.28-4.51-13.86,1.5-19.03,4.51c-5.18,3-8.93,7.22-11.27,12.65-2.34,5.43-3.51,12.06-3.51,19.91v18.53c0,8.35,1.21,15.27,3.63,20.78Z\"/><polygon points=\"1414.67 91.77 1414.67 155.37 1360.33 155.37 1360.33 91.77 1316.01 91.77 1316.01 259.03 1360.33 259.03 1360.33 194.18 1414.67 194.18 1414.67 259.03 1458.98 259.03 1458.98 91.77 1414.67 91.77\"/><path d=\"M1480.02,91.77h48.58l58.09,101.19-.33-101.19h44.65v167.26h-44.65l-62.01-101.19.25,101.19h-44.57V91.77Z\"/><path d=\"M22.03,220.1c5.67,0,10.35-.71,14.02-2.13,3.67-1.41,6.34-3.71,8.01-6.88,1.67-3.17,2.5-7.59,2.5-13.27v-107.42h45.32v105.92c0,15.36-2.13,27.75-6.38,37.18-4.26,9.43-10.6,16.28-19.03,20.53-8.43,4.26-19.41,6.39-32.93,6.39-6.68,0-13.06-.42-19.15-1.25-6.09-.84-10.89-1.58-14.4-2.25v-38.06l1-1c7.18,1.67,14.19,2.42,21.03,2.25Z\"/><path d=\"M117.18,126.95c6.84-12.93,16.19-22.49,28.04-28.67,11.85-6.17,25.37-9.26,40.56-9.26s29.46,3.09,41.31,9.26c11.85,6.18,21.11,15.74,27.79,28.67,6.68,12.94,10.02,29.26,10.02,48.95s-3.05,34.39-9.14,47.07c-6.09,12.69-15.02,22.33-26.79,28.92-11.77,6.6-26.17,9.89-43.19,9.89s-30.88-3.34-42.57-10.02c-11.69-6.68-20.66-16.4-26.92-29.17-6.26-12.77-9.39-28.33-9.39-46.7,0-19.7,3.42-36.01,10.27-48.95ZM155.61,205.2c2.42,5.51,6.13,9.68,11.14,12.52,5.01,2.84,11.35,4.26,19.03,4.26s14.02-1.46,19.03-4.38c5.01-2.92,8.72-7.09,11.14-12.52,2.42-5.42,3.63-12.31,3.63-20.66v-18.53c0-7.84-1.17-14.48-3.51-19.91-2.34-5.42-6.01-9.64-11.02-12.65s-11.44-4.51-19.28-4.51-13.86,1.5-19.03,4.51c-5.18,3-8.93,7.22-11.27,12.65-2.34,5.43-3.51,12.06-3.51,19.91v18.53c0,8.35,1.21,15.27,3.63,20.78Z\"/><polygon points=\"380.09 91.77 380.09 155.37 325.75 155.37 325.75 91.77 281.43 91.77 281.43 259.03 325.75 259.03 325.75 194.18 380.09 194.18 380.09 259.03 424.4 259.03 424.4 91.77 380.09 91.77\"/><path d=\"M445.43,91.77h48.58l58.09,101.19-.33-101.19h44.65v167.26h-44.65l-62.01-101.19.25,101.19h-44.57V91.77Z\"/><path stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M754.28,328.55s.03-.06.05-.09\"/><path d=\"M861.77,331.93c-.15.19-.3.37-.46.56l.35-.43s.04-.06.1-.13Z\"/><path d=\"M992.02,218.74c-5.26-30.97-31.79-51.75-63.22-50.07-2.98.16-4.23,1.05-5.08,3.97-4.62,15.73-22.92,21.84-36.11,12.22-9.68-7.05-13.05-19.96-7.7-29.48,2.57-4.59,7.34-6.7,12.24-5.43-2.99,5.07-3.26,7.61-1.05,10.64,2.15,2.94,5.1,4.37,8.7,3.96,3.78-.43,6.22-2.82,7.71-6.16,3.62-8.11-1.29-17.68-10.63-20.84-13.02-4.4-26.41,2.67-30.81,16.21-7.73,23.77,7.3,43.73,21.45,50.82,4.4,2.21,9.33,3.37,14.41,5.15-.13,1.87-.51,4.27-.4,6.64.21,4.46-.23,9.24,1.24,13.29,4.91,13.53,15.14,21.26,29.16,23.69,13.1,2.27,25.02-6.77,26.83-19.94.64-4.65-.34-9.02-3.33-12.69-4.24-5.22-9.8-7.76-16.55-7.15-2.94.26-5.85,1.54-6.36,4.68-.32,1.98.39,4.58,1.56,6.26,1.79,2.58,4.71,2.19,7.44,1.17.75-.28,1.53-.52,2.29-.77,2.36,7.59-2.25,13.12-9.78,12.03-4.11-.6-7.24-2.75-9.26-6.29-2.79-4.91-3.35-10.2-2.53-15.79,1.6-10.96,17.46-20.03,29.93-17.33,9.18,1.98,14.49,8.32,18.83,16.14,11.93,21.52-1.42,46.05-17.97,54.46-9.5,4.83-19.21,4.5-28.94,1.32-19.23-6.28-32.02-18.51-34.99-39.29-.6-4.19-.56-8.47-.82-12.86-10.55.27-18.08-4.61-23.87-12.89-3.48-4.97-7.37-9.64-10.97-14.53-6.56-8.92-13.04-17.9-19.58-26.88.63-.9,1.13-1.7,1.72-2.43,8.8-11.05,17.63-22.07,26.4-33.13,5.68-7.17,13.09-10.04,22.11-9.35,1.22.09,2.59-.38,3.72-.94,3.22-1.6,5.13-3.98,4.5-7.92-.2-1.28.2-2.7.52-4.02,4.17-17.16,25.55-30.79,43.06-27.48,6.78,1.28,9.34,4.57,9.4,11.5.03,2.93-.67,4.24-3.95,4.36-5.1.19-7.28,4.52-5.03,9.22,1.95,4.06,7.05,5.96,11.89,4.18,5.79-2.13,9.45-6.45,11.42-12.15,3.76-10.91.27-22.64-9.05-30.53-3.41-2.88-6.99-5.56-10.62-8.43,5.17-6.78,6.12-13.94,1.54-21.28-2.87-4.6-7.18-6.78-13.09-6.3,4.23,7.24,4.38,14.16-1,20.58-5.21,6.23-12.48,6.86-19.8,6.47,0,2.9.09,5.42-.05,7.92-.05.92-.37,2.28-1,2.63-3.62,1.96-6.74,4.75-11.21,5.3-2.72.33-5.57,1.92-7.78,3.67-6.31,4.99-9.66,11.85-10.19,19.77-.31,4.68-2.72,7.44-6.53,8.99-7.79,3.17-13.87,8.43-19.42,14.51-.98,1.07-2.1,2.01-3.16,3.01-.22-.16-.44-.33-.66-.49.74-1.92,1.47-3.85,2.22-5.76,3.1-7.91,5.91-15.94,6.35-24.49.17-3.28,1.43-4.83,4.25-6.32,7.91-4.19,14.36-9.88,15.99-19.33,1.01-5.88.41-11.72-1.62-17.31-.83-2.27-.2-3.54,1.72-4.55,2.03-1.07,4-2.3,6.11-3.17,1.71-.7,2.29-1.64,2.29-3.48,0-3.02.17-6.08.62-9.06,1.51-9.89,7.55-14.87,17.5-14.66,1.03.02,2.06,0,3.1,0-.8-5.19-4.71-8.83-9.93-9.37-6.17-.63-11.52,1.41-16.41,4.98-1.14.83-2.25,1.71-3.11,2.37-2.45-2.19-4.36-4.58-6.83-5.99-10.17-5.84-20.79-5.89-31.27-1.02-5.85,2.72-9.67,7.59-11.19,13.99-1.3,5.48,2.36,10.96,7.87,11.96,3.98.72,8.45-1.58,10.17-5.22,1.47-3.11,1-4.29-3.14-7.97-.05-.05,0-.19,0-1.12,2.5.73,5.04.95,7.01,2.13,9.63,5.77,15.02,18.67,12.67,30.26-2.54,12.53-10.78,20.94-20.86,27.88-1.86,1.29-3.81,2.45-5.61,3.59.1.55.11.76.17.95.24.69.49,1.38.74,2.07,3.45,9.46,3.11,18.66-1.87,27.54-1.84,3.28-3.62,6.62-5.67,9.76-4.27,6.54-8.7,12.98-13.7,20.41-6.76-11.1-13.12-21.4-19.32-31.8-3.66-6.13-4.15-12.86-2.83-19.74.6-3.13,1.58-6.19,2.3-8.94-4.15-3.1-8.32-5.76-11.97-9.01-10.95-9.76-17.57-21.24-13.47-36.64,1.83-6.87,5.41-12.43,11.5-16.18,2.34-1.44,4.87-2.46,8-1.44-5.3,3.78-5.78,7.02-1.87,10.78,3.35,3.22,7.65,3.76,11.49,1.43,4.1-2.48,5.95-7.04,4.39-11.65-2.72-8.04-8.29-13.35-16.54-15.21-12.36-2.78-23.75-1.36-32.33,9.37-1.09-.87-2.05-1.68-3.06-2.42-4.78-3.51-10.01-5.53-16.07-5.2-4.73.25-9.7,3.7-10.49,9.44,1.25,0,2.5,0,3.74,0,8.99-.06,14.87,4.63,16.7,13.43.36,1.73.85,3.58.6,5.28-.81,5.54,1.33,8.65,6.6,10.69,4.59,1.78,4.39,2.31,3.15,7.07-4.01,15.39-.51,25.01,12.27,33.58,1.38.93,3.08,1.45,4.32,2.52,1.03.9,2,2.24,2.31,3.54,1.59,6.69,2.74,13.48,4.49,20.12,1.15,4.39,3.06,8.58,4.62,12.86-1.64-.82-2.77-1.98-3.87-3.17-5.05-5.49-10.26-10.88-17.34-13.67-5.47-2.16-8.5-5.32-9.04-11.62-1.03-12.05-8.82-19.76-20.76-22.65-3.1-.75-5.86-3.07-8.64-4.88-.53-.35-.57-1.67-.59-2.55-.07-2.55-.03-5.11-.03-7.69-16.72,2.07-29.56-10.94-21.22-26.85-5.17-.72-9.35.99-12.47,5.83-4.76,7.37-3.68,14.66,1.24,21.61-3.43,2.7-6.81,5.15-9.95,7.88-5.56,4.85-9.75,10.52-10.77,18.18-1.41,10.6,2.94,20.58,11.34,24.43,2.05.94,4.68,1.18,6.95.93,4.19-.47,7.27-4.44,7.03-8.32-.22-3.64-2.08-5.02-7.25-5.71-.86-.12-2.11-.99-2.32-1.75-1.95-7.1,1.93-12.66,9.27-13.83,6.1-.98,11.9.3,17.56,2.19,15.61,5.23,25.95,16.55,26.21,32.61.01.75.29,1.8.82,2.17,2.56,1.82,4.75,4.19,8.6,3.46,2.46-.47,5.38-.19,7.7.74,3.61,1.46,7.71,3.04,10.15,5.84,10.69,12.31,20.85,25.07,31.05,37.5-.59,1.08-.75,1.46-.99,1.79-10.74,14.44-21.51,28.85-32.2,43.32-4.55,6.15-10.76,9.07-18.18,9.82-1.11.11-2.23.17-3.56.26,0,1.42,0,2.66,0,3.89.06,6.74-.65,13.38-3.19,19.69-6.33,15.73-18.6,24.53-34.37,29.04-7.36,2.11-14.83,2.57-22.24,0-25.79-8.96-35.35-41.98-18.31-63.26,5.31-6.63,12.08-10.05,20.72-9.73,14.83.56,25.2,11.57,23.86,25.49-.79,8.23-6.11,14.07-13.07,14.5-5.68.36-11.36-4.23-8.62-11.9,1.6.36,3.2.84,4.84,1.06,2.99.41,5.04-.98,6.25-3.66,1.88-4.16-.93-8.36-6.05-9.07-8.8-1.22-18.13,5.45-19.81,14.16-2.39,12.4,8.6,25.51,21.55,25.98,17.39.62,39.15-16.01,35.5-38.14-.35-2.12-.33-4.31-.47-6.39.64-.25.92-.44,1.21-.47,11.53-1.26,20.28-7.19,27.36-16.05,11.25-14.07,11.94-31.47,4.65-44.69-6.57-11.92-23.62-15.38-34-7.11-7.17,5.71-7.98,16.28-1.67,21.7,3.34,2.87,8.16,2.77,11.79-.25,3.36-2.79,3.88-6.65,1.45-10.66-.39-.64-.75-1.29-1.19-2.07,7.78-.27,11.92,2.63,14.12,9.66,3.27,10.44-2.51,22.75-13.14,28-9.87,4.87-22.67,1.73-28.75-7.41-2.15-3.23-3.49-7-5.53-11.21-5.32.31-11.21.16-16.94,1.07-20.91,3.3-35.77,14.99-44.7,34-7.96,16.93-6.91,34.16-.15,51.17,4.34,10.93,10.47,20.77,20.04,27.97,13.76,10.34,29.21,11.5,45.41,7.75,4.32-1,8.59-2.24,13.64-3.58-4.65,8.3-9.42,15.49-17.22,20.01-4.71,2.73-9.56,2.75-14.26-.27-1.66-1.06-3.28-2.18-4.9-3.26-5.98,5.37-6.43,10.71-1.02,16.63,2.06,2.25,2.48,3.85,1.02,6.69-3.84,7.49-1.75,15.6-2.25,23.33,3.7-2.66,6.94-5.5,10.63-7.52,4.21-2.3,8.81-3.87,13.98-6.07,9.25,4.54,13.83,4.05,19.87-2.89-6.47-4.48-11.46-10.06-11.05-18.49.38-7.79,3.8-14.41,8.89-20.24.21,3.03-.04,5.92-.14,8.82-.49,13.82,3.44,26.03,13.38,35.94,7.39,7.35,15.78,12.04,26.76,9.54,9.27-2.1,21.48-9.01,22.98-23.06.09-.88.41-1.88.96-2.55,6.88-8.43,2.72-19.11-3.69-23.85-2.27-1.68-4.55-3.36-6.7-5.18-2.8-2.36-4.76-5.25-4.81-9.22.43-.06.83-.19,1.22-.17,2.57.18,4.69-.35,4.97-3.4.21-2.26-1.77-4.29-4.61-4.67-3.6-.49-6.85.48-9.7,2.73-5.01,3.97-7.28,11.09-5.25,17.57,1.13,3.64,2.82,7.1,4.25,10.65-10.71,8.51-11.31,14.26-2.44,24.83,2.86-1.35,3.76-3.59,4.21-6.36.63-3.89,3.82-5.06,6.83-2.65,2.11,1.69,2.79,3.79,1.55,6.39-3.43,7.22-13.84,10.17-20.46,5.63-7.48-5.13-11.77-12.43-11.98-21.36-.52-21.65,8.87-39.16,24.11-53.83,3.58-3.45,8.13-5.64,13.28-5.69,3.75-.03,6.48-1.84,9.31-4.22-3.59-4.14-4.28-8.92-3.3-13.94.58-2.96,1.39-5.92,2.52-8.71,5.1-12.63,13-23.58,20.95-34.49,1.56-2.14,3.19-4.24,5-6.64,9.93,13.14,19.62,25.89,25.84,40.81,3.21,7.7,5.1,15.61-.88,23.66,2.84,1.25,5.23,3.13,7.71,3.23,8.06.34,14.26,4.14,19.3,9.84,10.17,11.51,17.17,24.76,19.59,40.03,1.79,11.32-.33,21.68-9.48,29.7-3.46,3.03-7.39,4.21-11.89,3.59-5.45-.75-10.51-4.76-11.16-8.79-.5-3.13,1.32-5.81,4.24-6.25,2.88-.43,4.26.72,4.65,4.41.3,2.8,1.64,4.55,4.24,5.64,3.11-4.16,6.14-8.19,5.91-13.62-.24-5.8-4.98-8.29-8.67-11.35,1.39-3.05,2.9-5.82,3.95-8.75,2.44-6.78,1.38-13.02-3.71-18.28-3.31-3.43-7.45-4.52-12.08-3.42-2.35.56-3.55,2.26-3.34,4.63.2,2.29,1.83,3.12,3.99,3.08.71-.01,1.42,0,2.14,0-.14,4.3-1.56,6.97-5.32,9.98-2.29,1.83-4.7,3.51-6.95,5.38-5.24,4.35-9.47,14.19-3.42,22.25.85,1.13,1.56,2.59,1.71,3.97,1.04,9.38,6.53,15.53,14.56,19.44,8.94,4.35,18.05,5.76,27.11-.24,16.43-10.89,22.58-26.45,21.14-45.58-.15-1.92-.33-3.84-.5-5.76.27-.07.54-.13.81-.2,4.27,6.01,7.95,12.35,8.57,19.97.71,8.82-4.46,14.58-11.81,19.97,3.63,1.95,6.46,3.99,9.62,5.03,4.6,1.52,8.36-1.54,11.02-2.91,4.83,2.14,8.81,3.77,12.67,5.66,2.49,1.22,4.9,2.67,7.17,4.26,1.61,1.13,2.93,2.69,4.79,4.44,0-4.89.34-9.1-.1-13.23-.42-4-1.34-8.01-2.6-11.83-.67-2.04-.7-3.16.85-4.79,5.95-6.26,5.65-12.51-.67-17.53-.95.76-1.88,1.6-2.9,2.3-4.26,2.93-8.8,4.47-13.87,2.26-9.41-4.09-14.23-12.36-19.67-21.42,4.38,1.32,7.81,2.5,11.31,3.39,14.74,3.71,29.21,3.9,42.95-3.75,9.67-5.38,16.22-13.68,21.33-23.26,7.38-13.86,10.97-28.55,8.31-44.2Z\"/>`\n\nconst JJ = (cls) => `<svg class=\"${cls||''}\" viewBox=\"0 0 1631 350.8\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\" role=\"img\" aria-label=\"John John\">${JJ_PATHS}</svg>`\n\nconst FUNDO = `<div class=\"bg\"><div class=\"grid\"></div>${JJ('mark')}</div>`\n\nconst CHIP = DEMO_URL\n  ? `<a class=\"chip\" href=\"${DEMO_URL}\" target=\"_blank\" rel=\"noopener\">Ver o provador funcionando</a>`\n  : ``\n\nconst GATE = `<div class=\"gwrap\">${FUNDO}\n  <div class=\"gcard\">\n    <div class=\"glock\"><img src=\"${LD2}\" style=\"height:22px\" alt=\"Data²\"><span>+</span>${JJ('gcb')}</div>\n    <div class=\"gsub\">Proposta · Veste S/A · John John</div>\n    <input class=\"ginput\" type=\"password\" placeholder=\"Senha de acesso\" autocomplete=\"off\"/>\n    <button class=\"gbtn\">Entrar</button>\n    <div class=\"gerr\">Senha incorreta — tente novamente.</div>\n  </div></div>`\n\nconst DECK = `<div class=\"deckwrap\">\n<header><img class=\"d2\" src=\"${LD2}\" alt=\"Data²\">${JJ('cb')}</header>\n<div class=\"counter\" id=\"counter\">01 — 11</div>\n<div class=\"hint\">↓ navegue</div>\n<nav class=\"dots\" id=\"dots\" aria-label=\"Slides\"></nav>\n<div class=\"deckroot\">\n\n  <!-- 01 · CAPA -->\n  <section class=\"capa\" id=\"capa\">\n    <div class=\"frame rv\">${FUNDO}\n      <div class=\"mid\">\n        <div class=\"lockup\">\n          <img class=\"d2m\" src=\"${LD2}\" alt=\"Data²\"><span class=\"plus\">+</span>${JJ('cbm')}\n        </div>\n        <div class=\"sub\">Provador virtual · POC e teste de 30 dias</div>\n      </div>\n    </div>\n    <div class=\"caption rv\">\n      <div class=\"frase-cap\">We build Data² while imagining a world where things just … work.</div>\n      <div class=\"meta\">Proposta · Setembro 2026</div>\n    </div>\n  </section>\n\n  <!-- 02 · ÍNDICE -->\n  <section id=\"indice\">\n    <div class=\"eyebrow rv\">Índice</div>\n    <div class=\"indice rv\">\n      <a href=\"#tese\"><span class=\"n\">01</span><span class=\"t\">A proposta</span></a>\n      <a href=\"#provador\"><span class=\"n\">02</span><span class=\"t\">O provador</span></a>\n      <a href=\"#pratica\"><span class=\"n\">03</span><span class=\"t\">Na prática</span></a>\n      <a href=\"#arq\"><span class=\"n\">04</span><span class=\"t\">Arquitetura</span></a>\n      <a href=\"#teste\"><span class=\"n\">05</span><span class=\"t\">O teste de 30 dias</span></a>\n      <a href=\"#kpis\"><span class=\"n\">06</span><span class=\"t\">O que vamos medir</span></a>\n      <a href=\"#comercial\"><span class=\"n\">07</span><span class=\"t\">Condições comerciais</span></a>\n      <a href=\"#respons\"><span class=\"n\">08</span><span class=\"t\">Responsabilidades</span></a>\n    </div>\n  </section>\n\n  <!-- 03 · STATEMENT -->\n  <section class=\"statement\" id=\"tese\">\n    <div class=\"eyebrow rv\">A proposta · <b>Veste S/A · John John</b></div>\n    <div class=\"split\">\n      <h1 class=\"rv\">O provador entra no site por 30 dias.</h1>\n      <p class=\"apoio destaque rv\">Sem custo de sprint, com 30 dias de geração de imagem\n        liberada e sem mexer no que já funciona. Esta proposta formaliza o que a POC entrega,\n        qual número ela precisa mover e como a continuidade vira um acordo pago.</p>\n    </div>\n  </section>\n\n  <!-- 04 · O PROVADOR -->\n  <section id=\"provador\">\n    <div class=\"eyebrow rv\">O provador</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(30px,6vh,60px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(28px,4vw,56px);line-height:1.12;max-width:16ch\">\n        Prova, combina e compra na mesma sessão.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A peça do catálogo é aplicada na foto do próprio\n        cliente, em segundos. Cada prova fica salva no nome dele, com o preço e o caminho de\n        volta para a página do produto.</p>\n    </div>\n    <div class=\"pilares\">\n      <div class=\"pilar rv d2\"><span class=\"k\">01 · Provador</span>\n        <div class=\"t\">A peça no corpo do cliente</div>\n        <p>Ele manda uma foto, escolhe a peça e vê o resultado. A roupa atual é removida antes\n           da troca, e o enquadramento da foto original é preservado.</p></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">02 · Meu Look</span>\n        <div class=\"t\">Várias peças, uma foto</div>\n        <p>Camisa, calça e jaqueta provadas em sequência, sobre o resultado anterior. É como o\n           cliente compra de verdade: por look, não por item.</p></div>\n      <div class=\"pilar rv d4\"><span class=\"k\">03 · Minhas Provas</span>\n        <div class=\"t\">A prova vira vitrine</div>\n        <p>Cada prova fica salva com preço e link para a PDP. O cliente volta para a galeria\n           dele e compra de lá, dias depois.</p></div>\n    </div>\n    <div class=\"rv d5\">${CHIP}</div>\n  </section>\n\n  <!-- 05 · NA PRÁTICA -->\n  <section id=\"pratica\">\n    <div class=\"eyebrow rv\">Na prática</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(26px,5vh,48px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Duas dúvidas que hoje terminam em devolução.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Se a peça combina com quem vai usar, e se ela\n        combina com o resto do look. Nenhuma das duas a foto da modelo responde, e as duas\n        acontecem no site do John John toda semana.</p>\n    </div>\n    <div class=\"comps rv\">\n      <div class=\"comp\">\n        <div class=\"ctit\">Decidir se a peça combina com você</div>\n        <div class=\"hj\">\n          <div class=\"lk\">Hoje · a foto da modelo</div>\n          <ul class=\"steps\">\n            <li>O cliente vê a peça num corpo que não é o dele</li>\n            <li>Tenta imaginar a cor e o comprimento em si mesmo</li>\n            <li>Compra na dúvida — e devolve quando não gosta do que vê</li>\n          </ul>\n        </div>\n        <div class=\"cn\">\n          <div class=\"lk\">Com o provador · segundos</div>\n          <ul class=\"steps\">\n            <li>Manda uma foto de corpo inteiro, uma vez</li>\n            <li>Escolhe a peça no catálogo e vê ela em si mesmo</li>\n            <li>Só leva o que já viu vestido</li>\n          </ul>\n        </div>\n      </div>\n      <div class=\"comp\">\n        <div class=\"ctit\">Montar um look</div>\n        <div class=\"hj\">\n          <div class=\"lk\">Hoje · quatro abas abertas</div>\n          <ul class=\"steps\">\n            <li>Abre uma aba por produto e compara fotos de modelos diferentes</li>\n            <li>Imagina a camisa com a calça sem nunca ver as duas juntas</li>\n            <li>Leva uma peça e deixa o resto</li>\n          </ul>\n        </div>\n        <div class=\"cn\">\n          <div class=\"lk\">Com o Meu Look · uma foto</div>\n          <ul class=\"steps\">\n            <li>Marca as peças que quer provar juntas</li>\n            <li>Prova as quatro na mesma foto, em sequência</li>\n            <li>Compra o look inteiro pela galeria de provas</li>\n          </ul>\n        </div>\n      </div>\n    </div>\n  </section>\n\n  <!-- 06 · ARQUITETURA -->\n  <section id=\"arq\">\n    <div class=\"eyebrow rv\">Arquitetura e integração</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,42px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Integramos por leitura. Nada do que funciona muda.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">O provador consulta a API pública de catálogo do\n        johnjohndenim.com.br no momento da prova. Não há réplica de base, carga noturna nem\n        alteração no tema da loja.</p>\n    </div>\n    <div class=\"arq rv\">\n      <div class=\"col\">\n        <div class=\"col-h\">Sistemas do John John · intocados</div>\n        <div class=\"box\"><div class=\"bn\">VTEX</div>\n          <div class=\"bd\">Catálogo, PDP, carrinho e checkout seguem como estão</div></div>\n        <div class=\"box\"><div class=\"bn\">johnjohndenim.com.br</div>\n          <div class=\"bd\">O provador entra como um bloco no tema, sem reescrever a loja</div></div>\n      </div>\n      <div class=\"link\"><span>leitura · API</span></div>\n      <div class=\"core\">\n        <div class=\"ch\">Data² · engine de provador</div>\n        <div class=\"box\"><div class=\"bn\">Leitura de catálogo</div>\n          <div class=\"bd\">Consulta as categorias reais — Camisas, Camisetas, Calças, Jaquetas,\n            Vestidos — na hora da prova</div></div>\n        <div class=\"box\"><div class=\"bn\">Provador de IA</div>\n          <div class=\"bd\">Checa a foto, decide o que remover e aplica a peça mantendo o\n            enquadramento</div></div>\n        <div class=\"box\"><div class=\"bn\">Painel de resultados</div>\n          <div class=\"bd\">Registra cada prova, cada clique em Comprar e os produtos mais\n            provados</div></div>\n      </div>\n      <div class=\"link\"><span>embed</span></div>\n      <div class=\"col\">\n        <div class=\"col-h\">Cliente final</div>\n        <div class=\"box\"><div class=\"bn\">Navegador</div>\n          <div class=\"bd\">Dentro do site, na mesma sessão de compra</div></div>\n        <div class=\"box\"><div class=\"bn\">Minhas Provas</div>\n          <div class=\"bd\">Galeria por e-mail, com preço e Comprar apontando para a PDP</div></div>\n      </div>\n    </div>\n  </section>\n\n  <!-- 05 · O TESTE DE 30 DIAS -->\n  <section id=\"teste\">\n    <div class=\"eyebrow rv\">O teste</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(20px,3.4vh,34px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Trinta dias no ar, com três marcos.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A janela começa no dia em que o provador entra no\n        site. As datas de calendário são fixadas na confirmação desta proposta.</p>\n    </div>\n    <div class=\"ondas rv\">\n      <div class=\"onda\"><div class=\"fase\">Dia 1</div><div class=\"t\">No ar</div>\n        <ul>\n          <li>Provador publicado como embed no site do John John</li>\n          <li>Geração de imagem liberada, até 2.000 provas</li>\n          <li>Linha de base de conversão registrada</li>\n          <li>Datapoints do funil chegando ao time do John John</li>\n        </ul></div>\n      <div class=\"onda\"><div class=\"fase\">Dia 15</div><div class=\"t\">Primeira leitura</div>\n        <ul>\n          <li>Volume de provas e taxa de conclusão</li>\n          <li>Ajuste de prompt e de catálogo, se preciso</li>\n          <li>Geração de imagem segue liberada até o dia 30</li>\n        </ul></div>\n      <div class=\"onda\"><div class=\"fase\">Dia 30</div><div class=\"t\">Análise e acordo</div>\n        <ul>\n          <li>Conversão de quem provou contra a média do site</li>\n          <li>Cliques em Comprar depois da prova</li>\n          <li>Resultados analisados e continuidade em acordo pago</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 09 · O QUE VAMOS MEDIR -->\n  <section id=\"kpis\">\n    <div class=\"eyebrow rv\">O que vamos medir</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,44px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Dois números decidem a continuidade.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Não medimos engajamento nem tempo de página. O\n        teste responde se quem prova compra mais — e se a prova leva ao carrinho. O provador\n        entra como embed no site, e a engine envia os datapoints do funil ao time do John John.</p>\n    </div>\n    <div class=\"kpis seis rv\">\n      <div class=\"k\"><div class=\"kt\">Conversão de quem provou</div>\n        <p>Taxa de conversão das sessões que geraram prova, contra a média do site no mesmo\n           período.</p>\n        <div class=\"kb\">Linha de base: a conversão do site hoje, informada no dia 1</div></div>\n      <div class=\"k\"><div class=\"kt\">Clique em Comprar após a prova</div>\n        <p>Quantas provas terminam em clique para a página do produto. Mede intenção direta,\n           não curiosidade.</p>\n        <div class=\"kb\">Registrado pela engine desde a primeira prova</div></div>\n      <div class=\"k\"><div class=\"kt\">Como medimos</div>\n        <p>Cada prova, cadastro e clique sai da engine como datapoint, com produto, horário e\n           sessão. O funil é montado na ferramenta que o John John já usa, e o painel da Data²\n           fica aberto durante os 30 dias para os dois lados conferirem o mesmo número.</p>\n        <div class=\"kb\">Formato combinado com o time: dataLayer, webhook ou export</div></div>\n    </div>\n  </section>\n\n  <!-- 10 · CONDIÇÕES COMERCIAIS -->\n  <section id=\"comercial\">\n    <div class=\"eyebrow rv\" style=\"margin-bottom:clamp(14px,2.4vh,24px)\">Condições comerciais</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,22px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">\n        Trinta dias liberados. Depois, um acordo pago.</h2>\n      <p class=\"apoio destaque rv\" style=\"margin-top:0\">A construção e o teste correm por conta\n        da Data², com a geração de imagem liberada por 30 dias, até 2.000 provas. No fim da\n        janela os resultados são analisados e a continuidade vira um acordo pago.</p>\n    </div>\n    <div class=\"custos rv\">\n      <div class=\"row\"><div>\n        <div class=\"t\">Construção da POC</div>\n        <div class=\"d\">Engine de provador com o catálogo John John ao vivo, Meu Look, Minhas\n          Provas, painel de resultados e o bloco de embed para o site.</div>\n      </div><div class=\"v\">R$ 0</div></div>\n      <div class=\"row\"><div>\n        <div class=\"t\">Teste de 30 dias</div>\n        <div class=\"d\">Acompanhamento durante a janela: ajuste de prompt, ajuste de catálogo e\n          leitura dos números com o time do John John.</div>\n      </div><div class=\"v\">R$ 0</div></div>\n      <div class=\"row\"><div>\n        <div class=\"t\">Geração de imagem · 30 dias</div>\n        <div class=\"d\">Até 2.000 provas na janela. Catálogo, storage e acessos incluídos.</div>\n      </div><div class=\"v\">Liberada</div></div>\n      <div class=\"row total\"><div>\n        <div class=\"t\">Depois dos 30 dias</div>\n        <div class=\"d\">Continuidade dimensionada pelo volume que a POC mostrou.</div>\n      </div><div class=\"v\">Acordo pago</div></div>\n    </div>\n    <p class=\"cond rv\">Ao se aproximar das 2.000 provas, a Data² avisa antes de qualquer\n      cobrança. Evolução de escopo é orçada em separado.</p>\n  </section>\n\n  <!-- 11 · RESPONSABILIDADES -->\n  <section id=\"respons\">\n    <div class=\"eyebrow rv\">Responsabilidades</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,44px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        O que cada lado põe na mesa.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A POC não depende de projeto de TI. Depende de\n        cinco liberações pequenas — e cada uma tem dono. A única que precisa de agenda é a\n        presença do time nas sessões de decisão.</p>\n    </div>\n    <div class=\"pilares dois\">\n      <div class=\"pilar rv d2\"><span class=\"k\">A Data² entrega</span>\n        <ul class=\"klist\">\n          <li><b>Engine no ar</b> — provador, Meu Look e Minhas Provas publicados e monitorados</li>\n          <li><b>Bloco de embed</b> — um trecho para o time de front colar, sem alterar o tema</li>\n          <li><b>Datapoints do funil</b> — cada prova, cadastro e clique no formato combinado</li>\n          <li><b>Painel de resultados</b> — aberto ao John John durante os 30 dias</li>\n          <li><b>Acompanhamento</b> — ajuste de prompt e de catálogo dentro da janela</li>\n        </ul></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">O John John libera</span>\n        <ul class=\"klist\">\n          <li><b>Time nas sessões de decisão</b> — Tech, UI/UX e stakeholders presentes nos pontos de decisão da janela</li>\n          <li><b>Publicação do embed</b> — um contato de plataforma que coloca o bloco no ar</li>\n          <li><b>Onde o provador entra</b> — página de produto, home ou landing de campanha</li>\n          <li><b>Linha de base de conversão</b> — o número do site hoje, para a comparação</li>\n          <li><b>Uso de imagem</b> — aprovação para o cliente gerar e guardar a própria prova</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 11 · FECHAMENTO -->\n  <section class=\"dark fecho\" data-dark id=\"fim\">${FUNDO}\n    <div class=\"mid\">\n      <img class=\"ring branco rv\" style=\"width:52px\" src=\"${RING}\" alt=\"\">\n      <p class=\"frase rv\">We build Data² while imagining a world where things just <i>…</i> work.</p>\n      <div class=\"lockup rv\">\n        <img class=\"d2m\" style=\"height:26px\" src=\"${LD2}\" alt=\"Data²\">\n        <span class=\"plus\" style=\"font-size:20px\">+</span>${JJ('cbm-s')}\n      </div>\n      <div class=\"sub rv\">gabi.carmo@data2.app · data2.app</div>\n    </div>\n  </section>\n\n</div></div>`\n\nfunction injectFonts(){\n  try{\n    if(!document.getElementById('akz-deck-fonts')){\n      const st=document.createElement('style')\n      st.id='akz-deck-fonts'\n      st.textContent=FONT_CSS\n      document.head.appendChild(st)\n    }\n  }catch(e){}\n}\n\nfunction initDeck(host){\n  const wrap=host&&host.querySelector('.deckwrap'); if(!wrap) return\n  if(wrap.dataset.init==='1') return\n  wrap.dataset.init='1'\n  const rootEl=wrap.querySelector('.deckroot')\n  const slides=[].slice.call(rootEl.querySelectorAll('section'))\n  const dots=wrap.querySelector('.dots'), counter=wrap.querySelector('.counter')\n  dots.innerHTML=''\n  const pad=n=>(n<10?'0':'')+n\n  let cur=0\n  counter.textContent=pad(1)+' — '+pad(slides.length)\n  slides.forEach((s,i)=>{\n    const a=document.createElement('a')\n    a.href='#'+s.id; a.setAttribute('aria-label','Slide '+(i+1))\n    dots.appendChild(a)\n  })\n  const links=[].slice.call(dots.children)\n  if(links[0])links[0].classList.add('on')\n\n  const setActive=i=>{\n    if(i===cur)return\n    cur=i\n    counter.textContent=pad(cur+1)+' — '+pad(slides.length)\n    links.forEach((l,k)=>l.classList.toggle('on',k===cur))\n    wrap.classList.toggle('on-dark',slides[cur].hasAttribute('data-dark'))\n    wrap.classList.toggle('last',cur===slides.length-1)\n  }\n\n  const revIO=new IntersectionObserver(es=>{\n    es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');revIO.unobserve(e.target)}})\n  },{root:rootEl,rootMargin:'0px 0px 12% 0px',threshold:.01})\n  slides.forEach(s=>revIO.observe(s))\n\n  const ratios=new Array(slides.length).fill(0)\n  const actIO=new IntersectionObserver(es=>{\n    es.forEach(e=>{const i=slides.indexOf(e.target);if(i>-1)ratios[i]=e.isIntersecting?e.intersectionRatio:0})\n    let bi=cur,bv=-1\n    ratios.forEach((r,i)=>{if(r>bv){bv=r;bi=i}})\n    if(bv>0)setActive(bi)\n  },{root:rootEl,threshold:[0,.15,.35,.55,.75,1]})\n  slides.forEach(s=>actIO.observe(s))\n\n  setTimeout(()=>{\n    if(!rootEl.querySelector('section.in'))slides.forEach(s=>s.classList.add('in'))\n  },500)\n\n  const rm=(typeof matchMedia==='function')&&matchMedia('(prefers-reduced-motion: reduce)').matches\n  const go=i=>{\n    i=Math.max(0,Math.min(slides.length-1,i))\n    slides[i].scrollIntoView({behavior:rm?'auto':'smooth'})\n  }\n\n  wrap.addEventListener('click',ev=>{\n    const a=ev.target.closest&&ev.target.closest('a[href^=\"#\"]'); if(!a)return\n    ev.preventDefault()\n    const t=slides.find(s=>s.id===a.getAttribute('href').slice(1))\n    if(t)t.scrollIntoView({behavior:rm?'auto':'smooth'})\n  })\n\n  window.addEventListener('keydown',e=>{\n    if(!wrap.isConnected)return\n    if(e.target&&/^(INPUT|TEXTAREA)$/.test(e.target.tagName))return\n    if(e.key==='ArrowDown'||e.key==='ArrowRight'||e.key==='PageDown'||e.key===' '){e.preventDefault();go(cur+1)}\n    if(e.key==='ArrowUp'||e.key==='ArrowLeft'||e.key==='PageUp'){e.preventDefault();go(cur-1)}\n    if(e.key==='Home'){e.preventDefault();go(0)}\n    if(e.key==='End'){e.preventDefault();go(slides.length-1)}\n  })\n}\n\n/* Este player não re-renderiza de forma confiável quando d2.state muda: a senha\n   gravava a sessão e o gate continuava na tela. Então a fase vive numa variável de\n   módulo e a troca de tela é feita na mão, com innerHTML. */\nlet phase = 'gate'\n\nfunction mountGate(host){\n  const card=host.querySelector('.gcard'); if(!card||card.dataset.wired==='1') return\n  card.dataset.wired='1'\n  const inp=host.querySelector('.ginput'), btn=host.querySelector('.gbtn')\n  const check=()=>{\n    if(!inp) return\n    if(inp.value===PASS){\n      try{sessionStorage.setItem('deckJohnJohnUnlocked','1')}catch(e){}\n      showDeck(host)\n    } else {\n      const er=host.querySelector('.gerr')\n      if(er)er.style.display='block'\n      card.classList.remove('shake');void card.offsetWidth;card.classList.add('shake')\n      if(inp.select)inp.select()\n    }\n  }\n  if(btn)btn.addEventListener('click',check)\n  if(inp)inp.addEventListener('keydown',e=>{if(e.key==='Enter')check()})\n}\n\nfunction showDeck(host){\n  phase='deck'\n  host.innerHTML=DECK\n  initDeck(host)\n}\n\nfunction sync(d2,root){\n  const host=(d2&&d2.$&&d2.$('.deckhost'))||((root||document).querySelector('.deckhost'))\n  if(!host) return\n  if(phase==='deck'){\n    if(!host.querySelector('.deckwrap')) host.innerHTML=DECK\n    initDeck(host)\n    return\n  }\n  if(!host.querySelector('.gwrap')) host.innerHTML=GATE\n  mountGate(host)\n}\n\nexport default {\n  css:`\n:host,.deckwrap,.gwrap{\n  --paper:#F5F4F0;\n  --ink:#15161A;\n  --muted:#6A6E77;\n  --accent:#96570F;\n  --accent-soft:#C89A4A;\n  --ok:#2C6B4A;\n  --bad:#A33427;\n  --dark:#17253F;\n  --dark-2:#22314F;\n  --linha:rgba(21,22,26,.13);\n  --mx:clamp(20px,4vw,56px);\n  --ext:'AkzExt','Helvetica Neue',Helvetica,Arial,sans-serif;\n  --lt:'AkzExtLight','Helvetica Neue',Helvetica,Arial,sans-serif;\n  --sf:-apple-system,BlinkMacSystemFont,'SF Pro Text','Inter','Helvetica Neue',Helvetica,Arial,sans-serif;\n}\n*{box-sizing:border-box;margin:0;padding:0}\n.deckhost{display:block;width:100%;height:100%}\n\n/* ---------- arquitetura de scroll ---------- */\n.deckwrap{position:relative;height:100vh;height:100svh;overflow:hidden;\n  background:var(--paper);color:var(--ink);font-family:var(--lt);\n  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}\n.deckroot{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;\n  scroll-snap-type:y proximity;scroll-behavior:smooth;\n  -webkit-overflow-scrolling:touch;overscroll-behavior-y:contain}\n\n/* ---------- chrome ---------- */\nheader{position:absolute;inset:0 0 auto 0;z-index:40;display:flex;\n  justify-content:space-between;align-items:center;padding:22px var(--mx) 34px;\n  pointer-events:none;background:linear-gradient(to bottom,var(--paper) 52%,rgba(245,244,240,0))}\n.deckwrap.on-dark header{background:none}\nheader img{height:16px;display:block;filter:brightness(0);opacity:.92}\nheader svg.cb{height:15px;width:auto;display:block;color:var(--ink)}\n.deckwrap.on-dark header img{filter:none;opacity:1}\n.deckwrap.on-dark header svg.cb{color:#fff}\n.counter{position:absolute;z-index:40;left:var(--mx);bottom:38px;font-family:var(--lt);\n  font-size:11px;letter-spacing:.22em;color:var(--muted)}\n.deckwrap.on-dark .counter{color:rgba(255,255,255,.75)}\nnav.dots{position:absolute;z-index:40;right:clamp(14px,2.4vw,32px);top:50%;\n  transform:translateY(-50%);display:flex;flex-direction:column;gap:14px}\nnav.dots a{width:6px;height:6px;border-radius:50%;background:var(--muted);opacity:.35;\n  transition:opacity .3s,transform .3s}\nnav.dots a.on{opacity:1;transform:scale(1.5);background:var(--ink)}\n.deckwrap.on-dark nav.dots a,.deckwrap.on-dark nav.dots a.on{background:#fff}\n.hint{position:absolute;z-index:40;right:var(--mx);bottom:38px;font-size:11px;\n  letter-spacing:.22em;color:var(--muted);text-transform:uppercase;transition:opacity .4s}\n.deckwrap.last .hint{opacity:0}\n.deckwrap.on-dark .hint{color:rgba(255,255,255,.6)}\n\n/* ---------- slide ---------- */\nsection{min-height:100vh;min-height:100svh;scroll-snap-align:start;position:relative;\n  display:flex;flex-direction:column;justify-content:center;\n  padding:clamp(84px,12vh,140px) clamp(20px,7vw,120px);overflow:hidden}\n.eyebrow{font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:clamp(20px,4vh,44px)}\n.eyebrow b{font-weight:400;color:var(--accent)}\n\n/* ---------- reveals ---------- */\n.rv{opacity:0;transform:translateY(26px);\n  transition:opacity .9s cubic-bezier(.2,.6,.2,1),transform .9s cubic-bezier(.2,.6,.2,1)}\nsection.in .rv{opacity:1;transform:none}\nsection.in .rv:nth-child(2),section.in .d2{transition-delay:.12s}\nsection.in .rv:nth-child(3),section.in .d3{transition-delay:.24s}\nsection.in .rv:nth-child(4),section.in .d4{transition-delay:.36s}\nsection.in .rv:nth-child(5),section.in .d5{transition-delay:.48s}\nsection.in .rv:nth-child(6),section.in .d6{transition-delay:.6s}\n\n/* ---------- tipografia ---------- */\nh1,h2{font-family:var(--ext);font-weight:400;text-wrap:balance}\n.apoio{max-width:44ch;font-size:clamp(15px,1.5vw,19px);line-height:1.65;color:var(--muted)}\n.apoio::before{content:'';display:block;width:36px;height:2px;background:var(--accent);\n  margin-bottom:18px}\n.apoio.destaque{color:var(--ink);font-size:clamp(16px,1.7vw,21px);line-height:1.6}\n.split{display:grid;grid-template-columns:1.4fr .8fr;gap:clamp(28px,6vw,90px);align-items:end}\n.split>*{min-width:0}\n@media(max-width:820px){.split{grid-template-columns:1fr}}\n\n.apoio,.lead,.pilar p,.onda li,.custos .row .d,.cond,.arq .box .bd,\n.kpis .k p,.klist li,.steps li{font-family:var(--sf)}\n\n:focus-visible{outline:2px solid var(--accent);outline-offset:4px}\n@media (prefers-reduced-motion:reduce){\n  .deckroot{scroll-behavior:auto}\n  .rv{opacity:1;transform:none;transition:none}\n}\n\n/* ---------- fundo gráfico ---------- */\n.bg{position:absolute;inset:0;z-index:0;overflow:hidden;background:\n  radial-gradient(ellipse 70% 60% at 18% 85%,rgba(35,71,127,.66),transparent 62%),\n  radial-gradient(ellipse 50% 45% at 88% 12%,rgba(200,154,74,.24),transparent 60%),\n  linear-gradient(135deg,var(--dark) 0%,var(--dark-2) 55%,#0E1729 100%)}\n.bg .grid{position:absolute;inset:0;\n  background-image:linear-gradient(rgba(255,255,255,.055) 1px,transparent 1px),\n                   linear-gradient(90deg,rgba(255,255,255,.055) 1px,transparent 1px);\n  background-size:64px 64px;\n  mask-image:radial-gradient(ellipse 85% 80% at 50% 50%,#000 30%,transparent 100%);\n  -webkit-mask-image:radial-gradient(ellipse 85% 80% at 50% 50%,#000 30%,transparent 100%)}\n.bg .mark{position:absolute;left:50%;transform:translateX(-50%);bottom:-7%;width:76%;height:auto;color:#fff;opacity:.05}\n@media(max-width:820px){.bg .mark{width:150%;bottom:4%}}\n\n/* ---------- capa ---------- */\n.capa{padding:clamp(72px,10vh,104px) var(--mx) clamp(62px,9vh,88px);background:var(--paper)}\n.capa .frame{position:relative;border-radius:clamp(18px,2.4vw,30px);overflow:hidden;flex:1;\n  min-height:min(58vh,640px);display:flex;align-items:center;justify-content:center;color:#fff}\n.capa .frame .mid{position:relative;z-index:1;display:flex;flex-direction:column;\n  align-items:center;text-align:center;gap:30px}\n.capa .frame .sub{font-family:var(--lt);font-size:clamp(12px,1.3vw,16px);letter-spacing:.16em;\n  text-transform:uppercase;opacity:.92}\n.capa .caption{display:flex;justify-content:space-between;align-items:baseline;gap:24px;\n  padding:clamp(16px,2.6vh,26px) 0 0;flex-wrap:wrap}\n.capa .caption .frase-cap{font-family:var(--lt);font-size:clamp(14px,1.55vw,19px);\n  color:var(--ink);letter-spacing:.01em}\n.capa .caption .meta{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--muted)}\n\n.lockup{display:flex;align-items:center;gap:clamp(18px,3vw,34px)}\n.lockup img{display:block}\n.lockup .d2m{height:clamp(30px,4.6vw,54px)}\n.lockup svg.cbm{height:clamp(26px,3.6vw,42px);width:auto;display:block;color:#fff}\n.lockup svg.cbm-s{height:21px;width:auto;display:block;color:#fff}\n.lockup .plus{font-family:var(--lt);font-size:clamp(22px,3vw,36px);line-height:1;opacity:.9}\n\n/* ---------- índice ---------- */\n.indice{display:grid;grid-template-columns:1fr 1fr;gap:0 clamp(28px,5vw,90px);\n  border-top:1px solid var(--linha)}\n.indice a{display:flex;align-items:baseline;gap:18px;padding:clamp(13px,2.2vh,22px) 0;\n  border-bottom:1px solid var(--linha);text-decoration:none;color:var(--ink)}\n.indice a .n{font-family:var(--lt);font-size:11px;letter-spacing:.18em;color:var(--muted);\n  min-width:28px;transition:color .25s}\n.indice a .t{font-family:var(--ext);font-size:clamp(16px,1.9vw,26px);line-height:1.25;\n  transition:transform .3s cubic-bezier(.2,.6,.2,1)}\n.indice a:hover .n,.indice a:focus-visible .n{color:var(--accent)}\n.indice a:hover .t,.indice a:focus-visible .t{transform:translateX(6px)}\n@media(max-width:820px){.indice{grid-template-columns:1fr}}\n\n/* ---------- statement ---------- */\n.statement h1{font-size:clamp(34px,5.6vw,86px);line-height:1.08;letter-spacing:-.01em;max-width:16ch}\n\n/* ---------- pilares ---------- */\n.pilares{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3.4vw,56px);\n  border-top:1px solid var(--linha);padding-top:clamp(26px,5vh,52px)}\n.pilares.dois{grid-template-columns:1fr 1fr}\n.pilar .k{display:block;font-size:11px;letter-spacing:.26em;color:var(--accent);\n  text-transform:uppercase;margin-bottom:18px}\n.pilar .t{font-family:var(--ext);font-size:clamp(17px,1.7vw,23px);line-height:1.3;margin-bottom:16px}\n.pilar p{font-size:14.5px;line-height:1.7;color:var(--muted)}\n@media(max-width:820px){.pilares,.pilares.dois{grid-template-columns:1fr}}\n\n/* ---------- chip de link ---------- */\n.chip{display:inline-flex;align-items:center;gap:10px;margin-top:clamp(22px,3.6vh,34px);\n  padding:11px 20px;border:1px solid var(--linha);border-radius:999px;font-family:var(--lt);\n  font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);\n  text-decoration:none;transition:border-color .25s}\n.chip:hover{border-color:var(--accent)}\n.chip.pend{color:var(--muted);border-style:dashed}\n\n/* ---------- hoje × depois ---------- */\n.comps{display:flex;flex-direction:column;gap:clamp(18px,3vh,32px)}\n.comp{display:grid;grid-template-columns:1fr 1fr;gap:clamp(18px,3vw,48px);\n  border-top:1px solid var(--linha);padding-top:clamp(14px,2.2vh,22px)}\n.comp .ctit{grid-column:1/-1;font-family:var(--ext);font-size:clamp(15px,1.6vw,20px)}\n.comp .lk{font-family:var(--lt);font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;\n  margin-bottom:12px}\n.comp .hj .lk{color:var(--bad)}\n.comp .cn .lk{color:var(--ok)}\n.steps{list-style:none;counter-reset:s;display:flex;flex-direction:column;gap:9px}\n.steps li{font-size:12.5px;line-height:1.55;color:var(--muted);display:flex;gap:10px;align-items:baseline}\n.steps li::before{counter-increment:s;content:counter(s);font-family:var(--lt);font-size:10px;\n  color:var(--bad);border:1px solid currentColor;border-radius:50%;width:17px;height:17px;\n  display:flex;align-items:center;justify-content:center;flex:none;position:relative;top:2px}\n.comp .cn .steps li::before{color:var(--ok)}\n@media(max-width:820px){.comp{grid-template-columns:1fr}}\n\n/* ---------- arquitetura ---------- */\n.arq{display:grid;grid-template-columns:1fr 44px 1.15fr 44px 1fr;gap:clamp(10px,1.2vw,18px);\n  align-items:stretch;border-top:1px solid var(--linha);padding-top:clamp(24px,4vh,44px)}\n.arq .col{display:flex;flex-direction:column;gap:10px}\n.arq .col-h{font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:8px;line-height:1.5}\n.arq .box{border:1px solid var(--linha);border-radius:14px;padding:13px 15px}\n.arq .box .bn{font-family:var(--ext);font-size:12.5px;line-height:1.4}\n.arq .box .bd{font-size:11.5px;color:var(--muted);margin-top:4px;line-height:1.5}\n.arq .core{background:var(--dark);color:#fff;border-radius:18px;padding:clamp(16px,2.4vh,26px);\n  display:flex;flex-direction:column;gap:12px;height:100%}\n.arq .core .ch{font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--accent-soft)}\n.arq .core .box{border-color:rgba(255,255,255,.25)}\n.arq .core .box .bd{color:rgba(255,255,255,.72)}\n.arq .link{position:relative;display:flex;align-items:center;justify-content:center}\n.arq .link::before{content:'';position:absolute;left:-6px;right:-6px;top:50%;height:1px;\n  background:var(--muted);opacity:.5}\n.arq .link span{position:relative;background:var(--paper);font-size:9px;letter-spacing:.2em;\n  text-transform:uppercase;color:var(--muted);transform:rotate(-90deg);white-space:nowrap;padding:0 6px}\n@media(max-width:900px){\n  .arq{grid-template-columns:1fr}\n  .arq .link{min-height:34px}\n  .arq .link::before{left:50%;right:auto;top:0;bottom:0;width:1px;height:auto}\n  .arq .link span{transform:none}\n}\n\n/* ---------- frentes de escopo ---------- */\n.leg{display:flex;gap:26px;flex-wrap:wrap;margin-bottom:clamp(18px,3vh,30px);\n  font-family:var(--sf);font-size:12.5px;color:var(--muted)}\n.leg span{display:flex;align-items:center;gap:8px}\n.leg i{width:9px;height:9px;border-radius:50%;display:inline-block}\n.leg .ok{background:var(--ok)}\n.leg .nv{background:var(--accent)}\n.frentes{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--linha);\n  column-gap:clamp(24px,4vw,64px)}\n.frentes .f{border-bottom:1px solid var(--linha);padding:clamp(12px,1.8vh,18px) 0}\n.frentes .fn{font-family:var(--ext);font-size:clamp(13px,1.2vw,15.5px);margin-bottom:8px;\n  display:flex;justify-content:space-between;gap:12px;align-items:baseline}\n.frentes .fn .ct{font-family:var(--sf);font-size:10.5px;color:var(--muted);white-space:nowrap;\n  letter-spacing:.04em}\n.frentes .fi{display:flex;gap:8px;align-items:baseline;font-family:var(--sf);font-size:12px;\n  line-height:1.5;color:var(--ink);padding:2.5px 0}\n.frentes .fi::before{content:'';width:6px;height:6px;border-radius:50%;flex:none;\n  position:relative;top:-1px}\n.frentes .fi.ok::before{background:var(--ok)}\n.frentes .fi.nv::before{background:var(--accent)}\n@media(max-width:900px){.frentes{grid-template-columns:1fr}}\n\n/* ---------- kpis ---------- */\n.kpis{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--linha)}\n.kpis.seis{grid-template-columns:repeat(3,1fr)}\n.kpis .k{padding:clamp(16px,2.6vh,26px) 22px clamp(16px,2.6vh,26px) 0;border-bottom:1px solid var(--linha)}\n.kpis .k .kt{font-family:var(--ext);font-size:clamp(13.5px,1.25vw,16px);line-height:1.35}\n.kpis .k p{font-size:12.5px;color:var(--muted);line-height:1.6;margin-top:8px}\n.kpis .k .kb{font-family:var(--sf);font-size:11.5px;color:var(--accent);margin-top:8px;line-height:1.5}\n@media(max-width:900px){.kpis,.kpis.seis{grid-template-columns:1fr 1fr}}\n@media(max-width:600px){.kpis,.kpis.seis{grid-template-columns:1fr}}\n\n/* ---------- cronograma ---------- */\n.ondas{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3vw,48px);\n  position:relative;padding-top:clamp(30px,5vh,52px)}\n.ondas::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:var(--linha)}\n.onda{position:relative}\n.onda::before{content:'';position:absolute;top:calc(-1 * clamp(30px,5vh,52px));left:0;\n  width:9px;height:9px;border-radius:50%;background:var(--accent);transform:translateY(-4px)}\n.onda .fase{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:10px}\n.onda .t{font-family:var(--ext);font-size:clamp(17px,1.8vw,24px);margin-bottom:18px}\n.onda ul{list-style:none}\n.onda li{font-size:14px;line-height:1.6;color:var(--muted);padding:9px 0;border-bottom:1px solid var(--linha)}\n@media(max-width:820px){.ondas{grid-template-columns:1fr}.onda::before{display:none}}\n\n/* ---------- investimento ---------- */\n.custos{border-top:1px solid var(--linha);max-width:880px}\n.custos .row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;gap:24px;\n  padding:clamp(10px,1.8vh,18px) 0;border-bottom:1px solid var(--linha)}\n.custos .row .t{font-family:var(--ext);font-size:clamp(15px,1.5vw,20px)}\n.custos .row .d{font-size:13px;color:var(--muted);margin-top:5px;max-width:56ch;line-height:1.6}\n.custos .row .v{font-family:var(--ext);font-size:clamp(19px,2.1vw,30px);\n  font-variant-numeric:tabular-nums;white-space:nowrap}\n.custos .row .v em{font-style:normal;color:var(--muted);font-family:var(--lt);font-size:.55em;\n  letter-spacing:.1em}\n.custos .total .v{color:var(--accent)}\n.cond{margin-top:clamp(14px,2.2vh,22px);font-size:13px;line-height:1.7;color:var(--muted);max-width:64ch}\n\n/* ---------- listas de compromisso ---------- */\n.klist{list-style:none;margin-top:6px}\n.klist li{font-size:13.5px;line-height:1.6;color:var(--ink);padding:10px 0;\n  border-bottom:1px solid var(--linha)}\n.klist li b{font-weight:600}\n\n/* ---------- fechamento ---------- */\n.dark{color:#fff}\n.dark .mid{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;\n  text-align:center;gap:34px}\n.dark .sub{font-family:var(--lt);font-size:clamp(13px,1.4vw,17px);letter-spacing:.14em;\n  text-transform:uppercase;opacity:.92}\n.fecho .mid{gap:44px}\n.fecho .frase{font-family:var(--lt);font-size:clamp(16px,1.8vw,25px);line-height:1.65;\n  letter-spacing:.025em;max-width:44ch;text-wrap:balance}\n.fecho .frase i{font-style:normal;opacity:.65}\n.ring{width:clamp(54px,5.4vw,80px);height:auto;display:block}\n.ring.branco{filter:brightness(0) invert(1)}\n@media(max-width:820px){.fecho .frase{font-size:15px}}\n\n/* ---------- mobile ---------- */\n@media(max-width:820px){\n  .deckroot{scroll-snap-type:y proximity}\n  /* véu de papel sob o contador, como o do header: em slide mais alto que a tela\n     o texto passaria por baixo do chrome */\n  .deckwrap::after{content:'';position:absolute;left:0;right:0;bottom:0;height:56px;z-index:35;\n    pointer-events:none;background:linear-gradient(to top,var(--paper) 42%,rgba(245,244,240,0));\n    height:78px}\n  .deckwrap.on-dark::after{display:none}\n  nav.dots{display:none}\n  section{padding:72px 20px 82px}\n  .counter,.hint{bottom:34px}\n  .eyebrow{margin-bottom:18px}\n  .capa{padding:60px var(--mx) 96px}\n  .capa .frame{min-height:42svh}\n  .capa .frame .mid{gap:18px;padding:0 20px}\n  .capa .frame .sub{font-size:10.5px;letter-spacing:.12em;text-wrap:balance;line-height:1.7}\n  .capa .lockup{gap:14px}\n  .capa .lockup .d2m{height:25px}\n  .capa .lockup svg.cbm{height:20px}\n  .capa .lockup .plus{font-size:17px}\n  .capa .caption{flex-direction:column;align-items:flex-start;gap:10px;padding:18px 2px 0}\n  .capa .caption .frase-cap{font-size:15px;line-height:1.5;max-width:34ch}\n}\n\n/* ---------- gate ---------- */\n.gwrap{position:relative;height:100vh;height:100svh;overflow:hidden;display:flex;\n  align-items:center;justify-content:center;background:var(--dark);font-family:var(--lt)}\n.gcard{position:relative;z-index:1;background:var(--paper);border-radius:20px;\n  padding:40px 36px 32px;width:min(380px,88vw);text-align:center;\n  box-shadow:0 30px 80px rgba(0,0,0,.45)}\n.glock{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:14px}\n.glock img{filter:brightness(0);display:block}\n.glock svg.gcb{height:17px;width:auto;display:block;color:var(--ink)}\n.glock span{font-family:var(--lt);font-size:15px;color:var(--ink)}\n.gsub{font-family:var(--lt);font-size:10px;letter-spacing:.24em;text-transform:uppercase;\n  color:var(--muted);margin-bottom:26px}\n.ginput{width:100%;box-sizing:border-box;padding:13px 14px;border:1px solid var(--linha);\n  border-radius:10px;background:#fff;font-size:15px;text-align:center;outline:none;\n  font-family:var(--sf);color:var(--ink)}\n.ginput:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(150,87,15,.16)}\n.gbtn{width:100%;margin-top:12px;padding:13px;border:none;border-radius:10px;\n  background:var(--accent);color:#fff;font-size:12px;letter-spacing:.24em;\n  text-transform:uppercase;cursor:pointer;font-family:var(--lt)}\n.gbtn:hover{filter:brightness(.9)}\n.gerr{display:none;color:var(--bad);font-size:13px;margin-top:12px;font-family:var(--sf)}\n.shake{animation:gsh .4s}\n@keyframes gsh{0%,100%{transform:translateX(0)}20%{transform:translateX(-9px)}\n  40%{transform:translateX(9px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}\n  `,\n  render(){ return `<div class=\"deckhost\"></div>` },\n  mounted(d2,root){\n    injectFonts()\n    let ok=OPEN\n    try{ if(sessionStorage.getItem('deckJohnJohnUnlocked')==='1') ok=true }catch(e){}\n    try{ if((location.search||'').indexOf('k='+PASS)>-1) ok=true }catch(e){}\n    if(ok) phase='deck'\n    sync(d2,root)\n  },\n  updated(d2,root){ sync(d2,root) }\n}"}}}}}}}},{"_id":"6a9acde99e4fa8f6878f53b4","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"veste-deck-proposta","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"/* ───────────────────────────────────────────────────────────────────────────\n   Deck — Proposta comercial · Veste S/A (grupo)\n   Provador virtual com IA para Le Lis, John John, Bo.Bô, Individual e Dudalina\n   Kit: apresentacao-data2 (Akzidenz Ext + logo oficial Data²) · 18 slides\n   Recriado a partir de proposta-veste.people-gabi-carmo, com o discurso\n   corrigido: a prova NÃO promete tamanho/caimento; o número da POC é\n   conversão de visitantes; troca/devolução é a leitura pós-janela.\n   ─────────────────────────────────────────────────────────────────────────── */\n\nconst SYS   = 'veste-app'\nconst F_EXT = `https://${SYS}.data2.media/file/iNSYotTKA1cv_fJAa1K4E`\nconst F_LT  = `https://${SYS}.data2.media/file/6bERTVll1aRDU3Xo9NghW`\nconst LD2   = `https://${SYS}.data2.media/file/WLXAyS4HOHvTFytXu7S4P`\nconst RING  = `https://${SYS}.data2.media/file/TA2eFNVhnB7dlI9EPAEWb`\nconst VESTE = `https://${SYS}.data2.media/file/F5sYItInqXvAlz2_oR4AO`\nconst RUSH  = `https://${SYS}.data2apis.com/rush`\n\nconst POC_URL = 'https://veste.people-gabi-carmo.next.data2.app/'\nconst PASS    = 'veste2026'\nconst OPEN    = false\n\nconst FONT_CSS = `@font-face{font-family:'AkzExt';src:url(${F_EXT}) format('opentype');font-weight:400;font-display:block}@font-face{font-family:'AkzExtLight';src:url(${F_LT}) format('opentype');font-weight:300;font-display:block}`\n\nconst VS = (cls) => `<img class=\"${cls || ''}\" src=\"${VESTE}\" alt=\"Veste\">`\nconst FUNDO = `<div class=\"bg\"><div class=\"grid\"></div><img class=\"mark\" src=\"${VESTE}\" alt=\"\"></div>`\n\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) => {\n  ua = String(ua || '')\n  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' : '—'\n  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' : ''\n  return os + (br ? ' · ' + br : '')\n}\n\nconst GATE = `<div class=\"gwrap\">${FUNDO}\n  <div class=\"gcard\">\n    <div class=\"glock\"><img src=\"${LD2}\" style=\"height:20px\" alt=\"Data²\"><span>+</span>${VS('gvs')}</div>\n    <div class=\"gsub\">Proposta · Veste S/A</div>\n    <input class=\"ginput\" type=\"password\" placeholder=\"Senha de acesso\" autocomplete=\"off\"/>\n    <button class=\"gbtn\">Entrar</button>\n    <div class=\"gerr\">Senha incorreta — tente novamente.</div>\n  </div></div>`\n\nconst DECK = `<div class=\"deckwrap\">\n<header><img class=\"d2\" src=\"${LD2}\" alt=\"Data²\">${VS('hvs')}</header>\n<div class=\"counter\" id=\"counter\">01 — 17</div>\n<div class=\"hint\">↓ navegue</div>\n<nav class=\"dots\" id=\"dots\" aria-label=\"Slides\"></nav>\n<div class=\"deckroot\">\n\n  <!-- 01 · CAPA -->\n  <section class=\"capa\" id=\"capa\">\n    <div class=\"frame rv\">${FUNDO}\n      <div class=\"mid\">\n        <div class=\"lockup\">\n          <img class=\"d2m\" src=\"${LD2}\" alt=\"Data²\"><span class=\"plus\">+</span>${VS('vsm')}\n        </div>\n        <div class=\"sub\">Provador virtual com IA · Proposta comercial</div>\n        <div class=\"brands\">Le Lis · John John · Bo.Bô · Individual · Dudalina</div>\n      </div>\n    </div>\n    <div class=\"caption rv\">\n      <div class=\"frase-cap\">We build Data² while imagining a world where things just … work.</div>\n      <div class=\"meta\">Proposta · Setembro 2026</div>\n    </div>\n  </section>\n\n  <!-- 02 · ÍNDICE -->\n  <section id=\"indice\">\n    <div class=\"eyebrow rv\">Índice</div>\n    <div class=\"indice rv\">\n      <a href=\"#proposta\"><span class=\"n\">01</span><span class=\"t\">A proposta</span></a>\n      <a href=\"#perda\"><span class=\"n\">02</span><span class=\"t\">O que a marca perde hoje</span></a>\n      <a href=\"#provador\"><span class=\"n\">03</span><span class=\"t\">O provador</span></a>\n      <a href=\"#limites\"><span class=\"n\">04</span><span class=\"t\">O que a prova responde</span></a>\n      <a href=\"#arq\"><span class=\"n\">05</span><span class=\"t\">Arquitetura e integração</span></a>\n      <a href=\"#escopo\"><span class=\"n\">06</span><span class=\"t\">Escopo da entrega</span></a>\n      <a href=\"#poc\"><span class=\"n\">07</span><span class=\"t\">A POC no período</span></a>\n      <a href=\"#medir\"><span class=\"n\">08</span><span class=\"t\">O que vamos medir</span></a>\n      <a href=\"#comercial\"><span class=\"n\">09</span><span class=\"t\">Condições comerciais</span></a>\n      <a href=\"#dep1\"><span class=\"n\">10</span><span class=\"t\">Depois do período · imagem</span></a>\n      <a href=\"#dep2\"><span class=\"n\">11</span><span class=\"t\">Depois do período · plataforma</span></a>\n      <a href=\"#dep3\"><span class=\"n\">12</span><span class=\"t\">Custo de operação por tier</span></a>\n      <a href=\"#respons\"><span class=\"n\">13</span><span class=\"t\">Responsabilidades</span></a>\n      <a href=\"#fora\"><span class=\"n\">14</span><span class=\"t\">Fora do escopo</span></a>\n    </div>\n  </section>\n\n  <!-- 03 · A PROPOSTA -->\n  <section class=\"statement\" id=\"proposta\">\n    <div class=\"eyebrow rv\">A proposta · <b>Veste S/A</b></div>\n    <div class=\"split\">\n      <h1 class=\"rv\">Um provador virtual para as cinco marcas do grupo.</h1>\n      <p class=\"apoio destaque rv\">Le Lis, John John, Bo.Bô, Individual e Dudalina em uma engine\n        só. Sem custo de sprint e com 30 dias de geração de imagem liberada. Esta proposta define\n        o que a Data² entrega, qual número a POC precisa mover e o que passa a valer depois.</p>\n    </div>\n  </section>\n\n  <!-- 04 · O QUE A MARCA PERDE HOJE -->\n  <section id=\"perda\">\n    <div class=\"eyebrow rv\">O problema</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(30px,6vh,60px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(28px,4vw,56px);line-height:1.12;max-width:16ch\">\n        A peça só é experimentada depois de comprada.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Quem compra online decide pela foto da modelo. A\n        primeira vez que vê a peça em si mesmo é quando o pedido chega em casa. Três coisas\n        nascem daí.</p>\n    </div>\n    <div class=\"pilares\">\n      <div class=\"pilar rv d2\"><span class=\"k\">01 · Troca e devolução</span>\n        <div class=\"t\">O que volta para o CD</div>\n        <p>Logística reversa, peça que retorna ao estoque fora de estação e crédito que o\n           cliente gasta onde quiser. A devolução cobra duas vezes: o frete e a margem da peça\n           parada.</p></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">02 · A peça que não é usada</span>\n        <div class=\"t\">Comprou e não veste</div>\n        <p>Nem toda compra frustrada volta. Parte fica no armário com a etiqueta inteira, e a\n           marca fica registrada como a que não deu certo. Não aparece no índice de devolução —\n           aparece na recompra que não acontece.</p></div>\n      <div class=\"pilar rv d4\"><span class=\"k\">03 · O visitante que sai sem comprar</span>\n        <div class=\"t\">A maior parte do tráfego</div>\n        <p>A maioria de quem entra no site sai sem levar nada. Uma parte dessa saída é a dúvida\n           sobre como a peça fica nele — e a foto da modelo não responde isso.</p></div>\n    </div>\n  </section>\n\n  <!-- 05 · O PROVADOR -->\n  <section id=\"provador\">\n    <div class=\"eyebrow rv\">O provador</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(30px,6vh,60px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(28px,4vw,56px);line-height:1.12;max-width:16ch\">\n        A peça na foto do próprio cliente, em segundos.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Ele manda uma foto, escolhe a peça no catálogo da\n        marca e vê o resultado. Cada prova fica salva no nome dele, com o preço e o caminho de\n        volta para a página do produto.</p>\n    </div>\n    <div class=\"pilares\">\n      <div class=\"pilar rv d2\"><span class=\"k\">01 · Provador</span>\n        <div class=\"t\">A peça no lugar da que ele está usando</div>\n        <p>A roupa atual é removida antes da troca, e o enquadramento da foto original é\n           preservado — sem cortar cabeça nem pés.</p></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">02 · Meu Look</span>\n        <div class=\"t\">Várias peças, uma foto</div>\n        <p>Camisa, calça e alfaiataria provadas em sequência, sobre o resultado anterior. É como\n           o cliente compra de verdade: por look, não por item.</p></div>\n      <div class=\"pilar rv d4\"><span class=\"k\">03 · Minhas Provas</span>\n        <div class=\"t\">A prova vira vitrine</div>\n        <p>Cada prova fica salva com preço e link para a página do produto. O cliente volta para\n           a galeria dele e compra de lá, dias depois.</p></div>\n    </div>\n    <div class=\"rv d5\"><a class=\"chip\" href=\"${POC_URL}\" target=\"_blank\" rel=\"noopener\">Ver o provador funcionando</a></div>\n  </section>\n\n  <!-- 06 · LIMITES DA PROVA -->\n  <section id=\"limites\">\n    <div class=\"eyebrow rv\">Limites da prova</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(26px,5vh,50px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:17ch\">\n        O provador mostra a peça em você. Ele não mede o seu corpo.</h2>\n      <p class=\"apoio destaque rv\" style=\"margin-top:0\">A prova responde se a peça combina com\n        quem está na foto. Ela não diz qual é o seu tamanho, e esta proposta não vende isso. A\n        distinção é o que faz o resultado do período ser discutível com número na mesa.</p>\n    </div>\n    <div class=\"pilares dois\">\n      <div class=\"pilar rv d2\"><span class=\"k\">A prova responde</span>\n        <ul class=\"klist\">\n          <li><b>Como fica em mim</b> — a peça no corpo do cliente, e não no da modelo</li>\n          <li><b>Cor, comprimento e proporção</b> — o que a foto de catálogo achata</li>\n          <li><b>A peça dentro de um look</b> — a camisa junto da calça que ele já escolheu</li>\n          <li><b>Se ele quer levar</b> — a decisão sai da imaginação e vai para a tela</li>\n        </ul></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">A prova não responde</span>\n        <ul class=\"klist neg\">\n          <li><b>Qual é o tamanho</b> — P, M ou G continua sendo tabela de medidas</li>\n          <li><b>Se vai apertar ou sobrar</b> — cintura, manga, comprimento de perna</li>\n          <li><b>Como o tecido cai em movimento</b> — peso e caimento real da peça</li>\n          <li><b>Nada disso entra na leitura</b> — o número do período é conversão, não caimento</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 07 · ARQUITETURA -->\n  <section id=\"arq\">\n    <div class=\"eyebrow rv\">Arquitetura e integração</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,42px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Integramos por leitura. Nada do que funciona muda.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">O provador consulta a API de catálogo de cada\n        marca no momento da prova. Não há réplica de base, carga noturna nem alteração no tema\n        das lojas.</p>\n    </div>\n    <div class=\"arq rv\">\n      <div class=\"col\">\n        <div class=\"col-h\">Sistemas da Veste · intocados</div>\n        <div class=\"box\"><div class=\"bn\">VTEX</div>\n          <div class=\"bd\">Catálogo, PDP, carrinho e checkout seguem como estão</div></div>\n        <div class=\"box\"><div class=\"bn\">Sites das cinco marcas</div>\n          <div class=\"bd\">O provador entra como um bloco no tema, sem reescrever a loja</div></div>\n      </div>\n      <div class=\"link\"><span>leitura · API</span></div>\n      <div class=\"core\">\n        <div class=\"ch\">Data² · engine de provador</div>\n        <div class=\"box\"><div class=\"bn\">Leitura de catálogo</div>\n          <div class=\"bd\">Consulta as categorias reais de cada marca na hora da prova</div></div>\n        <div class=\"box\"><div class=\"bn\">Provador de IA</div>\n          <div class=\"bd\">Checa a foto, decide o que remover e aplica a peça mantendo o\n            enquadramento</div></div>\n        <div class=\"box\"><div class=\"bn\">Painel de resultados</div>\n          <div class=\"bd\">Registra cada prova, cada clique em Comprar e os produtos mais\n            provados</div></div>\n      </div>\n      <div class=\"link\"><span>embed</span></div>\n      <div class=\"col\">\n        <div class=\"col-h\">Cliente final</div>\n        <div class=\"box\"><div class=\"bn\">Navegador</div>\n          <div class=\"bd\">Dentro do site, na mesma sessão de compra</div></div>\n        <div class=\"box\"><div class=\"bn\">Minhas Provas</div>\n          <div class=\"bd\">Galeria por e-mail, com preço e Comprar apontando para a PDP</div></div>\n      </div>\n    </div>\n  </section>\n\n  <!-- 08 · ESCOPO DA ENTREGA -->\n  <section id=\"escopo\">\n    <div class=\"eyebrow rv\">Escopo da entrega</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(20px,3.4vh,34px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">\n        Uma engine, cinco camadas de marca.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A engine de provador está publicada e testada com\n        catálogo real. A sprint acrescenta a camada de cada marca — catálogo, prompt calibrado e\n        o bloco de embed.</p>\n    </div>\n    <div class=\"leg rv\">\n      <span><i class=\"ok\"></i>Validado — já roda na engine</span>\n      <span><i class=\"nv\"></i>Novo — específico das marcas da Veste</span>\n    </div>\n    <div class=\"frentes rv\">\n      <div class=\"f\">\n        <div class=\"fn\">Provador <span class=\"ct\">3 validados · 1 novo</span></div>\n        <div class=\"fi ok\">Checagem da foto antes de gerar</div>\n        <div class=\"fi ok\">Remove a peça atual em vez de sobrepor</div>\n        <div class=\"fi ok\">Preserva o enquadramento — não corta cabeça nem pés</div>\n        <div class=\"fi nv\">Prompt calibrado por marca, do jeans à alfaiataria</div>\n      </div>\n      <div class=\"f\">\n        <div class=\"fn\">Catálogo <span class=\"ct\">1 validado · 2 novos</span></div>\n        <div class=\"fi ok\">Leitura ao vivo, sem réplica de base</div>\n        <div class=\"fi nv\">Categorias das cinco marcas, masculino e feminino</div>\n        <div class=\"fi nv\">Filtro que deixa fora calçados, acessórios e cartões</div>\n      </div>\n      <div class=\"f\">\n        <div class=\"fn\">Meu Look e Minhas Provas <span class=\"ct\">3 validados</span></div>\n        <div class=\"fi ok\">Provar várias peças em sequência na mesma foto</div>\n        <div class=\"fi ok\">Galeria por e-mail, com preço e Comprar</div>\n        <div class=\"fi ok\">Cliente remove a prova que não quiser guardar</div>\n      </div>\n      <div class=\"f\">\n        <div class=\"fn\">Painel de resultados <span class=\"ct\">2 validados · 2 novos</span></div>\n        <div class=\"fi ok\">Provas geradas, conclusão e tempo de geração</div>\n        <div class=\"fi ok\">Cliques em Comprar e produtos mais provados</div>\n        <div class=\"fi nv\">Comparação contra a linha de base de cada site</div>\n        <div class=\"fi nv\">Visão consolidada do grupo, marca por marca</div>\n      </div>\n    </div>\n  </section>\n\n  <!-- 09 · A POC NO PERÍODO -->\n  <section id=\"poc\">\n    <div class=\"eyebrow rv\">O período de prova</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(20px,3.4vh,34px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        Trinta dias no ar, com três marcos de leitura.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A janela começa no dia em que o provador entra no\n        primeiro site. As datas de calendário são fixadas na confirmação desta proposta.</p>\n    </div>\n    <div class=\"ondas rv\">\n      <div class=\"onda\"><div class=\"fase\">Dia 1</div><div class=\"t\">No ar</div>\n        <ul>\n          <li>Provador publicado no site da primeira marca</li>\n          <li>Geração de imagem liberada, até 2.000 provas</li>\n          <li>Linha de base de conversão registrada com o time da Veste</li>\n          <li>Painel medindo desde a primeira prova</li>\n        </ul></div>\n      <div class=\"onda\"><div class=\"fase\">Dia 15</div><div class=\"t\">Primeira leitura</div>\n        <ul>\n          <li>Volume de provas e taxa de conclusão</li>\n          <li>Ajuste de prompt e de catálogo, se preciso</li>\n          <li>Extensão para as marcas seguintes</li>\n        </ul></div>\n      <div class=\"onda\"><div class=\"fase\">Dia 30</div><div class=\"t\">Análise e acordo</div>\n        <ul>\n          <li>Conversão de quem provou contra a média do site</li>\n          <li>Cliques em Comprar depois da prova</li>\n          <li>Resultados analisados e continuidade em acordo pago</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 10 · O QUE VAMOS MEDIR -->\n  <section id=\"medir\">\n    <div class=\"eyebrow rv\">O que vamos medir</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(22px,3.8vh,42px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:17ch\">\n        A conversão de visitantes é o número que a POC prova.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Tempo de página e engajamento ficam de fora. O\n        período responde se quem prova compra mais. A troca é a segunda leitura, e ela só fecha\n        depois da janela de devolução da Veste.</p>\n    </div>\n    <div class=\"kpis rv\">\n      <div class=\"k\"><div class=\"kt\">Conversão de visitantes</div>\n        <p>Taxa de conversão das sessões que geraram prova, contra a média do site no mesmo\n           período. É o que fica provado dentro dos 30 dias.</p>\n        <div class=\"kb\">Linha de base: a conversão do site hoje, informada no dia 1</div></div>\n      <div class=\"k\"><div class=\"kt\">Clique em Comprar após a prova</div>\n        <p>Quantas provas terminam em clique para a página do produto. Mede intenção direta, não\n           curiosidade.</p>\n        <div class=\"kb\">Registrado pela engine desde a primeira prova</div></div>\n      <div class=\"k\"><div class=\"kt\">Troca e devolução</div>\n        <p>Cada pedido que veio de uma prova fica marcado. Quando a janela de devolução fecha, o\n           índice de troca desses pedidos é comparado com o do resto da base.</p>\n        <div class=\"kb\">Não fecha em 30 dias — a marcação sai pronta da POC</div></div>\n      <div class=\"k\"><div class=\"kt\">Como medimos</div>\n        <p>Cada prova, cadastro e clique é gravado com produto e horário. O painel fica aberto\n           durante o período, para os dois lados olharem o mesmo número.</p></div>\n    </div>\n  </section>\n\n  <!-- 11 · CONDIÇÕES COMERCIAIS -->\n  <section id=\"comercial\">\n    <div class=\"eyebrow rv\">Condições comerciais</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.6vh,26px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.2vw,44px);line-height:1.12;max-width:24ch\">\n        Trinta dias liberados. Depois, um acordo pago.</h2>\n      <p class=\"apoio destaque rv\" style=\"margin-top:0\">A construção e o teste correm por conta\n        da Data², com a geração de imagem liberada por 30 dias, até 2.000 provas. No fim da\n        janela os resultados são analisados e a continuidade vira um acordo pago.</p>\n    </div>\n    <div class=\"custos rv\">\n      <div class=\"row\"><div>\n        <div class=\"t\">Construção da POC</div>\n        <div class=\"d\">Engine de provador com o catálogo das cinco marcas ao vivo, Meu Look,\n          Minhas Provas, painel de resultados e o bloco de embed para os sites.</div>\n      </div><div class=\"v\">R$ 0</div></div>\n      <div class=\"row\"><div>\n        <div class=\"t\">Teste no período</div>\n        <div class=\"d\">Acompanhamento durante a janela: ajuste de prompt, ajuste de catálogo e\n          leitura dos números com o time da Veste.</div>\n      </div><div class=\"v\">R$ 0</div></div>\n      <div class=\"row\"><div>\n        <div class=\"t\">Geração de imagem · 30 dias</div>\n        <div class=\"d\">Até 2.000 provas na janela. Catálogo, storage e acessos incluídos.</div>\n      </div><div class=\"v\">Liberada</div></div>\n      <div class=\"row total\"><div>\n        <div class=\"t\">Depois dos 30 dias</div>\n        <div class=\"d\">Continuidade dimensionada pelo volume que a POC mostrou.</div>\n      </div><div class=\"v\">Acordo pago</div></div>\n    </div>\n    <p class=\"cond rv\">Ao se aproximar das 2.000 provas, a Data² avisa antes de qualquer\n      cobrança. Evolução de escopo é orçada em separado.</p>\n  </section>\n\n  <!-- 12 · DEPOIS DO PERÍODO · IMAGEM -->\n  <section id=\"dep1\">\n    <div class=\"eyebrow rv\">Depois do período · 01</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(18px,3vh,30px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">\n        Cada prova gera uma imagem.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Quando a janela fecha, a imagem passa a ser\n        cobrada. Quanto maior o volume mensal contratado, menor o preço por imagem. Valores em\n        US$.</p>\n    </div>\n    <div class=\"tbw rv\"><table class=\"tb\"><thead><tr>\n      <th>Faixa</th><th>Mínimo mensal</th><th>Desconto</th><th>Preço/imagem</th>\n      <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    <p class=\"cond rv\">O tier é escolhido pelo volume mensal que a Veste se compromete a\n      consumir. Abaixo do mínimo, vale o preço cheio da faixa sem compromisso.</p>\n  </section>\n\n  <!-- 13 · DEPOIS DO PERÍODO · PLATAFORMA -->\n  <section id=\"dep2\">\n    <div class=\"eyebrow rv\">Depois do período · 02</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(18px,3vh,30px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">\n        O que o provador consome da plataforma.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Custos de uso da plataforma Data² para operar o\n        provador depois do período, conforme a tabela pública. Valores em US$.</p>\n    </div>\n    <div class=\"tbw rv\" style=\"max-width:640px\"><table class=\"tb\" style=\"min-width:380px\"><thead><tr>\n      <th>Item</th><th>Preço</th></tr></thead><tbody>\n      <tr><td>Tokens de IA · análise da foto</td><td><strong>US$ 0,0001</strong> / token</td></tr>\n      <tr><td>Escrita de dados</td><td><strong>US$ 0,0005</strong> / registro</td></tr>\n      <tr><td>Leitura de dados</td><td><strong>US$ 0,00001</strong> / registro</td></tr>\n      <tr><td>Rush · listener (backend)</td><td><strong>US$ 0,0001</strong> / invocação</td></tr>\n      <tr><td>Connect · request (catálogo)</td><td><strong>US$ 0,0001</strong> / request</td></tr>\n      <tr><td>Media · armazenamento das provas</td><td><strong>US$ 1,00</strong> / GB</td></tr>\n    </tbody></table></div>\n    <a class=\"chip\" href=\"https://data2.app/pricing\" target=\"_blank\" rel=\"noopener\">Ver a tabela completa em data2.app/pricing</a>\n  </section>\n\n  <!-- 14 · DEPOIS DO PERÍODO · OPERAÇÃO POR TIER -->\n  <section id=\"dep3\">\n    <div class=\"eyebrow rv\">Depois do período · 03</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(18px,3vh,30px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">\n        Custo de operação estimado, mês a mês.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Imagem e plataforma somadas, por tier. A conta é\n        estimada — o que vale na fatura é o volume medido.</p>\n    </div>\n    <div class=\"tbw rv\"><table class=\"tb\"><thead><tr>\n      <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=\"cond rv\">Plataforma estimada com base em cerca de 1.000 tokens de IA por prova, na\n      análise da foto — perto de 90% do custo de plataforma e a principal alavanca. Enviando a\n      foto de análise reduzida (~500 tokens por prova), essa parcela cai aproximadamente pela\n      metade. Valores em US$, conforme data2.app/pricing.</p>\n  </section>\n\n  <!-- 15 · RESPONSABILIDADES -->\n  <section id=\"respons\">\n    <div class=\"eyebrow rv\">Responsabilidades</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,44px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        O que cada lado põe na mesa.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">A entrega não depende de projeto de TI. Depende de\n        quatro liberações pequenas — e cada uma tem dono.</p>\n    </div>\n    <div class=\"pilares dois\">\n      <div class=\"pilar rv d2\"><span class=\"k\">A Data² entrega</span>\n        <ul class=\"klist\">\n          <li><b>Engine no ar</b> — provador, Meu Look e Minhas Provas publicados e monitorados</li>\n          <li><b>Bloco de embed</b> — um trecho para o time de front colar, sem alterar o tema</li>\n          <li><b>Painel de resultados</b> — aberto à Veste durante o período</li>\n          <li><b>Acompanhamento</b> — ajuste de prompt e de catálogo dentro da janela</li>\n        </ul></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">A Veste libera</span>\n        <ul class=\"klist\">\n          <li><b>Publicação do embed</b> — um contato de plataforma por marca que coloca o bloco no ar</li>\n          <li><b>Onde o provador entra</b> — página de produto, home ou landing de campanha</li>\n          <li><b>Linha de base de conversão</b> — o número de cada site hoje, para a comparação</li>\n          <li><b>Uso de imagem</b> — aprovação para o cliente gerar e guardar a própria prova</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 16 · FORA DO ESCOPO -->\n  <section id=\"fora\">\n    <div class=\"eyebrow rv\">Limites</div>\n    <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(24px,4vh,44px)\">\n      <h2 class=\"rv\" style=\"font-size:clamp(26px,3.6vw,52px);line-height:1.12;max-width:16ch\">\n        O que esta proposta não faz.</h2>\n      <p class=\"apoio rv\" style=\"margin-top:0\">Dizer o limite agora é o que faz o período\n        terminar sem discussão. Nada da coluna da direita está contratado aqui, e evolução de\n        escopo é orçada em separado.</p>\n    </div>\n    <div class=\"pilares dois\">\n      <div class=\"pilar rv d2\"><span class=\"k\">Fora do escopo</span>\n        <ul class=\"klist neg\">\n          <li><b>Recomendação de tamanho</b> — a prova não mede o corpo do cliente</li>\n          <li><b>Integração com ERP, OMS ou estoque</b> — o provador lê catálogo, e só</li>\n          <li><b>App nativo</b> — o provador roda no navegador</li>\n          <li><b>Fechar o ciclo de troca em 30 dias</b> — a janela de devolução é mais longa</li>\n        </ul></div>\n      <div class=\"pilar rv d3\"><span class=\"k\">Fica para depois do período</span>\n        <ul class=\"klist\">\n          <li>Provador na página de produto de toda a base</li>\n          <li>Leitura de troca com a janela de devolução fechada</li>\n          <li>Campanha com a prova que o próprio cliente gerou</li>\n          <li>Provador na loja física</li>\n        </ul></div>\n    </div>\n  </section>\n\n  <!-- 17 · FECHAMENTO -->\n  <section class=\"dark fecho\" data-dark id=\"fim\">${FUNDO}\n    <div class=\"mid\">\n      <img class=\"ring branco rv\" style=\"width:52px\" src=\"${RING}\" alt=\"\">\n      <p class=\"frase rv\">We build Data² while imagining a world where things just <i>…</i> work.</p>\n      <div class=\"lockup rv\">\n        <img class=\"d2m\" style=\"height:26px\" src=\"${LD2}\" alt=\"Data²\">\n        <span class=\"plus\" style=\"font-size:20px\">+</span>${VS('vsm-s')}\n      </div>\n      <div class=\"sub rv\">gabi.carmo@data2.app · +55 (11) 98962-6170 · data2.app</div>\n    </div>\n  </section>\n\n</div></div>`\n\nconst acessosHTML = (st) => {\n  const status = st.status || 'locked'\n  let inner\n  if (status === 'loading') {\n    inner = `<div class=\"acmsg\">Carregando…</div>`\n  } else if (status !== 'done') {\n    inner = `<div class=\"acgate\">\n      <input class=\"ginput acpass\" type=\"password\" placeholder=\"Senha do painel\" autocomplete=\"off\"/>\n      <button class=\"gbtn acbtn\">Entrar</button>\n      ${st.error ? '<div class=\"acerr\">' + esc(st.error) + '</div>' : ''}\n    </div>`\n  } else {\n    const ac = st.ac || {}\n    const recent = ac.recent || []\n    const seen = {}\n    recent.forEach(r => { if (r && r.clientId) seen[r.clientId] = 1 })\n    const rows = recent.slice(0, 60).map(r => `<tr><td>${fmtDT(r.createdAt)}</td><td>${r.event === 'login' ? 'Entrou com a senha' : 'Abriu'}</td><td>${esc(shortDev(r.device))}</td><td class=\"cid\">${esc(String(r.clientId || '').slice(0, 10))}</td></tr>`).join('')\n    inner = `<div class=\"ackpis\">\n        <div class=\"ackpi\"><div class=\"acl\">Aberturas</div><div class=\"acv\">${ac.opens || 0}</div></div>\n        <div class=\"ackpi\"><div class=\"acl\">Entradas com a senha</div><div class=\"acv\">${ac.logins || 0}</div></div>\n        <div class=\"ackpi\"><div class=\"acl\">Dispositivos distintos</div><div class=\"acv\">${Object.keys(seen).length}</div></div>\n      </div>\n      <div class=\"acsub\">Acessos recentes</div>\n      <div class=\"tbw\"><table class=\"tb\"><thead><tr><th>Quando</th><th>Evento</th><th>Dispositivo</th><th>ID do navegador</th></tr></thead><tbody>${rows || '<tr><td>Sem acessos ainda.</td><td>—</td><td>—</td><td>—</td></tr>'}</tbody></table></div>\n      <button class=\"chip acrefresh\" style=\"cursor:pointer;background:none\">Atualizar</button>`\n  }\n  return `<div class=\"acwrap\">\n    <header><img class=\"d2\" src=\"${LD2}\" alt=\"Data²\">${VS('hvs')}</header>\n    <div class=\"acbody\">\n      <div class=\"eyebrow\">Monitoramento · Proposta Veste S/A</div>\n      <h2 class=\"actit\">Quem acessou</h2>\n      ${inner}\n    </div>\n  </div>`\n}\n\nfunction injectFonts() {\n  try {\n    if (!document.getElementById('akz-deck-fonts')) {\n      const st = document.createElement('style')\n      st.id = 'akz-deck-fonts'\n      st.textContent = FONT_CSS\n      document.head.appendChild(st)\n    }\n  } catch (e) {}\n}\n\nfunction initDeck(host) {\n  const wrap = host && host.querySelector('.deckwrap'); if (!wrap) return\n  if (wrap.dataset.init === '1') return\n  wrap.dataset.init = '1'\n  const rootEl = wrap.querySelector('.deckroot')\n  const slides = [].slice.call(rootEl.querySelectorAll('section'))\n  const dots = wrap.querySelector('.dots'), counter = wrap.querySelector('.counter')\n  dots.innerHTML = ''\n  const pad = n => (n < 10 ? '0' : '') + n\n  let cur = 0\n  counter.textContent = pad(1) + ' — ' + pad(slides.length)\n  slides.forEach((s, i) => {\n    const a = document.createElement('a')\n    a.href = '#' + s.id; a.setAttribute('aria-label', 'Slide ' + (i + 1))\n    dots.appendChild(a)\n  })\n  const links = [].slice.call(dots.children)\n  if (links[0]) links[0].classList.add('on')\n\n  const setActive = i => {\n    if (i === cur) return\n    cur = i\n    counter.textContent = pad(cur + 1) + ' — ' + pad(slides.length)\n    links.forEach((l, k) => l.classList.toggle('on', k === cur))\n    wrap.classList.toggle('on-dark', slides[cur].hasAttribute('data-dark'))\n    wrap.classList.toggle('last', cur === slides.length - 1)\n  }\n\n  const revIO = new IntersectionObserver(es => {\n    es.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); revIO.unobserve(e.target) } })\n  }, { root: rootEl, rootMargin: '0px 0px 12% 0px', threshold: .01 })\n  slides.forEach(s => revIO.observe(s))\n\n  const ratios = new Array(slides.length).fill(0)\n  const actIO = new IntersectionObserver(es => {\n    es.forEach(e => { const i = slides.indexOf(e.target); if (i > -1) ratios[i] = e.isIntersecting ? e.intersectionRatio : 0 })\n    let bi = cur, bv = -1\n    ratios.forEach((r, i) => { if (r > bv) { bv = r; bi = i } })\n    if (bv > 0) setActive(bi)\n  }, { root: rootEl, threshold: [0, .15, .35, .55, .75, 1] })\n  slides.forEach(s => actIO.observe(s))\n\n  setTimeout(() => {\n    if (!rootEl.querySelector('section.in')) slides.forEach(s => s.classList.add('in'))\n  }, 500)\n\n  const rm = (typeof matchMedia === 'function') && matchMedia('(prefers-reduced-motion: reduce)').matches\n  const go = i => {\n    i = Math.max(0, Math.min(slides.length - 1, i))\n    slides[i].scrollIntoView({ behavior: rm ? 'auto' : 'smooth' })\n  }\n\n  wrap.addEventListener('click', ev => {\n    const a = ev.target.closest && ev.target.closest('a[href^=\"#\"]'); if (!a) return\n    ev.preventDefault()\n    const t = slides.find(s => s.id === a.getAttribute('href').slice(1))\n    if (t) t.scrollIntoView({ behavior: rm ? 'auto' : 'smooth' })\n  })\n\n  window.addEventListener('keydown', e => {\n    if (!wrap.isConnected) return\n    if (e.target && /^(INPUT|TEXTAREA)$/.test(e.target.tagName)) return\n    if (e.key === 'ArrowDown' || e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { e.preventDefault(); go(cur + 1) }\n    if (e.key === 'ArrowUp' || e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); go(cur - 1) }\n    if (e.key === 'Home') { e.preventDefault(); go(0) }\n    if (e.key === 'End') { e.preventDefault(); go(slides.length - 1) }\n  })\n}\n\n/* Este player não re-renderiza de forma confiável quando d2.state muda: a senha\n   gravava a sessão e o gate continuava na tela. Então a fase vive numa variável\n   de módulo e a troca de tela é feita na mão, com innerHTML. */\nlet phase = 'gate'\nlet acState = { status: 'locked' }\nlet clientId = ''\n\nfunction cid() {\n  if (clientId) return clientId\n  try {\n    clientId = window.localStorage.getItem('veste_prop_cid') || ''\n    if (!clientId) {\n      clientId = 'c' + Date.now().toString(36) + Math.floor(Math.random() * 1e6).toString(36)\n      window.localStorage.setItem('veste_prop_cid', clientId)\n    }\n  } catch (e) { clientId = 'c-anon' }\n  return clientId\n}\n\nfunction track(event) {\n  try {\n    /* probe automatizado (inspect_code_component, crawler headless) não é\n       abertura de proposta — não suja o painel de acessos */\n    if (navigator.webdriver === true) return\n    fetch(RUSH + '/veste-prop-track', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        event: event,\n        clientId: cid(),\n        device: String(navigator.userAgent || '').slice(0, 180),\n        ref: String(document.referrer || '')\n      })\n    }).catch(() => {})\n  } catch (e) {}\n}\n\nfunction paintAcessos(host) {\n  host.innerHTML = acessosHTML(acState)\n  const wire = (sel, evt, fn) => { const el = host.querySelector(sel); if (el) el.addEventListener(evt, fn) }\n  wire('.acbtn', 'click', () => {\n    const el = host.querySelector('.acpass')\n    const pw = (el && el.value) || ''\n    if (!pw) { acState = { status: 'locked', error: 'Digite a senha.' }; paintAcessos(host); return }\n    loadAcessos(host, pw)\n  })\n  wire('.acpass', 'keydown', (e) => { if (e.key === 'Enter') { const b = host.querySelector('.acbtn'); if (b) b.click() } })\n  wire('.acrefresh', 'click', () => {\n    let pw = ''\n    try { pw = window.localStorage.getItem('veste_prop_admin') || '' } catch (e) {}\n    loadAcessos(host, pw)\n  })\n}\n\nfunction loadAcessos(host, pw) {\n  acState = { status: 'loading' }\n  paintAcessos(host)\n  fetch(RUSH + '/veste-prop-acessos', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ password: pw })\n  })\n    .then(r => r.json())\n    .then(r => {\n      const s = (r && r.data) ? r.data : r\n      if (!s || s.ok !== true) {\n        try { window.localStorage.removeItem('veste_prop_admin') } catch (e) {}\n        acState = { status: 'locked', error: pw ? 'Senha incorreta.' : null }\n      } else {\n        try { window.localStorage.setItem('veste_prop_admin', pw) } catch (e) {}\n        acState = { status: 'done', ac: s }\n      }\n      paintAcessos(host)\n    })\n    .catch(err => {\n      acState = { status: 'locked', error: 'Falha ao carregar: ' + String((err && err.message) || err) }\n      paintAcessos(host)\n    })\n}\n\nfunction mountGate(host) {\n  const card = host.querySelector('.gcard'); if (!card || card.dataset.wired === '1') return\n  card.dataset.wired = '1'\n  const inp = host.querySelector('.ginput'), btn = host.querySelector('.gbtn')\n  const check = () => {\n    if (!inp) return\n    if (inp.value === PASS) {\n      try { sessionStorage.setItem('deckVesteUnlocked', '1') } catch (e) {}\n      track('login')\n      showDeck(host)\n    } else {\n      const er = host.querySelector('.gerr')\n      if (er) er.style.display = 'block'\n      card.classList.remove('shake'); void card.offsetWidth; card.classList.add('shake')\n      if (inp.select) inp.select()\n    }\n  }\n  if (btn) btn.addEventListener('click', check)\n  if (inp) inp.addEventListener('keydown', e => { if (e.key === 'Enter') check() })\n}\n\nfunction showDeck(host) {\n  phase = 'deck'\n  host.innerHTML = DECK\n  initDeck(host)\n}\n\nfunction sync(d2, root) {\n  const host = (d2 && d2.$ && d2.$('.deckhost')) || ((root || document).querySelector('.deckhost'))\n  if (!host) return\n  if (phase === 'acessos') {\n    if (!host.querySelector('.acwrap')) paintAcessos(host)\n    return\n  }\n  if (phase === 'deck') {\n    if (!host.querySelector('.deckwrap')) host.innerHTML = DECK\n    initDeck(host)\n    return\n  }\n  if (!host.querySelector('.gwrap')) host.innerHTML = GATE\n  mountGate(host)\n}\n\nexport default {\n  css: `\n:host,.deckwrap,.gwrap,.acwrap{\n  --paper:#F5F4F0;\n  --ink:#15161A;\n  --muted:#6A6E77;\n  --accent:#96570F;\n  --accent-soft:#C89A4A;\n  --ok:#2C6B4A;\n  --bad:#A33427;\n  --dark:#17253F;\n  --dark-2:#22314F;\n  --linha:rgba(21,22,26,.13);\n  --mx:clamp(20px,4vw,56px);\n  --ext:'AkzExt','Helvetica Neue',Helvetica,Arial,sans-serif;\n  --lt:'AkzExtLight','Helvetica Neue',Helvetica,Arial,sans-serif;\n  --sf:-apple-system,BlinkMacSystemFont,'SF Pro Text','Inter','Helvetica Neue',Helvetica,Arial,sans-serif;\n}\n*{box-sizing:border-box;margin:0;padding:0}\n.deckhost{display:block;width:100%;height:100%}\n\n/* ---------- arquitetura de scroll ---------- */\n.deckwrap{position:relative;height:100vh;height:100svh;overflow:hidden;\n  background:var(--paper);color:var(--ink);font-family:var(--lt);\n  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}\n.deckroot{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;\n  scroll-snap-type:y proximity;scroll-behavior:smooth;\n  -webkit-overflow-scrolling:touch;overscroll-behavior-y:contain}\n\n/* ---------- chrome ---------- */\nheader{position:absolute;inset:0 0 auto 0;z-index:40;display:flex;\n  justify-content:space-between;align-items:center;padding:22px var(--mx) 34px;\n  pointer-events:none;background:linear-gradient(to bottom,var(--paper) 52%,rgba(245,244,240,0))}\n.deckwrap.on-dark header{background:none}\nheader img.d2{height:16px;display:block;filter:brightness(0);opacity:.92}\nheader img.hvs{height:13px;width:auto;display:block;opacity:.92}\n.deckwrap.on-dark header img.d2{filter:none;opacity:1}\n.deckwrap.on-dark header img.hvs{filter:brightness(0) invert(1);opacity:1}\n.counter{position:absolute;z-index:40;left:var(--mx);bottom:38px;font-family:var(--lt);\n  font-size:11px;letter-spacing:.22em;color:var(--muted)}\n.deckwrap.on-dark .counter{color:rgba(255,255,255,.75)}\nnav.dots{position:absolute;z-index:40;right:clamp(14px,2.4vw,32px);top:50%;\n  transform:translateY(-50%);display:flex;flex-direction:column;gap:12px}\nnav.dots a{width:6px;height:6px;border-radius:50%;background:var(--muted);opacity:.35;\n  transition:opacity .3s,transform .3s}\nnav.dots a.on{opacity:1;transform:scale(1.5);background:var(--ink)}\n.deckwrap.on-dark nav.dots a,.deckwrap.on-dark nav.dots a.on{background:#fff}\n.hint{position:absolute;z-index:40;right:var(--mx);bottom:38px;font-size:11px;\n  letter-spacing:.22em;color:var(--muted);text-transform:uppercase;transition:opacity .4s}\n.deckwrap.last .hint{opacity:0}\n.deckwrap.on-dark .hint{color:rgba(255,255,255,.6)}\n\n/* ---------- slide ---------- */\nsection{min-height:100vh;min-height:100svh;scroll-snap-align:start;position:relative;\n  display:flex;flex-direction:column;justify-content:center;\n  padding:clamp(84px,12vh,140px) clamp(20px,7vw,120px)}\n.eyebrow{font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:clamp(20px,4vh,44px)}\n.eyebrow b{font-weight:400;color:var(--accent)}\n\n/* ---------- reveals ---------- */\n.rv{opacity:0;transform:translateY(26px);\n  transition:opacity .9s cubic-bezier(.2,.6,.2,1),transform .9s cubic-bezier(.2,.6,.2,1)}\nsection.in .rv{opacity:1;transform:none}\nsection.in .rv:nth-child(2),section.in .d2{transition-delay:.12s}\nsection.in .rv:nth-child(3),section.in .d3{transition-delay:.24s}\nsection.in .rv:nth-child(4),section.in .d4{transition-delay:.36s}\nsection.in .rv:nth-child(5),section.in .d5{transition-delay:.48s}\nsection.in .rv:nth-child(6),section.in .d6{transition-delay:.6s}\n\n/* ---------- tipografia ---------- */\nh1,h2{font-family:var(--ext);font-weight:400;text-wrap:balance}\n.apoio{max-width:44ch;font-size:clamp(15px,1.5vw,19px);line-height:1.65;color:var(--muted)}\n.apoio::before{content:'';display:block;width:36px;height:2px;background:var(--accent);\n  margin-bottom:18px}\n.apoio.destaque{color:var(--ink);font-size:clamp(16px,1.7vw,21px);line-height:1.6}\n.split{display:grid;grid-template-columns:1.4fr .8fr;gap:clamp(28px,6vw,90px);align-items:end}\n.split>*{min-width:0}\n@media(max-width:820px){.split{grid-template-columns:1fr}}\n\n.apoio,.lead,.pilar p,.onda li,.custos .row .d,.cond,.arq .box .bd,\n.kpis .k p,.klist li,.tb{font-family:var(--sf)}\n\n:focus-visible{outline:2px solid var(--accent);outline-offset:4px}\n@media (prefers-reduced-motion:reduce){\n  .deckroot{scroll-behavior:auto}\n  .rv{opacity:1;transform:none;transition:none}\n}\n\n/* ---------- fundo gráfico ---------- */\n.bg{position:absolute;inset:0;z-index:0;overflow:hidden;background:\n  radial-gradient(ellipse 70% 60% at 18% 85%,rgba(35,71,127,.66),transparent 62%),\n  radial-gradient(ellipse 50% 45% at 88% 12%,rgba(200,154,74,.24),transparent 60%),\n  linear-gradient(135deg,var(--dark) 0%,var(--dark-2) 55%,#0E1729 100%)}\n.bg .grid{position:absolute;inset:0;\n  background-image:linear-gradient(rgba(255,255,255,.055) 1px,transparent 1px),\n                   linear-gradient(90deg,rgba(255,255,255,.055) 1px,transparent 1px);\n  background-size:64px 64px;\n  mask-image:radial-gradient(ellipse 85% 80% at 50% 50%,#000 30%,transparent 100%);\n  -webkit-mask-image:radial-gradient(ellipse 85% 80% at 50% 50%,#000 30%,transparent 100%)}\n.bg .mark{position:absolute;left:50%;transform:translateX(-50%);bottom:6%;width:74%;height:auto;\n  filter:brightness(0) invert(1);opacity:.055}\n@media(max-width:820px){.bg .mark{width:150%;bottom:12%}}\n\n/* ---------- capa ---------- */\n.capa{padding:clamp(72px,10vh,104px) var(--mx) clamp(62px,9vh,88px);background:var(--paper)}\n.capa .frame{position:relative;border-radius:clamp(18px,2.4vw,30px);overflow:hidden;flex:1;\n  min-height:min(58vh,640px);display:flex;align-items:center;justify-content:center;color:#fff}\n.capa .frame .mid{position:relative;z-index:1;display:flex;flex-direction:column;\n  align-items:center;text-align:center;gap:28px}\n.capa .frame .sub{font-family:var(--lt);font-size:clamp(12px,1.3vw,16px);letter-spacing:.16em;\n  text-transform:uppercase;opacity:.92}\n.capa .frame .brands{font-family:var(--lt);font-size:clamp(10px,1.05vw,12.5px);\n  letter-spacing:.24em;text-transform:uppercase;opacity:.6}\n.capa .caption{display:flex;justify-content:space-between;align-items:baseline;gap:24px;\n  padding:clamp(16px,2.6vh,26px) 0 0;flex-wrap:wrap}\n.capa .caption .frase-cap{font-family:var(--lt);font-size:clamp(14px,1.55vw,19px);\n  color:var(--ink);letter-spacing:.01em}\n.capa .caption .meta{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--muted)}\n\n.lockup{display:flex;align-items:center;gap:clamp(18px,3vw,34px)}\n.lockup img{display:block}\n.lockup .d2m{height:clamp(30px,4.6vw,54px)}\n.lockup img.vsm{height:clamp(20px,2.9vw,34px);width:auto;filter:brightness(0) invert(1)}\n.lockup img.vsm-s{height:17px;width:auto;filter:brightness(0) invert(1)}\n.lockup .plus{font-family:var(--lt);font-size:clamp(22px,3vw,36px);line-height:1;opacity:.9}\n\n/* ---------- índice ---------- */\n.indice{display:grid;grid-template-columns:1fr 1fr;gap:0 clamp(28px,5vw,90px);\n  border-top:1px solid var(--linha)}\n.indice a{display:flex;align-items:baseline;gap:18px;padding:clamp(10px,1.7vh,17px) 0;\n  border-bottom:1px solid var(--linha);text-decoration:none;color:var(--ink)}\n.indice a .n{font-family:var(--lt);font-size:11px;letter-spacing:.18em;color:var(--muted);\n  min-width:28px;transition:color .25s}\n.indice a .t{font-family:var(--ext);font-size:clamp(15px,1.7vw,23px);line-height:1.25;\n  transition:transform .3s cubic-bezier(.2,.6,.2,1)}\n.indice a:hover .n,.indice a:focus-visible .n{color:var(--accent)}\n.indice a:hover .t,.indice a:focus-visible .t{transform:translateX(6px)}\n@media(max-width:820px){.indice{grid-template-columns:1fr}}\n\n/* ---------- statement ---------- */\n.statement h1{font-size:clamp(34px,5.6vw,86px);line-height:1.08;letter-spacing:-.01em;max-width:16ch}\n\n/* ---------- pilares ---------- */\n.pilares{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3.4vw,56px);\n  border-top:1px solid var(--linha);padding-top:clamp(26px,5vh,52px)}\n.pilares.dois{grid-template-columns:1fr 1fr}\n.pilar .k{display:block;font-size:11px;letter-spacing:.26em;color:var(--accent);\n  text-transform:uppercase;margin-bottom:18px}\n.pilar .t{font-family:var(--ext);font-size:clamp(17px,1.7vw,23px);line-height:1.3;margin-bottom:16px}\n.pilar p{font-size:14.5px;line-height:1.7;color:var(--muted)}\n@media(max-width:820px){.pilares,.pilares.dois{grid-template-columns:1fr}}\n\n/* ---------- chip de link ---------- */\n.chip{display:inline-flex;align-items:center;gap:10px;margin-top:clamp(22px,3.6vh,34px);\n  padding:11px 20px;border:1px solid var(--linha);border-radius:999px;font-family:var(--lt);\n  font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);\n  text-decoration:none;transition:border-color .25s}\n.chip:hover{border-color:var(--accent)}\n\n/* ---------- arquitetura ---------- */\n.arq{display:grid;grid-template-columns:1fr 44px 1.15fr 44px 1fr;gap:clamp(10px,1.2vw,18px);\n  align-items:stretch;border-top:1px solid var(--linha);padding-top:clamp(24px,4vh,44px)}\n.arq .col{display:flex;flex-direction:column;gap:10px}\n.arq .col-h{font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:8px;line-height:1.5}\n.arq .box{border:1px solid var(--linha);border-radius:14px;padding:13px 15px}\n.arq .box .bn{font-family:var(--ext);font-size:12.5px;line-height:1.4}\n.arq .box .bd{font-size:11.5px;color:var(--muted);margin-top:4px;line-height:1.5}\n.arq .core{background:var(--dark);color:#fff;border-radius:18px;padding:clamp(16px,2.4vh,26px);\n  display:flex;flex-direction:column;gap:12px;height:100%}\n.arq .core .ch{font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--accent-soft)}\n.arq .core .box{border-color:rgba(255,255,255,.25)}\n.arq .core .box .bd{color:rgba(255,255,255,.72)}\n.arq .link{position:relative;display:flex;align-items:center;justify-content:center}\n.arq .link::before{content:'';position:absolute;left:-6px;right:-6px;top:50%;height:1px;\n  background:var(--muted);opacity:.5}\n.arq .link span{position:relative;background:var(--paper);font-size:9px;letter-spacing:.2em;\n  text-transform:uppercase;color:var(--muted);transform:rotate(-90deg);white-space:nowrap;padding:0 6px}\n@media(max-width:900px){\n  .arq{grid-template-columns:1fr}\n  .arq .link{min-height:34px}\n  .arq .link::before{left:50%;right:auto;top:0;bottom:0;width:1px;height:auto}\n  .arq .link span{transform:none}\n}\n\n/* ---------- frentes de escopo ---------- */\n.leg{display:flex;gap:26px;flex-wrap:wrap;margin-bottom:clamp(18px,3vh,30px);\n  font-family:var(--sf);font-size:12.5px;color:var(--muted)}\n.leg span{display:flex;align-items:center;gap:8px}\n.leg i{width:9px;height:9px;border-radius:50%;display:inline-block}\n.leg .ok{background:var(--ok)}\n.leg .nv{background:var(--accent)}\n.frentes{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--linha);\n  column-gap:clamp(24px,4vw,64px)}\n.frentes .f{border-bottom:1px solid var(--linha);padding:clamp(12px,1.8vh,18px) 0}\n.frentes .fn{font-family:var(--ext);font-size:clamp(13px,1.2vw,15.5px);margin-bottom:8px;\n  display:flex;justify-content:space-between;gap:12px;align-items:baseline}\n.frentes .fn .ct{font-family:var(--sf);font-size:10.5px;color:var(--muted);white-space:nowrap;\n  letter-spacing:.04em}\n.frentes .fi{display:flex;gap:8px;align-items:baseline;font-family:var(--sf);font-size:12px;\n  line-height:1.5;color:var(--ink);padding:2.5px 0}\n.frentes .fi::before{content:'';width:6px;height:6px;border-radius:50%;flex:none;\n  position:relative;top:-1px}\n.frentes .fi.ok::before{background:var(--ok)}\n.frentes .fi.nv::before{background:var(--accent)}\n@media(max-width:900px){.frentes{grid-template-columns:1fr}}\n\n/* ---------- kpis ---------- */\n.kpis{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--linha)}\n.kpis .k{padding:clamp(16px,2.6vh,26px) 22px clamp(16px,2.6vh,26px) 0;border-bottom:1px solid var(--linha)}\n.kpis .k .kt{font-family:var(--ext);font-size:clamp(13.5px,1.25vw,16px);line-height:1.35}\n.kpis .k p{font-size:12.5px;color:var(--muted);line-height:1.6;margin-top:8px}\n.kpis .k .kb{font-family:var(--sf);font-size:11.5px;color:var(--accent);margin-top:8px;line-height:1.5}\n@media(max-width:900px){.kpis{grid-template-columns:1fr 1fr}}\n@media(max-width:600px){.kpis{grid-template-columns:1fr}}\n\n/* ---------- cronograma ---------- */\n.ondas{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3vw,48px);\n  position:relative;padding-top:clamp(30px,5vh,52px)}\n.ondas::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:var(--linha)}\n.onda{position:relative}\n.onda::before{content:'';position:absolute;top:calc(-1 * clamp(30px,5vh,52px));left:0;\n  width:9px;height:9px;border-radius:50%;background:var(--accent);transform:translateY(-4px)}\n.onda .fase{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:10px}\n.onda .t{font-family:var(--ext);font-size:clamp(17px,1.8vw,24px);margin-bottom:18px}\n.onda ul{list-style:none}\n.onda li{font-size:14px;line-height:1.6;color:var(--muted);padding:9px 0;border-bottom:1px solid var(--linha)}\n@media(max-width:820px){.ondas{grid-template-columns:1fr}.onda::before{display:none}}\n\n/* ---------- investimento ---------- */\n.custos{border-top:1px solid var(--linha);max-width:880px}\n.custos .row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:baseline;gap:24px;\n  padding:clamp(10px,1.8vh,18px) 0;border-bottom:1px solid var(--linha)}\n.custos .row .t{font-family:var(--ext);font-size:clamp(15px,1.5vw,20px)}\n.custos .row .d{font-size:13px;color:var(--muted);margin-top:5px;max-width:56ch;line-height:1.6}\n.custos .row .v{font-family:var(--ext);font-size:clamp(19px,2.1vw,30px);\n  font-variant-numeric:tabular-nums;white-space:nowrap}\n.custos .row .v em{font-style:normal;color:var(--muted);font-family:var(--lt);font-size:.55em;\n  letter-spacing:.1em}\n.custos .total .v{color:var(--accent)}\n.cond{margin-top:clamp(14px,2.2vh,22px);font-size:13px;line-height:1.7;color:var(--muted);max-width:64ch}\n\n/* ---------- tabelas comerciais ---------- */\n.tbw{overflow-x:auto;border-top:1px solid var(--linha)}\n.tb{width:100%;border-collapse:collapse;min-width:560px}\n.tb th{text-align:left;font-family:var(--lt);font-weight:400;font-size:10px;letter-spacing:.2em;\n  text-transform:uppercase;color:var(--muted);padding:14px 22px 12px 0;\n  border-bottom:1px solid var(--linha);white-space:nowrap}\n.tb td{padding:13px 22px 13px 0;font-size:13px;color:var(--muted);line-height:1.5;\n  border-bottom:1px solid var(--linha);white-space:nowrap;font-variant-numeric:tabular-nums}\n.tb td:first-child{font-family:var(--ext);color:var(--ink);font-size:13.5px}\n.tb td strong{font-family:var(--ext);font-weight:400;color:var(--ink)}\n.tb tbody tr:last-child td{border-bottom:0}\n\n/* ---------- listas de compromisso ---------- */\n.klist{list-style:none;margin-top:6px}\n.klist li{font-size:13.5px;line-height:1.6;color:var(--ink);padding:10px 0;\n  border-bottom:1px solid var(--linha);display:flex;gap:10px;align-items:baseline}\n.klist li b{font-weight:600}\n.klist li::before{content:'';width:6px;height:6px;border-radius:50%;flex:none;\n  background:var(--ok);position:relative;top:-1px}\n.klist.neg li::before{background:var(--bad)}\n\n/* ---------- fechamento ---------- */\n.dark{color:#fff}\n.dark .mid{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;\n  text-align:center;gap:34px}\n.dark .sub{font-family:var(--lt);font-size:clamp(12px,1.3vw,16px);letter-spacing:.14em;\n  text-transform:uppercase;opacity:.92}\n.fecho .mid{gap:44px}\n.fecho .frase{font-family:var(--lt);font-size:clamp(16px,1.8vw,25px);line-height:1.65;\n  letter-spacing:.025em;max-width:44ch;text-wrap:balance}\n.fecho .frase i{font-style:normal;opacity:.65}\n.ring{width:clamp(54px,5.4vw,80px);height:auto;display:block}\n.ring.branco{filter:brightness(0) invert(1)}\n@media(max-width:820px){.fecho .frase{font-size:15px}}\n\n/* ---------- mobile ---------- */\n@media(max-width:820px){\n  .deckroot{scroll-snap-type:y proximity}\n  .deckwrap::after{content:'';position:absolute;left:0;right:0;bottom:0;z-index:35;\n    pointer-events:none;background:linear-gradient(to top,var(--paper) 42%,rgba(245,244,240,0));\n    height:78px}\n  .deckwrap.on-dark::after{display:none}\n  nav.dots{display:none}\n  section{padding:72px 20px 82px}\n  .counter,.hint{bottom:34px}\n  .eyebrow{margin-bottom:18px}\n  .capa{padding:60px var(--mx) 96px}\n  .capa .frame{min-height:42svh}\n  .capa .frame .mid{gap:16px;padding:0 20px}\n  .capa .frame .sub{font-size:10.5px;letter-spacing:.12em;text-wrap:balance;line-height:1.7}\n  .capa .frame .brands{font-size:9px;letter-spacing:.18em;line-height:1.8}\n  .capa .lockup{gap:14px}\n  .capa .lockup .d2m{height:25px}\n  .capa .lockup img.vsm{height:16px}\n  .capa .lockup .plus{font-size:17px}\n  .capa .caption{flex-direction:column;align-items:flex-start;gap:10px;padding:18px 2px 0}\n  .capa .caption .frase-cap{font-size:15px;line-height:1.5;max-width:34ch}\n}\n\n/* ---------- gate ---------- */\n.gwrap{position:relative;height:100vh;height:100svh;overflow:hidden;display:flex;\n  align-items:center;justify-content:center;background:var(--dark);font-family:var(--lt)}\n.gcard{position:relative;z-index:1;background:var(--paper);border-radius:20px;\n  padding:40px 36px 32px;width:min(380px,88vw);text-align:center;\n  box-shadow:0 30px 80px rgba(0,0,0,.45)}\n.glock{display:flex;align-items:center;justify-content:center;gap:12px;margin-bottom:14px}\n.glock img{filter:brightness(0);display:block}\n.glock img.gvs{height:14px;width:auto;filter:none}\n.glock span{font-family:var(--lt);font-size:15px;color:var(--ink)}\n.gsub{font-family:var(--lt);font-size:10px;letter-spacing:.24em;text-transform:uppercase;\n  color:var(--muted);margin-bottom:26px}\n.ginput{width:100%;box-sizing:border-box;padding:13px 14px;border:1px solid var(--linha);\n  border-radius:10px;background:#fff;font-size:15px;text-align:center;outline:none;\n  font-family:var(--sf);color:var(--ink)}\n.ginput:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(150,87,15,.16)}\n.gbtn{width:100%;margin-top:12px;padding:13px;border:none;border-radius:10px;\n  background:var(--accent);color:#fff;font-size:12px;letter-spacing:.24em;\n  text-transform:uppercase;cursor:pointer;font-family:var(--lt)}\n.gbtn:hover{filter:brightness(.9)}\n.gerr{display:none;color:var(--bad);font-size:13px;margin-top:12px;font-family:var(--sf)}\n.shake{animation:gsh .4s}\n@keyframes gsh{0%,100%{transform:translateX(0)}20%{transform:translateX(-9px)}\n  40%{transform:translateX(9px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}\n\n/* ---------- painel de acessos ---------- */\n.acwrap{position:relative;min-height:100vh;min-height:100svh;background:var(--paper);\n  color:var(--ink);font-family:var(--lt);overflow-y:auto}\n.acwrap header{position:relative;background:none}\n.acbody{max-width:900px;margin:0 auto;padding:8px clamp(20px,5vw,48px) 80px}\n.actit{font-family:var(--ext);font-weight:400;font-size:clamp(28px,4vw,46px);\n  line-height:1.1;margin-bottom:clamp(22px,4vh,40px)}\n.acgate{max-width:320px}\n.acerr{color:var(--bad);font-size:13px;margin-top:12px;font-family:var(--sf)}\n.acmsg{color:var(--muted);font-family:var(--sf);padding:26px 0}\n.ackpis{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(14px,2vw,26px);\n  border-top:1px solid var(--linha);padding-top:clamp(18px,3vh,28px);\n  margin-bottom:clamp(26px,5vh,46px)}\n.acl{font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:10px}\n.acv{font-family:var(--ext);font-size:clamp(30px,4.4vw,48px);font-variant-numeric:tabular-nums}\n.acsub{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--muted);\n  margin-bottom:14px}\n.acwrap .tb{min-width:480px}\n.acwrap .tb .cid{color:var(--muted);font-size:11.5px}\n@media(max-width:700px){.ackpis{grid-template-columns:1fr}}\n  `,\n  render() { return `<div class=\"deckhost\"></div>` },\n  mounted(d2, root) {\n    injectFonts()\n    let admin = false\n    try { admin = ((window.location.search || '') + (window.location.hash || '')).indexOf('acessos') >= 0 } catch (e) {}\n    if (admin) {\n      phase = 'acessos'\n      sync(d2, root)\n      let saved = ''\n      try { saved = window.localStorage.getItem('veste_prop_admin') || '' } catch (e) {}\n      const host = (d2 && d2.$ && d2.$('.deckhost')) || ((root || document).querySelector('.deckhost'))\n      if (saved && host) loadAcessos(host, saved)\n      return\n    }\n    let ok = OPEN\n    try { if (sessionStorage.getItem('deckVesteUnlocked') === '1') ok = true } catch (e) {}\n    try { if ((location.search || '').indexOf('k=' + PASS) > -1) ok = true } catch (e) {}\n    if (ok) phase = 'deck'\n    track('open')\n    sync(d2, root)\n  },\n  updated(d2, root) { sync(d2, root) }\n}\n"}}}}}}}},{"_id":"6aaac5b70ea1ff2bdef8a389","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"kickoff-tryon-guia","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n:host{display:block;width:100%;height:100%}\n.wrap{\n  --paper:#F5F4F0; --ink:#15161A; --muted:#6A6E77; --accent:#96570F; --linha:rgba(21,22,26,.13);\n  background:var(--paper); color:var(--ink); font-family:-apple-system,BlinkMacSystemFont,'Helvetica Neue',Helvetica,Arial,sans-serif;\n  width:100%; min-height:100%; overflow-y:auto; padding:clamp(24px,4vw,64px);\n  box-sizing:border-box;\n}\n*{box-sizing:border-box}\nh1{font-size:clamp(24px,3vw,36px);font-weight:600;margin:0 0 6px;letter-spacing:-.01em}\nh2{font-size:clamp(17px,2vw,22px);font-weight:600;margin:36px 0 12px;color:var(--accent)}\n.meta{font-size:13px;color:var(--muted);margin-bottom:18px}\n.badge{display:inline-block;background:var(--accent);color:#fff;font-size:11px;letter-spacing:.08em;\n  text-transform:uppercase;padding:4px 10px;border-radius:999px;margin-bottom:14px}\np{font-size:14.5px;line-height:1.65;color:var(--ink);margin:0 0 10px;max-width:78ch}\np.muted{color:var(--muted)}\nhr{border:none;border-top:1px solid var(--linha);margin:22px 0}\nul{margin:0 0 10px;padding-left:20px}\nli{font-size:14px;line-height:1.65;margin-bottom:8px;max-width:74ch}\nli b{font-weight:600}\n.risk{background:#FBE9E4;border-left:3px solid #A33427;padding:10px 14px;border-radius:6px;margin:10px 0}\n.ok{background:#E8F0EA;border-left:3px solid #2C6B4A;padding:10px 14px;border-radius:6px;margin:10px 0}\ncode{background:rgba(21,22,26,.06);padding:1px 6px;border-radius:4px;font-size:12.5px}\n.foot{margin-top:44px;padding-top:16px;border-top:1px solid var(--linha);font-size:12px;color:var(--muted)}\n`,\n  render(d2) {\n    return `<div class=\"wrap\">\n      <div class=\"badge\">Sprint · Grupo Veste</div>\n      <h1>Kickoff Sprint · Provador Virtual com IA</h1>\n      <div class=\"meta\">16/09/2026 · 14:00–16:00 · gui</div>\n      <p><b>Objetivo do kickoff:</b> alinhar as partes antes de iniciar a Sprint (2–4 semanas, 4 sessões Data²↔Veste) que constrói o provador virtual com IA para as 5 marcas do grupo. Guia de uso ao vivo — preencher decisões durante a call.</p>\n      <p class=\"muted\">Proposta de referência: <code>proposta-veste.veste-app.next.data2.app</code> (senha <code>veste2026</code>).</p>\n\n      <div class=\"risk\"><b>Marca piloto do Dia 1: John John</b> (confirmado por gui, 2026-09-16). Hoje não existe nenhuma prova ao vivo em site nenhum da Veste — é a primeira vez. O catálogo da John John (VTEX) tem request criado mas ainda não validado tecnicamente — só o da Le Lis está testado hoje. Isso é ação técnica bloqueante antes do Dia 1.</div>\n\n      <hr/>\n      <h2>Status técnico real hoje</h2>\n      <ul>\n        <li><b>Catálogo por marca:</b> só Le Lis está de fato prototipado e testado, contra a VTEX Intelligent Search real (<code>lelis.com.br/.../intelligent-search/product_search</code>). John John, Bo.Bô e Dudalina têm requests criados mas não confirmados. Individual não tem integração nenhuma ainda.</li>\n        <li><b>Sessão de busca:</b> existe uma chamada <code>CREATE SESSION</code> para <code>service.smarthint.co</code> (SmartHint) — provedor terceiro separado da VTEX; precisa confirmar se é o caminho oficial.</li>\n        <li><b>Geração de imagem:</b> motor nano-banana já plugado, copiado do engine validado (Dudalina try-on, Roue etc).</li>\n        <li><b>Clothing-Swap / Shoes-Swap:</b> componentes clonados do engine validado — ainda não adaptados/testados com catálogo real da Veste.</li>\n        <li><b>Nada de embed, evento host↔widget, ou jornada de cliente foi definido ainda</b> — núcleo do bloco Tech de hoje.</li>\n      </ul>\n\n      <hr/>\n      <h2>Abertura · 14:00–14:10 (10 min)</h2>\n      <ul>\n        <li>Apresentações — quem está na sala de cada lado, papel de cada um</li>\n        <li>Reconfirmar o que a Sprint entrega e o que fica pra depois (fora de escopo: tamanho, ERP/OMS/estoque, app nativo, loja física)</li>\n        <li>Reconfirmar condições travadas: R$ 0 de sprint, 30 dias de imagem liberada, teto de 2.000 provas, D1/D15/D30</li>\n      </ul>\n\n      <h2>Bloco 1 · Negócio — 14:10–14:45 (35 min)</h2>\n      <ul>\n        <li>John John confirmada como marca piloto — catálogo dela precisa ser validado antes do Dia 1</li>\n        <li>Individual fica fora do piloto, ou entra depois? Precisa virar decisão explícita</li>\n        <li><b>Baseline de conversão</b> — número de hoje da John John, quem do lado Veste confirma no Dia 1</li>\n        <li>Confirmar as 3 métricas: conversão de visitantes (o número que a POC prova), clique em Comprar pós-prova, troca/devolução (leitura só pós-janela, não fecha em 30 dias)</li>\n        <li>Quem aprova uso/consentimento de imagem do cliente (LGPD)</li>\n        <li>Donos de cada lado — contato de negócio e de produto/plataforma</li>\n        <li>Critério de sucesso combinado <b>antes</b> de rodar</li>\n      </ul>\n\n      <h2>Bloco 2 · Marca &amp; UX — 14:45–15:20 (35 min)</h2>\n      <ul>\n        <li>Onde o provador entra na jornada — PDP, home ou landing de campanha?</li>\n        <li>Fluxo: upload de foto → escolha da peça → resultado → Meu Look → Minhas Provas (galeria por e-mail)</li>\n        <li>Estética John John — widget herda o visual do site, ou casca única Data²+Veste?</li>\n        <li>Como comunicar o limite da prova na UI — não promete tamanho/caimento</li>\n        <li>Onde aparece o opt-in de imagem no fluxo</li>\n        <li>Quem no time de marca aprova os textos da jornada</li>\n      </ul>\n\n      <h2>Bloco 3 · Tech — 15:20–15:50 (30 min)</h2>\n      <ul>\n        <li>Contrato de catálogo da John John — mesma VTEX Intelligent Search da Le Lis? SmartHint entra em algum ponto?</li>\n        <li>Formato do embed — script tag, web component, iframe? Alinhar com o time de front da John John</li>\n        <li><b>Eventos host → embed:</b> SKU/produto da PDP atual, sessão/usuário logado, marca/contexto</li>\n        <li><b>Eventos embed → host:</b> \"prova concluída\", \"clique em Comprar\" (navega ou só reporta?), erro/falha de geração</li>\n        <li>Autenticação/CORS da API de catálogo pública — chave, rate limit, allowlist?</li>\n        <li>Contato técnico de plataforma da John John — quem publica o embed</li>\n        <li>Onde fica o tracking Data² vs. o que a Veste já tem (GA/GTM) — evitar dado duplicado na leitura do Dia 30</li>\n      </ul>\n\n      <h2>Encerramento · 15:50–16:00 (10 min)</h2>\n      <ul>\n        <li>Resumo das decisões de cada bloco (preencher ao vivo)</li>\n        <li>Datas de calendário fixadas: Dia 1 / Dia 15 / Dia 30</li>\n        <li>Quem faz o quê até a próxima sessão, com prazo</li>\n        <li>Próxima sessão da Sprint agendada</li>\n      </ul>\n\n      <div class=\"foot\">Doc completo (com comentários e histórico): guia no Claude Docs, linkado na conversa com o gui.</div>\n    </div>`\n  }\n}"}}}}}}}},{"_id":"6aac12eab0831151b3b065b7","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"provador-standalone","package":"veste-app","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 RULER_ICON = `<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"9\" width=\"20\" height=\"6\" rx=\"1\"/><path d=\"M6 9v2.2M9.5 9v3M13 9v2.2M16.5 9v3\"/></svg>`\nconst WARNING_ICON = `<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 3.5 21 19.5H3z\"/><path d=\"M12 9.5v4.2M12 16.8h.01\"/></svg>`\nconst REFRESH_ICON = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"vertical-align:-2px;margin-right:4px\"><polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/></svg>`\nconst UPLOAD_ICON = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"vertical-align:-2px;margin-right:4px\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg>`\nconst THUMBS_UP_ICON = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M7 10v11H4a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h3zm0 0 5-7a2 2 0 0 1 2 2v3h5a2 2 0 0 1 2 2l-1.5 8a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V10z\"/></svg>`\nconst THUMBS_DOWN_ICON = `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17 14V3h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-3zm0 0-5 7a2 2 0 0 1-2-2v-3H5a2 2 0 0 1-2-2l1.5-8a2 2 0 0 1 2-2H15a2 2 0 0 1 2 2v10z\"/></svg>`\nconst SHARE_ICON = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" style=\"vertical-align:-2px;margin-right:6px\"><circle cx=\"18\" cy=\"5\" r=\"3\"/><circle cx=\"6\" cy=\"12\" r=\"3\"/><circle cx=\"18\" cy=\"19\" r=\"3\"/><line x1=\"8.6\" y1=\"10.6\" x2=\"15.4\" y2=\"6.4\"/><line x1=\"8.6\" y1=\"13.4\" x2=\"15.4\" y2=\"17.6\"/></svg>`\n\n// In-memory only — same product + same customer photo (+ color/size/extra)\n// within THIS session reuses the already-generated result instead of paying\n// for a new AI call. Resets on page reload, which is fine: it's just a\n// same-session speed/cost win, not meant to survive across visits.\nconst tryonResultCache = new Map()\nfunction tryonCacheKey(product, extra, color, size, userPhotoUrl) {\n  return [(product && product.productId) || '', (extra && extra.productId) || '', color || '', size || '', userPhotoUrl || ''].join('|')\n}\n\n// ---- Category blocklist (bolsas/cintos/carteiras/etc. can't be \"worn\") ----\n// Fetched once per page load from the Painel's blocklist table (tryon-blocklist\n// rush, public read-only) and cached in d2.state so every render/openProduct\n// check is synchronous after the first fetch.\nasync function ensureBlocklist(d2) {\n  if (d2.state.blocklist) return d2.state.blocklist\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/tryon-blocklist', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({}),\n    })\n    const json = await res.json()\n    const categories = (json && json.data && json.data.categories) || []\n    d2.state.blocklist = categories\n    return categories\n  } catch (err) {\n    console.warn('[provador] blocklist fetch failed', err)\n    d2.state.blocklist = []\n    return []\n  }\n}\nfunction isCategoryBlocked(product, blocklist) {\n  return !!matchedBlockedCategory(product, blocklist)\n}\nfunction matchedBlockedCategory(product, blocklist) {\n  if (!blocklist || !blocklist.length) return null\n  const cats = (product && product.categories) || []\n  if (!cats.length) return null\n  const joined = cats.join(' / ').toLowerCase()\n  const hit = blocklist.find((b) => b && joined.includes(String(b).toLowerCase()))\n  return hit || null\n}\n\nexport default {\n  css: `\n@import url('https://fonts.googleapis.com/css2?family=Exo+2:wght@400;500;600;700&display=swap');\n:host{display:block;width:100%;height:100%}\n.wrap{\n  --paper:#FFFFFF; --ink:#000000; --muted:#727273; --accent:#96570F; --accent-soft:#C89A4A;\n  --linha:#e2e2e2; --card:#FFFFFF; --danger:#A33427;\n  width:100%;height:100%;overflow-y:auto;background:var(--paper);color:var(--ink);\n  font-family:'Exo 2',-apple-system,BlinkMacSystemFont,'Helvetica Neue',Helvetica,Arial,sans-serif;\n  box-sizing:border-box;\n}\n*{box-sizing:border-box}\n.closebtn{position:fixed;top:14px;right:14px;z-index:20;border:1px solid var(--linha);background:#fff;\n  border-radius:999px;width:34px;height:34px;box-shadow:0 4px 14px rgba(0,0,0,.12);\n  display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:20px;line-height:1;color:var(--muted)}\n.closebtn:hover{color:var(--ink);border-color:var(--ink)}\n.body{padding:clamp(18px,4vw,40px);max-width:1100px;margin:0 auto}\n.disclaimer{font-size:11.5px;color:var(--muted);line-height:1.5;max-width:60ch;margin:0 0 22px}\n\n/* search */\n.searchbar{display:flex;gap:10px;margin-bottom:26px}\n.searchbar input{flex:1;padding:14px 16px;border:1px solid var(--linha);border-radius:12px;\n  font-size:15px;background:#fff;color:var(--ink)}\n.searchbar input:focus{outline:2px solid var(--accent);outline-offset:1px}\n.searchbar button{padding:0 22px;border:0;border-radius:2px;background:#080707;color:#fff;\n  font-size:14px;font-weight:500;letter-spacing:normal;text-transform:uppercase;cursor:pointer}\n.searchbar button:disabled{opacity:.5;cursor:default}\n.searchbar button:active,.cta:active,.addbag:active,.checkout:active,.chip:active,.iconbtn:active{transform:scale(.96)}\nbutton,.chip,.iconbtn,.card,.cta{transition:transform .12s ease,box-shadow .12s ease,border-color .12s ease}\n.screen-enter{animation:screenIn .38s cubic-bezier(.2,.7,.2,1)}\n@keyframes screenIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}\n.uploadzone.drag{border-color:var(--accent);background:#FBF3E8;transform:scale(1.01)}\n.hint{font-size:12.5px;color:var(--muted);margin:-14px 0 22px}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px}\n.card{background:var(--card);border:1px solid var(--linha);border-radius:14px;overflow:hidden;\n  cursor:pointer;transition:transform .15s,box-shadow .15s;display:flex;flex-direction:column}\n.card:hover{transform:translateY(-2px);box-shadow:0 10px 24px rgba(21,22,26,.08)}\n.card .ph{width:100%;aspect-ratio:3/4;background:#eee;background-size:cover;background-position:center;background-repeat:no-repeat}\n.card .info{padding:10px 12px 14px}\n.card .nm{font-size:13px;line-height:1.35;margin-bottom:6px;min-height:34px;font-weight:400;color:#000}\n.card .pr{font-size:13px;color:#000;font-weight:700}\n.empty{padding:60px 0;text-align:center;color:var(--muted);font-size:14px}\n.spin{display:inline-block;width:16px;height:16px;border:2px solid rgba(0,0,0,.15);\n  border-top-color:var(--ink);border-radius:50%;animation:sp .7s linear infinite;vertical-align:-3px}\n@keyframes sp{to{transform:rotate(360deg)}}\n\n/* product */\n.pgrid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,4vw,48px)}\n@media(max-width:720px){.pgrid{grid-template-columns:1fr}}\n.pimg{width:100%;aspect-ratio:3/4;border-radius:16px;background:#eee;background-size:cover;background-position:center;background-repeat:no-repeat}\n.pname{font-size:clamp(20px,2.4vw,26px);font-weight:400;margin:4px 0 6px;line-height:1.25;color:#000}\n.pbrand{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#666;margin-bottom:14px;font-weight:400}\n.pprice{font-size:22px;color:#000;font-weight:700;margin-bottom:2px}\n.pinstall{font-size:11px;color:#333;font-weight:700;margin-bottom:22px}\n.ptitlerow{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}\n.shareicon{flex:none;border:1px solid var(--linha);background:#fff;border-radius:999px;width:34px;height:34px;\n  display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;color:var(--muted)}\n.sizeguide{font-size:12.5px;color:var(--ink);text-decoration:underline;cursor:pointer;margin:14px 0 0;display:inline-block}\n.accordion{margin-top:26px;border-top:1px solid var(--linha)}\n.acc-item{border-bottom:1px solid var(--linha)}\n.acc-head{display:flex;justify-content:space-between;align-items:center;padding:14px 0;cursor:pointer;\n  font-size:11px;letter-spacing:.16em;text-transform:uppercase;font-weight:600}\n.acc-body{padding:0 0 16px;font-size:13.5px;line-height:1.65;color:var(--muted);display:none}\n.acc-item.open .acc-body{display:block}\n.acc-item.open .acc-chev{transform:rotate(180deg)}\n.acc-chev{transition:transform .2s}\n.optlabel{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);margin:4px 0 6px}\n.opts{display:flex;flex-wrap:wrap;gap:8px}\n.chip{padding:8px 16px;border-radius:999px;border:1px solid var(--linha);background:#fff;\n  font-size:13px;cursor:pointer}\n.chip.on{background:var(--ink);color:#fff;border-color:var(--ink)}\n.cta{margin-top:30px;width:100%;padding:16px;border:0;border-radius:2px;\n  background:var(--danger);color:#fff;font-size:14px;letter-spacing:normal;text-transform:uppercase;\n  font-weight:500;cursor:pointer}\n.cta:hover{filter:brightness(1.05)}\n\n/* upload */\n.uploadzone{border:2px dashed var(--linha);border-radius:18px;padding:clamp(40px,8vw,80px) 20px;\n  text-align:center;background:#fff;cursor:pointer;transition:border-color .15s}\n.uploadzone:hover{border-color:var(--accent)}\n.uploadzone .ic{font-size:34px;margin-bottom:14px}\n.uploadzone .t{font-size:16px;font-weight:600;margin-bottom:6px}\n.uploadzone .s{font-size:13px;color:var(--muted)}\n.instr{margin-top:22px;padding:16px 18px;background:#fff;border:1px solid var(--linha);border-radius:12px;\n  font-size:13px;color:var(--muted);line-height:1.6}\n.instr b{color:var(--ink)}\n\n/* result */\n.rgrid{display:grid;grid-template-columns:1.1fr 1fr;gap:22px}\n@media(max-width:820px){.rgrid{grid-template-columns:1fr}}\n.rimg{width:100%;aspect-ratio:3/4;border-radius:16px;background:#e5e5e5;background-size:cover;\n  background-position:center;background-repeat:no-repeat;position:relative;overflow:hidden}\n.rimg .load{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;\n  justify-content:center;gap:12px;background:rgba(245,244,240,.9);font-size:13px;color:var(--muted)}\n.genprogress{position:absolute;top:0;left:0;width:100%;height:3px;background:rgba(0,0,0,.08);overflow:hidden;z-index:2}\n.genbar{position:absolute;top:0;left:-40%;height:100%;width:40%;background:var(--accent);animation:genprog 1.4s ease-in-out infinite}\n@keyframes genprog{0%{left:-40%}50%{left:60%}100%{left:100%}}\n.gentime{font-size:11.5px;color:var(--muted);margin-top:-4px}\n.generr{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;\n  gap:12px;background:rgba(255,255,255,.96);font-size:13.5px;color:var(--ink);text-align:center;padding:28px;z-index:3}\n.generr-ic{color:var(--muted);display:flex}\n.generr-msg{max-width:36ch;line-height:1.5}\n.generr.friendly{background:rgba(251,243,232,.97)}\n.generr.friendly .generr-ic{color:var(--accent)}\n.actrow{display:flex;gap:10px;margin-top:12px;flex-wrap:wrap}\n.iconbtn{flex:1;min-width:76px;padding:10px 8px;border:1px solid var(--linha);border-radius:10px;\n  background:#fff;font-size:11.5px;cursor:pointer;text-align:center}\n.panel{background:#fff;border:1px solid var(--linha);border-radius:16px;padding:20px;display:flex;\n  flex-direction:column;gap:8px}\n.colorthumbs{gap:8px}\n.colorthumb{width:64px;height:64px;padding:0;border-radius:10px;background-size:cover!important;background-position:center center!important;background-repeat:no-repeat!important;\n  border:2px solid transparent;cursor:pointer;flex:none}\n.colorthumb.on{border-color:var(--ink)}\n.helpblock{display:flex;flex-direction:column;gap:14px;margin:2px 0;padding-bottom:16px;border-bottom:1px solid #eee}\n.helprow{display:flex;align-items:center;gap:10px;font-size:12px;color:#252525}\n.helprow.act-personal-shopper{cursor:pointer}\n.helprow u{color:#252525}\n.helpdivider{border-top:1px solid #eee}\n.helprow2{display:flex;gap:24px}\n.helpcol{display:flex;align-items:center;gap:10px;font-size:14px;color:#000}\n.helpcol.act-sizeguide-size{cursor:pointer}\n.helpcol.act-sizeguide-measures{cursor:pointer}\n.helpicon{width:25px;height:25px;flex:none;object-fit:contain}\n.helpicon-emoji{font-size:20px;flex:none}\n.addbag{padding:14px;border:0;border-radius:4px;background:#080707;color:#fff;font-size:14px;\n  letter-spacing:normal;text-transform:uppercase;font-weight:400;cursor:pointer}\n.primary-btn{margin-top:16px;width:100%;padding:16px;border:0;border-radius:4px;background:#080707;\n  color:#fff;font-size:14px;text-transform:uppercase;font-weight:400;letter-spacing:normal;cursor:pointer}\n.sug{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:6px}\n.sugcard{border:2px solid var(--linha);border-radius:10px;padding:10px;font-size:11.5px;color:var(--muted);\n  text-align:center;cursor:pointer}\n.sugcard.on{border-color:#080707}\n.sugcard .sugcheck{position:absolute;top:6px;right:6px;width:20px;height:20px;border-radius:999px;\n  background:#080707;color:#fff;display:none;align-items:center;justify-content:center;font-size:12px}\n.sugcard.on .sugcheck{display:flex}\n.gallery{display:flex;gap:10px;overflow-x:auto;margin-top:26px;padding-bottom:6px}\n.carousel{position:relative}\n.carousel .gallery{margin-top:0}\n.carousel-arrow{position:absolute;top:50%;transform:translateY(-50%);width:28px;height:28px;border-radius:999px;\n  background:#fff;border:1px solid var(--linha);display:none;align-items:center;justify-content:center;\n  cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,.15);z-index:2;font-size:16px;line-height:1;color:var(--muted)}\n.carousel-arrow:hover{color:var(--ink)}\n.carousel-arrow.prev{left:-6px}\n.carousel-arrow.next{right:-6px}\n.gitem{flex:none;width:74px;height:98px;border-radius:10px;background:#eee;background-size:cover;\n  background-position:center;background-repeat:no-repeat;border:2px solid transparent;cursor:pointer}\n.gitem.on{border-color:var(--accent)}\n.checkout,.outline-cta{margin-top:18px;width:100%;padding:14px;border:1px solid #080707;border-radius:4px;background:#fff;\n  color:#080707;font-size:14px;font-weight:400;letter-spacing:normal;text-transform:uppercase;cursor:pointer}\n.iconbtn.fb-selected{border-color:var(--ink);background:#f4f4f4}\n.iconbtn svg{vertical-align:-3px}\n.refazer-confirm{display:flex;align-items:center;gap:10px;flex-wrap:wrap;background:#FBF3E8;border:1px solid var(--linha);\n  border-radius:10px;padding:10px 12px;margin-top:10px;font-size:12.5px;color:var(--ink)}\n.refazer-confirm .rc-btn{border:1px solid var(--ink);border-radius:8px;padding:6px 12px;background:#fff;cursor:pointer;font-size:12px}\n.refazer-confirm .rc-btn.yes{background:#080707;color:#fff;border-color:#080707}\n.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);background:#000;color:#fff;\n  padding:12px 20px;border-radius:999px;font-size:13px;z-index:50;box-shadow:0 10px 30px rgba(0,0,0,.25)}\n.chip-oos{opacity:.4;text-decoration:line-through;cursor:not-allowed}\n.airesult-disclaimer{font-size:11px;color:var(--muted);line-height:1.5;margin-top:8px}\n.uploadgrid{display:grid;grid-template-columns:1.1fr 1fr;gap:clamp(20px,4vw,40px);align-items:start}\n@media(max-width:720px){.uploadgrid{grid-template-columns:1fr}}\n.uploadtips{padding-top:4px}\n.tipslist{margin:0;padding-left:18px;font-size:13px;color:var(--muted);line-height:1.9}\n`,\n\n  render(d2) {\n    const s = d2.state\n    const screen = s.screen || 'search'\n    const step = { search: 1, product: 2, upload: 3, loading: 3, result: 4 }[screen] || 1\n\n    // No visible header/steps bar — navigation here is modal and host-driven,\n// there's nothing for the shopper to navigate between. Just a close button.\nconst header = `<div class=\"closebtn\" data-act=\"close\" title=\"Fechar provador\">&times;</div>`\n\n    let inner = ''\n    try {\n      if (screen === 'search') inner = renderSearch(d2)\n      else if (screen === 'product') inner = renderProduct(d2)\n      else if (screen === 'upload') inner = renderUpload(d2)\n      else if (screen === 'loading') inner = `<div class=\"empty\"><span class=\"spin\"></span>&nbsp; carregando…</div>`\n      else inner = renderResult(d2)\n    } catch (renderErr) {\n      console.error('[provador] render() threw on screen', screen, renderErr)\n      inner = `<div class=\"empty\">Erro ao renderizar (${esc(screen)}): ${esc(renderErr && renderErr.message)}</div>`\n    }\n\n    const toast = s.toast ? `<div class=\"toast\">${esc(s.toast)}</div>` : ''\n    return `<div class=\"wrap\">${header}<div class=\"body\"><div class=\"screen-enter\" data-key=\"${screen}\">${inner}</div></div></div>${toast}`\n  },\n\n  mounted(d2) {\n    d2.state.screen = d2.state.screen || 'search'\n    updateCarouselArrows(d2)\n\n    // ---- PowerCord embed bridge — real Veste<->Data2 protocol ----\n    // Veste -> Data2: tryon-initialize(customerId), tryon-open(productId, skuId?)\n    // Data2 -> Veste: tryon-add-to-cart([skuID,...]), tryon-checkout(), tryon-sizeBay(productId), close-tryon\n    // Veste -> Data2: cart-state({cartItems})\n    d2.state.cart = d2.state.cart || []\n    ensureBlocklist(d2)\n    try {\n      window.__powercordEmit = (eventName, data) => {\n        window.parent.postMessage({ eventName, data }, '*')\n        logEvent(d2, eventName, 'out', data)\n      }\n      const fetchProductById = async (productId) => {\n        // jj-product does an EXACT id lookup (VTEX legacy Catalog System).\n        // jj-search's Intelligent Search fq=productId is a silent no-op —\n        // it always falls back to a generic ranked list, ignoring the id.\n        const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-product?productId=' + encodeURIComponent(productId))\n        const json = await res.json()\n        const products = (json && json.data && json.data.products) || []\n        return products[0] || null\n      }\n      const openProduct = async (productId, skuId) => {\n        // 'search' is the standalone-testing landing only — inside the embed\n        // it must NEVER surface. No/invalid productId still waiting to\n        // resolve just means \"still loading\", not \"show the catalog\".\n        if (!productId) { d2.state.screen = 'loading'; return }\n        // Loading state FIRST — never let the iframe show the previous\n        // product/result while the new one resolves (bad perceived quality).\n        d2.state.screen = 'loading'\n        try {\n          const rawProduct = await fetchProductById(productId)\n          if (!rawProduct) { flashToast(d2, 'Produto não encontrado (id ' + productId + ').'); return }\n          // Legacy catalog lookup only returns THIS color's SKUs — colors live\n          // as separate productIds in this catalog. Enrich with sibling colors\n          // found via a text search grouped by the same product family.\n          const product = await enrichWithColorSiblings(rawProduct)\n          const blocklist = await ensureBlocklist(d2)\n          if (isCategoryBlocked(product, blocklist)) {\n            // Server-side defense-in-depth lives in jj-tryon-generate — this is\n            // the client guard for the Veste host calling tryon-open directly\n            // with a productId, bypassing our own catalog/CTA UI entirely.\n            flashToast(d2, 'Essa peça não pode ser experimentada no provador virtual (categoria incompatível).')\n            window.__powercordEmit && window.__powercordEmit('close-tryon', {})\n            return\n          }\n          const colors = variationValues(product, 'Cor')\n          const sizes = variationValues(product, 'Tamanho')\n          let preselColor = colors[0] || '', preselSize = sizes[0] || ''\n          if (skuId) {\n            const item = (product.items || []).find((it) => it.itemId === skuId)\n            if (item) {\n              const c = (item.variations || []).find((v) => v.name === 'Cor')\n              const s = (item.variations || []).find((v) => v.name === 'Tamanho')\n              if (c && c.values && c.values[0]) preselColor = c.values[0]\n              if (s && s.values && s.values[0]) preselSize = s.values[0]\n            }\n          }\n          d2.setState({\n            product,\n            color: preselColor,\n            size: preselSize,\n            lookExtra: null,\n            suggestions: [],\n          })\n          loadCrosssell(d2, product.productId)\n          // Photo already on file (this session or a prior visit) — skip\n          // straight to the result instead of asking to upload again.\n          if (d2.state.userPhoto) {\n            generate(d2)\n          } else {\n            d2.state.screen = 'upload'\n          }\n        } catch (err) {\n          console.warn('[provador] openProduct failed', err)\n          flashToast(d2, 'Não deu pra carregar esse produto agora. Tenta de novo.')\n        }\n      }\n      window.__provadorOpenProduct = openProduct\n      window.__provadorFetchProduct = fetchProductById\n      window.addEventListener('message', (e) => {\n        const msg = e.data\n        if (!msg || typeof msg !== 'object' || !msg.eventName) return\n        console.log('[provador][powercord] received from host:', msg.eventName, msg.data)\n        logEvent(d2, msg.eventName, 'in', msg.data)\n        if (msg.eventName === 'tryon-initialize') {\n          const sessionCode = typeof msg.data === 'string' ? msg.data : (msg.data && msg.data.customerId) || null\n          initSession(d2, sessionCode)\n        }\n        if (msg.eventName === 'tryon-open') {\n          const productId = msg.data && msg.data.productId\n          const skuId = msg.data && msg.data.skuId\n          openProduct(productId, skuId)\n        }\n        if (msg.eventName === 'cart-state') {\n          // The real host often echoes back ONLY skuIds (no image/name/price)\n          // — backfill from whatever we already know locally (e.g. items we\n          // ourselves just added via .addbag, which DO carry the full data)\n          // instead of blindly overwriting with the host's poorer shape.\n          const raw = Array.isArray(msg.data) ? msg.data : (msg.data && msg.data.cartItems) || []\n          const prevBySkuId = {}\n          ;(d2.state.cart || []).forEach((it) => { if (it && it.skuId) prevBySkuId[it.skuId] = it })\n          const items = raw.map((entry) => {\n            const item = (typeof entry === 'string') ? { skuId: entry } : (entry || {})\n            const known = prevBySkuId[item.skuId] || {}\n            return {\n              skuId: item.skuId,\n              productId: item.productId || known.productId || '',\n              productName: item.productName || known.productName || '',\n              imageUrl: item.imageUrl || known.imageUrl || '',\n              price: (item.price != null ? item.price : known.price != null ? known.price : null),\n              quantity: item.quantity || known.quantity || 1,\n            }\n          })\n          d2.state.cart = items\n        }\n      })\n      // Guest handshake: tells the host's PowerCord instance we're alive.\n      // This is the PowerCord library's own handshake (bare string), separate\n      // from the tryon-initialize BUSINESS event above.\n      window.parent.postMessage('handshake', '*')\n    } catch (err) {\n      console.warn('[provador][powercord] bridge init failed', err)\n    }\n\n    d2.on('.back', 'click', () => {\n      const order = ['search', 'product', 'upload', 'result']\n      const i = order.indexOf(d2.state.screen || 'search')\n      const next = order[Math.max(0, i - 1)]\n      d2.state.screen = next\n      if (i === 0) window.__powercordEmit && window.__powercordEmit('close-tryon', {})\n    })\n    d2.on('.closebtn', 'click', () => {\n      window.__powercordEmit && window.__powercordEmit('close-tryon', {})\n    })\n\n    // ---- search ----\n    d2.on('.searchbar input', 'keydown', (e) => { if (e.key === 'Enter') runSearch(d2) })\n    d2.on('.go', 'click', () => runSearch(d2))\n    d2.on('.card', 'click', (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const product = (d2.state.results || [])[idx]\n      if (!product) return\n      const colors = variationValues(product, 'Cor')\n      const sizes = variationValues(product, 'Tamanho')\n      d2.setState({\n        screen: 'product',\n        product,\n        color: colors[0] || '',\n        size: sizes[0] || '',\n        lookExtra: null,\n        suggestions: [],\n      })\n      loadCrosssell(d2, product.productId)\n    })\n\n    // ---- product ----\n    d2.on('.chip', 'click', (e, el) => {\n      const kind = el.dataset.kind\n      const val = el.dataset.val\n      if (el.dataset.oos === '1') { flashToast(d2, 'Esse tamanho está sem estoque no momento.'); return }\n      if (kind === 'color') {\n        const changed = val !== d2.state.color\n        d2.state.color = val\n        if (changed && d2.state.screen === 'result' && d2.state.userPhoto) generate(d2)\n      }\n      if (kind === 'size') d2.state.size = val\n    })\n    d2.on('.cta', 'click', () => {\n      if (d2.state.userPhoto) generate(d2)\n      else d2.state.screen = 'upload'\n    })\n    d2.on('.act-sizeguide-size', 'click', () => {\n      const p = d2.state.product\n      window.__powercordEmit && window.__powercordEmit('tryon-size-bay-size', p && p.productId)\n    })\n    d2.on('.act-sizeguide-measures', 'click', () => {\n      const p = d2.state.product\n      window.__powercordEmit && window.__powercordEmit('tryon-size-bay-measures', p && p.productId)\n    })\n    // NOTE: 'tryon-personalShopper' is NOT part of the original event\n    // protocol agreed with Veste (only tryon-sizeBay/checkout/add-to-cart/\n    // close-tryon/cart-state were spec'd) — confirm this exact event name\n    // with them before relying on it in production.\n    d2.on('.act-personal-shopper', 'click', () => {\n      const p = d2.state.product\n      window.__powercordEmit && window.__powercordEmit('tryon-personalShopper', p && p.productId)\n    })\n    d2.on('.acc-head', 'click', (e, el) => {\n      const item = el.closest('.acc-item')\n      const key = item && item.dataset.acc\n      d2.state.openAcc = d2.state.openAcc === key ? '' : key\n    })\n\n    // ---- upload ----\n    // iPhones (and most phone cameras) store the sensor's raw pixels in\n    // landscape and rely on an EXIF Orientation tag to tell viewers \"rotate\n    // this for display\" — browsers/Fotos respect it, but many backends\n    // (including vision/image-gen APIs) read the raw bytes and ignore it,\n    // so the SAME file can look upright in the preview but sideways once it\n    // reaches the AI. Re-painting onto a <canvas> bakes in whatever\n    // orientation the browser's own (EXIF-aware) image decoder already\n    // applied, producing a fresh file with no orientation tag to misread —\n    // preview and AI pipeline then always see the exact same pixels.\n    function normalizeImageOrientation(file) {\n      return new Promise(async (resolve) => {\n        if (!file || !/^image\\//.test(file.type)) { resolve(file); return }\n        try {\n          // CRITICAL: a plain <img> + canvas.drawImage(img,...) does NOT apply\n          // EXIF orientation in most browsers — only the <img> tag's own\n          // on-screen paint does (image-orientation:from-image CSS default).\n          // drawImage() draws the RAW decoded bitmap, ignoring the tag, which\n          // silently re-bakes the sideways pixels as \"fixed\" with no EXIF left\n          // to warn anyone afterward. createImageBitmap's\n          // `imageOrientation:'from-image'` option is the one API that\n          // actually decodes WITH the correction applied — use it, not <img>.\n          const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' })\n          const canvas = document.createElement('canvas')\n          canvas.width = bitmap.width\n          canvas.height = bitmap.height\n          canvas.getContext('2d').drawImage(bitmap, 0, 0)\n          canvas.toBlob((blob) => {\n            resolve(blob ? new File([blob], file.name || 'photo.jpg', { type: 'image/jpeg' }) : file)\n          }, 'image/jpeg', 0.92)\n        } catch (err) {\n          resolve(file)\n        }\n      })\n    }\n    const doUpload = async (rawFile) => {\n      if (!rawFile) return\n      d2.setState({ uploading: true, uploadError: '', photoInvalidReason: '' })\n      const stopUploadMsgs = cycleMessages(d2, ['Subindo sua foto…', 'Quase lá…'])\n      try {\n        const file = await normalizeImageOrientation(rawFile)\n        const out = await d2.drive(d2.params.uploadDrive).upload([file])\n        stopUploadMsgs()\n        const url = out && out[0] && out[0].fileUrl\n        if (!url) throw new Error('upload sem retorno')\n        d2.setState({ uploading: false, uploadPreview: url, uploadPreviewLoaded: false, validatingPhoto: true })\n        const preloadImg = new Image()\n        preloadImg.onload = () => { d2.state.uploadPreviewLoaded = true }\n        preloadImg.onerror = () => { d2.state.uploadPreviewLoaded = true }\n        preloadImg.src = url\n        const stopValidateMsgs = cycleMessages(d2, ['Verificando sua foto…', 'Conferindo enquadramento e iluminação…'])\n        const validation = await validatePhoto(url)\n        stopValidateMsgs()\n        if (validation && validation.valid === false) {\n          d2.setState({ validatingPhoto: false, uploadPreview: '', photoInvalidReason: validation.reason || 'Essa foto não funcionou — tenta outra.' })\n        } else {\n          d2.setState({ validatingPhoto: false, userPhotoBodyCoverage: (validation && validation.bodyCoverage) || '' })\n        }\n      } catch (err) {\n        stopUploadMsgs()\n        d2.setState({ uploading: false, validatingPhoto: false, uploadError: 'Não deu pra subir a foto. Tenta de novo — ' + (err && err.message ? err.message : 'erro') })\n      }\n    }\n    d2.on('.approve-photo', 'click', () => {\n      const url = d2.state.uploadPreview\n      if (!url) return\n      // Direct proxy assignment — guaranteed synchronous, unlike setState's\n      // batched writes — so generate() reads the new userPhoto immediately\n      // instead of racing an early-return on stale state.\n      d2.state.userPhoto = url\n      d2.state.uploadPreview = ''\n      syncSession(d2, { userPhotoUrl: url })\n      generate(d2)\n    })\n    d2.on('.retake-photo', 'click', () => {\n      console.log('[provador] retake-photo clicked')\n      d2.state.uploadPreview = ''\n    })\n    d2.on('.retry-invalid-photo', 'click', () => {\n      d2.setState({ photoInvalidReason: '', uploadPreview: '', uploadError: '' })\n    })\n    d2.on('.related-prev', 'click', (e, el) => {\n      const track = el.parentElement && el.parentElement.querySelector('.related-track')\n      if (track) track.scrollBy({ left: -160, behavior: 'smooth' })\n    })\n    d2.on('.related-next', 'click', (e, el) => {\n      const track = el.parentElement && el.parentElement.querySelector('.related-track')\n      if (track) track.scrollBy({ left: 160, behavior: 'smooth' })\n    })\n    d2.on('.cart-item-click', 'click', (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const item = (d2.state.cart || [])[idx]\n      if (item && window.__provadorOpenProduct) window.__provadorOpenProduct(item.productId, item.skuId)\n    })\n    d2.on('.uploadzone', 'click', () => { d2.$('.filepick')?.click() })\n    d2.on('.filepick', 'change', (e, el) => { doUpload(el.files && el.files[0]) })\n    d2.on('.uploadzone', 'dragover', (e, el) => { e.preventDefault(); el.classList.add('drag') })\n    d2.on('.uploadzone', 'dragleave', (e, el) => { el.classList.remove('drag') })\n    d2.on('.uploadzone', 'drop', (e, el) => {\n      e.preventDefault(); el.classList.remove('drag')\n      const file = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]\n      doUpload(file)\n    })\n\n    // ---- result actions ----\n    d2.on('.act-refazer', 'click', () => { d2.state.showRefazerConfirm = false; generate(d2, { force: true }) })\n    d2.on('.act-trocar', 'click', () => { d2.state.userPhoto = ''; d2.state.resultUrl = ''; d2.state.showRefazerConfirm = false; d2.state.screen = 'upload' })\n    d2.on('.act-share', 'click', () => {\n      const p = d2.state.product || {}\n      const priceRaw = (p.priceRange && p.priceRange.sellingPrice && p.priceRange.sellingPrice.lowPrice)\n        || (p.items && p.items[0] && p.items[0].sellers && p.items[0].sellers[0] && p.items[0].sellers[0].commertialOffer && p.items[0].sellers[0].commertialOffer.Price)\n        || null\n      window.__powercordEmit && window.__powercordEmit('tryon-share', {\n        productId: p.productId || '',\n        productName: p.productName || '',\n        price: priceRaw,\n        color: d2.state.color || '',\n        size: d2.state.size || '',\n        skuId: resolveSkuId(p, d2.state.color, d2.state.size),\n        resultImageUrl: d2.state.resultUrl || '',\n        userPhotoUrl: d2.state.userPhoto || '',\n      })\n      flashToast(d2, 'Compartilhado!')\n    })\n    d2.on('.act-feedback-up', 'click', () => {\n      d2.setState({ feedbackGiven: 'positive', showRefazerConfirm: false })\n      logEvent(d2, 'feedback', 'out', { verdict: 'positive', productId: d2.state.product && d2.state.product.productId, resultUrl: d2.state.resultUrl })\n      flashToast(d2, 'Obrigado pelo feedback!')\n    })\n    d2.on('.act-feedback-down', 'click', () => {\n      d2.setState({ feedbackGiven: 'negative', showRefazerConfirm: true })\n      logEvent(d2, 'feedback', 'out', { verdict: 'negative', productId: d2.state.product && d2.state.product.productId, resultUrl: d2.state.resultUrl })\n    })\n    d2.on('.confirm-refazer-yes', 'click', () => { d2.state.showRefazerConfirm = false; generate(d2, { force: true }) })\n    d2.on('.confirm-refazer-no', 'click', () => { d2.state.showRefazerConfirm = false })\n    d2.on('.addbag', 'click', () => {\n      const p = d2.state.product\n      const skuId = resolveSkuId(p, d2.state.color, d2.state.size)\n      if (!skuId) { flashToast(d2, 'Selecione cor e tamanho antes de adicionar à sacola.'); return }\n      // Push a full object (not a bare skuId) — renderCart needs imageUrl/\n      // productName to show a real thumb instead of a blank gray tile.\n      const priceRaw = (p.priceRange && p.priceRange.sellingPrice && p.priceRange.sellingPrice.lowPrice)\n        || (p.items && p.items[0] && p.items[0].sellers && p.items[0].sellers[0] && p.items[0].sellers[0].commertialOffer && p.items[0].sellers[0].commertialOffer.Price)\n        || null\n      const cartItem = {\n        skuId,\n        productId: p.productId,\n        productName: p.productName,\n        imageUrl: imageForColor(p, d2.state.color) || firstImage(p),\n        price: priceRaw,\n        quantity: 1,\n      }\n      d2.state.cart = [...(d2.state.cart || []), cartItem]\n      window.__powercordEmit && window.__powercordEmit('tryon-add-to-cart', [skuId])\n      flashToast(d2, 'Adicionado à sacola.')\n    })\n    d2.on('.checkout', 'click', () => {\n      if (!(d2.state.cart || []).length) { flashToast(d2, 'Sacola vazia — adicione uma peça antes do checkout.'); return }\n      window.__powercordEmit && window.__powercordEmit('tryon-checkout', {})\n    })\n    d2.on('.gitem-hist-click', 'click', async (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const item = (d2.state.gallery || [])[idx]\n      if (!item) return\n      await preloadImage(item.url)\n      d2.state.resultUrl = item.url\n      if (item.product) d2.state.product = item.product\n    })\n    d2.on('.sugcard-click', 'click', async (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const pick = (d2.state.suggestions || [])[idx]\n      if (!pick) return\n      d2.state.screen = 'loading'\n      const full = await window.__provadorFetchProduct(pick.productId)\n      if (!full) { flashToast(d2, 'Produto não encontrado.'); d2.state.screen = 'loading'; return }\n      const item = await enrichWithColorSiblings(full)\n      const colors = variationValues(item, 'Cor')\n      const sizes = variationValues(item, 'Tamanho')\n      d2.setState({\n        product: item,\n        color: colors[0] || '',\n        size: sizes[0] || '',\n        lookExtra: null,\n      })\n      loadCrosssell(d2, item.productId)\n      generate(d2)\n    })\n  },\n\n  updated(d2) {\n    updateCarouselArrows(d2)\n  },\n}\n\nfunction updateCarouselArrows(d2) {\n  requestAnimationFrame(() => {\n    const root = d2.root\n    if (!root || !root.querySelectorAll) return\n    root.querySelectorAll('.carousel').forEach((wrap) => {\n      const track = wrap.querySelector('.related-track')\n      if (!track) return\n      const hasOverflow = track.scrollWidth > track.clientWidth + 2\n      const prev = wrap.querySelector('.related-prev')\n      const next = wrap.querySelector('.related-next')\n      if (prev) prev.style.display = hasOverflow ? 'flex' : 'none'\n      if (next) next.style.display = hasOverflow ? 'flex' : 'none'\n    })\n  })\n}\n\nfunction renderSearch(d2) {\n  const s = d2.state\n  const results = s.results || []\n  let list\n  if (s.loadingSearch) {\n    list = `<div class=\"empty\"><span class=\"spin\"></span>&nbsp; buscando no catálogo John John…</div>`\n  } else if (s.searchError) {\n    list = `<div class=\"empty\">${esc(s.searchError)}</div>`\n  } else if (!results.length) {\n    list = `<div class=\"empty\">${s.searched ? 'Nada encontrado — tenta outro termo.' : 'Busque uma peça pra começar (ex: \"blusa\", \"saia\", \"vestido\").'}</div>`\n  } else {\n    list = `<div class=\"grid\">${results.map((p, i) => {\n      const img = thumbImageOf(p.items && p.items[0])\n      const price = p.priceRange && p.priceRange.sellingPrice ? d2.fmt.currency(p.priceRange.sellingPrice.lowPrice, 'BRL', { locale: 'pt-BR' }) : ''\n      return `<div class=\"card\" data-idx=\"${i}\" data-key=\"${esc(p.productId || i)}\">\n        <div class=\"ph\" style=\"background-image:url('${esc(img)}')\"></div>\n        <div class=\"info\"><div class=\"nm\">${esc(p.productName)}</div><div class=\"pr\">${price}</div></div>\n      </div>`\n    }).join('')}</div>`\n  }\n  return `<div class=\"disclaimer\">Catálogo real da John John (VTEX Intelligent Search). O provador mostra como a peça fica em você — não substitui a tabela de medidas.</div>\n    <div class=\"searchbar\">\n      <input type=\"text\" placeholder=\"Buscar produto…\" value=\"${esc(d2.state.query || '')}\" data-bind=\"query\"/>\n      <button class=\"go\" ${d2.state.loadingSearch ? 'disabled' : ''}>Buscar</button>\n    </div>\n    ${list}`\n}\n\nfunction renderProduct(d2) {\n  const p = d2.state.product\n  if (!p) return `<div class=\"empty\">Nenhum produto selecionado.</div>`\n  const img = (p.items && p.items[0] && p.items[0].images && p.items[0].images[0] && p.items[0].images[0].imageUrl) || ''\n  const price = p.priceRange && p.priceRange.sellingPrice ? d2.fmt.currency(p.priceRange.sellingPrice.lowPrice, 'BRL', { locale: 'pt-BR' }) : ''\n  const colors = variationValues(p, 'Cor')\n  const sizes = variationValues(p, 'Tamanho')\n  const installments = p.priceRange && p.priceRange.sellingPrice ? d2.fmt.currency(p.priceRange.sellingPrice.lowPrice / 6, 'BRL', { locale: 'pt-BR' }) : ''\n  const desc = htmlToText(p.description)\n  const open = d2.state.openAcc || ''\n  return `<div class=\"pgrid\">\n    <div class=\"pimg\" style=\"background-image:url('${esc(img)}')\"></div>\n    <div>\n      <div class=\"ptitlerow\">\n        <div>\n          <div class=\"pbrand\">${esc(p.brand || 'John John')}</div>\n          <div class=\"pname\">${esc(p.productName)}</div>\n        </div>\n        <div class=\"shareicon act-share\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"18\" cy=\"5\" r=\"3\"/><circle cx=\"6\" cy=\"12\" r=\"3\"/><circle cx=\"18\" cy=\"19\" r=\"3\"/><line x1=\"8.6\" y1=\"10.6\" x2=\"15.4\" y2=\"6.4\"/><line x1=\"8.6\" y1=\"13.4\" x2=\"15.4\" y2=\"17.6\"/></svg></div>\n      </div>\n      <div class=\"pprice\">${price}</div>\n      <div class=\"pinstall\">${installments ? `6x de ${installments} sem juros` : ''}</div>\n      ${colors.length ? `<div class=\"optlabel\">Cores</div><div class=\"opts\">${colors.map(c =>\n        `<div class=\"chip ${c === d2.state.color ? 'on' : ''}\" data-kind=\"color\" data-val=\"${esc(c)}\">${esc(c)}</div>`).join('')}</div>` : ''}\n      ${sizes.length ? `<div class=\"optlabel\">Medidas</div><div class=\"opts\">${sizes.map(sz =>\n        `<div class=\"chip ${sz === d2.state.size ? 'on' : ''}\" data-kind=\"size\" data-val=\"${esc(sz)}\">${esc(sz)}</div>`).join('')}</div>\n        <div class=\"sizeguide act-sizeguide-measures\">Tabela de medidas</div>` : ''}\n      ${isCategoryBlocked(p, d2.state.blocklist)\n        ? `<div class=\"disclaimer\" style=\"margin-top:20px;background:#FBF3E8;padding:12px 14px;border-radius:10px\">Essa peça (categoria incompatível — ex: bolsa, cinto, carteira) não pode ser experimentada no provador virtual.</div>`\n        : `<button class=\"cta\">Provar</button>`}\n      <div class=\"accordion\">\n        <div class=\"acc-item ${open === 'desc' ? 'open' : ''}\" data-acc=\"desc\">\n          <div class=\"acc-head\">Descrição<span class=\"acc-chev\">&#9660;</span></div>\n          <div class=\"acc-body\">${esc(desc) || 'Sem descrição disponível para este produto.'}</div>\n        </div>\n        <div class=\"acc-item ${open === 'size' ? 'open' : ''}\" data-acc=\"size\">\n          <div class=\"acc-head\">Encontre o seu tamanho<span class=\"acc-chev\">&#9660;</span></div>\n          <div class=\"acc-body\">Guia de medidas não disponível neste protótipo — em produção, viria da mesma API de catálogo da John John.</div>\n        </div>\n      </div>\n    </div>\n  </div>`\n}\n\nfunction renderUpload(d2) {\n  const s = d2.state\n\n  if (s.validatingPhoto) {\n    return `<div class=\"rimg\" style=\"background-image:url('${esc(s.uploadPreview)}');max-width:360px;margin:0 auto 20px\">\n      <div class=\"load\"><span class=\"spin\"></span>${esc(s.uploadStageMsg || 'Verificando sua foto…')}</div>\n    </div>`\n  }\n\n  if (s.photoInvalidReason) {\n    return `<div class=\"empty\">${esc(s.photoInvalidReason)}</div>\n    <div class=\"disclaimer\" style=\"text-align:center;margin:12px auto 0;max-width:48ch\">Tenta outra foto seguindo as dicas ao lado.</div>\n    <button class=\"retry-invalid-photo primary-btn\">Tentar com outra foto</button>`\n  }\n\n  if (s.uploadPreview) {\n    const loaded = !!s.uploadPreviewLoaded\n    return `<div class=\"rimg\" style=\"background-image:url('${esc(s.uploadPreview)}');max-width:360px;margin:0 auto 20px\">\n      ${loaded ? '' : `<div class=\"load\"><span class=\"spin\"></span>Carregando prévia…</div>`}\n    </div>\n    <div class=\"disclaimer\" style=\"text-align:center;margin:0 auto 16px;max-width:48ch\">Confira sua foto antes de continuar. Ao confirmar, você concorda com os termos de uso do provador virtual (referência — termos completos a definir com a Veste).</div>\n    <button class=\"approve-photo primary-btn\">Confirmar e ver o resultado</button>\n    <div class=\"sizeguide retake-photo\" style=\"text-align:center;display:block;margin-top:14px\">usar outra foto</div>`\n  }\n  return `<div class=\"uploadgrid\">\n    <div class=\"uploadzone\">\n      <input class=\"filepick\" type=\"file\" accept=\"image/*\" style=\"display:none\"/>\n      ${s.uploading\n        ? `<div class=\"ic\"><span class=\"spin\"></span></div><div class=\"t\">${esc(s.uploadStageMsg || 'Subindo sua foto…')}</div>`\n        : `<div class=\"ic\">📷</div><div class=\"t\">Suba uma imagem</div><div class=\"s\">Clique pra escolher uma foto sua</div>`}\n    </div>\n    <div class=\"uploadtips\">\n      <div class=\"optlabel\" style=\"margin-top:0\">Como escolher uma boa foto</div>\n      <ul class=\"tipslist\">\n        <li>De frente, olhando pra câmera</li>\n        <li>Corpo inteiro ou pelo menos do tórax pra cima</li>\n        <li>Boa iluminação — evite contraluz ou ambientes escuros</li>\n        <li>Fundo neutro, sem muita bagunça atrás de você</li>\n        <li>Roupas justas ajudam a IA a entender melhor seu corpo</li>\n        <li>Evite fotos em grupo — só você na foto</li>\n      </ul>\n      ${s.uploadError ? `<div class=\"empty\">${esc(s.uploadError)}</div>` : ''}\n    </div>\n  </div>`\n}\n\nfunction renderResult(d2) {\n  const s = d2.state\n  const p = s.product || {}\n  const priceRaw = (p.priceRange && p.priceRange.sellingPrice && p.priceRange.sellingPrice.lowPrice)\n    || (p.items && p.items[0] && p.items[0].sellers && p.items[0].sellers[0] && p.items[0].sellers[0].commertialOffer && p.items[0].sellers[0].commertialOffer.Price)\n    || null\n  const price = priceRaw ? d2.fmt.currency(priceRaw, 'BRL', { locale: 'pt-BR' }) : ''\n  const colors = variationValues(p, 'Cor')\n  const sizes = variationValues(p, 'Tamanho')\n  const gallery = s.gallery || []\n\n  const img = s.resultUrl || s.userPhoto || ''\n  const loading = s.generating\n    ? `<div class=\"genprogress\"><div class=\"genbar\"></div></div><div class=\"load\"><span class=\"spin\"></span>${esc(s.genStageMsg || 'Analisando a peça e gerando sua prova…')}<div class=\"gentime\">Costuma levar de 15 a 25 segundos.</div></div>` : ''\n  const err = s.genError ? `<div class=\"generr ${s.genFriendly ? 'friendly' : ''}\">\n      <span class=\"generr-ic\">${s.genFriendly ? RULER_ICON : WARNING_ICON}</span>\n      <span class=\"generr-msg\">${esc(s.genError)}</span>\n    </div>` : ''\n\n  return `<div class=\"rgrid\">\n    <div>\n      <div class=\"rimg\" style=\"background-image:url('${esc(img)}')\">${loading}${err}</div>\n      <div class=\"airesult-disclaimer\">Imagem gerada por inteligência artificial — é uma representação de como a peça pode ficar em você. Outros produtos que aparecem na foto (acessórios, calçados, etc.) não estão inclusos.</div>\n      ${(!s.generating && (s.resultUrl || s.genError)) ? `\n      <div class=\"actrow\">\n        <div class=\"iconbtn act-refazer\">${REFRESH_ICON}refazer</div>\n        <div class=\"iconbtn act-feedback-up ${s.feedbackGiven === 'positive' ? 'fb-selected' : ''}\">${THUMBS_UP_ICON}</div>\n        <div class=\"iconbtn act-feedback-down ${s.feedbackGiven === 'negative' ? 'fb-selected' : ''}\">${THUMBS_DOWN_ICON}</div>\n        <div class=\"iconbtn act-trocar\">${UPLOAD_ICON}trocar</div>\n      </div>\n      ${s.showRefazerConfirm ? `\n      <div class=\"refazer-confirm\">\n        <span>Não curtiu? Deseja refazer?</span>\n        <div class=\"rc-btn yes confirm-refazer-yes\">Sim, refazer</div>\n        <div class=\"rc-btn confirm-refazer-no\">Não</div>\n      </div>` : ''}\n      <button class=\"outline-cta act-share\">${SHARE_ICON}Compartilhar</button>` : ''}\n      ${gallery.length ? `<div class=\"gallery\">${gallery.map((g, i) =>\n        `<div class=\"gitem gitem-hist-click ${g.url === s.resultUrl ? 'on' : ''}\" data-idx=\"${i}\" data-key=\"${i}\"\n          style=\"background-image:url('${esc(g.url)}')\" title=\"${esc(g.name)}\"></div>`).join('')}</div>` : ''}\n    </div>\n    <div class=\"panel\" style=\"max-height:100%;overflow-y:auto\">\n      <div>\n        <div class=\"pname\" style=\"font-size:19px\">${esc(p.productName || '')}</div>\n        <div class=\"pprice\">${price}</div>\n      </div>\n      ${sizes.length ? `<div><div class=\"optlabel\">Medidas</div><div class=\"opts\">${sizes.map(sz => {\n        const oos = !sizeHasStock(p, sz, s.color)\n        return `<div class=\"chip ${sz === s.size ? 'on' : ''} ${oos ? 'chip-oos' : ''}\" data-kind=\"size\" data-val=\"${esc(sz)}\" data-oos=\"${oos ? '1' : ''}\">${esc(sz)}</div>`\n      }).join('')}</div></div>` : ''}\n      ${colors.length > 1 ? `<div><div class=\"optlabel\">Cores</div><div class=\"opts colorthumbs\">${colors.map(c => {\n        const cimg = imageForColor(p, c)\n        return `<div class=\"chip colorthumb ${c === s.color ? 'on' : ''}\" data-kind=\"color\" data-val=\"${esc(c)}\" title=\"${esc(c)}\" style=\"background-image:url('${esc(cimg)}')\"></div>`\n      }).join('')}</div></div>` : ''}\n      <div class=\"helpblock\">\n        <div class=\"helprow whatsapp-shopper act-personal-shopper\">\n          <span class=\"helpicon-emoji\">💬</span>\n          <span>Está com dúvidas? <u>Fale com nossa Personal Shopper</u></span>\n        </div>\n        <div class=\"helpdivider\"></div>\n        <div class=\"helprow2\">\n          <div class=\"helpcol act-sizeguide-size\"><img class=\"helpicon\" src=\"https://veste-app.data2.media/file/ITdz6VMW-IqztvjjHL1HX\" alt=\"\"/><span>Descubra o seu tamanho</span></div>\n          <div class=\"helpcol act-sizeguide-measures\"><img class=\"helpicon\" src=\"https://veste-app.data2.media/file/v7RD6oqiPajrmPyrLDFrV\" alt=\"\"/><span>Tabela de Medidas</span></div>\n        </div>\n      </div>\n      <button class=\"addbag\">Adicionar à sacola</button>\n      ${renderSuggestions(s)}\n      ${renderCart(s)}\n      ${(s.cart || []).length ? '<button class=\"checkout\">Ir para sacola</button>' : ''}\n    </div>\n  </div>`\n}\n\nfunction renderCart(s) {\n  const items = s.cart || []\n  if (!items.length) return ''\n  return `<div>\n    <div class=\"optlabel\">Sua sacola</div>\n    <div class=\"gallery\">${items.map((c, i) => `\n      <div class=\"gitem cart-item-click\" data-idx=\"${i}\" title=\"${esc(c.productName || c.skuId || '')}\"\n        style=\"background-image:url('${esc(c.imageUrl || '')}')\"></div>`).join('')}</div>\n  </div>`\n}\n\nfunction renderSuggestions(s) {\n  const picks = (s.suggestions || []).slice(0, 8)\n  if (!picks.length) return ''\n  return `<div>\n    <div class=\"optlabel\">Compre Junto</div>\n    <div class=\"carousel\">\n      <div class=\"carousel-arrow prev related-prev\">&#8249;</div>\n      <div class=\"gallery related-track\">${picks.map((p, i) =>\n        `<div class=\"gitem sugcard-click\" data-idx=\"${i}\" data-key=\"${esc(p.productId)}\" title=\"${esc(p.productName)}\"\n          style=\"background-image:url('${esc(p.imageUrl)}')\"></div>`).join('')}</div>\n      <div class=\"carousel-arrow next related-next\">&#8250;</div>\n    </div>\n  </div>`\n}\n\nfunction firstItemWithImages(items) {\n  return (items || []).find((it) => it.images && it.images.length && it.images[0] && it.images[0].imageUrl) || null\n}\n\nfunction imageForColor(product, colorName) {\n  // Some SKUs in this catalog only carry images on the color's \"representative\"\n  // size — other sizes of the exact same color can have an EMPTY images array.\n  // Blindly taking the first color-matching item can land on one of those and\n  // show a blank swatch, so scan every matching item for one that actually has\n  // a photo before falling back.\n  const colorItems = (product.items || []).filter((it) =>\n    (it.variations || []).some((v) => v.name === 'Cor' && (v.values || [])[0] === colorName))\n  const item = firstItemWithImages(colorItems)\n  return (item && thumbImageOf(item)) || firstImage(product)\n}\n\nasync function loadCrosssell(d2, productId) {\n  // \"Compre Junto\" = VTEX's own real cross-sell recommendations — merges\n  // both recommendation types (\"accessories\" a.k.a. Complete o Look, and\n  // \"showtogether\") since showtogether alone is often empty for a given\n  // product while accessories usually has real curated data.\n  try {\n    const buildTargetUrl = (type) => {\n      const variables = JSON.stringify({ identifier: { field: 'id', value: String(productId) }, type, filter: 'ALL_AVAILABLE' })\n      const variablesB64 = btoa(variables)\n      const extensions = JSON.stringify({\n        persistedQuery: {\n          version: 1,\n          sha256Hash: 'efbe0d6d6698fa852dd37a3a8cbe219c3adaf8baaf342402addd5d0f378927f7',\n          sender: 'johnjohndenim.buy-together@0.x',\n          provider: 'johnjohndenim.product-recommendations@0.x',\n        },\n        variables: variablesB64,\n      })\n      const params = new URLSearchParams({\n        workspace: 'master', maxAge: 'short', appsEtag: 'remove', domain: 'store',\n        locale: 'pt-BR', operationName: 'CROSSELING_PRODUCTS', variables: '{}', extensions,\n      })\n      return 'https://www.johnjohndenim.com.br/_v/segment/graphql/v1?' + params.toString()\n    }\n    const fetchType = async (type) => {\n      const targetUrl = buildTargetUrl(type)\n      const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-crosssell?targetUrl=' + encodeURIComponent(targetUrl))\n      const json = await res.json()\n      return (json && json.data && json.data.data && json.data.data.productRecommendations) || []\n    }\n    const [accessories, showtogether] = await Promise.all([fetchType('accessories'), fetchType('showtogether')])\n    const seen = new Set([String(productId)])\n    const picks = []\n    ;[...accessories, ...showtogether].forEach((rec) => {\n      const id = String(rec.productId)\n      if (seen.has(id)) return\n      seen.add(id)\n      const firstItem = (rec.items || [])[0]\n      const priceRaw = firstItem && firstItem.sellers && firstItem.sellers[0] && firstItem.sellers[0].commertialOffer && firstItem.sellers[0].commertialOffer.Price\n      const imageUrl = thumbImageOf(firstItem)\n      picks.push({ productId: rec.productId, productName: rec.productName, price: priceRaw || null, imageUrl })\n    })\n    d2.state.suggestions = picks.slice(0, 8)\n  } catch (err) {\n    console.warn('[provador] crosssell failed', err)\n  }\n}\n\nasync function enrichWithColorSiblings(product) {\n  // In this catalog each COLOR is its own separate productId, and there's no\n  // reliable NAME-based way to tell a real color sibling apart from a\n  // same-family style with a different print (a name heuristic here caused a\n  // real false-positive merge across two different-gender products). Use\n  // VTEX's own first-party \"similars\" endpoint instead — it's the store's\n  // own manually-curated same-item/different-color linking, not a guess.\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-similars?productId=' + encodeURIComponent(product.productId))\n    const json = await res.json()\n    const siblings = (json && json.data && json.data.products) || []\n    if (!siblings.length) return product\n    const mergedItems = [...(product.items || [])]\n    siblings.forEach((sib) => {\n      (sib.items || []).forEach((it) => { mergedItems.push(it) })\n    })\n    return { ...product, items: mergedItems }\n  } catch (err) {\n    return product\n  }\n}\n\nfunction firstImage(p) {\n  const item = firstItemWithImages((p && p.items) || [])\n  return thumbImageOf(item)\n}\n\n// The catalog's FIRST product photo is almost always the model wearing a full,\n// styled look (belt, bag, top, pants all in one shot) — great for the PDP hero,\n// useless as a small thumbnail since it doesn't say which product it's pointing\n// at. The SECOND photo is normally the flat/still shot of just that one item, so\n// prefer it for any small thumbnail (search cards, color swatches, cart,\n// Compre Junto). Falls back to the first photo if there's no second one.\nfunction thumbImageOf(item) {\n  const imgs = (item && item.images) || []\n  return (imgs[1] && imgs[1].imageUrl) || (imgs[0] && imgs[0].imageUrl) || ''\n}\n\nfunction allImages(p, colorName) {\n  // After color-sibling enrichment, p.items can hold SKUs from several\n  // different sibling products (one per color) — items[0] is no longer\n  // guaranteed to be the color actually selected. Resolve by color when we\n  // have one; also skip any matching item that carries an empty images\n  // array (some size SKUs in this catalog don't have their own photos).\n  const items = (p && p.items) || []\n  const colorItems = colorName\n    ? items.filter((it) => (it.variations || []).some((v) => v.name === 'Cor' && (v.values || [])[0] === colorName))\n    : []\n  const item = firstItemWithImages(colorItems) || firstItemWithImages(items)\n  const imgs = (item && item.images) || []\n  return imgs.map((i) => i.imageUrl).filter(Boolean).slice(0, 8)\n}\n\nfunction resolveSkuId(product, color, size) {\n  if (!product) return null\n  const items = product.items || []\n  const match = items.find((it) => {\n    const vals = {}\n    ;(it.variations || []).forEach((v) => { vals[v.name] = (v.values || [])[0] })\n    const colorOk = !color || vals['Cor'] === color\n    const sizeOk = !size || vals['Tamanho'] === size || vals['Tamanho do Produto'] === size\n    return colorOk && sizeOk\n  })\n  return (match && match.itemId) || (items[0] && items[0].itemId) || null\n}\n\nfunction variationValues(product, name) {\n  const aliases = name === 'Tamanho' ? ['Tamanho', 'Tamanho do Produto'] : [name]\n  const set = new Set()\n  ;(product.items || []).forEach((it) => {\n    (it.variations || []).forEach((v) => { if (aliases.indexOf(v.name) > -1) (v.values || []).forEach((val) => set.add(val)) })\n  })\n  return Array.from(set)\n}\n\nfunction sizeHasStock(product, sizeVal, colorVal) {\n  const items = (product && product.items) || []\n  let candidates = items.filter((it) => {\n    const vals = {}\n    ;(it.variations || []).forEach((v) => { vals[v.name] = (v.values || [])[0] })\n    return vals['Tamanho'] === sizeVal || vals['Tamanho do Produto'] === sizeVal\n  })\n  if (colorVal) {\n    const withColor = candidates.filter((it) => {\n      const vals = {}\n      ;(it.variations || []).forEach((v) => { vals[v.name] = (v.values || [])[0] })\n      return vals['Cor'] === colorVal\n    })\n    if (withColor.length) candidates = withColor\n  }\n  if (!candidates.length) return true\n  return candidates.some((it) => {\n    const seller = it.sellers && it.sellers[0]\n    const offer = seller && seller.commertialOffer\n    if (!offer || typeof offer.AvailableQuantity !== 'number') return true\n    return offer.AvailableQuantity > 0 && offer.IsAvailable !== false\n  })\n}\n\nasync function runSearch(d2) {\n  const input = d2.$('.searchbar input')\n  const q = (input && input.value || '').trim()\n  if (!q) return\n  d2.setState({ loadingSearch: true, searchError: '', query: q, searched: true })\n  try {\n    const base = d2.rush(d2.params.searchApi).url()\n    const res = await fetch(base + '?search=' + encodeURIComponent(q))\n    if (!res.ok) throw new Error('HTTP ' + res.status)\n    const json = await res.json()\n    const products = (json && json.data && json.data.products) || []\n    d2.setState({ results: products, loadingSearch: false })\n  } catch (err) {\n    d2.setState({ loadingSearch: false, searchError: 'Busca falhou: ' + (err && err.message ? err.message : 'erro') })\n  }\n}\n\n\n\nasync function generate(d2, opts) {\n  const s = d2.state\n  const p = s.product\n  const candidateImgs = allImages(p, s.color)\n  if (!s.userPhoto || !candidateImgs.length) return\n  const extra = s.lookExtra\n  const extraCandidateImgs = extra ? allImages(extra) : []\n  const force = !!(opts && opts.force)\n  const blockedCat = matchedBlockedCategory(p, s.blocklist)\n  if (blockedCat) {\n    // Defense-in-depth: the CTA is already hidden for a blocked category, but\n    // the host can fire tryon-open directly with any productId (openProduct()\n    // also guards this) — and jj-tryon-generate re-checks blockedCategory\n    // server-side regardless of what the client sends.\n    d2.setState({ screen: 'result', generating: false, genError: 'Essa peça não pode ser experimentada no provador virtual (categoria incompatível).', genFriendly: true, resultUrl: '' })\n    return\n  }\n  const cacheKey = tryonCacheKey(p, extra, s.color, s.size, s.userPhoto)\n  const cachedUrl = force ? null : tryonResultCache.get(cacheKey)\n  if (cachedUrl) {\n    d2.setState({ screen: 'result', generating: true, genError: '', genFriendly: false, resultUrl: '', feedbackGiven: '', showRefazerConfirm: false })\n    await preloadImage(cachedUrl)\n    const cacheName = p.productName + (extra ? ' + ' + extra.productName : '')\n    const cacheGallery = [{ url: cachedUrl, name: cacheName, product: p }, ...(s.gallery || [])].slice(0, 8)\n    d2.setState({ resultUrl: cachedUrl, generating: false, gallery: cacheGallery })\n    return\n  }\n  d2.setState({ screen: 'result', generating: true, genError: '', genFriendly: false, resultUrl: '', feedbackGiven: '', showRefazerConfirm: false })\n  const stopGenMsgs = cycleMessages(d2, [\n    'Analisando a peça…',\n    'Aplicando a peça na sua foto…',\n    'Ajustando os detalhes finais…',\n  ], 4000, 'genStageMsg')\n  try {\n    // Unified generation pipeline — same rush used by provador-standalone AND\n    // tryon-test-lab, so both always run the exact same analyze+prompt+draw\n    // strategy. The prompt/strategy itself lives ONLY server-side (never\n    // shipped to the browser bundle).\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-tryon-generate', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        userPhotoUrl: s.userPhoto,\n        garmentImages: candidateImgs,\n        garmentProductName: p.productName,\n        extraImages: extraCandidateImgs,\n        extraProductName: extra ? extra.productName : '',\n        userPhotoBodyCoverage: s.userPhotoBodyCoverage || '',\n        blockedCategory: blockedCat || '',\n      }),\n    })\n    const json = await res.json()\n    const result = (json && json.data) || {}\n    if (result.blocked) {\n      stopGenMsgs()\n      d2.setState({\n        generating: false,\n        genError: result.blockedReason || 'Essa foto não dá pra provar essa peça direito — tenta com outra foto.',\n        genFriendly: true,\n      })\n      return\n    }\n    const url = result.resultUrl\n    if (!url) throw new Error('sem imagem retornada')\n    // Preload before swapping the background-image — otherwise the loading\n    // overlay disappears the instant resultUrl is set, but the browser still\n    // needs to download the (often multi-MB) generated PNG, so the panel\n    // flashes blank/half-drawn for a beat. Keep the spinner up until the\n    // image is actually decoded and ready to paint.\n    await preloadImage(url)\n    stopGenMsgs()\n    tryonResultCache.set(cacheKey, url)\n    const name = p.productName + (extra ? ' + ' + extra.productName : '')\n    const gallery = [{ url, name, garment: result.garmentImageUsed, product: p }, ...(s.gallery || [])].slice(0, 8)\n    d2.setState({ resultUrl: url, generating: false, gallery })\n    logGarmentSwap(d2, {\n      productId: p.productId,\n      productName: p.productName,\n      skuId: resolveSkuId(p, s.color, s.size),\n      userPhotoUrl: s.userPhoto,\n      referenceImagesUsed: result.referenceImagesUsed || [],\n      resultImageUrl: url,\n      analysisJson: JSON.stringify({ analysis: result.analysisG, extraAnalysis: result.analysisE, retried: !!result.retried, retryReason: result.retryReason || '' }),\n      success: true,\n      errorMessage: '',\n    })\n  } catch (err) {\n    stopGenMsgs()\n    const technicalDetail = (err && err.message) ? err.message : 'erro'\n    const msg = 'Não deu pra gerar sua prova agora — tenta de novo em instantes.'\n    d2.setState({ generating: false, genError: msg, genFriendly: false })\n    logGarmentSwap(d2, {\n      productId: p && p.productId,\n      productName: p && p.productName,\n      skuId: p ? resolveSkuId(p, s.color, s.size) : '',\n      userPhotoUrl: s.userPhoto,\n      referenceImagesUsed: [],\n      resultImageUrl: '',\n      analysisJson: '',\n      success: false,\n      errorMessage: 'Geração falhou: ' + technicalDetail,\n    })\n  }\n}\n\n// Tiny nanoid-compatible generator (URL-safe alphabet, 21 chars) — used\n// synchronously at session-init time, so it can't depend on an async d2.lib load.\nfunction nanoid(size = 21) {\n  const alphabet = 'ModuleSymbhasOwnPr0123456789ABCDEFGHNRVfgctiUvz_-wLunknI'\n  let id = ''\n  const bytes = crypto.getRandomValues(new Uint8Array(size))\n  for (let i = 0; i < size; i++) id += alphabet[bytes[i] & 63]\n  return id\n}\n\nasync function syncSession(d2, extra) {\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/tryon-session-sync', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        sessionId: d2.state.sessionId,\n        customerId: d2.state.customerId || '',\n        anonymous: !d2.state.customerId,\n        originHost: (document.referrer && new URL(document.referrer).host) || '',\n        ...extra,\n      }),\n    })\n    const json = await res.json()\n    return (json && json.data) || json\n  } catch (err) {\n    console.warn('[provador] session sync failed', err)\n    return null\n  }\n}\n\nasync function initSession(d2, sessionCodeFromHost) {\n  const sessionId = sessionCodeFromHost || ('anon-' + nanoid())\n  d2.setState({ sessionId, customerId: sessionCodeFromHost || '' })\n  const synced = await syncSession(d2)\n  if (synced && synced.lastUserPhotoUrl && !d2.state.userPhoto) {\n    d2.state.userPhoto = synced.lastUserPhotoUrl\n  }\n}\n\nasync function logEvent(d2, eventName, direction, payload) {\n  if (!d2.state.sessionId) return\n  try {\n    await fetch('https://veste-app.data2apis.com/rush/listener/tryon-log-event', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        sessionId: d2.state.sessionId,\n        eventName,\n        direction,\n        payloadJson: JSON.stringify(payload == null ? null : payload),\n      }),\n    })\n  } catch (err) {\n    console.warn('[provador] event log failed', err)\n  }\n}\n\nasync function logGarmentSwap(d2, fields) {\n  if (!d2.state.sessionId) return\n  try {\n    await fetch('https://veste-app.data2apis.com/rush/listener/tryon-log-swap', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ sessionId: d2.state.sessionId, ...fields }),\n    })\n  } catch (err) {\n    console.warn('[provador] garment-swap log failed', err)\n  }\n}\n\nfunction flashToast(d2, msg) {\n  d2.state.toast = msg\n  clearTimeout(flashToast._t)\n  flashToast._t = setTimeout(() => { d2.state.toast = '' }, 2600)\n}\n\nfunction preloadImage(url) {\n  return new Promise((resolve) => {\n    if (!url) { resolve(); return }\n    const img = new Image()\n    img.onload = () => resolve()\n    img.onerror = () => resolve()\n    img.src = url\n  })\n}\n\nfunction cycleMessages(d2, messages, intervalMs = 1800, stateKey = 'uploadStageMsg') {\n  let i = 0\n  d2.state[stateKey] = messages[0]\n  const t = setInterval(() => {\n    i = (i + 1) % messages.length\n    d2.state[stateKey] = messages[i]\n  }, intervalMs)\n  return () => clearInterval(t)\n}\n\nasync function validatePhoto(url) {\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-validate-photo', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ imageUrl: url }),\n    })\n    const json = await res.json()\n    return (json && json.data) || null\n  } catch (err) {\n    console.warn('[provador] photo validation failed', err)\n    return null\n  }\n}\n\nfunction htmlToText(html) {\n  if (!html) return ''\n  const div = document.createElement('div')\n  div.innerHTML = String(html)\n  return (div.textContent || '').replace(/\\s+/g, ' ').trim()\n}\n\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n}\n"}},"searchApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchApi"}}}},"drawAssistant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"drawAssistant"}}}},"uploadDrive":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"uploadDrive"}}}},"analyzeApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"analyzeApi"}}}}}}}}}},{"_id":"6ab3d7501a04d8166dcff3c7","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"tryon-test-lab","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n:host{display:block;width:100%;height:100%}\n.wrap{\n  --paper:#F7F7F5; --ink:#0a0a0a; --muted:#6b6b6b; --linha:#e2e2e2; --card:#fff;\n  --ok:#1f7a3d; --bad:#b3261e;\n  width:100%;height:100%;overflow-y:auto;background:var(--paper);color:var(--ink);\n  font-family:-apple-system,BlinkMacSystemFont,'Helvetica Neue',Helvetica,Arial,sans-serif;\n  box-sizing:border-box;\n}\n*{box-sizing:border-box}\n.body{padding:clamp(16px,3vw,36px);max-width:1200px;margin:0 auto}\n.hdr{display:flex;align-items:baseline;gap:10px;margin-bottom:4px}\n.hdr h1{font-size:18px;font-weight:700;margin:0}\n.hdr span{font-size:12px;color:var(--muted)}\n.sub{font-size:12.5px;color:var(--muted);margin-bottom:24px;line-height:1.5;max-width:70ch}\n.section{background:var(--card);border:1px solid var(--linha);border-radius:12px;padding:18px;margin-bottom:16px}\n.section h2{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);margin:0 0 12px;font-weight:700}\n\n.photorow{display:flex;gap:16px;align-items:center;flex-wrap:wrap}\n.photothumb{width:88px;height:112px;border-radius:8px;background:#eee;background-size:cover;background-position:center;border:1px solid var(--linha);flex:none}\n.uploadzone{border:2px dashed var(--linha);border-radius:10px;padding:20px;text-align:center;cursor:pointer;\n  font-size:13px;color:var(--muted);flex:1;min-width:200px}\n.uploadzone:hover{border-color:#999}\n.uploadzone.drag{border-color:var(--ink);background:#f2f2f2}\n.smalllink{font-size:12px;color:var(--ink);text-decoration:underline;cursor:pointer}\n\n.searchbar{display:flex;gap:8px;margin-bottom:14px}\n.searchbar input{flex:1;padding:10px 12px;border:1px solid var(--linha);border-radius:8px;font-size:14px}\n.searchbar button{padding:0 18px;border:0;border-radius:8px;background:var(--ink);color:#fff;font-size:13px;cursor:pointer}\n.searchbar button:disabled{opacity:.5}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:10px}\n.gcard{position:relative;border:2px solid var(--linha);border-radius:10px;overflow:hidden;cursor:pointer;background:#fff}\n.gcard.selected{border-color:var(--ink)}\n.gcard.disabled{opacity:.35;cursor:not-allowed}\n.gcard .ph{width:100%;aspect-ratio:3/4;background:#eee;background-size:cover;background-position:center}\n.gcard .nm{font-size:10.5px;padding:6px 8px;line-height:1.3;min-height:30px}\n.gcard .catpill{position:absolute;top:6px;left:6px;background:rgba(0,0,0,.65);color:#fff;font-size:9px;\n  letter-spacing:.05em;text-transform:uppercase;padding:2px 6px;border-radius:999px}\n.gcard .check{position:absolute;top:6px;right:6px;width:20px;height:20px;border-radius:999px;background:var(--ink);\n  color:#fff;display:none;align-items:center;justify-content:center;font-size:12px}\n.gcard.selected .check{display:flex}\n.empty{padding:30px 0;text-align:center;color:var(--muted);font-size:13px}\n.spin{display:inline-block;width:14px;height:14px;border:2px solid rgba(0,0,0,.15);border-top-color:var(--ink);\n  border-radius:50%;animation:sp .7s linear infinite;vertical-align:-2px}\n@keyframes sp{to{transform:rotate(360deg)}}\n\n.selrow{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:14px}\n.selchip{display:flex;align-items:center;gap:8px;border:1px solid var(--linha);border-radius:999px;padding:4px 12px 4px 4px;font-size:12.5px}\n.selchip img{width:26px;height:26px;border-radius:999px;object-fit:cover}\n.selchip .x{cursor:pointer;color:var(--muted);font-weight:700}\n.testbtn{padding:12px 22px;border:0;border-radius:8px;background:var(--ink);color:#fff;font-size:13.5px;\n  font-weight:600;cursor:pointer}\n.testbtn:disabled{opacity:.4;cursor:default}\n\n.resultgrid{display:grid;grid-template-columns:340px 1fr;gap:22px}\n@media(max-width:700px){.resultgrid{grid-template-columns:1fr}}\n.rimg{width:100%;aspect-ratio:3/4;border-radius:10px;background:#eee;background-size:cover;background-position:center;\n  position:relative;overflow:hidden}\n.rimg .load{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:8px;\n  background:rgba(247,247,245,.9);font-size:12.5px;color:var(--muted)}\n.verdictrow{display:flex;gap:10px;margin-top:14px}\n.vbtn{flex:1;padding:12px;border-radius:8px;border:1.5px solid var(--linha);background:#fff;font-size:13.5px;\n  font-weight:600;cursor:pointer}\n.vbtn.ok{border-color:var(--ok);color:var(--ok)}\n.vbtn.bad{border-color:var(--bad);color:var(--bad)}\n.notebox{margin-top:10px}\n.notebox textarea{width:100%;min-height:70px;border:1px solid var(--linha);border-radius:8px;padding:10px;font-size:13px;\n  font-family:inherit;resize:vertical}\n.notebox button{margin-top:8px;padding:9px 16px;border:0;border-radius:8px;background:var(--bad);color:#fff;\n  font-size:13px;cursor:pointer}\n.debugbox{margin-top:16px;font-size:11.5px;color:var(--muted);background:#fafafa;border:1px solid var(--linha);\n  border-radius:8px;padding:10px;line-height:1.6;white-space:pre-wrap}\n.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%);background:var(--ink);color:#fff;\n  padding:12px 20px;border-radius:999px;font-size:13px;z-index:50;box-shadow:0 10px 30px rgba(0,0,0,.25)}\n`,\n\n  render(d2) {\n    const s = d2.state\n    const photoRow = `<div class=\"photorow\">\n      ${s.testPhoto ? `<div class=\"photothumb\" style=\"background-image:url('${esc(s.testPhoto)}')\"></div>` : ''}\n      <div class=\"uploadzone ${s.dragging ? 'drag' : ''}\">\n        <input class=\"filepick\" type=\"file\" accept=\"image/*\" style=\"display:none\"/>\n        ${s.uploading ? `<span class=\"spin\"></span> ${esc(s.uploadStageMsg || 'subindo…')}` : (s.testPhoto ? 'Trocar a foto de teste' : 'Clique ou arraste uma foto de teste aqui')}\n      </div>\n    </div>\n    ${s.uploadError ? `<div class=\"empty\">${esc(s.uploadError)}</div>` : ''}`\n\n    let searchBlock\n    if (s.loadingSearch) {\n      searchBlock = `<div class=\"empty\"><span class=\"spin\"></span> buscando…</div>`\n    } else if (s.searchError) {\n      searchBlock = `<div class=\"empty\">${esc(s.searchError)}</div>`\n    } else if (!(s.results || []).length) {\n      searchBlock = `<div class=\"empty\">${s.searched ? 'Nada encontrado.' : 'Busque uma peça (ex: \"blusa\", \"calça\").'}</div>`\n    } else {\n      const sel = s.selected || []\n      searchBlock = `<div class=\"grid\">${s.results.map((p, i) => {\n        const img = firstImage(p)\n        const cat = classify(p)\n        const isSel = sel.some((x) => x.productId === p.productId)\n        const disabled = !isSel && !canAdd(sel, p)\n        return `<div class=\"gcard ${isSel ? 'selected' : ''} ${disabled ? 'disabled' : ''}\" data-idx=\"${i}\" data-key=\"${esc(p.productId || i)}\">\n          <div class=\"catpill\">${cat}</div>\n          <div class=\"check\">&#10003;</div>\n          <div class=\"ph\" style=\"background-image:url('${esc(img)}')\"></div>\n          <div class=\"nm\">${esc(p.productName)}</div>\n        </div>`\n      }).join('')}</div>`\n    }\n\n    const sel = s.selected || []\n    const selRow = sel.length ? `<div class=\"selrow\">${sel.map((p, i) =>\n      `<div class=\"selchip\"><img src=\"${esc(firstImage(p))}\"/>${esc(p.productName)}<span class=\"x\" data-idx=\"${i}\">&times;</span></div>`\n    ).join('')}\n      <button class=\"testbtn\" ${(!s.testPhoto || !sel.length || s.testing) ? 'disabled' : ''}>${s.testing ? 'Testando…' : 'Testar combinação'}</button>\n    </div>` : `<div class=\"sub\">Selecione 1 peça, ou 2 compatíveis (blusa + calça), pra testar.</div>`\n\n    let resultBlock = ''\n    if (s.testing || s.resultUrl || s.genError) {\n      const loading = s.testing ? `<div class=\"load\"><span class=\"spin\"></span>&nbsp;Analisando e gerando…</div>` : ''\n      const img = s.resultUrl || s.testPhoto || ''\n      const err = s.genError ? `<div class=\"empty\">${esc(s.genError)}</div>` : ''\n      const verdict = (s.resultUrl && !s.testing) ? `<div class=\"verdictrow\">\n          <button class=\"vbtn ok act-ok\">✅ Sucesso</button>\n          <button class=\"vbtn bad act-bad\">❌ Problema</button>\n        </div>\n        ${s.showNote ? `<div class=\"notebox\">\n          <textarea placeholder=\"O que saiu errado? (opcional, mas ajuda muito)\">${esc(s.noteDraft || '')}</textarea>\n          <button class=\"act-sendbad\">Registrar problema</button>\n        </div>` : ''}\n        ${s.reported ? `<div class=\"sub\" style=\"color:var(--ok);margin-top:8px\">Registrado — obrigado!</div>` : ''}` : ''\n      const debug = (s.lastAnalyses && s.lastAnalyses.length) ? `<div class=\"debugbox\">${s.lastAnalyses.map((a, i) =>\n        `#${i + 1}: ${esc(a.garment_type)} · zona: ${esc(a.body_zone)} · outros na foto: ${esc(a.other_items_in_photo || 'none')}\\nimagem usada: ${esc(a.imageUrl)}`\n      ).join('\\n\\n')}</div>` : ''\n      resultBlock = `<div class=\"section\"><h2>Resultado</h2>\n        <div class=\"resultgrid\">\n          <div class=\"rimg\" style=\"background-image:url('${esc(img)}')\">${loading}</div>\n          <div>${err}${verdict}${debug}</div>\n        </div>\n      </div>`\n    }\n\n    const toast = s.toast ? `<div class=\"toast\">${esc(s.toast)}</div>` : ''\n\n    return `<div class=\"wrap\"><div class=\"body\">\n      <div class=\"hdr\"><h1>Provador · Test Lab</h1><span>uso interno — não é a experiência do cliente</span></div>\n      <div class=\"sub\">Suba uma foto de teste, escolha 1 ou 2 peças compatíveis (blusa + calça), gere a prova e reporte se ficou bom ou não. Todo problema reportado fica salvo pra gente melhorar o pipeline.</div>\n      <div class=\"section\"><h2>1 · Foto de teste</h2>${photoRow}</div>\n      <div class=\"section\"><h2>2 · Escolher peça(s)</h2>\n        <div class=\"searchbar\">\n          <input type=\"text\" placeholder=\"Buscar produto…\" value=\"${esc(s.query || '')}\"/>\n          <button class=\"go\" ${s.loadingSearch ? 'disabled' : ''}>Buscar</button>\n        </div>\n        ${searchBlock}\n        <div style=\"margin-top:14px\">${selRow}</div>\n      </div>\n      ${resultBlock}\n    </div></div>${toast}`\n  },\n\n  mounted(d2) {\n    d2.on('.searchbar input', 'keydown', (e) => { if (e.key === 'Enter') runSearch(d2) })\n    d2.on('.go', 'click', () => runSearch(d2))\n\n    d2.on('.uploadzone', 'click', () => { d2.$('.filepick')?.click() })\n    d2.on('.filepick', 'change', (e, el) => { doUpload(d2, el.files && el.files[0]) })\n    d2.on('.uploadzone', 'dragover', (e, el) => { e.preventDefault(); d2.state.dragging = true })\n    d2.on('.uploadzone', 'dragleave', () => { d2.state.dragging = false })\n    d2.on('.uploadzone', 'drop', (e, el) => {\n      e.preventDefault(); d2.state.dragging = false\n      const file = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]\n      doUpload(d2, file)\n    })\n\n    d2.on('.gcard', 'click', (e, el) => {\n      if (el.classList.contains('disabled')) return\n      const idx = Number(el.dataset.idx)\n      const p = (d2.state.results || [])[idx]\n      if (!p) return\n      const sel = d2.state.selected || []\n      const already = sel.findIndex((x) => x.productId === p.productId)\n      if (already > -1) {\n        d2.state.selected = sel.filter((_, i) => i !== already)\n      } else if (canAdd(sel, p)) {\n        d2.state.selected = [...sel, p]\n      } else {\n        flashToast(d2, sel.length >= 2 ? 'Já tem 2 peças — remova uma pra trocar.' : 'Só dá pra combinar peças compatíveis (blusa + calça).')\n      }\n    })\n    d2.on('.selchip .x', 'click', (e, el) => {\n      const idx = Number(el.dataset.idx)\n      d2.state.selected = (d2.state.selected || []).filter((_, i) => i !== idx)\n    })\n\n    d2.on('.testbtn', 'click', () => runTest(d2))\n    d2.on('.act-ok', 'click', () => report(d2, 'sucesso', ''))\n    d2.on('.act-bad', 'click', () => { d2.state.showNote = true })\n    d2.on('.act-sendbad', 'click', (e, el) => {\n      const ta = el.parentElement.querySelector('textarea')\n      report(d2, 'problema', ta ? ta.value : '')\n    })\n  },\n}\n\nfunction firstImage(p) {\n  return (p && p.items && p.items[0] && p.items[0].images && p.items[0].images[0] && p.items[0].images[0].imageUrl) || ''\n}\n\nfunction allImages(p) {\n  const imgs = (p && p.items && p.items[0] && p.items[0].images) || []\n  return imgs.map((i) => i.imageUrl).filter(Boolean).slice(0, 8)\n}\n\nfunction classify(p) {\n  const text = ((p.categories || []).join(' ') + ' ' + (p.productName || '')).toLowerCase()\n  if (/vestido/.test(text)) return 'vestido'\n  if (/cal(ç|c)a|saia|short|bermuda/.test(text)) return 'bottom'\n  if (/camis|blusa|regata|cropped|malha|su[eé]ter|cardigan/.test(text)) return 'top'\n  if (/jaqueta|casaco|blazer/.test(text)) return 'outerwear'\n  return 'other'\n}\n\nfunction canAdd(selected, candidate) {\n  if (selected.length === 0) return true\n  if (selected.length >= 2) return false\n  const a = classify(selected[0])\n  const b = classify(candidate)\n  const pair = [a, b].sort().join('+')\n  return pair === 'bottom+top'\n}\n\nasync function runSearch(d2) {\n  const input = d2.$('.searchbar input')\n  const q = (input && input.value || '').trim()\n  if (!q) return\n  d2.setState({ loadingSearch: true, searchError: '', query: q, searched: true })\n  try {\n    const base = d2.rush(d2.params.searchApi).url()\n    const res = await fetch(base + '?search=' + encodeURIComponent(q))\n    if (!res.ok) throw new Error('HTTP ' + res.status)\n    const json = await res.json()\n    const products = (json && json.data && json.data.products) || []\n    d2.setState({ results: products, loadingSearch: false })\n  } catch (err) {\n    d2.setState({ loadingSearch: false, searchError: 'Busca falhou: ' + (err && err.message ? err.message : 'erro') })\n  }\n}\n\n// Phone cameras (especially iPhone) store the raw sensor pixels in landscape\n// and rely on an EXIF Orientation tag for viewers to rotate on display —\n// browsers respect it, but many backends (vision/image-gen APIs included)\n// read the raw bytes and ignore it. Re-painting onto a <canvas> bakes in\n// whatever orientation the browser's own EXIF-aware decoder already applied,\n// so the uploaded file always matches what the preview showed.\nfunction normalizeImageOrientation(file) {\n  return new Promise(async (resolve) => {\n    if (!file || !/^image\\//.test(file.type)) { resolve(file); return }\n    try {\n      // See provador-standalone's normalizeImageOrientation for why this must\n      // use createImageBitmap (EXIF-aware) and never <img>+drawImage (not).\n      const bitmap = await createImageBitmap(file, { imageOrientation: 'from-image' })\n      const canvas = document.createElement('canvas')\n      canvas.width = bitmap.width\n      canvas.height = bitmap.height\n      canvas.getContext('2d').drawImage(bitmap, 0, 0)\n      canvas.toBlob((blob) => {\n        resolve(blob ? new File([blob], file.name || 'photo.jpg', { type: 'image/jpeg' }) : file)\n      }, 'image/jpeg', 0.92)\n    } catch (err) {\n      resolve(file)\n    }\n  })\n}\n\nasync function doUpload(d2, rawFile) {\n  if (!rawFile) return\n  d2.setState({ uploading: true, uploadError: '' })\n  try {\n    const file = await normalizeImageOrientation(rawFile)\n    const out = await d2.drive(d2.params.uploadDrive).upload([file])\n    const url = out && out[0] && out[0].fileUrl\n    if (!url) throw new Error('upload sem retorno')\n    d2.state.uploadStageMsg = 'Verificando enquadramento da foto…'\n    const validation = await validatePhoto(url)\n    if (validation && validation.valid === false) {\n      d2.setState({ uploading: false, uploadError: validation.reason || 'Essa foto não serve pro provador — tenta outra.' })\n      return\n    }\n    d2.setState({ testPhoto: url, testPhotoBodyCoverage: (validation && validation.bodyCoverage) || '', uploading: false })\n  } catch (err) {\n    d2.setState({ uploading: false, uploadError: 'Falha no upload: ' + (err && err.message ? err.message : 'erro') })\n  }\n}\n\nasync function validatePhoto(url) {\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-validate-photo', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ imageUrl: url }),\n    })\n    const json = await res.json()\n    return (json && json.data) || null\n  } catch (err) {\n    console.warn('[test-lab] photo validation failed', err)\n    return null\n  }\n}\n\n\n\nasync function runTest(d2) {\n  const s = d2.state\n  const sel = s.selected || []\n  if (!s.testPhoto || !sel.length) return\n  d2.setState({ testing: true, resultUrl: '', genError: '', reported: false, showNote: false, noteDraft: '' })\n  try {\n    // Same unified rush the real app uses — lab and app can never drift onto\n    // different analyze/prompt/draw strategies again.\n    const garment = sel[0]\n    const extra = sel[1] || null\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-tryon-generate', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        userPhotoUrl: s.testPhoto,\n        garmentImages: allImages(garment),\n        garmentProductName: garment.productName,\n        extraImages: extra ? allImages(extra) : [],\n        extraProductName: extra ? extra.productName : '',\n        userPhotoBodyCoverage: s.testPhotoBodyCoverage || '',\n      }),\n    })\n    const json = await res.json()\n    const result = (json && json.data) || {}\n    if (result.blocked) throw new Error(result.blockedReason || 'Bloqueado pelo pipeline.')\n    const url = result.resultUrl\n    if (!url) throw new Error('sem imagem retornada')\n    const lastAnalyses = [\n      result.analysisG ? { ...result.analysisG, imageUrl: result.garmentImageUsed } : null,\n      result.analysisE ? { ...result.analysisE, imageUrl: result.extraImageUsed } : null,\n    ].filter(Boolean)\n    d2.setState({ resultUrl: url, testing: false, lastAnalyses })\n  } catch (err) {\n    d2.setState({ testing: false, genError: 'Falhou: ' + (err && err.message ? err.message : 'erro') })\n  }\n}\n\nasync function hostGeneratedImage(d2, dataUri) {\n  if (!dataUri || !dataUri.startsWith('data:')) return dataUri\n  try {\n    const [meta, b64] = dataUri.split(',')\n    const bin = atob(b64)\n    const bytes = new Uint8Array(bin.length)\n    for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n    const type = (meta.match(/data:(.*?);/) || [])[1] || 'image/png'\n    const file = new File([bytes], `tryon-${Date.now()}.png`, { type })\n    const out = await d2.drive(d2.params.uploadDrive).upload([file])\n    return (out && out[0] && out[0].fileUrl) || dataUri\n  } catch (err) {\n    return dataUri\n  }\n}\n\nasync function report(d2, verdict, note) {\n  const s = d2.state\n  const sel = s.selected || []\n  try {\n    const hostedResultUrl = await hostGeneratedImage(d2, s.resultUrl)\n    await d2.data('feedbackDs').create({\n      verdict,\n      note: note || '',\n      productNames: sel.map((p) => p.productName).join(' + '),\n      productIds: sel.map((p) => p.productId).join(','),\n      categories: sel.map(classify).join('+'),\n      userPhotoUrl: s.testPhoto,\n      resultImageUrl: hostedResultUrl,\n      referenceImagesUsed: (s.lastAnalyses || []).map((a) => a.imageUrl).join(', '),\n      analysisJson: JSON.stringify(s.lastAnalyses || []),\n      testedAt: new Date().toISOString(),\n    })\n    d2.setState({ reported: true, showNote: false })\n    flashToast(d2, verdict === 'sucesso' ? 'Sucesso registrado!' : 'Problema registrado — obrigado!')\n  } catch (err) {\n    flashToast(d2, 'Falha ao registrar: ' + (err && err.message ? err.message : 'erro'))\n  }\n}\n\nfunction flashToast(d2, msg) {\n  d2.state.toast = msg\n  clearTimeout(flashToast._t)\n  flashToast._t = setTimeout(() => { d2.state.toast = '' }, 2600)\n}\n\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n}\n"}},"searchApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchApi"}}}},"analyzeApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"analyzeApi"}}}},"drawAssistant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"drawAssistant"}}}},"uploadDrive":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"uploadDrive"}}}},"feedbackDs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"feedbackDs"}}}}}}}}}},{"_id":"6ab3fbe732359c5bc8fdcbc6","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"docs-tryon","package":"veste-app","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 GUEST_URL = 'https://provador.veste-app.next.data2.app'\n\nconst EVENTS = [\n  { name: 'tryon-initialize', dir: 'out', payload: 'customerId: string', desc: 'Enviar assim que onInitialized disparar. Vazio/null → Data2 trata como sessão anônima e gera um id aleatório.' },\n  { name: 'tryon-open', dir: 'out', payload: '{ productId, skuId? }', desc: 'Abre o provador nesse produto. skuId (opcional) pré-seleciona cor/tamanho. productId vazio mostra o catálogo interno.' },\n  { name: 'tryon-add-to-cart', dir: 'in', payload: '[skuID, ...]', desc: 'Cliente clicou \"adicionar à sacola\" dentro do provador. Chame sua própria API de carrinho com esses SKUs.' },\n  { name: 'tryon-checkout', dir: 'in', payload: '{} (vazio)', desc: 'Só é enviado se o carrinho do lado Data2 tiver itens. Redirecione o cliente pro checkout/PDP.' },\n  { name: 'tryon-size-bay-size', dir: 'in', payload: 'productId: string', desc: 'Cliente clicou em \"Descubra o seu tamanho\". Abra seu próprio fluxo de recomendação de tamanho (SizeBay ou equivalente) pra esse produto.' },\n  { name: 'tryon-size-bay-measures', dir: 'in', payload: 'productId: string', desc: 'Cliente clicou em \"Tabela de Medidas\". Abra sua própria tabela de medidas pra esse produto.' },\n  { name: 'tryon-personalShopper', dir: 'in', payload: 'productId: string', desc: '⚠️ Ainda não confirmado com o time Veste — nome de evento sujeito a mudança. Cliente clicou em \"Fale com nossa Personal Shopper\". Abra seu fluxo de atendimento/WhatsApp pra esse produto.' },\n  { name: 'tryon-share', dir: 'in', payload: '{ productId, productName, price, color, size, skuId, resultImageUrl, userPhotoUrl }', desc: 'Cliente clicou em \"Compartilhar\". resultImageUrl vem vazio se ele ainda não gerou nenhuma prova (compartilhando direto da página do produto). Use pra abrir seu próprio share sheet / gerar o link.' },\n  { name: 'close-tryon', dir: 'in', payload: '{} (vazio)', desc: 'Cliente fechou o provador. Esconda/feche o lightbox.' },\n  { name: 'cart-state', dir: 'out', payload: '{ cartItems: [{ skuId, productId, productName, imageUrl, price, quantity }] }', desc: 'Informa o Data2 do estado atual do carrinho (ex.: logo após confirmar um tryon-add-to-cart) — o provador mostra um mini-carrinho real dentro do widget, e o cliente pode experimentar qualquer item que já está no carrinho.' },\n]\n\nfunction eventRows(events) {\n  return events.map((e) => `\n    <tr>\n      <td><code>${e.name}</code></td>\n      <td><span class=\"dir ${e.dir}\">${e.dir === 'out' ? 'Veste → Data2' : 'Data2 → Veste'}</span></td>\n      <td><code>${esc(e.payload)}</code></td>\n      <td>${esc(e.desc)}</td>\n    </tr>`).join('')\n}\n\nconst PRISM_SPEC = { url: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/prism.js', global: 'Prism', css: 'https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism-tomorrow.min.css' }\nconst PRISM_LANG_CLASS = { bash: '', html: 'language-markup', js: 'language-javascript' }\n\nfunction codebox(lang, id, s, code) {\n  const cls = PRISM_LANG_CLASS[lang] || ''\n  return `<div class=\"codebox\" data-key=\"${id}\">\n    <div class=\"cb-head\"><span>${lang}</span><button class=\"cb-copy\">Copiar</button></div>\n    <pre><code${cls ? ` class=\"${cls}\"` : ''}>${esc(code)}</code></pre>\n  </div>`\n}\n\nfunction esc(str) {\n  return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n}\n\nconst SNIPPET_CODE = [\n  '<!-- 1. Biblioteca PowerCord (vendorizada — ver Instalação acima) -->',\n  '<script src=\"/vendor/powercord.js\"><' + '/script>',\n  '',\n  '<style>',\n  '  #tryon-lightbox { position: fixed; inset: 0; background: rgba(10,10,10,.55);',\n  '    display: none; align-items: center; justify-content: center; z-index: 9999; }',\n  '  #tryon-lightbox.open { display: flex; }',\n  '  #tryon-panel { width: min(1100px, 94vw); height: min(880px, 94vh);',\n  '    background: #fff; border-radius: 18px; overflow: hidden; position: relative; }',\n  '  @media (max-width: 700px) {',\n  '    #tryon-panel { width: 100vw; height: 100vh; border-radius: 0; }',\n  '  }',\n  '</style>',\n  '',\n  '<div id=\"tryon-lightbox\">',\n  '  <div id=\"tryon-panel\"></div>',\n  '</div>',\n  '',\n  '<script>',\n  '  var TRYON_GUEST_URL = \"' + GUEST_URL + '\";',\n  '  var CURRENT_CUSTOMER_ID = null; // id do cliente logado, ou null para anônimo',\n  '',\n  '  // onInitialized é OBRIGATÓRIO — a lib quebra sem ele (ver Notas técnicas).',\n  '  var tryon = new PowerCord(TRYON_GUEST_URL, {',\n  '    onInitialized: function () {',\n  '      tryon.emit(\"tryon-initialize\", CURRENT_CUSTOMER_ID);',\n  '    },',\n  '  });',\n  '',\n  '  tryon.createInstance({',\n  '    selector: \"#tryon-panel\",',\n  '    style: { position: \"static\", width: \"100%\", height: \"100%\" },',\n  '  });',\n  '',\n  '  // ---- tratamento default para TODOS os eventos vindos do Data2 ----',\n  '  tryon.addEventListener(\"tryon-add-to-cart\", function (skuIds) {',\n  '    // skuIds: array de SKUs a adicionar. Chame sua própria API de carrinho:',\n  '    // addToCart(skuIds).then(function (cart) {',\n  '    //   tryon.emit(\"cart-state\", { cartItems: cart });',\n  '    // });',\n  '  });',\n  '',\n  '  tryon.addEventListener(\"tryon-checkout\", function () {',\n  '    // Só chega aqui se o carrinho (lado Data2) tiver itens.',\n  '    // window.location.href = \"/checkout\";',\n  '  });',\n  '',\n  '  tryon.addEventListener(\"tryon-size-bay-size\", function (productId) {',\n  '    // Cliente clicou em \"Descubra o seu tamanho\" — abra seu fluxo de',\n  '    // recomendação de tamanho para este productId.',\n  '  });',\n  '',\n  '  tryon.addEventListener(\"tryon-size-bay-measures\", function (productId) {',\n  '    // Cliente clicou em \"Tabela de Medidas\" para este productId.',\n  '  });',\n  '',\n  '  tryon.addEventListener(\"close-tryon\", function () {',\n  '    document.getElementById(\"tryon-lightbox\").classList.remove(\"open\");',\n  '  });',\n  '<' + '/script>',\n].join('\\n')\n\n\nexport default {\n  css: `\n:host{display:block;width:100%;height:100%}\n.wrap{\n  --paper:#F5F4F0; --ink:#15161A; --muted:#6A6E77; --accent:#96570F; --linha:rgba(21,22,26,.13); --card:#FFFFFF;\n  background:var(--paper); color:var(--ink); font-family:-apple-system,BlinkMacSystemFont,'Helvetica Neue',Helvetica,Arial,sans-serif;\n  width:100%; min-height:100%; overflow-y:auto; box-sizing:border-box;\n}\n*{box-sizing:border-box}\n.hdr{position:sticky;top:0;z-index:5;background:rgba(245,244,240,.92);backdrop-filter:blur(6px);\n  border-bottom:1px solid var(--linha);padding:16px clamp(20px,4vw,56px)}\n.hdr .brand{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--muted)}\n.hdr .brand b{color:var(--ink)}\n.hdr h1{font-size:clamp(20px,2.4vw,26px);margin:4px 0 0;font-weight:600;letter-spacing:-.01em}\n.body{max-width:920px;margin:0 auto;padding:clamp(24px,4vw,56px)}\n.lede{font-size:15px;line-height:1.7;color:var(--ink);max-width:70ch;margin:0 0 8px}\n.lede.muted{color:var(--muted)}\n.pill{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;\n  background:#fff;border:1px solid var(--linha);border-radius:999px;padding:5px 12px;margin:12px 0 24px}\n.pill b{color:var(--accent)}\nnav.toc{display:flex;flex-wrap:wrap;gap:8px 18px;margin:20px 0 40px;padding:14px 18px;background:#fff;\n  border:1px solid var(--linha);border-radius:12px}\nnav.toc a{font-size:12.5px;color:var(--muted);text-decoration:none;cursor:pointer}\nnav.toc a:hover{color:var(--accent)}\nsection{margin:52px 0}\nsection h2{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);margin:0 0 6px;font-weight:700}\nsection h3{font-size:19px;margin:0 0 14px;font-weight:600}\np{font-size:14.5px;line-height:1.7;color:var(--ink);margin:0 0 12px;max-width:74ch}\np.muted{color:var(--muted)}\nul{margin:0 0 14px;padding-left:20px}\nli{font-size:14px;line-height:1.65;margin-bottom:6px;max-width:70ch}\n.codebox{position:relative;background:#15161A;color:#E7E6E2;border-radius:12px;margin:14px 0 20px;overflow:hidden}\n.codebox .cb-head{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;\n  background:rgba(255,255,255,.05);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:#9A9A94}\n.codebox .cb-copy{border:1px solid rgba(255,255,255,.25);background:transparent;color:#E7E6E2;\n  border-radius:6px;padding:4px 10px;font-size:11px;cursor:pointer;font-family:inherit}\n.codebox .cb-copy:hover{background:rgba(255,255,255,.1)}\n.codebox .cb-copy.done{border-color:#5FBE84;color:#5FBE84}\n.codebox pre{margin:0;padding:18px 20px;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;\n  font-size:12.5px;line-height:1.65;white-space:pre}\n.codebox code{color:#E7E6E2}\n.codebox pre[class*=\"language-\"]{background:transparent!important;margin:0!important;padding:18px 20px!important}\n.codebox code[class*=\"language-\"]{background:transparent!important;text-shadow:none;font-family:inherit;font-size:inherit;line-height:inherit;white-space:pre}\n.risk{background:#FBE9E4;border-left:3px solid #A33427;padding:12px 16px;border-radius:6px;margin:14px 0;font-size:13.5px;line-height:1.6}\n.ok{background:#E8F0EA;border-left:3px solid #2C6B4A;padding:12px 16px;border-radius:6px;margin:14px 0;font-size:13.5px;line-height:1.6}\ntable.evt{width:100%;border-collapse:collapse;margin:14px 0 20px;font-size:13px;background:#fff;border:1px solid var(--linha);border-radius:10px;overflow:hidden}\ntable.evt th{text-align:left;font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);\n  background:#EFEEE8;padding:10px 14px;border-bottom:1px solid var(--linha)}\ntable.evt td{padding:12px 14px;border-bottom:1px solid var(--linha);vertical-align:top}\ntable.evt tr:last-child td{border-bottom:none}\ntable.evt code{background:rgba(21,22,26,.06);padding:2px 6px;border-radius:4px;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.dir{display:inline-block;font-size:10.5px;font-weight:700;letter-spacing:.04em;padding:2px 8px;border-radius:999px}\n.dir.out{background:#FBE9E4;color:#A33427}\n.dir.in{background:#E4EDFB;color:#1F4FA3}\ncode.inline{background:rgba(21,22,26,.06);padding:1px 6px;border-radius:4px;font-size:12.5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}\n.foot{margin-top:60px;padding-top:18px;border-top:1px solid var(--linha);font-size:12px;color:var(--muted)}\n`,\n\n  render(d2) {\n    const s = d2.state\n    d2.lib(PRISM_SPEC)\n    return `<div class=\"wrap\">\n      <div class=\"hdr\">\n        <div class=\"brand\"><b>Data²</b> · Veste — Provador Virtual</div>\n        <h1>Guia de integração (embed)</h1>\n      </div>\n      <div class=\"body\">\n        <p class=\"lede\">Como o time de front da Veste embute o provador virtual em qualquer PDP, usando <code class=\"inline\">PowerCord</code> (mensagens via <code class=\"inline\">postMessage</code> entre a página host e o app Data² num iframe).</p>\n        <div class=\"pill\">Guest URL: <b>${GUEST_URL}</b></div>\n\n        <nav class=\"toc\">\n          <a data-goto=\"instalacao\">1. Instalação</a>\n          <a data-goto=\"snippet\">2. Snippet completo</a>\n          <a data-goto=\"enviando\">3. Enviando um evento</a>\n          <a data-goto=\"eventos\">4. Referência de eventos</a>\n          <a data-goto=\"notas\">5. Notas técnicas</a>\n        </nav>\n\n        <section id=\"instalacao\">\n          <h2>01 · Instalação</h2>\n          <h3>Biblioteca PowerCord</h3>\n          <p>A lib (<code class=\"inline\">@data2/powercord</code>) ainda não está publicada num registro público de pacotes — o jeito mais confiável hoje é <b>vendorizar o arquivo</b> (copiar o source pro seu próprio projeto) em vez de apontar pra um CDN de terceiros em produção.</p>\n          ${codebox('bash', 'instalacao', s, [\n            '# baixa o source oficial (arquivo único, sem dependências)',\n            'curl -o powercord.js \\\\',\n            '  https://raw.githubusercontent.com/data-two/powercord/main/lib/powercord.js',\n          ].join('\\n'))}\n          <p class=\"muted\">Alternativa rápida pra prototipar: <code class=\"inline\">&lt;script src=\"https://raw.githubusercontent.com/data-two/powercord/main/lib/powercord.js\"&gt;&lt;/script&gt;</code> — não recomendado pra produção (GitHub Raw não é um CDN com SLA).</p>\n        </section>\n\n        <section id=\"snippet\">\n          <h2>02 · Snippet completo</h2>\n          <h3>HTML + CSS + JS de referência</h3>\n          <p>Lightbox (esconde/mostra o provador, tamanho seguro fixo no desktop, full-screen no mobile), tratamento default pra <b>todos</b> os eventos que o Data² envia, e o disparo de <code class=\"inline\">tryon-initialize</code> assim que a conexão é confirmada. Onde plugar isso fica a critério do time de tech da Veste.</p>\n          ${codebox('html', 'snippet', s, SNIPPET_CODE)}\n        </section>\n\n        <section id=\"enviando\">\n          <h2>03 · Enviando um evento</h2>\n          <h3>Exemplo: abrir o provador num produto</h3>\n          <p>Chame isso a partir do gatilho de \"Experimentar\" (botão, link, o que fizer mais sentido), passando o <code class=\"inline\">productId</code> do produto atual:</p>\n          ${codebox('js', 'enviando', s, [\n            '// dentro do <script> do snippet acima',\n            'function abrirProvador(productId) {',\n            \"  document.getElementById('tryon-lightbox').classList.add('open');\",\n            \"  tryon.emit('tryon-open', { productId: productId });\",\n            '}',\n            '',\n            '// ex.: <button onclick=\"abrirProvador(\\'19318\\')\">Experimentar</button>',\n          ].join('\\n'))}\n        </section>\n\n        <section id=\"eventos\">\n          <h2>04 · Referência completa de eventos</h2>\n          <p>Todo evento trafega como <code class=\"inline\">{ eventName, data }</code> via <code class=\"inline\">postMessage</code> — a lib PowerCord empacota isso pra você (<code class=\"inline\">tryon.emit(eventName, data)</code> do lado da Veste, <code class=\"inline\">tryon.addEventListener(eventName, cb)</code> pra ouvir).</p>\n          <table class=\"evt\">\n            <tr><th>Evento</th><th>Direção</th><th>Payload</th><th>Descrição</th></tr>\n            ${eventRows(EVENTS)}\n          </table>\n        </section>\n\n        <section id=\"notas\">\n          <h2>05 · Notas técnicas</h2>\n\n          <div class=\"ok\"><b>CORS:</b> os endpoints públicos do Data² (catálogo, lookup de produto) já respondem com <code class=\"inline\">Access-Control-Allow-Origin: *</code> — não é necessário nenhum allowlist do lado da Veste pra esses.</div>\n          <p>URL do provador publicado (ambiente de homologação): <code class=\"inline\">${GUEST_URL}</code>.</p>\n        </section>\n\n        <div class=\"foot\">Data² · Sprint Provador Virtual — Grupo Veste. Dúvidas: contato técnico combinado no kickoff.</div>\n      </div>\n    </div>`\n  },\n\n  mounted(d2, root) {\n    highlightCode(d2, root)\n    d2.on('.cb-copy', 'click', (e, el) => {\n      const box = el.closest('.codebox')\n      const codeEl = box && box.querySelector('code')\n      const text = codeEl ? codeEl.textContent : ''\n      navigator.clipboard.writeText(text).then(() => {\n        el.classList.add('done')\n        el.textContent = 'Copiado!'\n        setTimeout(() => { el.classList.remove('done'); el.textContent = 'Copiar' }, 1600)\n      })\n    })\n    d2.on('[data-goto]', 'click', (e, el) => {\n      const id = el.dataset.goto\n      const target = d2.$('#' + id)\n      if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' })\n    })\n  },\n\n  updated(d2, root) {\n    highlightCode(d2, root)\n  },\n}\n\nfunction highlightCode(d2, root) {\n  const Prism = d2.lib(PRISM_SPEC)\n  if (!Prism) return\n  Prism.highlightAllUnder(root)\n}\n"}}}}}}}},{"_id":"6abfc90b1a6b03943b979ef2","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Categorias_Bloqueadas","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#191D1A}\n    *{box-sizing:border-box}\n    .page{padding:32px 32px 48px;display:flex;flex-direction:column;gap:24px;min-height:100%;background:#F6F6F3}\n    .crumb{font-size:11.5px;font-weight:600;letter-spacing:.04em;color:#898781}\n    .crumb b{color:#52514e;font-weight:600}\n    h1{font-size:26px;font-weight:700;letter-spacing:-.01em;margin:6px 0 0;color:#0b0b0b}\n    .desc{margin:6px 0 0;color:#727273;font-size:13.5px;max-width:70ch}\n    .grid{display:grid;grid-template-columns:1fr 1fr;gap:24px;align-items:start}\n    .card{background:#fff;border:1px solid #E5E3DD;border-radius:12px;box-shadow:0 1px 2px rgba(11,11,11,0.03);padding:24px}\n    .eyebrow{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#898781;margin:0 0 14px}\n    .addrow{display:flex;gap:10px}\n    input{flex:1;padding:11px 14px;border:1px solid #D3D0C8;border-radius:8px;font-size:13.5px;outline:none;transition:border-color .15s}\n    input:focus{border-color:#2a78d6}\n    button.add{padding:0 18px;border:none;border-radius:8px;background:#0b0b0b;color:#fff;font-size:13px;font-weight:600;cursor:pointer;transition:opacity .15s}\n    button.add:hover{opacity:.85}\n    button.add:disabled{opacity:.4;cursor:default}\n    .list{margin-top:18px;display:flex;flex-direction:column}\n    .row{display:flex;align-items:center;justify-content:space-between;padding:13px 2px;border-bottom:1px solid #E5E3DD}\n    .row:last-child{border-bottom:none}\n    .catname{font-size:14.5px;font-weight:500}\n    .rm{border:none;background:transparent;color:#d03b3b;font-size:12px;font-weight:600;cursor:pointer;padding:6px 10px;border-radius:7px;transition:background .15s}\n    .rm:hover{background:#fbeceb}\n    .empty{color:#898781;font-size:13px;padding:20px 0;text-align:center}\n    .stat-value{font-size:40px;font-weight:700;line-height:1;margin:6px 0 4px;color:#d03b3b}\n    .stat-sub{font-size:12.5px;color:#52514e;margin-bottom:18px}\n    .note{font-size:12px;color:#898781;line-height:1.55;background:#F6F6F3;border-radius:10px;padding:12px 14px;margin-top:4px}\n    .matchlist{margin-top:14px;display:flex;flex-direction:column;gap:6px;max-height:160px;overflow:auto}\n    .matchitem{font-size:12.5px;color:#52514e;background:#F6F6F3;border-radius:7px;padding:7px 10px}\n    .skel{color:#898781;font-size:13px}\n  `,\n  render(d2) {\n    const ds = d2.data('blocklist')\n    const rows = ds.records() || []\n    const s = d2.state\n    const esc = (v) => String(v == null ? '' : v).replace(/</g, '&lt;')\n    return `<div class=\"page\">\n      <div>\n        <div class=\"crumb\">Provador Virtual &nbsp;›&nbsp; <b>Curadoria e Configuração</b> &nbsp;›&nbsp; Restrições de Categoria</div>\n        <h1>Restrições de Categoria</h1>\n        <p class=\"desc\">Categorias que nunca devem entrar no pipeline de geração do provador (bolsas, cintos, carteiras etc. não podem ser \"vestidas\").</p>\n      </div>\n      <div class=\"grid\">\n        <div class=\"card\">\n          <div class=\"eyebrow\">Adicionar categoria</div>\n          <div class=\"addrow\">\n            <input class=\"newcat\" placeholder=\"ex: Óculos de Sol\" value=\"${esc(s.newCat || '')}\"/>\n            <button class=\"add\" ${(!s.newCat) ? 'disabled' : ''}>+ Adicionar</button>\n          </div>\n          <div class=\"eyebrow\" style=\"margin-top:26px\">Bloqueadas atualmente (${rows.length})</div>\n          <div class=\"list\">\n            ${rows.length ? rows.map(r => `\n              <div class=\"row\" data-key=\"${esc(r._id)}\">\n                <span class=\"catname\">${esc(r.category)}</span>\n                <button class=\"rm\" data-id=\"${esc(r._id)}\">Remover</button>\n              </div>`).join('') : `<div class=\"empty\">Nenhuma categoria bloqueada ainda.</div>`}\n          </div>\n        </div>\n        <div class=\"card\">\n          <div class=\"eyebrow\">Testados em categoria bloqueada (histórico)</div>\n          ${s.stats ? `\n            <div class=\"stat-value\">${s.stats.blockedCategoryMatches}</div>\n            <div class=\"stat-sub\">de ${s.stats.totalDistinctTested} produtos distintos testados</div>\n            <div class=\"matchlist\">${(s.stats.matchedNames||[]).map(n=>`<div class=\"matchitem\">${esc(n.trim())}</div>`).join('') || ''}</div>\n          ` : `<div class=\"skel\">Carregando…</div>`}\n          <div class=\"note\">Leitura best-effort: comparação por substring do nome do produto contra a lista de categorias (não há categoria salva nas trocas já feitas). Use como sinal, não como fonte de verdade.</div>\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    d2.rush(d2.params.statsApi).invoke({}).then(r => { d2.state.stats = (r && r.data) ? r.data : r }).catch(e => { d2.log('stats error', e) })\n    d2.on('.newcat', 'input', (e, el) => { d2.state.newCat = el.value })\n    d2.on('.newcat', 'keydown', (e, el) => { if (e.key === 'Enter' && d2.state.newCat) addCat(d2) })\n    d2.on('.add', 'click', () => addCat(d2))\n    d2.on('.rm', 'click', (e, el) => { d2.data('blocklist').remove(el.dataset.id) })\n  },\n}\nfunction addCat(d2) {\n  const name = (d2.state.newCat || '').trim()\n  if (!name) return\n  d2.data('blocklist').create({ category: name, createdAt: new Date().toISOString() })\n  d2.state.newCat = ''\n}"}},"blocklist":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"blocklist"}}}},"statsApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statsApi"}}}}}}}}}},{"_id":"6abfc91be892b0a312cefdf3","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Funil_de_Conversao","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#191D1A}\n    *{box-sizing:border-box}\n    .page{padding:32px 32px 48px;display:flex;flex-direction:column;gap:24px;min-height:100%;background:#F6F6F3}\n    .crumb{font-size:11.5px;font-weight:600;letter-spacing:.04em;color:#898781}\n    .crumb b{color:#52514e;font-weight:600}\n    h1{font-size:26px;font-weight:700;letter-spacing:-.01em;margin:6px 0 0;color:#0b0b0b}\n    .desc{margin:6px 0 0;color:#727273;font-size:13.5px;max-width:70ch}\n    .grid{display:grid;grid-template-columns:1.6fr 1fr;gap:24px;align-items:start}\n    .card{background:#fff;border:1px solid #E5E3DD;border-radius:12px;box-shadow:0 1px 2px rgba(11,11,11,0.03);padding:24px}\n    .eyebrow{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#898781;margin:0 0 18px}\n    .stagerow{margin-bottom:16px}\n    .stagehead{display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px}\n    .stagename{font-weight:600;color:#0b0b0b}\n    .stagecount{color:#52514e;font-variant-numeric:tabular-nums}\n    .track{height:10px;border-radius:5px;background:#ECEBE6;overflow:hidden}\n    .fill{height:100%;border-radius:5px;background:#2a78d6;transition:width .5s ease}\n    .drop{font-size:11px;color:#d03b3b;margin-top:4px}\n    .stat-big{font-size:34px;font-weight:700;margin:4px 0}\n    .stat-sub{font-size:12.5px;color:#52514e;margin-bottom:14px;line-height:1.5}\n    .divider{height:1px;background:#EFEEEA;margin:18px 0}\n    .fail{color:#d03b3b;font-weight:700}\n    .skel{color:#898781;font-size:13px}\n  `,\n  render(d2) {\n    const s = d2.state\n    if (!s.data) return `<div class=\"page\"><div class=\"skel\">Carregando funil…</div></div>`\n    const stages = s.data.stages || []\n    const max = Math.max(1, ...stages.map(x => x.count))\n    return `<div class=\"page\">\n      <div>\n        <div class=\"crumb\">Provador Virtual &nbsp;›&nbsp; <b>Visão Geral</b> &nbsp;›&nbsp; Funil de Conversão</div>\n        <h1>Funil de Conversão</h1>\n        <p class=\"desc\">Da abertura do provador até o checkout — contagem de sessões distintas em cada etapa.</p>\n      </div>\n      <div class=\"grid\">\n        <div class=\"card\">\n          <div class=\"eyebrow\">Sessões por etapa</div>\n          ${stages.map((st, i) => {\n            const prev = i > 0 ? stages[i-1].count : st.count\n            const drop = (i > 0 && prev > 0) ? Math.round(100 * (1 - st.count / prev)) : null\n            return `<div class=\"stagerow\">\n              <div class=\"stagehead\"><span class=\"stagename\">${i+1}. ${st.stage}</span><span class=\"stagecount\">${st.count}</span></div>\n              <div class=\"track\"><div class=\"fill\" style=\"width:${Math.round(100*st.count/max)}%\"></div></div>\n              ${drop !== null ? `<div class=\"drop\">−${drop}% vs. etapa anterior</div>` : ''}\n            </div>`\n          }).join('')}\n        </div>\n        <div class=\"card\">\n          <div class=\"eyebrow\">Qualidade da geração (pipeline 2026-10-02)</div>\n          <div class=\"stat-big\">${s.data.retriedCount} de ${s.data.totalSwaps}</div>\n          <div class=\"stat-sub\">gerações precisaram do self-heal (retry) — proxy de acerto de primeira tentativa</div>\n          <div class=\"divider\"></div>\n          <div class=\"stat-big fail\">${s.data.failedCount}</div>\n          <div class=\"stat-sub\">gerações falharam sem resultado</div>\n          <div class=\"divider\"></div>\n          <div class=\"stat-big\">${s.data.comboCount}</div>\n          <div class=\"stat-sub\">testes combo (2 peças) — recurso novo pós-rework</div>\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    d2.rush(d2.params.funnelApi).invoke({}).then(r => { d2.state.data = (r && r.data) ? r.data : r }).catch(e => { d2.log('funnel error', e) })\n  },\n}"}},"funnelApi":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"funnelApi"}}}}}}}}}},{"_id":"6abfc928ea8ac94fb093ba0d","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Curadoria_de_Resultados","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#191D1A}\n    *{box-sizing:border-box}\n    .page{padding:32px 32px 48px;display:flex;flex-direction:column;gap:24px;min-height:100%;background:#F6F6F3}\n    .crumb{font-size:11.5px;font-weight:600;letter-spacing:.04em;color:#898781}\n    .crumb b{color:#52514e;font-weight:600}\n    h1{font-size:26px;font-weight:700;letter-spacing:-.01em;margin:6px 0 0;color:#0b0b0b}\n    .desc{margin:6px 0 0;color:#727273;font-size:13.5px;max-width:70ch}\n    .sectionTitle{font-size:15px;font-weight:700;margin:6px 0 2px}\n    .seg{display:inline-flex;padding:3px;gap:2px;background:#ECEBE6;border-radius:9px;flex-wrap:wrap}\n    .seg button{border:0;background:transparent;height:30px;padding:0 14px;border-radius:7px;font-size:12.5px;font-weight:600;color:#727273;cursor:pointer}\n    .seg button[aria-pressed=\"true\"]{background:#fff;color:#0b0b0b;box-shadow:0 1px 2px rgba(0,0,0,.08)}\n    .list{display:flex;flex-direction:column;gap:12px}\n    .card{background:#fff;border:1px solid #E5E3DD;border-radius:12px;padding:16px;box-shadow:0 1px 2px rgba(11,11,11,0.03)}\n    .cardtop{display:flex;align-items:center;gap:18px}\n    .thumbs{display:flex;align-items:center;gap:8px;flex:none}\n    .thumb{width:84px;height:104px;border-radius:9px;background-size:cover;background-position:center;background-color:#eee;cursor:zoom-in}\n    .thumb.sm{width:64px;height:80px}\n    .arrow{color:#c3c2b7;font-size:16px}\n    .info{flex:1;min-width:0}\n    .pname{font-size:14.5px;font-weight:700;margin-bottom:3px}\n    .psession{font-size:11.5px;color:#898781;margin-bottom:8px}\n    .pills{display:flex;gap:8px;flex-wrap:wrap}\n    .pill{font-size:11px;font-weight:700;padding:4px 10px;border-radius:999px}\n    .pill.ok{background:#e6f4ea;color:#0ca30c}\n    .pill.bad{background:#fbeceb;color:#d03b3b}\n    .pill.retry{background:#fff6e0;color:#c98500}\n    .pill.warn{background:#fff6e0;color:#c98500}\n    .pill.fbpos{background:#e6f0fb;color:#2a78d6}\n    .pill.fbneg{background:#fbeceb;color:#d03b3b}\n    .pill.src{background:#ECEBE6;color:#52514e}\n    .pill.resolved{background:#e6f4ea;color:#0ca30c}\n    .note{font-size:12px;color:#52514e;margin-top:6px;font-style:italic}\n    .empty{color:#898781;font-size:13px;padding:24px 0;text-align:center}\n    .lightbox{position:fixed;inset:0;background:rgba(11,11,11,0.82);display:flex;align-items:center;justify-content:center;z-index:999;cursor:zoom-out}\n    .lightbox img{max-width:86vw;max-height:86vh;border-radius:10px}\n    .rerunbar{margin-top:14px;padding-top:14px;border-top:1px solid #EFEEEA;display:flex;align-items:center;gap:10px;flex-wrap:wrap}\n    .btn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 14px;border-radius:8px;border:1px solid #D3D0C8;background:#fff;color:#0b0b0b;font-size:12.5px;font-weight:600;cursor:pointer}\n    .btn:hover{background:#F6F6F3}\n    .btn:disabled{opacity:.5;cursor:default}\n    .btn.primary{background:#0b0b0b;border-color:#0b0b0b;color:#fff}\n    .btn.primary:hover{opacity:.9}\n    .btn.ghost{border-color:transparent;background:transparent;color:#52514e}\n    .rerunpanel{margin-top:12px;background:#F6F6F3;border-radius:10px;padding:14px}\n    .comparerow{display:flex;align-items:center;gap:14px;flex-wrap:wrap}\n    .complabel{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#898781;text-align:center;margin-top:4px}\n    .histlist{margin-top:10px;display:flex;flex-direction:column;gap:6px}\n    .histrow{display:flex;align-items:center;gap:10px;font-size:12px;color:#52514e;background:#fff;border:1px solid #E5E3DD;border-radius:8px;padding:8px 10px}\n    .histrow img{width:36px;height:46px;border-radius:5px;object-fit:cover;background:#eee}\n    .histrow .t{flex:1}\n  `,\n  render(d2) {\n    const ds = d2.data('swaps')\n    const evDs = d2.data('events')\n    const labDs = d2.data('labFeedback')\n    const runsDs = d2.data('regressionRuns')\n    if (ds.loading || evDs.loading || labDs.loading || runsDs.loading) return `<div class=\"page\"><div class=\"empty\">Carregando…</div></div>`\n    const s = d2.state\n    const status = s.status || 'all'\n    const labStatus = s.labStatus || 'all'\n    const esc = (v) => String(v == null ? '' : v).replace(/</g, '&lt;')\n\n    const feedbackEvents = (evDs.records() || []).filter(r => r.eventName === 'feedback')\n    const fbByResultUrl = {}\n    feedbackEvents.forEach(r => {\n      try { const p = JSON.parse(r.payloadJson || '{}'); if (p.resultUrl) fbByResultUrl[p.resultUrl] = p.verdict } catch (e) {}\n    })\n\n    let rows = (ds.records() || []).slice()\n    rows.sort((a, b) => new Date(b.createdAt||0) - new Date(a.createdAt||0))\n    if (status === 'success') rows = rows.filter(r => r.success === true)\n    if (status === 'failed') rows = rows.filter(r => r.success === false)\n    if (status === 'retry') rows = rows.filter(r => (r.analysisJson||'').includes('\"retried\":true'))\n    if (status === 'feedback') rows = rows.filter(r => fbByResultUrl[r.resultImageUrl] != null)\n\n    const seg = (key, label, current, setKey) => `<button data-set=\"${setKey}\" data-status=\"${key}\" aria-pressed=\"${current===key}\">${label}</button>`\n\n    const swapCards = rows.length ? rows.slice(0, 60).map(r => {\n      const fb = fbByResultUrl[r.resultImageUrl]\n      // `success:true` only ever meant \"the pipeline didn't throw\" — it is NOT\n      // a quality signal. Real AI self-validation (the retry+QA-judge pass)\n      // only exists in records generated from 2026-10-02 onward (recognizable\n      // by the `\"retried\"` key in analysisJson, which every post-rework run\n      // writes regardless of whether a retry actually fired). Older rows with\n      // success:true but no validation must not be labeled \"Sucesso\" — that\n      // falsely implies a check happened. Real feedback (fb/lab) always wins\n      // when present, since a human actually looked at it.\n      const wasValidated = (r.analysisJson||'').includes('\"retried\"')\n      const statusPill = !r.success\n        ? `<span class=\"pill bad\">Falhou</span>`\n        : wasValidated\n          ? `<span class=\"pill ok\">Sucesso (validado por IA)</span>`\n          : `<span class=\"pill warn\">Gerado — sem validação (pré-10/2026)</span>`\n      return `\n      <div class=\"card\" data-key=\"${esc(r._id)}\">\n        <div class=\"cardtop\">\n          <div class=\"thumbs\">\n            <div class=\"thumb\" style=\"background-image:url('${esc(r.userPhotoUrl)}')\" data-zoom=\"${esc(r.userPhotoUrl)}\"></div>\n            <span class=\"arrow\">→</span>\n            <div class=\"thumb\" style=\"background-image:url('${esc(r.resultImageUrl)}')\" data-zoom=\"${esc(r.resultImageUrl)}\"></div>\n          </div>\n          <div class=\"info\">\n            <div class=\"pname\">${esc(r.productName)}</div>\n            <div class=\"psession\">sessão ${esc(r.sessionId)} · ${esc((r.createdAt||'').replace('T',' ').slice(0,16))}</div>\n            <div class=\"pills\">\n              ${statusPill}\n              ${(r.analysisJson||'').includes('\"retried\":true') ? `<span class=\"pill retry\">Precisou retry</span>` : ''}\n              ${fb === 'positive' ? `<span class=\"pill fbpos\">App: Positivo</span>` : ''}\n              ${fb === 'negative' ? `<span class=\"pill fbneg\">App: Negativo</span>` : ''}\n            </div>\n          </div>\n        </div>\n      </div>`\n    }).join('') : `<div class=\"empty\">Nenhum resultado para esse filtro.</div>`\n\n    let labRows = (labDs.records() || []).slice()\n    labRows.sort((a, b) => new Date(b.testedAt||0) - new Date(a.testedAt||0))\n    if (labStatus === 'sucesso') labRows = labRows.filter(r => r.verdict === 'sucesso')\n    if (labStatus === 'problema') labRows = labRows.filter(r => r.verdict === 'problema')\n\n    const allRuns = runsDs.records() || []\n    const labCards = labRows.length ? labRows.slice(0, 40).map(r => {\n      const caseRuns = allRuns.filter(run => run.feedbackId === r._id).sort((a,b) => new Date(b.runAt||0) - new Date(a.runAt||0))\n      const resolved = caseRuns.find(run => run.resolved === true)\n      const latestRun = caseRuns[0]\n      const busy = s.rerunBusy === r._id\n      const showHist = s.histOpen === r._id\n      const canRerun = r.verdict === 'problema' && (r.productIds || '').trim().length > 0\n      return `\n      <div class=\"card\" data-key=\"${esc(r._id)}\">\n        <div class=\"cardtop\">\n          <div class=\"thumbs\">\n            <div class=\"thumb\" style=\"background-image:url('${esc(r.userPhotoUrl)}')\" data-zoom=\"${esc(r.userPhotoUrl)}\"></div>\n            <span class=\"arrow\">→</span>\n            <div class=\"thumb\" style=\"background-image:url('${esc(r.resultImageUrl)}')\" data-zoom=\"${esc(r.resultImageUrl)}\"></div>\n          </div>\n          <div class=\"info\">\n            <div class=\"pname\">${esc(r.productNames)}</div>\n            <div class=\"psession\">${esc((r.testedAt||'').replace('T',' ').slice(0,16))}</div>\n            <div class=\"pills\">\n              <span class=\"pill ${r.verdict==='sucesso'?'ok':'bad'}\">${r.verdict==='sucesso'?'Sucesso':'Problema'}</span>\n              <span class=\"pill src\">Fonte: Lab de Testes</span>\n              ${resolved ? `<span class=\"pill resolved\">✓ Resolvido em ${esc((resolved.runAt||'').slice(0,10))}</span>` : ''}\n            </div>\n            ${r.note ? `<div class=\"note\">\"${esc(r.note)}\"</div>` : ''}\n          </div>\n        </div>\n        ${canRerun ? `\n        <div class=\"rerunbar\">\n          <button class=\"btn primary act-rerun\" data-id=\"${esc(r._id)}\" ${busy ? 'disabled' : ''}>${busy ? 'Testando com pipeline atual…' : 'Testar novamente com pipeline atual'}</button>\n          ${caseRuns.length ? `<button class=\"btn ghost act-togglehist\" data-id=\"${esc(r._id)}\">${showHist ? 'Ocultar' : 'Ver'} histórico (${caseRuns.length})</button>` : ''}\n        </div>\n        ${latestRun ? `\n        <div class=\"rerunpanel\">\n          <div class=\"comparerow\">\n            <div><div class=\"thumb\" style=\"background-image:url('${esc(r.userPhotoUrl)}')\"></div><div class=\"complabel\">Foto original</div></div>\n            <span class=\"arrow\">→</span>\n            <div><div class=\"thumb\" style=\"background-image:url('${esc(r.resultImageUrl)}')\"></div><div class=\"complabel\">Resultado antigo</div></div>\n            <span class=\"arrow\">→</span>\n            <div><div class=\"thumb\" style=\"background-image:url('${esc(latestRun.resultUrl)}')\" data-zoom=\"${esc(latestRun.resultUrl)}\"></div><div class=\"complabel\">Novo teste (${esc((latestRun.runAt||'').slice(0,10))})</div></div>\n            ${!latestRun.blocked ? `<button class=\"btn ${latestRun.resolved?'':'primary'} act-resolve\" data-runid=\"${esc(latestRun._id)}\" ${latestRun.resolved?'disabled':''}>${latestRun.resolved ? '✓ Marcado como resolvido' : 'Marcar como resolvido'}</button>` : `<span class=\"pill bad\">Bloqueado: ${esc(latestRun.blockedReason||'')}</span>`}\n          </div>\n        </div>` : ''}\n        ${showHist && caseRuns.length ? `\n        <div class=\"histlist\">\n          ${caseRuns.map(run => `<div class=\"histrow\">\n            ${run.resultUrl ? `<img src=\"${esc(run.resultUrl)}\"/>` : ''}\n            <span class=\"t\">${esc((run.runAt||'').replace('T',' ').slice(0,16))} ${run.blocked ? '— bloqueado' : ''}</span>\n            ${run.resolved ? `<span class=\"pill resolved\">Resolvido</span>` : ''}\n          </div>`).join('')}\n        </div>` : ''}\n        ` : ''}\n      </div>`\n    }).join('') : `<div class=\"empty\">Nenhum feedback do Lab para esse filtro.</div>`\n\n    return `<div class=\"page\">\n      <div>\n        <div class=\"crumb\">Provador Virtual &nbsp;›&nbsp; <b>Curadoria e Configuração</b> &nbsp;›&nbsp; Curadoria de Resultados</div>\n        <h1>Curadoria de Resultados</h1>\n        <p class=\"desc\">Foto original × resultado gerado, lado a lado — com feedback do app e do Lab de Testes.</p>\n      </div>\n\n      <div>\n        <div class=\"sectionTitle\">Gerações do app (provador embutido)</div>\n        <div class=\"seg\" style=\"margin:10px 0 14px\">${seg('all','Todos',status,'status')}${seg('success','Sem erro técnico',status,'status')}${seg('failed','Falhou',status,'status')}${seg('retry','Precisou retry',status,'status')}${seg('feedback','Com feedback do app',status,'status')}</div>\n        <div class=\"list\">${swapCards}</div>\n      </div>\n\n      <div>\n        <div class=\"sectionTitle\">Feedback do Lab de Testes (QA interno)</div>\n        <div class=\"seg\" style=\"margin:10px 0 14px\">${seg('all','Todos',labStatus,'lab')}${seg('sucesso','Sucesso',labStatus,'lab')}${seg('problema','Problema',labStatus,'lab')}</div>\n        <div class=\"list\">${labCards}</div>\n      </div>\n\n      ${s.zoom ? `<div class=\"lightbox\"><img src=\"${esc(s.zoom)}\"/></div>` : ''}\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.seg button', 'click', (e, el) => {\n      const set = el.dataset.set\n      if (set === 'lab') d2.state.labStatus = el.dataset.status\n      else d2.state.status = el.dataset.status\n    })\n    d2.on('.thumb', 'click', (e, el) => { if (el.dataset.zoom) d2.state.zoom = el.dataset.zoom })\n    d2.on('.lightbox', 'click', () => { d2.state.zoom = null })\n    d2.on('.act-togglehist', 'click', (e, el) => {\n      const id = el.dataset.id\n      d2.state.histOpen = d2.state.histOpen === id ? null : id\n    })\n    d2.on('.act-rerun', 'click', (e, el) => rerunCase(d2, el.dataset.id))\n    d2.on('.act-resolve', 'click', (e, el) => resolveRun(d2, el.dataset.runid))\n  },\n}\n\nfunction firstItemImages(p) {\n  const imgs = (p && p.items && p.items[0] && p.items[0].images) || []\n  return imgs.map(i => i.imageUrl).filter(Boolean).slice(0, 8)\n}\n// Old stored test photos can carry EXIF Orientation metadata (iPhone etc. save\n// raw sensor pixels landscape and rely on the tag for upright display) — the\n// live app normalizes this on fresh uploads, but a REGRESSION RE-TEST reuses\n// an already-stored (pre-fix) URL, so it still feeds the model a sideways\n// image. Re-painting onto a canvas bakes in whatever orientation the\n// browser's own EXIF-aware decoder already applied, exactly like the\n// upload-time fix — then we re-host the corrected bytes before re-testing.\nasync function normalizeRemotePhoto(url) {\n  try {\n    const res = await fetch(url, { mode: 'cors' })\n    const blob = await res.blob()\n    // CRITICAL: a plain <img> + canvas.drawImage(img,...) does NOT apply EXIF\n    // orientation in most browsers — only the <img> tag's own on-screen paint\n    // does (via the image-orientation:from-image CSS default). drawImage()\n    // historically draws the RAW decoded bitmap, ignoring the tag, which\n    // silently re-bakes the sideways pixels as \"fixed\" with no EXIF left to\n    // warn anyone. createImageBitmap's `imageOrientation:'from-image'` option\n    // is the one API that actually decodes WITH the correction applied.\n    const bitmap = await createImageBitmap(blob, { imageOrientation: 'from-image' })\n    const canvas = document.createElement('canvas')\n    canvas.width = bitmap.width\n    canvas.height = bitmap.height\n    canvas.getContext('2d').drawImage(bitmap, 0, 0)\n    return await new Promise((resolve) => {\n      canvas.toBlob((outBlob) => {\n        resolve(outBlob ? new File([outBlob], 'normalized.jpg', { type: 'image/jpeg' }) : null)\n      }, 'image/jpeg', 0.92)\n    })\n  } catch (e) { return null }\n}\nasync function fetchProductById(productId) {\n  try {\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-product?productId=' + encodeURIComponent(productId))\n    const json = await res.json()\n    return (json && json.data && json.data.products && json.data.products[0]) || null\n  } catch (e) { return null }\n}\nasync function rerunCase(d2, feedbackId) {\n  const labDs = d2.data('labFeedback')\n  const row = (labDs.records() || []).find(r => r._id === feedbackId)\n  if (!row) return\n  const ids = (row.productIds || '').split(',').map(x => x.trim()).filter(Boolean)\n  if (!ids.length) return\n  d2.state.rerunBusy = feedbackId\n  try {\n    const garment = await fetchProductById(ids[0])\n    const extra = ids[1] ? await fetchProductById(ids[1]) : null\n    if (!garment) throw new Error('produto não encontrado no catálogo (id ' + ids[0] + ')')\n    let userPhotoForRetest = row.userPhotoUrl\n    const normFile = await normalizeRemotePhoto(row.userPhotoUrl)\n    if (normFile && d2.params.uploadDrive) {\n      const uploaded = await d2.drive(d2.params.uploadDrive).upload([normFile])\n      if (uploaded && uploaded[0] && uploaded[0].fileUrl) userPhotoForRetest = uploaded[0].fileUrl\n    }\n    const res = await fetch('https://veste-app.data2apis.com/rush/listener/jj-tryon-generate', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({\n        userPhotoUrl: userPhotoForRetest,\n        garmentImages: firstItemImages(garment),\n        garmentProductName: garment.productName,\n        extraImages: extra ? firstItemImages(extra) : [],\n        extraProductName: extra ? extra.productName : '',\n        userPhotoBodyCoverage: '',\n      }),\n    })\n    const json = await res.json()\n    const result = (json && json.data) || {}\n    await d2.data('regressionRuns').create({\n      feedbackId,\n      runAt: new Date().toISOString(),\n      resultUrl: result.resultUrl || '',\n      blocked: !!result.blocked,\n      blockedReason: result.blockedReason || '',\n      resolved: false,\n      note: '',\n    })\n  } catch (err) {\n    await d2.data('regressionRuns').create({\n      feedbackId,\n      runAt: new Date().toISOString(),\n      resultUrl: '',\n      blocked: true,\n      blockedReason: 'Falha ao re-testar: ' + (err && err.message ? err.message : 'erro'),\n      resolved: false,\n      note: '',\n    })\n  } finally {\n    d2.state.rerunBusy = null\n  }\n}\nasync function resolveRun(d2, runId) {\n  await d2.data('regressionRuns').update({ _id: runId, resolved: true })\n}"}},"swaps":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"swaps"}}}},"events":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"events"}}}},"labFeedback":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"labFeedback"}}}},"regressionRuns":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"regressionRuns"}}}}}}}}}},{"_id":"6abfc9341a6b03943b979f8b","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Analytics","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#191D1A}\n    *{box-sizing:border-box}\n    .page{padding:32px 32px 48px;display:flex;flex-direction:column;gap:24px;min-height:100%;background:#F6F6F3}\n    .crumb{font-size:11.5px;font-weight:600;letter-spacing:.04em;color:#898781}\n    .crumb b{color:#52514e;font-weight:600}\n    h1{font-size:26px;font-weight:700;letter-spacing:-.01em;margin:6px 0 0;color:#0b0b0b}\n    .desc{margin:6px 0 0;color:#727273;font-size:13.5px;max-width:70ch}\n    .row1{display:grid;grid-template-columns:repeat(4,1fr);gap:18px}\n    .tile{background:#fff;border:1px solid #E5E3DD;border-radius:12px;padding:18px;box-shadow:0 1px 2px rgba(11,11,11,0.03)}\n    .tile .lbl{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#898781;margin-bottom:8px}\n    .tile .val{font-size:28px;font-weight:700}\n    .grid2{display:grid;grid-template-columns:1.4fr 1fr;gap:22px}\n    .card{background:#fff;border:1px solid #E5E3DD;border-radius:12px;padding:22px;box-shadow:0 1px 2px rgba(11,11,11,0.03)}\n    .eyebrow{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#898781;margin:0 0 16px}\n    .bars{display:flex;align-items:flex-end;gap:6px;height:160px}\n    .bar{flex:1;background:#2a78d6;border-radius:4px 4px 0 0;position:relative}\n    .bar span{position:absolute;bottom:100%;left:50%;transform:translateX(-50%);font-size:10px;color:#52514e;white-space:nowrap;margin-bottom:3px}\n    .xaxis{display:flex;gap:6px;margin-top:6px}\n    .xaxis span{flex:1;text-align:center;font-size:10px;color:#898781}\n    .prow{display:flex;justify-content:space-between;align-items:center;padding:9px 0;border-bottom:1px solid #EFEEEA;font-size:13px}\n    .prow:last-child{border-bottom:none}\n    .pname{flex:1;margin-right:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n    .pcount{font-weight:700;color:#2a78d6}\n    .skel{color:#898781;font-size:13px}\n    .splitrow{display:flex;gap:18px;margin-top:16px}\n    .splitcol{flex:1;text-align:center;background:#F6F6F3;border-radius:10px;padding:14px}\n    .splitcol .v{font-size:22px;font-weight:700}\n    .splitcol .l{font-size:11px;color:#898781;margin-top:4px}\n  `,\n  render(d2) {\n    const ds = d2.data('swaps')\n    if (ds.loading) return `<div class=\"page\"><div class=\"skel\">Carregando analytics…</div></div>`\n    const d = computeStats(ds.records() || [])\n    const byDay = (d.byDay || []).slice(-14)\n    const maxDay = Math.max(1, ...byDay.map(x => x.count))\n    return `<div class=\"page\">\n      <div>\n        <div class=\"crumb\">Provador Virtual &nbsp;›&nbsp; <b>Visão Geral</b> &nbsp;›&nbsp; Dashboard</div>\n        <h1>Visão Geral</h1>\n        <p class=\"desc\">Visão geral de uso do provador virtual.</p>\n      </div>\n      <div class=\"row1\">\n        <div class=\"tile\"><div class=\"lbl\">Taxa de sucesso geral</div><div class=\"val\">${d.successRateOverall}%</div></div>\n        <div class=\"tile\"><div class=\"lbl\">Total de gerações</div><div class=\"val\">${d.totalGenerations}</div></div>\n        <div class=\"tile\"><div class=\"lbl\">Sessões distintas</div><div class=\"val\">${d.distinctSessions}</div></div>\n        <div class=\"tile\"><div class=\"lbl\">Médio por sessão</div><div class=\"val\">${d.avgGenerationsPerSession}</div></div>\n      </div>\n      <div class=\"grid2\">\n        <div class=\"card\">\n          <div class=\"eyebrow\">Gerações por dia</div>\n          <div class=\"bars\">${byDay.map(b => `<div class=\"bar\" style=\"height:${Math.max(4,Math.round(100*b.count/maxDay))}%\"><span>${b.count}</span></div>`).join('')}</div>\n          <div class=\"xaxis\">${byDay.map(b => `<span>${(b.bucket||'').slice(5)}</span>`).join('')}</div>\n          <div class=\"splitrow\">\n            <div class=\"splitcol\"><div class=\"v\">${d.singleCount}</div><div class=\"l\">testes 1 peça · ${d.singleSuccessRate}% sucesso</div></div>\n            <div class=\"splitcol\"><div class=\"v\">${d.comboCount}</div><div class=\"l\">testes combo (2 peças) · ${d.comboSuccessRate}% sucesso</div></div>\n          </div>\n        </div>\n        <div class=\"card\">\n          <div class=\"eyebrow\">Produtos mais testados</div>\n          ${(d.topProducts||[]).map(p => `<div class=\"prow\"><span class=\"pname\">${String(p.product).replace(/</g,'&lt;')}</span><span class=\"pcount\">${p.count}</span></div>`).join('') || '<div class=\"skel\">Sem dados</div>'}\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2) {},\n}\nfunction computeStats(rows) {\n  const total = rows.length\n  const succeeded = rows.filter(r => r.success === true).length\n  const isCombo = (r) => { try { return /\"extraAnalysis\":\\{/.test(r.analysisJson || '') } catch (e) { return false } }\n  const combos = rows.filter(isCombo)\n  const singles = rows.filter(r => !isCombo(r))\n  const pct = (num, den) => den > 0 ? (100 * num / den).toFixed(1) : '0.0'\n  const sessions = Array.from(new Set(rows.map(r => r.sessionId)))\n  const byDayMap = {}, nameMap = {}\n  rows.forEach(r => {\n    const day = (r.createdAt || '').slice(0, 10)\n    if (day) byDayMap[day] = (byDayMap[day] || 0) + 1\n    const nm = r.productName || '—'\n    nameMap[nm] = (nameMap[nm] || 0) + 1\n  })\n  const byDay = Object.keys(byDayMap).sort().map(k => ({ bucket: k, count: byDayMap[k] }))\n  const topProducts = Object.keys(nameMap).map(k => ({ product: k, count: nameMap[k] })).sort((a, b) => b.count - a.count).slice(0, 8)\n  return {\n    totalGenerations: total,\n    successRateOverall: pct(succeeded, total),\n    comboCount: combos.length,\n    comboSuccessRate: pct(combos.filter(r => r.success === true).length, combos.length),\n    singleCount: singles.length,\n    singleSuccessRate: pct(singles.filter(r => r.success === true).length, singles.length),\n    distinctSessions: sessions.length,\n    avgGenerationsPerSession: sessions.length > 0 ? (total / sessions.length).toFixed(2) : '0.00',\n    byDay, topProducts,\n  }\n}"}},"swaps":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"swaps"}}}}}}}}}},{"_id":"6abfcb480484650e9b1c568c","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Login_Form","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}\n    .f{display:flex;flex-direction:column;gap:12px}\n    input{padding:12px 14px;border:1px solid rgba(11,11,11,0.16);border-radius:9px;font-size:14px;outline:none;color:#0b0b0b}\n    input:focus{border-color:#2a78d6}\n    button{margin-top:8px;padding:13px;border:none;border-radius:9px;background:#0b0b0b;color:#fff;font-size:14px;font-weight:700;cursor:pointer}\n    button:disabled{opacity:.5;cursor:default}\n    .err{color:#d03b3b;font-size:12.5px;margin-top:-4px}\n  `,\n  render(d2) {\n    const s = d2.state\n    return `<div class=\"f\">\n      <input class=\"email\" type=\"email\" placeholder=\"E-mail\" value=\"${(s.email||'').replace(/\"/g,'&quot;')}\"/>\n      <input class=\"password\" type=\"password\" placeholder=\"Senha\" value=\"${(s.password||'').replace(/\"/g,'&quot;')}\"/>\n      ${s.err ? `<div class=\"err\">${s.err}</div>` : ''}\n      <button class=\"submit\" ${s.busy?'disabled':''}>${s.busy?'Entrando…':'Entrar'}</button>\n    </div>`\n  },\n  mounted(d2) {\n    d2.on('.email', 'input', (e, el) => { d2.state.email = el.value })\n    d2.on('.password', 'input', (e, el) => { d2.state.password = el.value })\n    d2.on('.password', 'keydown', (e) => { if (e.key === 'Enter') doLogin(d2) })\n    d2.on('.submit', 'click', () => doLogin(d2))\n  },\n}\nasync function doLogin(d2) {\n  d2.state.busy = true\n  d2.state.err = ''\n  try {\n    await d2.auth(d2.params.auth).login({ email: d2.state.email, password: d2.state.password })\n    d2.router(d2.params.router).goTo('/categorias')\n  } catch (e) {\n    d2.state.err = 'E-mail ou senha inválidos.'\n  } finally {\n    d2.state.busy = false\n  }\n}"}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}},"router":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"router"}}}}}}}}}},{"_id":"6abfccf5272474af8169f0e3","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"Painel_Login_Screen","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    :host{display:block;width:100%;height:100%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif}\n    .page{width:100%;height:100%;background:#f9f9f7;display:flex;align-items:center;justify-content:center;box-sizing:border-box}\n    .card{width:380px;background:#ffffff;border:1px solid rgba(11,11,11,0.10);border-radius:16px;box-shadow:0 1px 2px rgba(11,11,11,0.04),0 12px 32px rgba(11,11,11,0.06);padding:40px 36px}\n    .kicker{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#898781;margin-bottom:10px}\n    h1{font-size:24px;font-weight:700;color:#0b0b0b;margin:0 0 26px}\n    .f{display:flex;flex-direction:column;gap:12px}\n    input{padding:13px 14px;border:1px solid rgba(11,11,11,0.16);border-radius:9px;font-size:14px;outline:none;color:#0b0b0b;box-sizing:border-box;width:100%}\n    input:focus{border-color:#2a78d6}\n    button{margin-top:6px;padding:14px;border:none;border-radius:9px;background:#0b0b0b;color:#fff;font-size:14px;font-weight:700;cursor:pointer}\n    button:hover{opacity:.9}\n    button:disabled{opacity:.5;cursor:default}\n    .err{color:#d03b3b;font-size:12.5px;margin-top:-2px}\n  `,\n  render(d2) {\n    const s = d2.state\n    return `<div class=\"page\"><div class=\"card\">\n      <div class=\"kicker\">PROVADOR · PAINEL INTERNO</div>\n      <h1>Entrar</h1>\n      <div class=\"f\">\n        <input class=\"email\" type=\"email\" placeholder=\"E-mail\" value=\"${(s.email||'').replace(/\"/g,'&quot;')}\" autocomplete=\"username\"/>\n        <input class=\"password\" type=\"password\" placeholder=\"Senha\" value=\"${(s.password||'').replace(/\"/g,'&quot;')}\" autocomplete=\"current-password\"/>\n        ${s.err ? `<div class=\"err\">${s.err}</div>` : ''}\n        <button class=\"submit\" ${s.busy ? 'disabled' : ''}>${s.busy ? 'Entrando…' : 'Entrar'}</button>\n      </div>\n    </div></div>`\n  },\n  mounted(d2) {\n    d2.on('.email', 'input', (e, el) => { d2.state.email = el.value })\n    d2.on('.password', 'input', (e, el) => { d2.state.password = el.value })\n    d2.on('.password', 'keydown', (e) => { if (e.key === 'Enter') doLogin(d2) })\n    d2.on('.submit', 'click', () => doLogin(d2))\n  },\n}\nasync function doLogin(d2) {\n  if (!d2.state.email || !d2.state.password) { d2.state.err = 'Preencha e-mail e senha.'; return }\n  d2.state.busy = true\n  d2.state.err = ''\n  try {\n    await d2.auth(d2.params.auth).login({ credentials: { email: d2.state.email, password: d2.state.password } })\n    d2.router(d2.params.router).goTo('/categorias')\n  } catch (e) {\n    d2.state.err = 'E-mail ou senha inválidos.'\n  } finally {\n    d2.state.busy = false\n  }\n}"}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}},"router":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"router"}}}}}}}}}},{"_id":"6abfc57256470c270675d1d9","component":"data2-core-types@Component","system":"698a0c028a310c894be98bc8","params":{"extends":"data2-core-primitives@Block","component":"SidebarNav","package":"veste-app","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"L_afVMxLga60CtEFfsz3v"}}}}}},{"_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":"6abfc7e50484650e9b1c4d47","component":"data2-rush@HTTPListener","reference":"tryon-funnel-stats","system":"698a0c028a310c894be98bc8","params":{}},{"_id":"6abfc6b36ef68381dfec13c2","component":"data2-rush@HTTPListener","reference":"tryon-blocklist-stats","system":"698a0c028a310c894be98bc8","params":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6abfc5b19043a5676f9b4e77","component":"data2-core-primitives@Router","system":"698a0c028a310c894be98bc8","displayName":"Painel Router","params":{"routerType":{"component":"data2-core-primitives@String","params":{"content":"browser"}},"routes":[{"component":"data2-core-primitives@Route","params":{"path":"","name":"Visão Geral","onEnter":null,"content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"Wf5ARjE4hgVEO_2VZsXQg"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}}}}]}},{"component":"data2-core-primitives@Route","params":{"path":"/categorias","name":"Restrições de Categoria","onEnter":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-flow@Step","params":{"action":{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-blocklist-stats"}},"prop":"invoke","args":[{}]}},"resultName":"stats"}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"CategoriasUI"}},"prop":"data.stats","value":{"component":"data2-core-types@Reference","params":{"reference":"stats"}}}}]}},"content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"2WIUyxIWjYoKzrr7SRslG"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}}}}]}},{"component":"data2-core-primitives@Route","params":{"path":"/funil","name":"Funil","onEnter":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-flow@Step","params":{"action":{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-funnel-stats"}},"prop":"invoke","args":[{}]}},"resultName":"r"}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"FunilUI"}},"prop":"data.stages","value":{"component":"data2-core-types@Reference","params":{"reference":"r.stages"}}}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"FunilUI"}},"prop":"data.retriedCount","value":{"component":"data2-core-types@Reference","params":{"reference":"r.retriedCount"}}}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"FunilUI"}},"prop":"data.failedCount","value":{"component":"data2-core-types@Reference","params":{"reference":"r.failedCount"}}}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"FunilUI"}},"prop":"data.totalSwaps","value":{"component":"data2-core-types@Reference","params":{"reference":"r.totalSwaps"}}}},{"component":"data2-core-types@Setter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"FunilUI"}},"prop":"data.comboCount","value":{"component":"data2-core-types@Reference","params":{"reference":"r.comboCount"}}}}]}},"content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"7F_YcG1w2Qg6hT_bgcwMK"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}}}}]}},{"component":"data2-core-primitives@Route","params":{"path":"/curadoria","name":"Curadoria","onEnter":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-garment-swapsDataSet"}},"prop":"sort","args":[{"createdAt":{"component":"data2-core-numerical-operators@Subtract","params":{"components":[0,1]}}}]}},{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-garment-swapsDataSet"}},"prop":"filter","args":[{},true]}}]}},"content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"iAKk5uJpshO12Kp7FUmAg"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}}}}]}},{"component":"data2-core-primitives@Route","params":{"path":"/analytics","name":"Analytics","content":{"component":"data2-core-types@ArtboardReference","params":{"reference":"Wf5ARjE4hgVEO_2VZsXQg"}},"policies":[{"component":"data2-core-auth@Policy","params":{"authenticationStrategy":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}}}}]}}],"login":{"component":"data2-core-types@ArtboardReference","params":{"reference":"AVX6BvdJdMSct2_IDkjIt"}}},"reference":"painel-router"},{"_id":"6abfc585c1d9b5fa79c8617e","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"Login — Painel Provador","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Login — Painel Provador"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"AVX6BvdJdMSct2_IDkjIt"},{"_id":"6abfc630ea8ac94fb093b1b3","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"Painel — Analytics","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Painel — Analytics"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"Wf5ARjE4hgVEO_2VZsXQg"},{"_id":"6abfc62f56470c270675d43f","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"Painel — Curadoria de Resultados","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Painel — Curadoria de Resultados"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"iAKk5uJpshO12Kp7FUmAg"},{"_id":"6abfc62ec1d9b5fa79c86394","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"Painel — Funil de Conversão","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Painel — Funil de Conversão"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"7F_YcG1w2Qg6hT_bgcwMK"},{"_id":"6abfc62dce21b725b315c150","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"Painel — Restrições de Categoria","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Painel — Restrições de Categoria"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"2WIUyxIWjYoKzrr7SRslG"},{"_id":"c8abcf515da5869f6ee32c42","parent":"6abfc585c1d9b5fa79c8617e","component":"veste-app@Painel_Login_Screen","system":"698a0c028a310c894be98bc8","displayName":"loginScreen","params":{"auth":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}},"router":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-router"}},"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":"zMVAI_FYFYiK5ia3kbWR9"},{"_id":"8a606f5a58873f7205b7636a","parent":"6abfc630ea8ac94fb093b1b3","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"bg","params":{"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}},"fill":{"component":"data2-core-types@Fill","params":{"backgrounds":[{"component":"data2-core-types@Background","params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"#f9f9f7"}}}}]}}},"index":1,"reference":"wImLK8TwIam1U6rrZO4hS"},{"_id":"f9aeb2c66827b18b220f2539","parent":"6abfc630ea8ac94fb093b1b3","component":"veste-app@SidebarNav","system":"698a0c028a310c894be98bc8","displayName":"sidebar","params":{"active":"analytics","super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":240,"height":900}}}},"index":4,"reference":"OT7JxLVUrMe_4DOValy6-"},{"_id":"74ae1d503b58d3d40c480ded","parent":"6abfc630ea8ac94fb093b1b3","component":"veste-app@Painel_Analytics","system":"698a0c028a310c894be98bc8","displayName":"content","params":{"analyticsApi":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-analytics-stats"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":240,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1200,"height":900}}},"swaps":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-garment-swapsDataSet"}}},"index":5,"reference":"efEJTOTWLktpLpYXLTaP7"},{"_id":"0ec8e22778d1032e2659236b","parent":"6abfc62f56470c270675d43f","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"bg","params":{"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}},"fill":{"component":"data2-core-types@Fill","params":{"backgrounds":[{"component":"data2-core-types@Background","params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"#f9f9f7"}}}}]}}},"index":1,"reference":"bFhmG3w_XNihIQ-oM9ZpA"},{"_id":"d4374f63fd2cc6af7834f70d","parent":"6abfc62f56470c270675d43f","component":"veste-app@SidebarNav","system":"698a0c028a310c894be98bc8","displayName":"sidebar","params":{"active":"curadoria","super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":240,"height":900}}}},"index":2,"reference":"PpYwfx5-iNHNdP_Xjvju0"},{"_id":"4da7b1721cf3caf30c7198d7","parent":"6abfc62f56470c270675d43f","component":"veste-app@Painel_Curadoria_de_Resultados","system":"698a0c028a310c894be98bc8","displayName":"content","params":{"swaps":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-garment-swapsDataSet"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":240,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1200,"height":900}}},"events":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-eventsDataSet"}},"labFeedback":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-feedbackDataSet"}},"regressionRuns":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"bTsNMUvb75YtL8sY0SbzADataSet"}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"x":240,"y":0,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1200,"height":900}},"uploadDrive":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"assets-tryon"}}},"index":3,"reference":"FPm_1mojn7i-u4lpNYpEH"},{"_id":"3f076f433c9ffbd7d898cb5b","parent":"6abfc62ec1d9b5fa79c86394","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"bg","params":{"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}},"fill":{"component":"data2-core-types@Fill","params":{"backgrounds":[{"component":"data2-core-types@Background","params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"#f9f9f7"}}}}]}}},"index":1,"reference":"kIo0-eFslVOYW5xb77kq2"},{"_id":"e81c4655ce4fd472dca4326b","parent":"6abfc62ec1d9b5fa79c86394","component":"veste-app@SidebarNav","system":"698a0c028a310c894be98bc8","displayName":"sidebar","params":{"active":"funil","super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":240,"height":900}}}},"index":2,"reference":"dSz_CEuOttluxWY_jtv0Y"},{"_id":"c92c94068c725908ab9498c3","parent":"6abfc62ec1d9b5fa79c86394","component":"veste-app@Painel_Funil_de_Conversao","system":"698a0c028a310c894be98bc8","displayName":"content","params":{"funnelApi":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-funnel-stats"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":240,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1200,"height":900}}}},"index":3,"reference":"oftSQ4KlL8kHOjk-_phBp"},{"_id":"50af29ccf97e4e928ec2d005","parent":"6abfc62dce21b725b315c150","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"bg","params":{"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}},"fill":{"component":"data2-core-types@Fill","params":{"backgrounds":[{"component":"data2-core-types@Background","params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"#f9f9f7"}}}}]}}},"index":1,"reference":"0cVYBjngOsAErr6ZSi4aV"},{"_id":"bd9bc771269cb4359c6e1184","parent":"6abfc62dce21b725b315c150","component":"veste-app@SidebarNav","system":"698a0c028a310c894be98bc8","displayName":"sidebar","params":{"active":"categorias","super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":240,"height":900}}}},"index":2,"reference":"2Vuw30gRpY-U7VS0JNF1y"},{"_id":"3dfa256c4796cb156e5c469f","parent":"6abfc62dce21b725b315c150","component":"veste-app@Painel_Categorias_Bloqueadas","system":"698a0c028a310c894be98bc8","displayName":"content","params":{"blocklist":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"INcWaNAVy-o9JcmVXp-ceDataSet"}},"statsApi":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"tryon-blocklist-stats"}},"super":{"position":{"component":"data2-core-types@Position","params":{"x":240,"y":0,"freeTransform":true,"left":true,"right":true,"top":true,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1200,"height":900}}}},"index":3,"reference":"zJpgZlE4LrGyyEmrBbZGp"},{"_id":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Artboard","system":"698a0c028a310c894be98bc8","displayName":"UC Source — SidebarNav","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"UC Source — SidebarNav"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":240,"height":820}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}},"componentClass":{"component":"data2-core-types@ComponentClass","params":{}}},"reference":"L_afVMxLga60CtEFfsz3v"},{"_id":"211b419d7b51e0fd6c688356","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"bg","params":{"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":240,"height":820}},"fill":{"component":"data2-core-types@Fill","params":{"backgrounds":[{"component":"data2-core-types@Background","params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"#0b0b0b"}}}}]}}},"index":0,"reference":"k04oVMEPrxiqI3hSq1XeH"},{"_id":"450d935a26dbf6b380b5d06a","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"sectionVisao","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":92,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":16}},"text":"VISÃO GERAL","textColor":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.32)"}},"textSize":10.5,"textFormat":{"component":"data2-core-types@TextFormat","params":{"fontWeight":700,"letterSpacing":0.6}}},"index":0,"reference":"JebnlmpDyuUoaIqHXIUj5"},{"_id":"9c4dc2bfa29110b8a2bb431b","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"brand","params":{"position":{"component":"data2-core-types@Position","params":{"x":24,"y":28,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":22}},"text":"PROVADOR","textColor":{"component":"data2-core-types@Color","params":{"rgba":"#ffffff"}},"textSize":13,"textFormat":{"component":"data2-core-types@TextFormat","params":{"fontWeight":700,"textTransform":"uppercase","letterSpacing":1}}},"index":1,"reference":"Oo7vip-W8I3uHdaoydONz"},{"_id":"ea5d2cf0fb56bed06901ce05","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"sectionOperacao","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":202,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":16}},"text":"CURADORIA E CONFIGURAÇÃO","textColor":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.32)"}},"textSize":10.5,"textFormat":{"component":"data2-core-types@TextFormat","params":{"fontWeight":700,"letterSpacing":0.6}}},"index":1,"reference":"P4KEOyYg9FbwJXMUfwmy3"},{"_id":"ef0d726a3742e845a79d2508","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"brandSub","params":{"position":{"component":"data2-core-types@Position","params":{"x":24,"y":50,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":18}},"text":"Painel interno · Veste","textColor":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.45)"}},"textSize":11},"index":2,"reference":"N8c_QV295o3Paya0LLXue"},{"_id":"ff90e2d1e7ad34c20cc305f9","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"navRestr","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":266,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":36}},"text":"Restrições de Categoria","textSize":14,"textFormat":{"component":"data2-core-types@TextFormat","params":{"verticalAlignment":"middle"}},"textColor":{"component":"data2-core-flow@If","params":{"compare":{"component":"data2-core-comparison-operators@Equal","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":"active"}},"comparisonSubject":"categorias"}},"then":{"component":"data2-core-types@Color","params":{"rgba":"#ffffff"}},"else":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.56)"}}}},"cursor":"pointer","onClick":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-routerInterface"}},"prop":"history.push","args":["/categorias"]}}]}}},"index":3,"reference":"5BYChFD0URbiFDwqp6cAw"},{"_id":"5c3cb5c90ab11a0d5b2a67ae","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"navFunil","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":160,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":36}},"text":"Funil de Conversão","textSize":14,"textFormat":{"component":"data2-core-types@TextFormat","params":{"verticalAlignment":"middle"}},"textColor":{"component":"data2-core-flow@If","params":{"compare":{"component":"data2-core-comparison-operators@Equal","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":"active"}},"comparisonSubject":"funil"}},"then":{"component":"data2-core-types@Color","params":{"rgba":"#ffffff"}},"else":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.56)"}}}},"cursor":"pointer","onClick":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-routerInterface"}},"prop":"history.push","args":["/funil"]}}]}}},"index":4,"reference":"5qaGS709y4Es0e3Gsc6Eo"},{"_id":"26f102d201c4800e8c1964f2","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"navCuradoria","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":228,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":36}},"text":"Curadoria de Resultados","textSize":14,"textFormat":{"component":"data2-core-types@TextFormat","params":{"verticalAlignment":"middle"}},"textColor":{"component":"data2-core-flow@If","params":{"compare":{"component":"data2-core-comparison-operators@Equal","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":"active"}},"comparisonSubject":"curadoria"}},"then":{"component":"data2-core-types@Color","params":{"rgba":"#ffffff"}},"else":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.56)"}}}},"cursor":"pointer","onClick":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-routerInterface"}},"prop":"history.push","args":["/curadoria"]}}]}}},"index":5,"reference":"r8RP7qs9nGDrw7VglDy1r"},{"_id":"bab79ebbf93b697f052a7633","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"navAnalytics","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":122,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":36}},"text":"Visão Geral","textSize":14,"textFormat":{"component":"data2-core-types@TextFormat","params":{"verticalAlignment":"middle"}},"textColor":{"component":"data2-core-flow@If","params":{"compare":{"component":"data2-core-comparison-operators@Equal","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":"active"}},"comparisonSubject":"analytics"}},"then":{"component":"data2-core-types@Color","params":{"rgba":"#ffffff"}},"else":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.56)"}}}},"cursor":"pointer","onClick":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-routerInterface"}},"prop":"history.push","args":["/"]}}]}}},"index":6,"reference":"BBnXWFPmRsphPm5IYoCKt"},{"_id":"f9e8e1b7974c26b7ddc2368d","parent":"6abfc4c71a6b03943b979363","component":"data2-core-primitives@Block","system":"698a0c028a310c894be98bc8","displayName":"navSair","params":{"position":{"component":"data2-core-types@Position","params":{"x":20,"y":760,"freeTransform":true,"left":true,"right":false,"top":false,"bottom":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":200,"height":30}},"text":"Sair","textSize":13,"textColor":{"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,0.4)"}},"cursor":"pointer","onClick":{"component":"data2-core-flow@Series","params":{"actions":[{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}},"prop":"logout","args":[{"component":"data2-core-types@Getter","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-auth"}},"prop":"session"}}]}},{"component":"data2-core-types@Interaction","params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"painel-routerInterface"}},"prop":"history.push","args":["/"]}}]}}},"index":7,"reference":"whfSNVcx2ZYGRcbjLmABq"}]}},{"_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":"6abfc66ece21b725b315c1f2","displayName":"Painel Provador","reference":"Eno_Qfa1Qg02c5AC-TpB5","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"painel-provador"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Router","reference":"painel-router"}}},"system":"698a0c028a310c894be98bc8","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6abfc47e56470c270675cf2a","component":"data2-core@AuthenticationStrategy","system":"698a0c028a310c894be98bc8","params":{"credentials":[{"key":"email"},{"key":"password"}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"WFRjb3S8FEaHeUX-YWfi0DataSet"}},"email":"email","password":"password"},"reference":"painel-auth"},{"_id":"6ab401fb5bec247f5fd16026","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"tryon-garment-swaps"}}},"reference":"tryon-garment-swapsDataSet"},{"_id":"6abfc7f7e9fa96de8aa0dfd1","component":"data2-core@Store","system":"698a0c028a310c894be98bc8","params":{"initialData":{"stages":[],"retriedCount":0,"failedCount":0,"totalSwaps":0,"comboCount":0}},"reference":"FunilUI"},{"_id":"6abfc70dea8ac94fb093b447","component":"data2-core@Store","system":"698a0c028a310c894be98bc8","params":{"initialData":{"newCategory":"","stats":{"totalDistinctTested":0,"blockedCategoryMatches":0}}},"reference":"CategoriasUI"},{"_id":"6abfc415e9fa96de8aa0d609","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"WFRjb3S8FEaHeUX-YWfi0"}}},"reference":"WFRjb3S8FEaHeUX-YWfi0DataSet"},{"_id":"6ab401fbc40aec6dd101145d","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"tryon-garment-swaps","schema":[{"component":"data2-core-types@Field","params":{"key":"sessionId","title":"Session ID","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"productId","title":"Product ID","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"productName","title":"Produto","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"skuId","title":"SKU ID","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"userPhotoUrl","title":"Foto do cliente","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"referenceImagesUsed","title":"Imagens de referência usadas","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"resultImageUrl","title":"Imagem resultado","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"analysisJson","title":"Análise da peça (JSON)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"success","title":"Sucesso","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"errorMessage","title":"Erro","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Quando","component":"data2-core-primitives@Date"}}]},"reference":"tryon-garment-swaps"},{"_id":"6abfc4150484650e9b1c430d","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"WFRjb3S8FEaHeUX-YWfi0","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-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"name","title":"Nome","component":"data2-core-primitives@String"}}]},"reference":"WFRjb3S8FEaHeUX-YWfi0"},{"_id":"6abfd253e892b0a312cf1665","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"bTsNMUvb75YtL8sY0SbzA"}}},"reference":"bTsNMUvb75YtL8sY0SbzADataSet"},{"_id":"6ab3d6f56ccf35a3331fb36f","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"tryon-feedback"}}},"reference":"tryon-feedbackDataSet"},{"_id":"6ab401f905b89c1cdd41ff00","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"tryon-events"}}},"reference":"tryon-eventsDataSet"},{"_id":"6abfc415e892b0a312ceef7d","component":"data2-core@DataSet","system":"698a0c028a310c894be98bc8","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"INcWaNAVy-o9JcmVXp-ce"}}},"reference":"INcWaNAVy-o9JcmVXp-ceDataSet"},{"_id":"6abfd253ea8ac94fb093d07f","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"bTsNMUvb75YtL8sY0SbzA","schema":[{"component":"data2-core-types@Field","params":{"key":"feedbackId","title":"Feedback ID","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"runAt","title":"Executado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"resultUrl","title":"URL do resultado","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"blocked","title":"Bloqueado","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"blockedReason","title":"Motivo do bloqueio","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"resolved","title":"Resolvido","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"note","title":"Nota","component":"data2-core-primitives@String"}}]},"reference":"bTsNMUvb75YtL8sY0SbzA"},{"_id":"6ab3d6f5c40aec6dd1009d91","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"tryon-feedback","schema":[{"component":"data2-core-types@Field","params":{"key":"verdict","title":"Resultado","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"note","title":"Observação","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"productNames","title":"Produtos testados","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"productIds","title":"IDs dos produtos","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"categories","title":"Categorias","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"userPhotoUrl","title":"Foto usada","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"resultImageUrl","title":"Imagem gerada","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"referenceImagesUsed","title":"Imagens de referência escolhidas","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"analysisJson","title":"Análise (debug)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"testedAt","title":"Testado em","component":"data2-core-primitives@Date"}}]},"reference":"tryon-feedback"},{"_id":"6ab401f92086c2e434149145","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"tryon-events","schema":[{"component":"data2-core-types@Field","params":{"key":"sessionId","title":"Session ID","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"eventName","title":"Evento","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"direction","title":"Direção","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"payloadJson","title":"Payload (JSON)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Quando","component":"data2-core-primitives@Date"}}]},"reference":"tryon-events"},{"_id":"6abfc4140484650e9b1c4308","component":"data2-core@Data2TableDataSource","system":"698a0c028a310c894be98bc8","params":{"table":"INcWaNAVy-o9JcmVXp-ce","schema":[{"component":"data2-core-types@Field","params":{"key":"category","title":"Categoria","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"createdAt","title":"Criado em","component":"data2-core-primitives@Date"}}]},"reference":"INcWaNAVy-o9JcmVXp-ce"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}