{"context":{"domain":"granado-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":"6a62e4640613f70fa449ff5d","name":"app","domain":"granado-app","visibility":"public","settings":{"defaultAccessPolicy":"public"},"updatedAt":"2026-08-26T18:18:29.764Z","createdAt":"2026-07-24T04:04:52.993Z"}]}},{"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":"6a63007ffd7945abd1edc83e","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kFarmaceutico","package":"granado-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":"function esc(s){ return (s==null?'':String(s)).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;'); }\nfunction money(d2,v){ const n = Number(v)||0; return d2.fmt.currency(n,'BRL',{maximumFractionDigits: (n%1===0?0:2)}); }\nfunction disc(p){ return (Number(p.listPrice)>Number(p.price)) ? Math.round((1-Number(p.price)/Number(p.listPrice))*100) : 0; }\nfunction initial(name){ const m=(name||'').replace(/^(Eau de Toilette|Perfume|Sabonete|Hidratante|Vela|Difusor|Óleo|Shampoo|Kit)\\s*/i,'').trim(); return (m||name||'G').charAt(0).toUpperCase(); }\n\nfunction productCard(d2,p){\n  if(!p) return '';\n  const d = disc(p);\n  return `<article class=\"pcard\" data-key=\"${esc(p.productId)}\">\n    <div class=\"pcard-imgwrap\">\n      <span class=\"pcard-mono\">${esc(initial(p.name))}</span>\n      <div class=\"pcard-img\" style=\"background-image:url('${esc(p.imageUrl)}')\"></div>\n      ${d>0?`<span class=\"pcard-disc\">-${d}%</span>`:''}\n    </div>\n    <div class=\"pcard-body\">\n      <span class=\"pcard-line\">${esc(p.brandLine)}</span>\n      <h4 class=\"pcard-name\">${esc(p.name)}</h4>\n      <div class=\"pcard-price\">\n        <span class=\"pcard-now\">${money(d2,p.price)}</span>\n        ${d>0?`<span class=\"pcard-was\">${money(d2,p.listPrice)}</span>`:''}\n      </div>\n      <button class=\"add-bag\" data-add=\"${esc(p.productId)}\"><span class=\"add-ic\">+</span> Adicionar à sacola</button>\n    </div>\n  </article>`;\n}\n\nfunction railHTML(d2,r,idx){\n  const cards=(r.products||[]).map(p=>productCard(d2,p)).filter(Boolean).join('');\n  if(!cards) return '';\n  return `<section class=\"rail\" data-key=\"rail-${idx}\">\n    <div class=\"rail-head\">\n      <h3 class=\"rail-title\">${esc(r.title)}</h3>\n      ${r.subtitle?`<span class=\"rail-sub\">${esc(r.subtitle)}</span>`:''}\n    </div>\n    <div class=\"rail-scroll\">${cards}</div>\n  </section>`;\n}\n\nfunction boxCard(d2){\n  const bullets=['5 a 6 produtos da linha Vintage','Curadoria com valor de ~R$250','+20% de desconto exclusivo','Frete grátis, renova sozinha'];\n  return `<section class=\"boxcard\" data-key=\"box\">\n    <div class=\"box-left\">\n      <span class=\"box-kicker\">Assinatura Granado</span>\n      <h3 class=\"box-title\">Granado Box</h3>\n      <p class=\"box-desc\">Uma caixa da nossa perfumaria e banho a cada 2 meses. Você economiza e ganha novidade sem pensar.</p>\n      <ul class=\"box-bul\">${bullets.map(b=>`<li>${esc(b)}</li>`).join('')}</ul>\n    </div>\n    <div class=\"box-right\">\n      <div class=\"box-off\">−20%</div>\n      <div class=\"box-price\"><b>${money(d2,150)}</b><span>a cada 2 meses</span></div>\n      <button class=\"box-cta\" data-subscribe>Assinar a Box</button>\n      <span class=\"box-fine\">cancele quando quiser</span>\n    </div>\n  </section>`;\n}\n\nfunction renderBlock(d2, b, byId, idx){\n  const delay = `style=\"animation-delay:${idx*120}ms\"`;\n  if(b.type==='say'){\n    if(!b.text) return '';\n    return `<div class=\"blk say\" ${delay}>\n      <div class=\"say-attr\"><span class=\"say-glyph\">✦</span> Seleção para você</div>\n      <p class=\"say-text\">${esc(b.text)}</p>\n    </div>`;\n  }\n  if(b.type==='whyChips'){\n    const chips=(b.chips||[]).map(c=>`<span class=\"why\">${esc(c)}</span>`).join('');\n    if(!chips) return '';\n    return `<div class=\"blk whyrow\" ${delay}><span class=\"why-label\">por que combina</span>${chips}</div>`;\n  }\n  if(b.type==='productRail'){\n    const items=(b.ids||[]).map(id=>byId[id]).filter(Boolean);\n    const cards=items.map(p=>productCard(d2,p)).join('');\n    if(!cards) return '';\n    return `<div class=\"blk rail\" ${delay}>\n      ${b.title?`<div class=\"rail-head\"><h3 class=\"rail-title\">${esc(b.title)}</h3></div>`:''}\n      <div class=\"rail-scroll\">${cards}</div>\n    </div>`;\n  }\n  if(b.type==='ritual'){\n    const items=(b.ids||[]).map(id=>byId[id]).filter(Boolean);\n    if(!items.length) return '';\n    const total=items.reduce((a,p)=>a+(Number(p.price)||0),0);\n    const listTotal=items.reduce((a,p)=>a+(Number(p.listPrice)||Number(p.price)||0),0);\n    const save=listTotal-total;\n    const ids=items.map(p=>p.productId).join(',');\n    const rows=items.map(p=>`<div class=\"kit-item\" data-key=\"${esc(p.productId)}\">\n        <div class=\"kit-thumb\"><span class=\"pcard-mono sm\">${esc(initial(p.name))}</span><div class=\"pcard-img\" style=\"background-image:url('${esc(p.imageUrl)}')\"></div></div>\n        <div class=\"kit-meta\"><span class=\"kit-name\">${esc(p.name)}</span><span class=\"kit-line\">${esc(p.brandLine)}</span></div>\n        <span class=\"kit-price\">${money(d2,p.price)}</span>\n      </div>`).join('');\n    return `<div class=\"blk kit\" ${delay}>\n      <div class=\"kit-head\"><span class=\"kit-kicker\">Kit sugerido</span><h3 class=\"kit-title\">${esc(b.title||'Seu kit Granado')}</h3></div>\n      ${b.story?`<p class=\"kit-story\">${esc(b.story)}</p>`:''}\n      <div class=\"kit-items\">${rows}</div>\n      <div class=\"kit-foot\">\n        <div class=\"kit-total\"><span>total do kit${save>0?` · você economiza ${money(d2,save)}`:''}</span><b>${money(d2,total)}</b></div>\n        <button class=\"kit-cta\" data-addkit=\"${esc(ids)}\">Adicionar kit à sacola</button>\n      </div>\n    </div>`;\n  }\n  if(b.type==='followUps'){\n    const chips=(b.chips||[]).map(c=>`<button class=\"fu\" data-followup=\"${esc(c)}\">${esc(c)} <i>→</i></button>`).join('');\n    if(!chips) return '';\n    return `<div class=\"blk furow\" ${delay}><span class=\"fu-label\">continue explorando</span>${chips}</div>`;\n  }\n  return '';\n}\n\nfunction md(s){\n  let t = esc(s==null?'':String(s));\n  t = t.replace(/\\*\\*([^*]+)\\*\\*/g,'<b>$1</b>');\n  t = t.replace(/(^|[^*])\\*([^*]+)\\*/g,'$1<i>$2</i>');\n  t = t.replace(/\\n/g,'<br/>');\n  return t;\n}\n\nfunction heroCard(d2,p){\n  if(!p) return '';\n  const d = disc(p);\n  return `<article class=\"hcard\" data-key=\"${esc(p.productId)}\">\n    <div class=\"hcard-imgwrap\"><span class=\"pcard-mono\">${esc(initial(p.name))}</span><div class=\"pcard-img\" style=\"background-image:url('${esc(p.imageUrl)}')\"></div>${d>0?`<span class=\"pcard-disc\">-${d}%</span>`:''}</div>\n    <div class=\"hcard-body\">\n      <span class=\"pcard-line\">${esc(p.brandLine)}</span>\n      <h4 class=\"hcard-name\">${esc(p.name)}</h4>\n      <div class=\"pcard-price\"><span class=\"pcard-now\">${money(d2,p.price)}</span>${d>0?`<span class=\"pcard-was\">${money(d2,p.listPrice)}</span>`:''}</div>\n      <button class=\"add-bag\" data-add=\"${esc(p.productId)}\"><span class=\"add-ic\">+</span> Adicionar à sacola</button>\n    </div>\n  </article>`;\n}\n\nfunction kitCard(d2,part,byId){\n  const items=(part.ids||[]).map(id=>byId[id]).filter(Boolean);\n  if(!items.length) return '';\n  const total=items.reduce((a,p)=>a+(Number(p.price)||0),0);\n  const listTotal=items.reduce((a,p)=>a+(Number(p.listPrice)||Number(p.price)||0),0);\n  const save=listTotal-total;\n  const ids=items.map(p=>p.productId).join(',');\n  const rows=items.map(p=>`<div class=\"kit-item\" data-key=\"${esc(p.productId)}\">\n      <div class=\"kit-thumb\"><span class=\"pcard-mono sm\">${esc(initial(p.name))}</span><div class=\"pcard-img\" style=\"background-image:url('${esc(p.imageUrl)}')\"></div></div>\n      <div class=\"kit-meta\"><span class=\"kit-name\">${esc(p.name)}</span><span class=\"kit-line\">${esc(p.brandLine)}</span></div>\n      <span class=\"kit-price\">${money(d2,p.price)}</span>\n    </div>`).join('');\n  return `<div class=\"kit\">\n    <div class=\"kit-head\"><span class=\"kit-kicker\">Kit sugerido</span><h3 class=\"kit-title\">${esc(part.title||'Seu kit Granado')}</h3></div>\n    ${part.story?`<p class=\"kit-story\">${esc(part.story)}</p>`:''}\n    <div class=\"kit-items\">${rows}</div>\n    <div class=\"kit-foot\">\n      <div class=\"kit-total\"><span>total do kit${save>0?` · você economiza ${money(d2,save)}`:''}</span><b>${money(d2,total)}</b></div>\n      <button class=\"kit-cta\" data-addkit=\"${esc(ids)}\">${esc(part.cta||'Adicionar kit à sacola')}</button>\n    </div>\n  </div>`;\n}\n\nfunction compareCard(d2,part,byId){\n  const items=(part.ids||[]).map(id=>byId[id]).filter(Boolean);\n  if(!items.length) return '';\n  const cols=items.map(p=>{\n    const d=disc(p);\n    return `<div class=\"cmp-col\" data-key=\"${esc(p.productId)}\">\n      <div class=\"cmp-imgwrap\"><span class=\"pcard-mono sm\">${esc(initial(p.name))}</span><div class=\"pcard-img\" style=\"background-image:url('${esc(p.imageUrl)}')\"></div></div>\n      <span class=\"pcard-line\">${esc(p.brandLine)}</span>\n      <h4 class=\"cmp-name\">${esc(p.name)}</h4>\n      <div class=\"cmp-cat\">${esc(p.category||'')}</div>\n      <div class=\"pcard-price\"><span class=\"pcard-now\">${money(d2,p.price)}</span>${d>0?`<span class=\"pcard-was\">${money(d2,p.listPrice)}</span>`:''}</div>\n      <button class=\"add-bag\" data-add=\"${esc(p.productId)}\"><span class=\"add-ic\">+</span> Adicionar</button>\n    </div>`;\n  }).join('');\n  return `<div class=\"cmp\"><span class=\"wg-label\">comparando</span><div class=\"cmp-grid\">${cols}</div></div>`;\n}\n\nfunction valueMathCard(d2,part){\n  const lines=(part.lines||[]);\n  if(!lines.length) return '';\n  const rows=lines.map(l=>{\n    if(typeof l==='string') return `<li><span>${esc(l)}</span></li>`;\n    const val=(l.value!=null)?(typeof l.value==='number'?money(d2,l.value):esc(l.value)):'';\n    return `<li><span>${esc(l.label||l.text||'')}</span>${val?`<b>${val}</b>`:''}</li>`;\n  }).join('');\n  return `<div class=\"vmath\"><span class=\"wg-label\">a conta que faz sentido</span><ul class=\"vmath-list\">${rows}</ul></div>`;\n}\n\nfunction scentCard(d2,part){\n  const notes=(part.notes||[]);\n  if(!notes.length) return '';\n  const rows=notes.map(n=>{\n    const lvl=Math.max(0,Math.min(5,Math.round(Number(n.intensity)||0)));\n    let dots='';\n    for(let i=0;i<5;i++){ dots+=`<span class=\"scent-dot ${i<lvl?'on':''}\"></span>`; }\n    return `<div class=\"scent-row\"><span class=\"scent-label\">${esc(n.label)}</span><span class=\"scent-meter\">${dots}</span></div>`;\n  }).join('');\n  return `<div class=\"scent\"><span class=\"wg-label\">perfil olfativo</span>${rows}</div>`;\n}\n\nfunction heritageCard(d2,part){\n  return `<div class=\"heritage\"><span class=\"wg-label\">✦ herança Granado</span>${part.title?`<h4 class=\"her-title\">${esc(part.title)}</h4>`:''}<p class=\"her-text\">${md(part.text||'')}</p></div>`;\n}\n\nfunction renderPart(d2, part, byId, idx){\n  if(!part || !part.type) return '';\n  const delay = `style=\"animation-delay:${idx*90}ms\"`;\n  if(part.type==='text'){\n    if(!part.md) return '';\n    return `<div class=\"blk txt\" ${delay}><div class=\"bubble\">${md(part.md)}</div></div>`;\n  }\n  const w = part.widget;\n  let inner='';\n  if(w==='productRail'){\n    const cards=(part.ids||[]).map(id=>byId[id]).filter(Boolean).map(p=>productCard(d2,p)).join('');\n    if(!cards) return '';\n    return `<div class=\"blk rail\" ${delay}>${part.title?`<div class=\"rail-head\"><h3 class=\"rail-title\">${esc(part.title)}</h3></div>`:''}<div class=\"rail-scroll\">${cards}</div></div>`;\n  }\n  if(w==='productCard'){\n    const p=byId[part.id]; if(!p) return '';\n    return `<div class=\"blk\" ${delay}>${heroCard(d2,p)}</div>`;\n  }\n  if(w==='kit'){ inner=kitCard(d2,part,byId); return inner?`<div class=\"blk\" ${delay}>${inner}</div>`:''; }\n  if(w==='boxOffer'){ return `<div class=\"blk\" ${delay}>${boxCard(d2)}</div>`; }\n  if(w==='valueMath'){ inner=valueMathCard(d2,part); return inner?`<div class=\"blk\" ${delay}>${inner}</div>`:''; }\n  if(w==='scentProfile'){ inner=scentCard(d2,part); return inner?`<div class=\"blk\" ${delay}>${inner}</div>`:''; }\n  if(w==='compare'){ inner=compareCard(d2,part,byId); return inner?`<div class=\"blk\" ${delay}>${inner}</div>`:''; }\n  if(w==='whyChips'){\n    const chips=(part.chips||[]).map(c=>`<span class=\"why\">${esc(c)}</span>`).join('');\n    if(!chips) return '';\n    return `<div class=\"blk whyrow\" ${delay}>${chips}</div>`;\n  }\n  if(w==='heritage'){ return `<div class=\"blk\" ${delay}>${heritageCard(d2,part)}</div>`; }\n  if(w==='followUps'){\n    const chips=(part.chips||[]).map(c=>`<button class=\"fu\" data-followup=\"${esc(c)}\">${esc(c)} <i>→</i></button>`).join('');\n    if(!chips) return '';\n    return `<div class=\"blk furow\" ${delay}>${chips}</div>`;\n  }\n  return '';\n}\n\nfunction bagDrawer(d2){\n  const cart = d2.state.cart || {};\n  const items = Object.keys(cart).map(k=>cart[k]);\n  const total = items.reduce((a,i)=>a+(Number(i.product.price)||0)*i.qty,0);\n  const open = !!d2.state.bagOpen;\n  const rows = items.length ? items.map(i=>`<div class=\"bag-row\" data-key=\"${esc(i.product.productId)}\">\n      <div class=\"bag-thumb\"><div class=\"pcard-img\" style=\"background-image:url('${esc(i.product.imageUrl||'')}')\"></div></div>\n      <div class=\"bag-info\"><span class=\"bag-name\">${esc(i.product.name)}</span><span class=\"bag-unit\">${money(d2,i.product.price)}${i.qty>1?` · ${i.qty}×`:''}</span></div>\n      <div class=\"bag-qty\">\n        <button class=\"qbtn qdec\" data-dec=\"${esc(i.product.productId)}\">−</button>\n        <span>${i.qty}</span>\n        <button class=\"qbtn qinc\" data-inc=\"${esc(i.product.productId)}\">+</button>\n      </div>\n    </div>`).join('') : `<div class=\"bag-empty\">Sua sacola está vazia.<br/>Explore a vitrine ou peça uma sugestão.</div>`;\n  return `<div class=\"bag-scrim ${open?'on':''}\" data-bag-close></div>\n    <aside class=\"bag ${open?'on':''}\">\n      <div class=\"bag-top\"><h3>Sua sacola</h3><button class=\"bag-x\" data-bag-close>✕</button></div>\n      <div class=\"bag-list\">${rows}</div>\n      <div class=\"bag-foot\">\n        <div class=\"bag-sum\"><span>Subtotal</span><b>${money(d2,total)}</b></div>\n        <button class=\"bag-checkout\" ${items.length?'':'disabled'} data-checkout>Finalizar compra</button>\n        <span class=\"bag-fine\">frete e pagamento na próxima etapa</span>\n      </div>\n    </aside>`;\n}\n\nexport default {\n  css: `\n  :host{--green:#004030;--green-2:#025a44;--sage:#81ada2;--bg:#f6f3f8;--bg-2:#f9f9f9;--card:#ffffff;--ink:#1d1d1d;--ink-2:#4a4a4a;--gold:#e08e00;--yellow:#ffee9c;--yellow-2:#ffeaaf;--red:#ff0000;--line:rgba(29,29,29,.12)}\n  .wrap{height:100%;display:flex;flex-direction:column;overflow:hidden;box-sizing:border-box;color:var(--ink);background:var(--bg);\n    font-family:'Matria',Georgia,'Iowan Old Style','Palatino Linotype',serif;-webkit-font-smoothing:antialiased}\n  .wrap *{box-sizing:border-box}\n  .disp{font-family:'Granado','Matria',Georgia,serif}\n\n  /* HEADER */\n  .bar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:16px;padding:12px 22px;\n    background:var(--green);color:#fff;box-shadow:0 6px 24px -14px rgba(0,64,48,.9)}\n  .bar-brand{font-family:'Granado','Matria',serif;font-size:26px;letter-spacing:.02em;line-height:1;color:#fff;cursor:pointer}\n  .bar-brand span{font-family:'Matria',serif;font-size:9px;letter-spacing:.34em;display:block;color:var(--sage);margin-top:2px}\n  .bar-search{flex:1;display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.22);\n    border-radius:10px;padding:8px 12px;max-width:520px;margin:0 auto}\n  .bar-search input{flex:1;border:0;background:transparent;outline:0;color:#fff;font-family:inherit;font-size:14px}\n  .bar-search input::placeholder{color:rgba(255,255,255,.6)}\n  .bar-search .mag{color:var(--sage);font-size:15px}\n  .bag-btn{cursor:pointer;border:0;font-family:inherit;font-size:13.5px;font-weight:600;color:var(--green);\n    background:var(--yellow);border-radius:10px;padding:9px 15px;display:inline-flex;align-items:center;gap:7px;white-space:nowrap}\n  .bag-btn:hover{background:var(--yellow-2)}\n  .bag-btn b{background:var(--green);color:#fff;border-radius:999px;min-width:19px;height:19px;display:inline-grid;place-items:center;font-size:11px;padding:0 5px}\n\n  /* HERO */\n  .hero{position:relative;text-align:center;padding:52px 24px 40px;\n    background:radial-gradient(1100px 460px at 50% -30%, rgba(2,90,68,.14), transparent 60%),linear-gradient(180deg,#fff,var(--bg))}\n  .hero-kick{font-size:11px;letter-spacing:.4em;text-transform:uppercase;color:var(--gold);font-weight:700}\n  .hero-word{font-family:'Granado','Matria',serif;font-size:clamp(52px,10vw,110px);line-height:.9;color:var(--green);margin:8px 0 4px;letter-spacing:.01em}\n  .hero-sub{max-width:560px;margin:6px auto 0;font-size:16px;line-height:1.6;color:var(--ink-2)}\n  .ask{display:flex;align-items:center;gap:10px;width:min(660px,94vw);margin:22px auto 0;background:var(--card);\n    border:1px solid var(--line);border-radius:14px;padding:8px 8px 8px 16px;box-shadow:0 20px 48px -26px rgba(0,64,48,.5)}\n  .ask .mag{font-size:18px;color:var(--green-2);flex:0 0 auto}\n  .ask input{flex:1;border:0;background:transparent;outline:0;font-size:16px;color:var(--ink);font-family:inherit;padding:9px 2px}\n  .ask input::placeholder{color:#9aa39f}\n  .ask button{flex:0 0 auto;border:0;cursor:pointer;font-family:inherit;font-size:14px;font-weight:600;letter-spacing:.01em;\n    color:#fff;background:linear-gradient(180deg,var(--green-2),var(--green));padding:12px 22px;border-radius:10px}\n  .ask button:hover{filter:brightness(1.08)}\n  .presets{display:flex;flex-wrap:wrap;gap:9px;justify-content:center;max-width:680px;margin:14px auto 0}\n  .preset{cursor:pointer;font-size:13.5px;color:var(--green);background:#fff;border:1px solid var(--line);border-radius:999px;padding:8px 15px}\n  .preset:hover{border-color:var(--gold);background:var(--yellow)}\n  .hero-note{display:inline-flex;align-items:center;gap:8px;font-size:12px;color:var(--ink-2);margin-top:16px}\n  .hero-note .dot{width:7px;height:7px;border-radius:50%;background:var(--green-2)}\n\n  /* STOREFRONT */\n  .shop{max-width:1120px;margin:0 auto;padding:8px 22px 60px;display:flex;flex-direction:column;gap:38px}\n  .rail{display:flex;flex-direction:column;gap:2px}\n  .rail-head{display:flex;align-items:baseline;gap:12px;margin:6px 0 12px;flex-wrap:wrap}\n  .rail-title{font-family:'Matria',serif;font-weight:700;font-size:22px;color:var(--green);margin:0}\n  .rail-sub{font-size:13px;color:var(--ink-2)}\n  .rail-scroll{display:flex;gap:16px;overflow-x:auto;padding:4px 2px 16px;scroll-snap-type:x mandatory}\n  .rail-scroll::-webkit-scrollbar{height:8px}\n  .rail-scroll::-webkit-scrollbar-thumb{background:var(--sage);border-radius:8px}\n  .pcard{scroll-snap-align:start;flex:0 0 218px;width:218px;background:var(--card);border:1px solid var(--line);border-radius:14px;overflow:hidden;\n    box-shadow:0 16px 36px -28px rgba(0,64,48,.6);transition:transform .22s,box-shadow .22s;display:flex;flex-direction:column}\n  .pcard:hover{transform:translateY(-4px);box-shadow:0 24px 46px -26px rgba(0,64,48,.6)}\n  .pcard-imgwrap{position:relative;height:196px;background:linear-gradient(160deg,#eef4f1,#dfeae5)}\n  .pcard-mono{position:absolute;inset:0;display:grid;place-items:center;font-family:'Granado','Matria',serif;font-size:56px;color:rgba(2,90,68,.28)}\n  .pcard-mono.sm{font-size:20px}\n  .pcard-img{position:absolute;inset:0;background-size:cover;background-position:center}\n  .pcard-disc{position:absolute;top:10px;left:10px;background:var(--red);color:#fff;font-size:11px;font-weight:700;padding:4px 8px;border-radius:6px}\n  .pcard-body{padding:12px 13px 14px;display:flex;flex-direction:column;gap:6px;flex:1}\n  .pcard-line{font-size:9.5px;letter-spacing:.18em;text-transform:uppercase;color:var(--gold);font-weight:700}\n  .pcard-name{font-family:'Matria',serif;font-weight:400;font-size:15px;line-height:1.28;color:var(--ink);margin:0;min-height:38px}\n  .pcard-price{display:flex;align-items:baseline;gap:9px;margin-top:auto}\n  .pcard-now{font-size:18px;font-weight:700;color:var(--green)}\n  .pcard-was{font-size:13px;color:#9aa39f;text-decoration:line-through}\n  .add-bag{margin-top:9px;cursor:pointer;border:1px solid var(--green);background:#fff;color:var(--green);font-family:inherit;\n    font-size:13px;font-weight:600;border-radius:9px;padding:9px 10px;display:flex;align-items:center;justify-content:center;gap:6px;transition:.16s}\n  .add-bag:hover{background:var(--green);color:#fff}\n  .add-bag .add-ic{font-size:15px;line-height:1}\n\n  /* BOX CARD */\n  .boxcard{display:flex;gap:24px;flex-wrap:wrap;align-items:center;justify-content:space-between;\n    background:linear-gradient(135deg,var(--green),var(--green-2));color:#fff;border-radius:20px;padding:28px 30px;position:relative;overflow:hidden}\n  .boxcard:before{content:\"\";position:absolute;right:-40px;top:-40px;width:220px;height:220px;border-radius:50%;background:rgba(129,173,162,.22)}\n  .box-left{flex:1;min-width:260px;position:relative;z-index:1}\n  .box-kicker{font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--yellow)}\n  .box-title{font-family:'Granado','Matria',serif;font-size:34px;margin:6px 0 8px;color:#fff}\n  .box-desc{font-size:14.5px;line-height:1.55;color:rgba(255,255,255,.86);max-width:440px;margin:0 0 12px}\n  .box-bul{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:6px 18px}\n  .box-bul li{font-size:13px;color:rgba(255,255,255,.92);padding-left:16px;position:relative}\n  .box-bul li:before{content:\"✦\";position:absolute;left:0;color:var(--yellow)}\n  .box-right{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:8px;text-align:center;min-width:180px}\n  .box-off{background:var(--yellow);color:var(--green);font-weight:700;font-size:18px;border-radius:999px;padding:6px 16px}\n  .box-price{display:flex;flex-direction:column;line-height:1.1}\n  .box-price b{font-family:'Granado','Matria',serif;font-size:34px}\n  .box-price span{font-size:11px;color:rgba(255,255,255,.7);letter-spacing:.04em}\n  .box-cta{cursor:pointer;border:0;font-family:inherit;font-size:14px;font-weight:700;color:var(--green);background:var(--yellow);\n    padding:13px 26px;border-radius:11px}\n  .box-cta:hover{background:var(--yellow-2)}\n  .box-fine{font-size:11px;color:rgba(255,255,255,.6)}\n\n  /* RESULTS */\n  .results{max-width:960px;margin:0 auto;padding:22px 22px 56px;display:flex;flex-direction:column;gap:30px}\n  .results-top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}\n  .back-shop{cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--green);font-family:inherit;font-size:13px;font-weight:600;border-radius:999px;padding:8px 16px}\n  .back-shop:hover{border-color:var(--gold)}\n  .turn{display:flex;flex-direction:column;gap:16px}\n  .user-q{align-self:flex-end;max-width:80%;font-size:16px;color:var(--green);background:#fff;border:1px solid var(--line);\n    border-right:3px solid var(--gold);border-radius:12px;padding:11px 16px}\n  .answer{display:flex;flex-direction:column;gap:18px}\n  .blk{animation:rise .5s both cubic-bezier(.2,.7,.2,1)}\n  @keyframes rise{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n  .say-attr{display:flex;align-items:center;gap:8px;font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--gold);font-weight:700;margin-bottom:2px}\n  .say-text{font-family:'Matria',serif;font-size:clamp(20px,3.2vw,27px);line-height:1.34;color:var(--green);margin:0;max-width:660px}\n  .whyrow{display:flex;flex-wrap:wrap;align-items:center;gap:9px}\n  .why-label,.fu-label{font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-2);margin-right:2px}\n  .why{font-size:12.5px;font-weight:600;color:var(--green);background:var(--yellow);border:1px solid rgba(224,142,0,.4);border-radius:999px;padding:6px 13px}\n\n  .kit{background:linear-gradient(180deg,var(--green),var(--green-2));color:#fff;border-radius:18px;padding:24px 24px 20px;\n    border:1px solid rgba(129,173,162,.4);box-shadow:0 30px 60px -34px rgba(0,64,48,.9);position:relative;overflow:hidden}\n  .kit-kicker{font-size:10px;letter-spacing:.3em;text-transform:uppercase;color:var(--yellow)}\n  .kit-title{font-family:'Granado','Matria',serif;font-size:26px;margin:6px 0 0;color:#fff}\n  .kit-story{font-size:15px;line-height:1.5;color:rgba(255,255,255,.85);margin:10px 0 14px;max-width:640px}\n  .kit-items{display:flex;flex-direction:column;border-top:1px solid rgba(129,173,162,.3)}\n  .kit-item{display:flex;align-items:center;gap:14px;padding:11px 2px;border-bottom:1px solid rgba(129,173,162,.2)}\n  .kit-thumb{position:relative;width:46px;height:46px;flex:0 0 auto;border-radius:9px;overflow:hidden;background:rgba(255,255,255,.1)}\n  .kit-meta{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}\n  .kit-name{font-size:14px;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .kit-line{font-size:9.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--yellow)}\n  .kit-price{font-size:14px;font-weight:700;color:#fff;flex:0 0 auto}\n  .kit-foot{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:16px;flex-wrap:wrap}\n  .kit-total{display:flex;flex-direction:column;gap:1px}\n  .kit-total span{font-size:11px;color:var(--yellow)}\n  .kit-total b{font-family:'Granado','Matria',serif;font-size:26px;color:#fff}\n  .kit-cta{cursor:pointer;border:0;font-family:inherit;font-size:14px;font-weight:700;color:var(--green);background:var(--yellow);padding:13px 22px;border-radius:11px}\n  .kit-cta:hover{background:var(--yellow-2)}\n\n  .furow{display:flex;flex-wrap:wrap;align-items:center;gap:10px}\n  .fu{cursor:pointer;font-size:13.5px;color:var(--green);background:#fff;border:1px solid var(--line);border-radius:999px;padding:9px 16px;display:inline-flex;align-items:center;gap:7px}\n  .fu i{font-style:normal;color:var(--gold);transition:transform .2s}\n  .fu:hover{border-color:var(--gold)}\n  .fu:hover i{transform:translateX(3px)}\n\n  /* THINKING + skeleton */\n  .thinking{display:flex;flex-direction:column;gap:12px}\n  .th-steps{display:flex;flex-direction:column;gap:8px}\n  .th-step{display:flex;align-items:center;gap:11px;font-size:14px;color:var(--ink-2);opacity:0;animation:stepin .5s forwards}\n  .th-step:nth-child(1){animation-delay:.05s}.th-step:nth-child(2){animation-delay:.6s}.th-step:nth-child(3){animation-delay:1.2s}\n  .th-step:before{content:\"\";width:9px;height:9px;border-radius:50%;background:var(--green-2);animation:pulse 1.4s infinite}\n  @keyframes stepin{from{opacity:0;transform:translateX(-6px)}to{opacity:1}}\n  @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(2,90,68,.5)}70%{box-shadow:0 0 0 9px rgba(2,90,68,0)}100%{box-shadow:0 0 0 0 rgba(2,90,68,0)}}\n  .th-bar{height:3px;border-radius:3px;background:rgba(2,90,68,.16);overflow:hidden;max-width:420px}\n  .th-bar i{display:block;height:100%;width:40%;background:linear-gradient(90deg,transparent,var(--green-2),transparent);animation:sweep 1.3s infinite}\n  @keyframes sweep{from{transform:translateX(-120%)}to{transform:translateX(320%)}}\n  .sk-rail{display:flex;gap:16px;overflow:hidden}\n  .sk-card{flex:0 0 218px;height:300px;border-radius:14px;background:linear-gradient(100deg,#eef2f0 30%,#e2ebe7 50%,#eef2f0 70%);background-size:200% 100%;animation:sweep2 1.3s infinite}\n  @keyframes sweep2{from{background-position:180% 0}to{background-position:-80% 0}}\n\n  .err{max-width:960px;margin:0 auto;color:var(--red);font-size:14px;padding:0 22px 10px}\n\n  /* BAG DRAWER */\n  .bag-scrim{position:fixed;inset:0;background:rgba(0,32,24,.42);opacity:0;pointer-events:none;transition:opacity .25s;z-index:40}\n  .bag-scrim.on{opacity:1;pointer-events:auto}\n  .bag{position:fixed;top:0;right:0;height:100%;width:min(400px,92vw);background:var(--bg-2);z-index:50;\n    display:flex;flex-direction:column;transform:translateX(102%);transition:transform .3s cubic-bezier(.3,.7,.2,1);box-shadow:-20px 0 50px -20px rgba(0,64,48,.5)}\n  .bag.on{transform:none}\n  .bag-top{display:flex;align-items:center;justify-content:space-between;padding:18px 20px;background:var(--green);color:#fff}\n  .bag-top h3{font-family:'Granado','Matria',serif;font-size:22px;margin:0}\n  .bag-x{cursor:pointer;border:0;background:transparent;color:#fff;font-size:18px}\n  .bag-list{flex:1;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:10px}\n  .bag-empty{color:var(--ink-2);text-align:center;padding:50px 20px;line-height:1.6;font-size:15px}\n  .bag-row{display:flex;align-items:center;gap:12px;background:#fff;border:1px solid var(--line);border-radius:12px;padding:10px}\n  .bag-thumb{width:52px;height:52px;border-radius:8px;overflow:hidden;position:relative;flex:0 0 auto;background:#eef4f1}\n  .bag-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}\n  .bag-name{font-size:13.5px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .bag-unit{font-size:12px;color:var(--ink-2)}\n  .bag-qty{display:flex;align-items:center;gap:8px}\n  .qbtn{cursor:pointer;width:26px;height:26px;border-radius:7px;border:1px solid var(--line);background:#fff;color:var(--green);font-size:15px;line-height:1}\n  .qbtn:hover{background:var(--yellow)}\n  .bag-foot{padding:16px 20px;border-top:1px solid var(--line);background:#fff;display:flex;flex-direction:column;gap:10px}\n  .bag-sum{display:flex;align-items:baseline;justify-content:space-between}\n  .bag-sum span{font-size:13px;color:var(--ink-2)}\n  .bag-sum b{font-family:'Granado','Matria',serif;font-size:26px;color:var(--green)}\n  .bag-checkout{cursor:pointer;border:0;font-family:inherit;font-size:15px;font-weight:700;color:#fff;background:linear-gradient(180deg,var(--green-2),var(--green));padding:14px;border-radius:11px}\n  .bag-checkout:disabled{opacity:.4;cursor:not-allowed}\n  .bag-fine{font-size:11px;color:var(--ink-2);text-align:center}\n\n  /* CHAT SHELL */\n  .bar{justify-content:space-between}\n  .conv{flex:1;overflow-y:auto;background:radial-gradient(900px 380px at 50% -12%, rgba(2,90,68,.08), transparent 60%),var(--bg)}\n  .conv-inner{max-width:760px;margin:0 auto;padding:26px 20px 34px;display:flex;flex-direction:column;gap:26px}\n  .turn{display:flex;flex-direction:column;gap:14px}\n  .answer{display:flex;flex-direction:column;gap:16px}\n  .blk{animation:rise .5s both cubic-bezier(.2,.7,.2,1)}\n  .blk.txt{display:flex}\n  .txt .bubble{max-width:660px;background:#fff;border:1px solid var(--line);border-radius:4px 16px 16px 16px;padding:14px 18px;font-size:16px;line-height:1.55;color:var(--ink);box-shadow:0 14px 30px -26px rgba(0,64,48,.6)}\n  .txt .bubble b{color:var(--green);font-weight:700}\n  .wl-hi{font-family:'Granado','Matria',serif;font-size:24px;color:var(--green);margin:0 0 6px}\n  .user-q{align-self:flex-end;max-width:82%;font-size:15.5px;color:#fff;background:linear-gradient(180deg,var(--green-2),var(--green));border-radius:16px 16px 4px 16px;padding:11px 16px;box-shadow:0 12px 26px -20px rgba(0,64,48,.9)}\n  .wg-label{display:block;font-size:10px;letter-spacing:.24em;text-transform:uppercase;color:var(--gold);font-weight:700;margin-bottom:8px}\n  .hcard{display:flex;gap:16px;background:var(--card);border:1px solid var(--line);border-radius:16px;overflow:hidden;padding:14px;box-shadow:0 18px 40px -28px rgba(0,64,48,.6);max-width:520px}\n  .hcard-imgwrap{position:relative;width:150px;height:180px;flex:0 0 auto;border-radius:12px;overflow:hidden;background:linear-gradient(160deg,#eef4f1,#dfeae5)}\n  .hcard-body{display:flex;flex-direction:column;gap:7px;flex:1;min-width:0;padding:4px 4px 2px}\n  .hcard-name{font-family:'Matria',serif;font-weight:400;font-size:19px;line-height:1.25;color:var(--ink);margin:0}\n  .cmp-grid{display:flex;gap:14px;overflow-x:auto;padding-bottom:6px}\n  .cmp-col{flex:1 1 0;min-width:180px;background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px;display:flex;flex-direction:column;gap:6px}\n  .cmp-imgwrap{position:relative;height:150px;border-radius:10px;overflow:hidden;background:linear-gradient(160deg,#eef4f1,#dfeae5)}\n  .cmp-name{font-family:'Matria',serif;font-weight:400;font-size:15px;line-height:1.25;margin:0;min-height:36px;color:var(--ink)}\n  .cmp-cat{font-size:11.5px;color:var(--ink-2)}\n  .vmath{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;max-width:440px}\n  .vmath-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column}\n  .vmath-list li{display:flex;align-items:baseline;justify-content:space-between;gap:14px;padding:8px 0;border-bottom:1px dashed var(--line);font-size:14px;color:var(--ink-2)}\n  .vmath-list li:last-child{border-bottom:0;color:var(--green);font-weight:700}\n  .vmath-list li b{font-family:'Matria',serif;font-size:16px;color:var(--green)}\n  .scent{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px;max-width:420px;display:flex;flex-direction:column;gap:9px}\n  .scent-row{display:flex;align-items:center;justify-content:space-between;gap:14px}\n  .scent-label{font-size:13.5px;color:var(--ink)}\n  .scent-meter{display:flex;gap:5px}\n  .scent-dot{width:11px;height:11px;border-radius:50%;background:rgba(2,90,68,.14)}\n  .scent-dot.on{background:var(--green-2)}\n  .heritage{border-left:3px solid var(--gold);background:linear-gradient(90deg,rgba(224,142,0,.06),transparent);border-radius:0 12px 12px 0;padding:12px 18px;max-width:640px}\n  .her-title{font-family:'Granado','Matria',serif;font-size:20px;color:var(--green);margin:2px 0 6px}\n  .her-text{font-size:15px;line-height:1.55;color:var(--ink-2);margin:0}\n  .thinking{display:flex;align-items:center;gap:12px;padding:6px 2px}\n  .th-glyph{width:34px;height:34px;border-radius:50%;background:var(--green);color:#fff;display:grid;place-items:center;font-size:19px;flex:0 0 auto}\n  .th-dots{display:flex;gap:5px}\n  .th-dots span{width:8px;height:8px;border-radius:50%;background:var(--green-2);animation:bounce 1.2s infinite}\n  .th-dots span:nth-child(2){animation-delay:.18s}.th-dots span:nth-child(3){animation-delay:.36s}\n  @keyframes bounce{0%,80%,100%{opacity:.25;transform:translateY(0)}40%{opacity:1;transform:translateY(-5px)}}\n  .th-cap{font-size:13px;color:var(--ink-2);font-style:italic}\n  .composer{flex:0 0 auto;background:rgba(246,243,248,.92);backdrop-filter:blur(8px);border-top:1px solid var(--line);padding:12px 20px 14px}\n  .composer .ask{margin:0 auto;width:100%;max-width:720px;box-shadow:0 14px 34px -26px rgba(0,64,48,.5)}\n  .chat-input{flex:1;border:0;background:transparent;outline:0;font-size:16px;color:var(--ink);font-family:inherit;padding:10px 4px}\n  .chat-input::placeholder{color:#9aa39f}\n  .chat-send{flex:0 0 auto;border:0;cursor:pointer;font-family:inherit;font-size:20px;font-weight:600;line-height:1;color:#fff;background:linear-gradient(180deg,var(--green-2),var(--green));width:46px;height:46px;border-radius:11px}\n  .chat-send:hover{filter:brightness(1.08)}\n  .composer-fine{text-align:center;font-size:11px;color:var(--ink-2);margin-top:7px;letter-spacing:.02em}\n\n  @media(max-width:560px){.bar-brand{font-size:22px}.bar-search{display:none}.box-bul{grid-template-columns:1fr}.conv-inner{padding:18px 14px 26px}.composer{padding:10px 12px 12px}.hcard{flex-direction:column}.hcard-imgwrap{width:100%;height:200px}}\n  `,\n  render(d2){\n    const cart = d2.state.cart || {};\n    const bagCount = Object.keys(cart).reduce((a,k)=>a+cart[k].qty,0);\n    const turns = d2.state.turns || [];\n    const loading = !!d2.state.loading;\n\n    const header = `<header class=\"bar\">\n      <div class=\"bar-brand disp\" data-home>Granado<span>Tradição desde 1870</span></div>\n      <button class=\"bag-btn\" data-bag-open>🛍 Sacola${bagCount?` <b>${bagCount}</b>`:''}</button>\n    </header>`;\n\n    const starters = ['um presente amadeirado até R$200','um perfume que lembra o Rio antigo','cuidado para pele ressecada','como funciona a Granado Box'];\n    const welcome = `<div class=\"turn\" data-key=\"welcome\">\n      <div class=\"answer\">\n        <div class=\"blk txt\"><div class=\"bubble\">\n          <p class=\"wl-hi\">Bem-vindo à Granado.</p>\n          Posso ajudar a encontrar um <b>presente</b>, um <b>perfume</b>, um <b>cuidado para a pele</b> ou os <b>cabelos</b> — ou falar sobre a nossa <b>Box</b>. Me conte o que você procura.\n        </div></div>\n        <div class=\"blk furow\">${starters.map(c=>`<button class=\"fu\" data-followup=\"${esc(c)}\">${esc(c)} <i>→</i></button>`).join('')}</div>\n      </div>\n    </div>`;\n\n    const feed = turns.map((t,ti)=>{\n      const byId={}; (t.products||[]).forEach(p=>{byId[p.productId]=p;});\n      const parts=(t.parts||[]).map((p,pi)=>renderPart(d2,p,byId,pi)).join('');\n      return `<div class=\"turn\" data-key=\"turn-${ti}\">\n        <div class=\"user-q\">${esc(t.q)}</div>\n        <div class=\"answer\">${parts}</div>\n      </div>`;\n    }).join('');\n\n    const think = loading ? `<div class=\"turn\" data-key=\"thinking\">\n      <div class=\"user-q\">${esc(d2.state.pendingQ||'')}</div>\n      <div class=\"answer\"><div class=\"thinking\">\n        <span class=\"th-glyph disp\">G</span>\n        <div class=\"th-dots\"><span></span><span></span><span></span></div>\n        <span class=\"th-cap\">consultando o acervo Granado…</span>\n      </div></div>\n    </div>` : '';\n\n    const err = d2.state.error ? `<div class=\"err\">${esc(d2.state.error)}</div>` : '';\n\n    const composer = `<div class=\"composer\">\n      <div class=\"ask\">\n        <input class=\"chat-input\" placeholder=\"Escreva para o assistente Granado…\" autocomplete=\"off\"/>\n        <button class=\"chat-send\" aria-label=\"Enviar\">→</button>\n      </div>\n      <div class=\"composer-fine\">assistente de compras · catálogo real Granado</div>\n    </div>`;\n\n    return `<div class=\"wrap\">${header}\n      <div class=\"conv\" data-conv>\n        <div class=\"conv-inner\">\n          ${turns.length?'':welcome}\n          ${feed}${think}${err}\n        </div>\n      </div>\n      ${composer}\n      ${bagDrawer(d2)}\n    </div>`;\n  },\n  mounted(d2){\n    if(!d2.state._fontsTried){\n      d2.state._fontsTried = true;\n      d2.rush('fontEmbed').invoke({}).then(res=>{\n        const f = (res && res.granado) ? res : ((res && res.data) ? res.data : res);\n        if(!f || !f.granado) return;\n        const mk = (arr)=>{ try{ return URL.createObjectURL(new Blob([new Uint8Array(arr)],{type:'font/woff2'})); }catch(e){ return null; } };\n        const defs = [['Granado',f.granado,'400'],['Matria',f.matriaR,'400'],['Matria',f.matriaB,'700']];\n        defs.forEach(([fam,arr,wt])=>{\n          if(!arr || !arr.length) return;\n          const url = mk(arr); if(!url) return;\n          try{\n            const ff = new FontFace(fam, 'url('+url+')', {weight:wt, style:'normal', display:'swap'});\n            ff.load().then(loaded=>{ (document.fonts||d2.root.ownerDocument.fonts).add(loaded); }).catch(()=>{});\n          }catch(e){}\n        });\n      }).catch(e=>{ d2.log('font error', e); });\n    }\n\n    const indexProducts = (arr)=>{\n      const idx = d2.state.catalogById || {};\n      (arr||[]).forEach(p=>{ if(p&&p.productId) idx[p.productId]=p; });\n      d2.state.catalogById = idx;\n    };\n\n    const addToBag = (product, qty)=>{\n      if(!product || !product.productId) return;\n      const cart = Object.assign({}, d2.state.cart||{});\n      const cur = cart[product.productId];\n      cart[product.productId] = { product, qty: (cur? cur.qty:0) + (qty||1) };\n      d2.state.cart = cart;\n      d2.state.bagOpen = true;\n    };\n    const changeQty = (id, delta)=>{\n      const cart = Object.assign({}, d2.state.cart||{});\n      const cur = cart[id]; if(!cur) return;\n      const q = cur.qty + delta;\n      if(q<=0){ delete cart[id]; } else { cart[id] = { product:cur.product, qty:q }; }\n      d2.state.cart = cart;\n    };\n\n    const scrollBottom = ()=>{ setTimeout(()=>{ const c=d2.$('[data-conv]'); if(c) c.scrollTop=c.scrollHeight; },60); };\n    const send = async (text)=>{\n      const q=(text||'').trim();\n      if(!q || d2.state.loading) return;\n      d2.state.error=null;\n      d2.state.pendingQ=q;\n      d2.state.loading=true;\n      scrollBottom();\n      const history=[];\n      (d2.state.turns||[]).forEach(t=>{\n        history.push({ role:'user', content:t.q });\n        const txt=(t.parts||[]).filter(p=>p&&p.type==='text').map(p=>p.md).join(' ');\n        history.push({ role:'assistant', content: txt || '[sugestões de produtos]' });\n      });\n      try{\n        const res = await d2.rush('farmaceutico').invoke({ message:q, history });\n        const payload = (res && res.parts) ? res : ((res && res.data) ? res.data : (res||{}));\n        indexProducts(payload.products);\n        const turns = d2.state.turns || [];\n        d2.state.turns = [...turns, { q, parts:payload.parts||[], products:payload.products||[] }];\n      }catch(e){\n        d2.state.error = 'Não consegui responder agora. Tente novamente em instantes.';\n        d2.log('chat error', e);\n      }finally{\n        d2.state.loading=false;\n        d2.state.pendingQ=null;\n        scrollBottom();\n      }\n    };\n    const fromInput = (sel)=>{ const el=d2.$(sel); if(el){ const v=el.value; el.value=''; send(v); } };\n\n    d2.on('.chat-send','click',()=>fromInput('.chat-input'));\n    d2.on('.chat-input','keydown',(e,el)=>{ if(e.key==='Enter'){ const v=el.value; el.value=''; send(v); } });\n    d2.on('.fu','click',(e,el)=>send(el.dataset.followup));\n\n    d2.on('.add-bag','click',(e,el)=>{ const p=(d2.state.catalogById||{})[el.dataset.add]; addToBag(p,1); });\n    d2.on('.kit-cta','click',(e,el)=>{\n      const ids=(el.dataset.addkit||'').split(',').filter(Boolean);\n      const idx=d2.state.catalogById||{};\n      ids.forEach(id=>{ if(idx[id]) addToBag(idx[id],1); });\n    });\n    d2.on('.box-cta','click',()=>{ addToBag({productId:'granado-box',name:'Granado Box — assinatura bimestral',price:150,imageUrl:'',brandLine:'Assinatura'},1); });\n    d2.on('.qdec','click',(e,el)=>changeQty(el.dataset.dec,-1));\n    d2.on('.qinc','click',(e,el)=>changeQty(el.dataset.inc,1));\n\n    d2.on('.bag-btn','click',()=>{ d2.state.bagOpen=true; });\n    d2.on('.bag-x','click',()=>{ d2.state.bagOpen=false; });\n    d2.on('.bag-scrim','click',()=>{ d2.state.bagOpen=false; });\n    d2.on('[data-home]','click',()=>{ d2.state.turns=[]; d2.state.error=null; });\n    d2.on('.bag-checkout','click',()=>{});\n  }\n};\n"}}}}}}}},{"_id":"6a734398a9d7cf0ea9962175","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granadoHomePage","package":"granado-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    * { box-sizing: border-box; }\n    :host { display:block; width:100%; height:100%; }\n    .page { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; color:#1a1a1a; background:#ffffff; width:100%; height:100%; overflow-y:auto; overflow-x:hidden; -webkit-overflow-scrolling:touch; }\n    .display { font-family: Matria, Georgia, 'Times New Roman', serif; }\n\n    .topbar { background:#025A44; color:#fff; text-align:center; font-size:12px; letter-spacing:0.06em; padding:8px 16px; }\n\n    .header { display:flex; align-items:center; justify-content:space-between; padding:20px 64px; background:#fff; flex-wrap:wrap; gap:16px; border-bottom:1px solid #f0ece4; }\n    .logo-wrap { text-align:center; }\n    .logo { font-size:26px; font-weight:700; letter-spacing:0.12em; color:#004030; line-height:1; }\n    .tagline { font-size:10px; letter-spacing:0.25em; color:#6b7280; margin-top:2px; }\n    .nav { display:flex; gap:28px; }\n    .nav a { color:#1a1a1a; text-decoration:none; font-size:13px; font-weight:700; letter-spacing:0.06em; text-transform:uppercase; }\n    @media (max-width:640px) { .nav { display:none; } }\n\n    .hero { background:#f5f0e6; padding:72px 24px 64px; }\n    .hero-inner { max-width:1312px; margin:0 auto; }\n    .hero h1 { font-size:44px; font-weight:700; line-height:1.15; margin:0 0 20px; max-width:640px; color:#1a1a1a; text-transform:uppercase; letter-spacing:0.01em; }\n    .hero p { font-size:17px; line-height:1.5; color:#4b5563; margin:0 0 28px; max-width:560px; }\n    .cta { display:inline-block; background:#004030; color:#f5f0e6; font-weight:700; font-size:13px; letter-spacing:0.1em; text-transform:uppercase; padding:16px 36px; text-decoration:none; border:none; cursor:pointer; }\n    @media (max-width:480px) { .hero h1 { font-size:30px; } }\n\n    .badges { display:grid; grid-template-columns:repeat(auto-fit, minmax(160px,1fr)); gap:16px; padding:32px 64px; border-bottom:1px solid #f0ece4; }\n    .badge { text-align:center; font-size:12px; color:#374151; line-height:1.4; }\n    .badge .b-title { font-weight:700; color:#004030; letter-spacing:0.04em; text-transform:uppercase; font-size:12px; }\n\n    section.block { padding:56px 24px; }\n    .section-inner { max-width:1392px; margin:0 auto; }\n    h2.section-title { font-size:26px; font-weight:700; margin:0 0 32px; color:#1a1a1a; }\n    .categories { background:#f5f0e6; }\n\n    .grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(260px,1fr)); gap:24px; }\n    .card { border:1px solid #ece7dc; overflow:hidden; background:#fff; transition:box-shadow .2s ease; }\n    .card:hover { box-shadow:0 8px 24px rgba(0,0,0,0.08); }\n    .card img { width:100%; height:220px; object-fit:contain; display:block; background:#faf8f3; padding:12px; }\n    .card .body { padding:16px 20px 20px; }\n    .card .name { font-size:15px; font-weight:600; margin:0 0 6px; color:#1a1a1a; }\n    .card .price { font-size:15px; font-weight:700; color:#004030; margin:0; }\n    .card a { text-decoration:none; color:inherit; }\n\n    .tile { border:1px solid #ece7dc; background:#fff; min-height:120px; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:700; letter-spacing:0.08em; text-transform:uppercase; color:#004030; text-align:center; padding:12px; }\n\n    .state-msg { padding:24px; color:#6b7280; font-size:15px; }\n\n    footer { background:#004030; color:#e8e4d8; padding:32px 64px; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:16px; font-size:13px; }\n    footer .links { display:flex; gap:24px; flex-wrap:wrap; }\n    footer a { color:#e8e4d8; text-decoration:none; }\n  `,\n  async mounted(d2) {\n    try {\n      const res = await fetch('https://granado-app.data2apis.com/rush/granado-home-feed', {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({}),\n      })\n      const json = await res.json()\n      const products = (json && json.data && json.data.products) || []\n      d2.setState({ products, status: products.length ? 'ready' : 'empty' })\n    } catch (e) {\n      d2.setState({ products: [], status: 'error' })\n    }\n  },\n  render(d2) {\n    const categories = ['Perfumaria', 'Sabonetes', 'Corpo e Banho', 'Casa']\n    const status = d2.state.status || 'loading'\n    const products = d2.state.products || []\n\n    const formatPrice = (v) => 'R$ ' + Number(v).toFixed(2).replace('.', ',')\n\n    const productsHtml = status === 'loading'\n      ? `<p class=\"state-msg\">Carregando produtos…</p>`\n      : status === 'error'\n        ? `<p class=\"state-msg\">Não foi possível carregar os produtos agora.</p>`\n        : status === 'empty'\n          ? `<p class=\"state-msg\">Nenhum produto encontrado.</p>`\n          : `<div class=\"grid\">\n              ${products.map(p => `\n                <a href=\"${p.productUrl || '#'}\" target=\"_blank\" rel=\"noopener\">\n                  <div class=\"card\" data-key=\"${p.name}\">\n                    <img src=\"${p.imageUrl}\" alt=\"${p.name}\" loading=\"lazy\" />\n                    <div class=\"body\">\n                      <p class=\"name\">${p.name}</p>\n                      <p class=\"price\">${formatPrice(p.price)}</p>\n                    </div>\n                  </div>\n                </a>\n              `).join('')}\n            </div>`\n\n    return `\n      <div class=\"page\">\n        <div class=\"topbar\">FRETE GRÁTIS ACIMA DE R$ 260 · TRADIÇÃO DESDE 1870</div>\n\n        <header class=\"header\">\n          <nav class=\"nav\">\n            <a href=\"#\">Perfumaria</a>\n            <a href=\"#\">Sabonetes</a>\n            <a href=\"#\">Casa</a>\n          </nav>\n          <div class=\"logo-wrap\">\n            <div class=\"logo display\">GRANADO</div>\n            <div class=\"tagline\">DESDE 1870</div>\n          </div>\n          <nav class=\"nav\">\n            <a href=\"#\">Ofertas</a>\n            <a href=\"#\">Sobre</a>\n          </nav>\n        </header>\n\n        <section class=\"hero\">\n          <div class=\"hero-inner\">\n            <h1 class=\"display\">Cuidado que vem de 1870</h1>\n            <p>Perfumaria, sabonetes e cuidados pessoais com tradição centenária, direto do Rio de Janeiro.</p>\n            <button class=\"cta\">Ver produtos</button>\n          </div>\n        </section>\n\n        <div class=\"badges\">\n          <div class=\"badge\"><div class=\"b-title\">Frete grátis</div>acima de R$ 260</div>\n          <div class=\"badge\"><div class=\"b-title\">Até 10x</div>sem juros</div>\n          <div class=\"badge\"><div class=\"b-title\">Entrega expressa</div>em até 1 dia útil</div>\n          <div class=\"badge\"><div class=\"b-title\">Clube Fidelidade</div>troque pontos por produtos</div>\n        </div>\n\n        <section class=\"block\">\n          <div class=\"section-inner\">\n            <h2 class=\"section-title\">Mais vendidos</h2>\n            ${productsHtml}\n          </div>\n        </section>\n\n        <section class=\"block categories\">\n          <div class=\"section-inner\">\n            <h2 class=\"section-title\">Categorias</h2>\n            <div class=\"grid\">\n              ${categories.map(c => `<div class=\"tile\" data-key=\"${c}\">${c}</div>`).join('')}\n            </div>\n          </div>\n        </section>\n\n        <footer>\n          <div>© 2026 Granado. Todos os direitos reservados.</div>\n          <div class=\"links\">\n            <a href=\"#\">Política de Privacidade</a>\n            <a href=\"#\">Termos de Uso</a>\n          </div>\n        </footer>\n      </div>\n    `\n  },\n}\n"}}}}}}}},{"_id":"6a7b3b2968641501c2dccdc9","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"b2bFaqsAdmin","package":"granado-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 CATEGORIES = [\n  { value: 'cadastro', label: 'Cadastro' },\n  { value: 'portfolio', label: 'Portfólio' },\n  { value: 'pedido_minimo', label: 'Pedido mínimo' },\n  { value: 'negociacao_alto_volume', label: 'Negociação alto volume' },\n  { value: 'avarias_devolucoes', label: 'Avarias/Devoluções' },\n]\n\nfunction esc(s) {\n  return (s ?? '').toString()\n    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\n}\n\nfunction catLabel(v) {\n  const found = CATEGORIES.find(c => c.value === v)\n  return found ? found.label : (v || '—')\n}\n\nfunction renderModal(record) {\n  const isEdit = !!record\n  const q = esc(record?.question)\n  const a = esc(record?.answer)\n  const kw = esc(record?.keywords)\n  const cat = record?.category || CATEGORIES[0].value\n  const active = record ? !!record.active : true\n  const options = CATEGORIES.map(c =>\n    `<option value=\"${c.value}\" ${c.value === cat ? 'selected' : ''}>${esc(c.label)}</option>`\n  ).join('')\n  return `\n    <div class=\"overlay\" id=\"overlay\">\n      <div class=\"modal\">\n        <div class=\"modalHdr\">\n          <h2>${isEdit ? 'Editar FAQ' : 'Nova FAQ'}</h2>\n          <button type=\"button\" class=\"closeBtn\" id=\"closeBtn\">&#10005;</button>\n        </div>\n        <div class=\"modalBody\">\n          <label>Pergunta</label>\n          <textarea data-field=\"question\" rows=\"2\" placeholder=\"Pergunta do cliente\">${q}</textarea>\n          <label>Resposta</label>\n          <textarea data-field=\"answer\" rows=\"4\" placeholder=\"Resposta padrão\">${a}</textarea>\n          <label>Categoria</label>\n          <select data-field=\"category\">${options}</select>\n          <label>Palavras-chave (separadas por vírgula)</label>\n          <input type=\"text\" data-field=\"keywords\" value=\"${kw}\" placeholder=\"ex: cadastro, registro, novo cliente\"/>\n          <label class=\"checkboxRow\"><input type=\"checkbox\" data-field=\"active\" ${active ? 'checked' : ''}/> Ativa</label>\n        </div>\n        <div class=\"modalFooter\">\n          <button type=\"button\" class=\"secondary\" id=\"cancelBtn\">Cancelar</button>\n          <button type=\"button\" class=\"primary\" id=\"saveBtn\">${isEdit ? 'Salvar alterações' : 'Criar FAQ'}</button>\n        </div>\n      </div>\n    </div>`\n}\n\nexport default {\n  css: `\n    :host{display:block;height:100%;width:100%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;}\n    .wrap{display:flex;flex-direction:column;height:100%;background:#f7f8fa;box-sizing:border-box;}\n    .hdr{display:flex;align-items:flex-start;justify-content:space-between;padding:20px 24px;border-bottom:1px solid #e6e8eb;background:#fff;}\n    .hdr h1{margin:0;font-size:20px;font-weight:700;color:#14181f;}\n    .muted{margin:4px 0 0;color:#767e8c;font-size:13px;}\n    .primary{background:#2f5bea;color:#fff;border:0;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;}\n    .primary:hover{background:#264ac4;}\n    .secondary{background:#fff;color:#333;border:1px solid #d7dae0;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:600;cursor:pointer;}\n    .toolbar{display:flex;flex-wrap:wrap;gap:12px;align-items:center;padding:16px 24px;background:#fff;border-bottom:1px solid #e6e8eb;}\n    .search{flex:1;min-width:220px;padding:9px 12px;border:1px solid #d7dae0;border-radius:8px;font-size:14px;}\n    .chips{display:flex;gap:8px;flex-wrap:wrap;}\n    .chip{padding:6px 12px;border-radius:999px;border:1px solid #d7dae0;background:#fff;color:#4a5261;font-size:12.5px;cursor:pointer;white-space:nowrap;}\n    .chip.active{background:#2f5bea;border-color:#2f5bea;color:#fff;}\n    .tableWrap{flex:1;overflow:auto;padding:0 24px 24px;}\n    table{width:100%;border-collapse:collapse;background:#fff;border-radius:10px;overflow:hidden;box-shadow:0 1px 2px rgba(20,24,31,.06);margin-top:16px;}\n    th{text-align:left;font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;color:#8a90a0;padding:12px 16px;border-bottom:1px solid #eceef1;}\n    td{padding:14px 16px;border-bottom:1px solid #eceef1;font-size:13.5px;color:#22262e;vertical-align:middle;}\n    .row{cursor:pointer;}\n    .row:hover{background:#f4f6fb;}\n    .q{max-width:420px;}\n    .pill{display:inline-block;padding:3px 10px;border-radius:999px;background:#eef1fb;color:#2f5bea;font-size:12px;font-weight:600;}\n    .dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px;}\n    .dot.on{background:#20b26c;}\n    .dot.off{background:#c8cdd6;}\n    .actions{white-space:nowrap;text-align:right;}\n    .actions button{border:0;background:transparent;font-size:15px;cursor:pointer;padding:4px 8px;border-radius:6px;}\n    .actions button:hover{background:#eceef1;}\n    .empty{text-align:center;color:#8a90a0;padding:40px 0;}\n    .overlay{position:fixed;inset:0;background:rgba(15,18,25,.45);display:flex;align-items:center;justify-content:center;z-index:1000;padding:20px;}\n    .modal{width:min(560px,100%);max-height:90vh;overflow:auto;background:#fff;border-radius:14px;box-shadow:0 24px 60px rgba(0,0,0,.25);}\n    .modalHdr{display:flex;align-items:center;justify-content:space-between;padding:18px 22px;border-bottom:1px solid #eceef1;}\n    .modalHdr h2{margin:0;font-size:16px;font-weight:700;}\n    .closeBtn{border:0;background:transparent;font-size:16px;cursor:pointer;color:#8a90a0;}\n    .modalBody{padding:18px 22px;display:flex;flex-direction:column;gap:12px;}\n    .modalBody label{font-size:12.5px;font-weight:600;color:#4a5261;}\n    .modalBody textarea,.modalBody select,.modalBody input[type=text]{padding:9px 12px;border:1px solid #d7dae0;border-radius:8px;font-size:13.5px;font-family:inherit;resize:vertical;}\n    .checkboxRow{display:flex;align-items:center;gap:8px;font-weight:600;color:#22262e;}\n    .modalFooter{display:flex;justify-content:flex-end;gap:10px;padding:16px 22px;border-top:1px solid #eceef1;}\n  `,\n  render(d2) {\n    const ds = d2.data()\n    if (!ds) return `<div style=\"padding:24px;font:14px sans-serif;color:#c00\">dataset param not bound.</div>`\n    if (ds.loading) return `<div class=\"wrap\"><p class=\"muted\" style=\"padding:24px\">Carregando…</p></div>`\n\n    const rows = ds.records() || []\n    const search = (d2.state.search || '').toLowerCase()\n    const catFilter = d2.state.catFilter || null\n\n    const filtered = rows.filter(r => {\n      if (catFilter && r.category !== catFilter) return false\n      if (search) {\n        const hay = `${r.question || ''} ${r.answer || ''} ${r.keywords || ''}`.toLowerCase()\n        if (!hay.includes(search)) return false\n      }\n      return true\n    })\n\n    const chips = [{ value: null, label: 'Todas' }, ...CATEGORIES].map(c => {\n      const isActive = catFilter === c.value\n      return `<button type=\"button\" class=\"chip ${isActive ? 'active' : ''}\" data-chip=\"${c.value ?? ''}\">${esc(c.label)}</button>`\n    }).join('')\n\n    const rowsHtml = filtered.length ? filtered.map(r => `\n      <tr class=\"row\" data-key=\"${r._id}\" data-id=\"${r._id}\">\n        <td class=\"q\">${esc(r.question)}</td>\n        <td><span class=\"pill\">${esc(catLabel(r.category))}</span></td>\n        <td><span class=\"dot ${r.active ? 'on' : 'off'}\"></span>${r.active ? 'Ativa' : 'Inativa'}</td>\n        <td class=\"actions\">\n          <button type=\"button\" class=\"edit-btn\" data-id=\"${r._id}\" title=\"Editar\">&#9998;</button>\n          <button type=\"button\" class=\"del-btn\" data-id=\"${r._id}\" title=\"Excluir\">&#128465;</button>\n        </td>\n      </tr>`).join('') : `<tr><td colspan=\"4\" class=\"empty\">Nenhuma FAQ encontrada.</td></tr>`\n\n    const editing = d2.state.editingId ? rows.find(r => r._id === d2.state.editingId) : null\n    const modal = d2.state.modalOpen ? renderModal(editing) : ''\n\n    return `\n      <div class=\"wrap\">\n        <div class=\"hdr\">\n          <div>\n            <h1>B2B · Admin FAQs</h1>\n            <p class=\"muted\">${filtered.length} de ${rows.length} FAQs</p>\n          </div>\n          <button type=\"button\" class=\"primary\" id=\"newBtn\">+ Nova FAQ</button>\n        </div>\n        <div class=\"toolbar\">\n          <input type=\"text\" class=\"search\" placeholder=\"Buscar por pergunta, resposta ou palavra-chave…\" value=\"${esc(d2.state.search || '')}\"/>\n          <div class=\"chips\">${chips}</div>\n        </div>\n        <div class=\"tableWrap\">\n          <table>\n            <thead><tr><th>Pergunta</th><th>Categoria</th><th>Status</th><th></th></tr></thead>\n            <tbody>${rowsHtml}</tbody>\n          </table>\n        </div>\n        ${modal}\n      </div>`\n  },\n  mounted(d2) {\n    const ds = () => d2.data()\n\n    d2.on('#newBtn', 'click', () => {\n      d2.setState({ editingId: null, modalOpen: true })\n    })\n\n    d2.on('.edit-btn', 'click', (e, el) => {\n      e.stopPropagation()\n      d2.setState({ editingId: el.dataset.id, modalOpen: true })\n    })\n\n    d2.on('.row', 'click', (e, el) => {\n      d2.setState({ editingId: el.dataset.id, modalOpen: true })\n    })\n\n    d2.on('.del-btn', 'click', async (e, el) => {\n      e.stopPropagation()\n      const id = el.dataset.id\n      const rec = ds().records().find(r => r._id === id)\n      const ok = await d2.dialog.confirm(\n        `Excluir a FAQ \"${(rec?.question || '').slice(0, 60)}\"? Esta ação não pode ser desfeita.`\n      )\n      if (!ok) return\n      await ds().remove(id)\n    })\n\n    d2.on('#closeBtn', 'click', () => { d2.state.modalOpen = false })\n    d2.on('#cancelBtn', 'click', () => { d2.state.modalOpen = false })\n    d2.on('.overlay', 'click', (e, el) => { if (e.target === el) d2.state.modalOpen = false })\n\n    d2.on('#saveBtn', 'click', async () => {\n      const question = (d2.$('[data-field=\"question\"]')?.value || '').trim()\n      const answer = (d2.$('[data-field=\"answer\"]')?.value || '').trim()\n      const category = d2.$('[data-field=\"category\"]')?.value || CATEGORIES[0].value\n      const keywords = (d2.$('[data-field=\"keywords\"]')?.value || '').trim()\n      const active = !!d2.$('[data-field=\"active\"]')?.checked\n\n      if (!question || !answer || !category) {\n        await d2.dialog.alert('Pergunta, resposta e categoria são obrigatórias.')\n        return\n      }\n\n      const id = d2.state.editingId\n      const payload = { question, answer, category, keywords, active }\n      if (id) {\n        await ds().update({ _id: id, ...payload })\n      } else {\n        await ds().create(payload)\n      }\n      d2.setState({ modalOpen: false, editingId: null })\n    })\n\n    d2.on('.search', 'input', (e, el) => { d2.state.search = el.value })\n    d2.on('.chip', 'click', (e, el) => { d2.state.catFilter = el.dataset.chip || null })\n  },\n}\n"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6a7b3b7dc2bdaafdb939e31c","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"b2bServicesAdmin","package":"granado-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":"function esc(s) {\n  return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;')\n}\n\nconst STRATEGIES = ['seeded_fake_lookup', 'seeded_fake_list', 'canned_example', 'handoff_after_collect']\n\nfunction nextRowId(d2) {\n  d2.root.__rowSeq = (d2.root.__rowSeq || 0) + 1\n  return 'r' + d2.root.__rowSeq\n}\n\nfunction blankDraft() {\n  return {\n    _id: null,\n    key: '', label: '', description: '',\n    paramsSchema: [],\n    mockStrategy: STRATEGIES[0],\n    mockConfigText: '{}',\n    responseTemplate: '',\n    clarifyingQuestionTemplate: '',\n    requiresHumanHandoff: false,\n    humanHandoffContact: '',\n    active: true,\n  }\n}\n\nfunction recordToDraft(d2, rec) {\n  return {\n    _id: rec._id,\n    key: rec.key || '',\n    label: rec.label || '',\n    description: rec.description || '',\n    paramsSchema: (rec.paramsSchema || []).map(p => ({\n      _rowId: nextRowId(d2), key: p.key || '', label: p.label || '', required: !!p.required,\n    })),\n    mockStrategy: rec.mockStrategy || STRATEGIES[0],\n    mockConfigText: JSON.stringify(rec.mockConfig || {}, null, 2),\n    responseTemplate: rec.responseTemplate || '',\n    clarifyingQuestionTemplate: rec.clarifyingQuestionTemplate || '',\n    requiresHumanHandoff: !!rec.requiresHumanHandoff,\n    humanHandoffContact: rec.humanHandoffContact || '',\n    active: rec.active !== false,\n  }\n}\n\nfunction openForm(d2, rec) {\n  d2.root.__draft = rec ? recordToDraft(d2, rec) : blankDraft()\n  d2.state.jsonError = ''\n  d2.state.formError = ''\n  d2.state.saving = false\n  d2.state.editingId = rec ? rec._id : null\n  d2.state.view = 'form'\n}\n\n// Read every uncontrolled input currently in the DOM back into the draft\n// BEFORE we ask render() to regenerate the form HTML — this is what keeps\n// the repeater (paramsSchema) and the JSON textarea from losing keystrokes\n// or getting clobbered when add/remove-row forces a re-render.\nfunction syncDraftFromDom(d2) {\n  const draft = d2.root.__draft\n  if (!draft) return\n  const simple = ['key', 'label', 'description', 'mockStrategy', 'responseTemplate', 'clarifyingQuestionTemplate', 'humanHandoffContact', 'mockConfigText']\n  simple.forEach(f => {\n    const el = d2.$(`[data-field=\"${f}\"]`)\n    if (el) draft[f] = el.value\n  })\n  const reqEl = d2.$('[data-field=\"requiresHumanHandoff\"]')\n  if (reqEl) draft.requiresHumanHandoff = reqEl.checked\n  const activeEl = d2.$('[data-field=\"active\"]')\n  if (activeEl) draft.active = activeEl.checked\n  d2.$$('.paramRow').forEach(rowEl => {\n    const rid = rowEl.getAttribute('data-key')\n    const row = draft.paramsSchema.find(r => r._rowId === rid)\n    if (!row) return\n    const kEl = rowEl.querySelector('[data-pfield=\"key\"]')\n    const lEl = rowEl.querySelector('[data-pfield=\"label\"]')\n    const rEl = rowEl.querySelector('[data-pfield=\"required\"]')\n    if (kEl) row.key = kEl.value\n    if (lEl) row.label = lEl.value\n    if (rEl) row.required = rEl.checked\n  })\n}\n\nasync function saveDraft(d2) {\n  const draft = d2.root.__draft\n  if (!draft) return\n  d2.state.jsonError = ''\n  d2.state.formError = ''\n\n  const key = (draft.key || '').trim()\n  const label = (draft.label || '').trim()\n  const description = (draft.description || '').trim()\n  const responseTemplate = (draft.responseTemplate || '').trim()\n\n  if (!key || !label || !description || !responseTemplate) {\n    d2.state.formError = 'Preencha key, label, descrição e modelo de resposta — são obrigatórios.'\n    d2.refresh()\n    return\n  }\n\n  const ds = d2.data()\n  const dup = ds.records().find(r => r.key === key && r._id !== draft._id)\n  if (dup) {\n    d2.state.formError = `A key \"${key}\" já está em uso por outro serviço.`\n    d2.refresh()\n    return\n  }\n\n  let mockConfig = {}\n  const txt = (draft.mockConfigText || '').trim()\n  if (txt) {\n    try {\n      mockConfig = JSON.parse(txt)\n    } catch (err) {\n      d2.state.jsonError = err.message || String(err)\n      d2.refresh()\n      return\n    }\n  }\n\n  const paramsSchema = draft.paramsSchema\n    .map(r => ({ key: (r.key || '').trim(), label: (r.label || '').trim(), required: !!r.required }))\n    .filter(r => r.key || r.label)\n\n  const payload = {\n    key, label, description,\n    paramsSchema,\n    mockStrategy: draft.mockStrategy,\n    mockConfig,\n    responseTemplate,\n    clarifyingQuestionTemplate: (draft.clarifyingQuestionTemplate || '').trim(),\n    requiresHumanHandoff: !!draft.requiresHumanHandoff,\n    humanHandoffContact: (draft.humanHandoffContact || '').trim(),\n    active: !!draft.active,\n  }\n\n  d2.state.saving = true\n  try {\n    if (draft._id) {\n      await ds.update({ _id: draft._id, ...payload })\n    } else {\n      await ds.create(payload)\n    }\n    d2.root.__draft = null\n    d2.state.saving = false\n    d2.state.view = 'list'\n  } catch (err) {\n    d2.state.saving = false\n    d2.state.formError = 'Erro ao salvar: ' + (err && err.message ? err.message : String(err))\n    d2.refresh()\n  }\n}\n\nfunction renderList(d2) {\n  const ds = d2.data()\n  if (ds.loading) return `<div class=\"wrap\"><p class=\"hint\">Carregando…</p></div>`\n  const filter = d2.state.filter || 'all'\n  const all = ds.records()\n  const rows = all.filter(r => filter === 'all' ? true : filter === 'active' ? !!r.active : !r.active)\n  return `<div class=\"wrap\">\n    <div class=\"head\">\n      <h1>B2B · Admin Serviços</h1>\n      <div class=\"filters\">\n        <button data-f=\"all\" ${filter === 'all' ? 'data-active' : ''}>Todos (${all.length})</button>\n        <button data-f=\"active\" ${filter === 'active' ? 'data-active' : ''}>Ativos (${all.filter(r => r.active).length})</button>\n        <button data-f=\"inactive\" ${filter === 'inactive' ? 'data-active' : ''}>Inativos (${all.filter(r => !r.active).length})</button>\n      </div>\n      <button class=\"newBtn\" data-new>+ Novo Serviço</button>\n    </div>\n    ${rows.length ? `<table>\n      <thead><tr><th>Key</th><th>Label</th><th>Mock Strategy</th><th>Ativo</th><th></th></tr></thead>\n      <tbody>${rows.map(r => `\n        <tr class=\"row\" data-key=\"${r._id}\" data-edit=\"${r._id}\">\n          <td class=\"strategy\">${esc(r.key)}</td>\n          <td>${esc(r.label)}</td>\n          <td class=\"strategy\">${esc(r.mockStrategy)}</td>\n          <td>${r.active ? '<span class=\"pill on\">ativo</span>' : '<span class=\"pill off\">inativo</span>'}</td>\n          <td><button class=\"delBtn\" data-del=\"${r._id}\">Excluir</button></td>\n        </tr>`).join('')}</tbody>\n    </table>` : `<div class=\"empty\">Nenhum serviço encontrado.</div>`}\n  </div>`\n}\n\nfunction renderForm(d2) {\n  const draft = d2.root.__draft || (d2.root.__draft = blankDraft())\n  const isNew = !draft._id\n  return `<div class=\"wrap\"><div class=\"formWrap\">\n    <button class=\"backBtn\" data-back>&larr; Voltar para lista</button>\n    <h2>${isNew ? 'Novo Serviço' : 'Editar Serviço'}</h2>\n\n    <div class=\"field\">\n      <label>Key (identificador único) *</label>\n      <input type=\"text\" data-field=\"key\" value=\"${esc(draft.key)}\" placeholder=\"ex: status_pedido\"/>\n    </div>\n\n    <div class=\"field\">\n      <label>Label (nome legível) *</label>\n      <input type=\"text\" data-field=\"label\" value=\"${esc(draft.label)}\"/>\n    </div>\n\n    <div class=\"field\">\n      <label>Descrição (para o roteador de IA) * — o campo mais importante: descreva claramente quando esta ação deve ser usada</label>\n      <textarea class=\"tall\" data-field=\"description\">${esc(draft.description)}</textarea>\n    </div>\n\n    <div class=\"field\">\n      <label>Parâmetros esperados (paramsSchema)</label>\n      <div class=\"paramsBox\">\n        ${draft.paramsSchema.length ? draft.paramsSchema.map(row => `\n          <div class=\"paramRow\" data-key=\"${row._rowId}\">\n            <input type=\"text\" data-pfield=\"key\" placeholder=\"key\" value=\"${esc(row.key)}\"/>\n            <input type=\"text\" data-pfield=\"label\" placeholder=\"label\" value=\"${esc(row.label)}\"/>\n            <label class=\"reqChk\"><input type=\"checkbox\" data-pfield=\"required\" ${row.required ? 'checked' : ''}/> obrigatório</label>\n            <button class=\"rmBtn\" data-rm=\"${row._rowId}\" title=\"Remover\">&times;</button>\n          </div>`).join('') : '<div class=\"hint\" style=\"margin-bottom:8px\">Nenhum parâmetro definido.</div>'}\n        <button class=\"addRowBtn\" data-addrow>+ Adicionar parâmetro</button>\n      </div>\n    </div>\n\n    <div class=\"field\">\n      <label>Estratégia de Mock *</label>\n      <select data-field=\"mockStrategy\">\n        ${STRATEGIES.map(s => `<option value=\"${s}\" ${s === draft.mockStrategy ? 'selected' : ''}>${s}</option>`).join('')}\n      </select>\n    </div>\n\n    <div class=\"field\">\n      <label>Configuração (JSON avançado)</label>\n      <textarea class=\"mono\" data-field=\"mockConfigText\">${esc(draft.mockConfigText)}</textarea>\n      ${d2.state.jsonError ? `<div class=\"err\">JSON inválido: ${esc(d2.state.jsonError)}</div>` : `<div class=\"hint\">JSON livre consumido pelo executor do mock.</div>`}\n    </div>\n\n    <div class=\"field\">\n      <label>Modelo de Resposta *</label>\n      <textarea class=\"tall\" data-field=\"responseTemplate\">${esc(draft.responseTemplate)}</textarea>\n      <div class=\"hint\">Placeholders {{nomeDoCampo}} são preenchidos em tempo de execução.</div>\n    </div>\n\n    <div class=\"field\">\n      <label>Pergunta de Esclarecimento</label>\n      <textarea data-field=\"clarifyingQuestionTemplate\">${esc(draft.clarifyingQuestionTemplate)}</textarea>\n    </div>\n\n    <div class=\"field checkRow\">\n      <input type=\"checkbox\" data-field=\"requiresHumanHandoff\" ${draft.requiresHumanHandoff ? 'checked' : ''}/>\n      <label style=\"margin:0\">Requer Transferência Humana</label>\n    </div>\n\n    <div class=\"field\" data-handoffwrap style=\"${draft.requiresHumanHandoff ? '' : 'display:none'}\">\n      <label>Contato Humano</label>\n      <input type=\"text\" data-field=\"humanHandoffContact\" value=\"${esc(draft.humanHandoffContact)}\"/>\n    </div>\n\n    <div class=\"field checkRow\">\n      <input type=\"checkbox\" data-field=\"active\" ${draft.active ? 'checked' : ''}/>\n      <label style=\"margin:0\">Ativo</label>\n    </div>\n\n    ${d2.state.formError ? `<div class=\"dupErr\">${esc(d2.state.formError)}</div>` : ''}\n\n    <div class=\"actions\">\n      <button class=\"saveBtn\" data-save ${d2.state.saving ? 'disabled' : ''}>${d2.state.saving ? 'Salvando…' : 'Salvar'}</button>\n      <button class=\"cancelBtn\" data-cancel>Cancelar</button>\n    </div>\n  </div></div>`\n}\n\nfunction wireForm(d2) {\n  d2.on('[data-back]', 'click', () => { d2.root.__draft = null; d2.state.view = 'list' })\n  d2.on('[data-cancel]', 'click', () => { d2.root.__draft = null; d2.state.view = 'list' })\n\n  d2.on('[data-addrow]', 'click', () => {\n    syncDraftFromDom(d2)\n    d2.root.__draft.paramsSchema.push({ _rowId: nextRowId(d2), key: '', label: '', required: false })\n    d2.refresh()\n  })\n  d2.on('[data-rm]', 'click', (e, el) => {\n    syncDraftFromDom(d2)\n    const rid = el.dataset.rm\n    d2.root.__draft.paramsSchema = d2.root.__draft.paramsSchema.filter(r => r._rowId !== rid)\n    d2.refresh()\n  })\n\n  d2.on('[data-field=\"requiresHumanHandoff\"]', 'change', (e, el) => {\n    const wrap = d2.$('[data-handoffwrap]')\n    if (wrap) wrap.style.display = el.checked ? '' : 'none'\n  })\n\n  d2.on('[data-save]', 'click', async () => {\n    syncDraftFromDom(d2)\n    await saveDraft(d2)\n  })\n}\n\nexport default {\n  css: `\n    .wrap { box-sizing:border-box; height:100%; overflow:auto; font:14px/1.4 -apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif; color:#1a1a1a; background:#f7f8fa; padding:24px; }\n    .head { display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; gap:12px; flex-wrap:wrap; }\n    .head h1 { font-size:20px; margin:0; font-weight:700; }\n    .filters { display:flex; gap:6px; }\n    .filters button { padding:6px 12px; border-radius:999px; border:1px solid #ddd; background:#fff; cursor:pointer; font-size:12px; }\n    .filters button[data-active] { background:#1a1a1a; color:#fff; border-color:#1a1a1a; }\n    .newBtn { padding:10px 16px; border-radius:8px; border:0; background:#5b47e0; color:#fff; font-weight:600; cursor:pointer; }\n    .newBtn:hover { background:#4a38c9; }\n    table { width:100%; border-collapse:collapse; background:#fff; border-radius:10px; overflow:hidden; box-shadow:0 1px 3px rgba(0,0,0,.06); }\n    th { text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:.03em; color:#888; padding:10px 14px; border-bottom:1px solid #eee; }\n    td { padding:12px 14px; border-bottom:1px solid #f0f0f0; font-size:13px; vertical-align:middle; }\n    tr.row:hover { background:#faf9ff; cursor:pointer; }\n    .pill { display:inline-block; padding:2px 8px; border-radius:999px; font-size:11px; font-weight:600; }\n    .pill.on { background:#e3f8e9; color:#1a7f3c; }\n    .pill.off { background:#f2f2f2; color:#888; }\n    .strategy { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; color:#555; }\n    .delBtn { background:none; border:0; color:#c0392b; cursor:pointer; font-size:12px; padding:4px 8px; border-radius:6px; }\n    .delBtn:hover { background:#fdecea; }\n    .empty { padding:40px; text-align:center; color:#999; }\n    .hint { font-size:11px; color:#999; margin-top:4px; }\n\n    .formWrap { max-width:720px; margin:0 auto; background:#fff; border-radius:12px; padding:28px; box-shadow:0 1px 3px rgba(0,0,0,.06); }\n    .formWrap h2 { margin:0 0 20px; font-size:18px; }\n    .field { margin-bottom:18px; }\n    .field label { display:block; font-size:12px; font-weight:600; color:#444; margin-bottom:6px; }\n    .field .err { font-size:12px; color:#c0392b; margin-top:4px; }\n    input[type=text], textarea, select { width:100%; box-sizing:border-box; padding:9px 10px; border:1px solid #ddd; border-radius:8px; font-size:13px; font-family:inherit; }\n    textarea { resize:vertical; min-height:60px; }\n    textarea.tall { min-height:110px; }\n    textarea.mono { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:12px; min-height:100px; }\n    .checkRow { display:flex; align-items:center; gap:8px; }\n    .checkRow input { width:auto; }\n    .paramsBox { border:1px solid #eee; border-radius:8px; padding:10px; }\n    .paramRow { display:flex; gap:8px; align-items:center; margin-bottom:8px; }\n    .paramRow input[type=text] { flex:1; }\n    .paramRow .reqChk { display:flex; align-items:center; gap:4px; font-size:11px; color:#666; white-space:nowrap; }\n    .paramRow .rmBtn { background:none; border:0; color:#c0392b; cursor:pointer; font-size:16px; line-height:1; padding:2px 6px; }\n    .addRowBtn { padding:6px 10px; border:1px dashed #bbb; border-radius:6px; background:#fafafa; cursor:pointer; font-size:12px; color:#555; }\n    .actions { display:flex; gap:10px; margin-top:24px; }\n    .saveBtn { padding:10px 20px; border:0; border-radius:8px; background:#5b47e0; color:#fff; font-weight:600; cursor:pointer; }\n    .saveBtn:disabled { opacity:.5; cursor:default; }\n    .cancelBtn { padding:10px 20px; border:1px solid #ddd; border-radius:8px; background:#fff; cursor:pointer; }\n    .backBtn { background:none; border:0; color:#5b47e0; cursor:pointer; font-size:13px; padding:0; margin-bottom:14px; }\n    .dupErr { color:#c0392b; font-size:12px; margin-top:8px; }\n  `,\n  render(d2) {\n    if (d2.state.view === 'form') return renderForm(d2)\n    return renderList(d2)\n  },\n  mounted(d2) {\n    d2.on('.filters button', 'click', (e, el) => { d2.state.filter = el.dataset.f })\n    d2.on('[data-new]', 'click', () => openForm(d2, null))\n    d2.on('.row', 'click', (e, el) => {\n      if (e.target.closest('[data-del]')) return\n      const rec = d2.data().records().find(r => r._id === el.dataset.edit)\n      if (rec) openForm(d2, rec)\n    })\n    d2.on('[data-del]', 'click', async (e, el) => {\n      e.stopPropagation()\n      const ok = await d2.dialog.confirm('Excluir este serviço? Essa ação não pode ser desfeita.')\n      if (!ok) return\n      await d2.data().remove(el.dataset.del)\n    })\n    wireForm(d2)\n  },\n}\n"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6a7b3dfa56d66740be26e178","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"b2bChatDemo","package":"granado-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, .wrap { box-sizing: border-box; }\n    .wrap { display:flex; flex-direction:column; height:100%; width:100%; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; background:#f4f6f5; }\n    .hdr { flex:0 0 auto; padding:18px 28px; background:#1f4d3d; color:#fff; display:flex; align-items:center; gap:12px; }\n    .hdr .dot { width:10px; height:10px; border-radius:50%; background:#5fd68f; box-shadow:0 0 0 3px rgba(95,214,143,.25); }\n    .hdr .title { font-size:16px; font-weight:600; }\n    .hdr .sub { font-size:12px; opacity:.75; margin-left:4px; }\n    .log { flex:1 1 auto; overflow-y:auto; padding:24px 28px; display:flex; flex-direction:column; gap:14px; }\n    .row { display:flex; flex-direction:column; max-width:62%; }\n    .row.user { align-self:flex-end; align-items:flex-end; }\n    .row.assistant { align-self:flex-start; align-items:flex-start; }\n    .bubble { padding:12px 16px; border-radius:16px; font-size:14px; line-height:1.5; white-space:pre-wrap; }\n    .row.user .bubble { background:#1f4d3d; color:#fff; border-bottom-right-radius:4px; }\n    .row.assistant .bubble { background:#fff; color:#1c2320; border:1px solid #e2e8e4; border-bottom-left-radius:4px; }\n    .row.error .bubble { background:#fdeceb; color:#9c2b23; border:1px solid #f4c6c1; }\n    .ts { font-size:11px; color:#8b968f; margin-top:4px; padding:0 4px; }\n    .handoff-badge { display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:600; color:#9a6400; background:#fff3d6; border:1px solid #f2d998; border-radius:999px; padding:4px 10px; margin-top:6px; }\n    .handoff-badge .ico { width:6px; height:6px; border-radius:50%; background:#c58a00; }\n    .handoff-note { font-size:12px; color:#6b756f; margin-top:6px; max-width:280px; }\n    .new-conv-btn { margin-left:auto; background:rgba(255,255,255,.12); border:1px solid rgba(255,255,255,.25); color:#fff; font-size:12px; font-weight:600; padding:7px 14px; border-radius:999px; cursor:pointer; }\n    .new-conv-btn:hover { background:rgba(255,255,255,.2); }\n    .typing { align-self:flex-start; display:flex; gap:5px; padding:14px 16px; background:#fff; border:1px solid #e2e8e4; border-radius:16px; border-bottom-left-radius:4px; }\n    .typing span { width:6px; height:6px; border-radius:50%; background:#9aa8a1; animation:blink 1.2s infinite ease-in-out; }\n    .typing span:nth-child(2){ animation-delay:.2s } .typing span:nth-child(3){ animation-delay:.4s }\n    @keyframes blink { 0%,80%,100%{opacity:.25} 40%{opacity:1} }\n    .reasoning { align-self:flex-start; max-width:78%; background:#fff; border:1px solid #e2e8e4; border-radius:16px; border-bottom-left-radius:4px; padding:14px 16px; display:flex; flex-direction:column; gap:9px; }\n    .reasoning .step { display:flex; align-items:center; gap:9px; font-size:12.5px; color:#8b968f; transition:color .2s; }\n    .reasoning .step.done { color:#3a453e; }\n    .reasoning .step .mark { width:14px; height:14px; border-radius:50%; flex:0 0 auto; display:flex; align-items:center; justify-content:center; font-size:9px; font-weight:700; }\n    .reasoning .step.done .mark { background:#1f4d3d; color:#fff; }\n    .reasoning .step.current .mark { border:2px solid #cdd6d1; border-top-color:#1f4d3d; animation:spin .7s linear infinite; }\n    @keyframes spin { to { transform:rotate(360deg) } }\n    .composer { flex:0 0 auto; padding:16px 28px 22px; background:#f4f6f5; border-top:1px solid #e2e8e4; display:flex; gap:10px; }\n    .composer input { flex:1; padding:13px 16px; border:1px solid #d7ded9; border-radius:12px; font-size:14px; outline:none; background:#fff; }\n    .composer input:focus { border-color:#1f4d3d; }\n    .composer button { padding:0 22px; border:none; border-radius:12px; background:#1f4d3d; color:#fff; font-size:14px; font-weight:600; cursor:pointer; }\n    .composer button:disabled { background:#a9b8b1; cursor:default; }\n    .empty { margin:auto; color:#8b968f; font-size:13px; text-align:center; max-width:320px; }\n  `,\n  render(d2) {\n    const store = d2.store('b2bChatStore')\n    const history = store.get('data.history', []) || []\n    const sending = store.get('data.sending', false)\n\n    const bubbles = history.map(m => {\n      const rowClass = m.isError ? 'row error' : `row ${m.role}`\n      const badge = m.handoff\n        ? `<div class=\"handoff-badge\"><span class=\"ico\"></span>Transferindo para atendimento humano</div>\n           <div class=\"handoff-note\">Nesta demo, isso marca o fim do atendimento automático — um especialista Granado assumiria a partir daqui. Clique em \"Nova conversa\" para testar outro cenário.</div>`\n        : ''\n      return `<div class=\"${rowClass}\" data-key=\"${m.id}\">\n        <div class=\"bubble\">${esc(m.content)}</div>\n        ${badge}\n        <div class=\"ts\">${fmtTime(m.at)}</div>\n      </div>`\n    }).join('')\n\n    const emptyState = history.length === 0\n      ? `<div class=\"empty\">Converse com o atendimento B2B da Granado. Pergunte sobre cadastro, pedidos, portfólio, avarias ou devoluções.</div>`\n      : ''\n\n    const steps = store.get('data.steps', []) || []\n    const reasoning = (sending && steps.length)\n      ? `<div class=\"reasoning\">${steps.map((s, i) => {\n          const isLast = i === steps.length - 1\n          return `<div class=\"step ${isLast ? 'current' : 'done'}\" data-key=\"${s.id}\"><span class=\"mark\">${isLast ? '' : '✓'}</span><span>${esc(s.label)}</span></div>`\n        }).join('')}</div>`\n      : ''\n    const typing = (sending && !steps.length)\n      ? `<div class=\"typing\"><span></span><span></span><span></span></div>`\n      : ''\n\n    return `<div class=\"wrap\">\n      <div class=\"hdr\"><span class=\"dot\"></span><span class=\"title\">Granado B2B</span><span class=\"sub\">Atendimento · Demo</span>${history.length ? '<button class=\"new-conv-btn\">Nova conversa</button>' : ''}</div>\n      <div class=\"log\">${emptyState}${bubbles}${reasoning}${typing}</div>\n      <div class=\"composer\">\n        <input class=\"chat-input\" type=\"text\" placeholder=\"Digite sua mensagem...\" ${sending ? 'disabled' : ''} />\n        <button class=\"send-btn\" ${sending ? 'disabled' : ''}>Enviar</button>\n      </div>\n    </div>`\n  },\n  mounted(d2) {\n    const send = async () => {\n      const store = d2.store('b2bChatStore')\n      const input = d2.$('.chat-input')\n      if (!input) return\n      const text = input.value.trim()\n      if (!text || store.get('data.sending', false)) return\n\n      const prevHistory = store.get('data.history', []) || []\n      const userMsg = { id: cid(), role: 'user', content: text, at: Date.now() }\n      const withUser = [...prevHistory, userMsg]\n      store.set('data.history', withUser)\n      store.set('data.input', '')\n      input.value = ''\n      store.set('data.sending', true)\n      store.set('data.steps', [])\n\n      const pendingSlot = store.get('data.pendingSlot', null) || null\n      const shortHistory = withUser.slice(-10).map(m => ({ role: m.role, content: m.content }))\n\n      try {\n        const res = await d2.rush('b2bChatTurn').stream({ message: text, pendingSlot, history: shortHistory }, (u) => {\n          const label = u && u.payload && u.payload.label\n          if (label) {\n            const cur = store.get('data.steps', []) || []\n            store.set('data.steps', [...cur, { id: cid(), label }])\n          }\n        })\n        const payload = (res && res.data) ? res.data : res\n        if (!payload || typeof payload.reply === 'undefined') {\n          throw new Error('Resposta inesperada do atendimento.')\n        }\n        const assistantMsg = {\n          id: cid(),\n          role: 'assistant',\n          content: payload.reply,\n          at: Date.now(),\n          handoff: !!payload.requiresHumanHandoff,\n        }\n        store.set('data.history', [...withUser, assistantMsg])\n        store.set('data.pendingSlot', payload.pendingSlot || null)\n      } catch (err) {\n        const errMsg = {\n          id: cid(),\n          role: 'assistant',\n          isError: true,\n          content: `Não foi possível contatar o atendimento agora. (${(err && err.message) || 'erro desconhecido'}) Tente novamente em instantes.`,\n          at: Date.now(),\n        }\n        store.set('data.history', [...withUser, errMsg])\n      } finally {\n        store.set('data.sending', false)\n        store.set('data.steps', [])\n      }\n    }\n\n    d2.on('.send-btn', 'click', send)\n    d2.on('.chat-input', 'keydown', (e) => { if (e.key === 'Enter') send() })\n    d2.on('.new-conv-btn', 'click', () => {\n      const store = d2.store('b2bChatStore')\n      store.set('data.history', [])\n      store.set('data.pendingSlot', null)\n      store.set('data.input', '')\n      store.set('data.sending', false)\n    })\n  },\n  updated(d2) {\n    const log = d2.$('.log')\n    if (log) log.scrollTop = log.scrollHeight\n  },\n}\n\nfunction esc(s) { return String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') }\nfunction cid() { return 'm' + Date.now() + Math.random().toString(36).slice(2, 8) }\nfunction fmtTime(ts) {\n  try {\n    return new Date(ts).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })\n  } catch (e) { return '' }\n}\n"}}}}}}}},{"_id":"6a7f710198c697c206264684","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bCanal","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 24px}\nh3.sec{font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:34px 0 12px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.card{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:18px 20px;box-shadow:0 1px 0 #E3D9BD;box-sizing:border-box}\n.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(215px,1fr));gap:14px}\n.kpi .lbl{font-size:11px;letter-spacing:1.8px;text-transform:uppercase;color:#8C6F2A;margin-bottom:8px}\n.kpi .val{font-family:Georgia,serif;font-size:34px;color:#1E4D3B;line-height:1.1}\n.delta{display:inline-block;background:#E7F0E9;color:#1E6B43;border-radius:999px;padding:2px 10px;font-size:12px;font-weight:700;margin-left:8px;vertical-align:middle}\n.kpi .sub{font-size:12.5px;color:#6B6753;margin-top:8px;line-height:1.45}\n.track{height:12px;background:#E4DCC2;border-radius:999px;overflow:hidden;margin-top:10px}\n.fillg{height:100%;background:#1E4D3B;border-radius:999px}\n.legend{display:flex;gap:14px;font-size:12px;color:#6B6753;margin-top:8px;flex-wrap:wrap}\n.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:5px}\n.split{display:grid;grid-template-columns:repeat(auto-fit,minmax(270px,1fr));gap:14px}\n.chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}\n.chip{background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:5px 13px;font-size:13px}\nul.tight{margin:8px 0 0;padding-left:18px;line-height:1.6;font-size:14px;color:#4A4A3C}\n.pain{border-left:4px solid #A33B2E}\n.pain .vs{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-top:10px}\n.pain .box{flex:1 1 180px;background:#F8F2E2;border:1px dashed #C9BC97;border-radius:8px;padding:10px 12px;font-size:13.5px;line-height:1.45}\n.pain .x{font-family:Georgia,serif;font-size:20px;color:#A33B2E;font-weight:700}\n.note{background:#1E4D3B;color:#F5F0E1;border-radius:10px;padding:18px 22px;line-height:1.55;margin-top:26px;font-size:14.5px}\n.note b{color:#E8CF8F}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:13px 17px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin-top:26px}\n.obs b{color:#3B3A2E}\n.obs.top{margin:0 0 22px}\n.refs{font-size:12.5px;color:#8A8672;margin-top:18px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.kpi .val{font-size:28px}}`,\n  render(d2){\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Visão estratégica · acumulado de 2025</p>\n  <h2>O canal B2B em números</h2>\n  <p class=\"lead\">O B2B (b2b.granado.com.br) é o canal onde lojistas compram produtos Granado | Phebo no atacado para revender — ele atende os clientes pequenos e médios que o time comercial não cobre um a um. Números do documento interno “Visão Atual do Canal B2B”; os percentuais de crescimento comparam o acumulado de 2025 (YTD) com o mesmo período de 2024 — o volume de pedidos é aproximadamente mensal.</p>\n  <div class=\"obs top\">ℹ️ <b>Escopo deste trabalho.</b> Os clientes menores, que compram pelo portal B2B, são a base que será atendida digitalmente. As grandes contas seguem com o time comercial e não fazem parte deste escopo por ora.</div>\n  <div class=\"kpis\">\n    <div class=\"card kpi\"><div class=\"lbl\">Faturamento bruto</div><div class=\"val\">+51,8%</div><div class=\"sub\">As vendas do canal em 2025 cresceram 51,8% em relação ao mesmo período de 2024.</div></div>\n    <div class=\"card kpi\"><div class=\"lbl\">Pedidos / mês (aprox.)</div><div class=\"val\">1.644<span class=\"delta\">+31,8%</span></div><div class=\"sub\">São cerca de 1.644 pedidos por mês — 31,8% a mais que no ano anterior.</div></div>\n    <div class=\"card kpi\"><div class=\"lbl\">Ticket médio</div><div class=\"val\">R$ 1.920<span class=\"delta\">+15,2%</span></div><div class=\"sub\">Cada pedido vale, em média, R$ 1.920 — 15% a mais que no ano passado.</div></div>\n    <div class=\"card kpi\"><div class=\"lbl\">Base ativa</div><div class=\"val\">56,8%</div>\n      <div class=\"track\"><div class=\"fillg\" style=\"width:56.8%\"></div></div>\n      <div class=\"legend\"><span><span class=\"dot\" style=\"background:#1E4D3B\"></span>3.012 ativos</span><span><span class=\"dot\" style=\"background:#C9BC97\"></span>2.287 inativos</span></div>\n      <div class=\"sub\">De 5.299 empresas cadastradas, 3.012 compraram recentemente; 2.287 estão paradas.</div>\n    </div>\n  </div>\n  <h3 class=\"sec\">Quem compra no canal</h3>\n  <div class=\"split\">\n    <div class=\"card\"><b>Perfil dos clientes</b>\n      <div class=\"chips\"><span class=\"chip\">Multimarcas</span><span class=\"chip\">E-commerces</span><span class=\"chip\">Lojas de presente &amp; nicho</span><span class=\"chip\">Pequenos e médios lojistas</span></div>\n    </div>\n    <div class=\"card\"><b>Como compram</b>\n      <ul class=\"tight\"><li>Compra operacional, de <b>reposição</b> — recorrente</li><li>Poucas unidades de cada produto, mas <b>muitos itens diferentes</b> por pedido (baixo volume por SKU, alto mix)</li><li>O site não estimula descoberta, upsell, cross-sell nem impulso</li></ul>\n    </div>\n  </div>\n  <h3 class=\"sec\">A dor estrutural</h3>\n  <div class=\"card pain\">\n    <b>Expectativa vs. realidade do portfólio</b>\n    <div class=\"vs\">\n      <div class=\"box\">🛍️ O lojista se cadastra esperando o <b>portfólio completo</b> Granado | Phebo…</div>\n      <span class=\"x\">×</span>\n      <div class=\"box\">📦 …e encontra um <b>mix restrito</b>, com linhas selecionadas para revenda.</div>\n    </div>\n    <ul class=\"tight\"><li>Frustração logo no início da relação</li><li>Tráfego não qualificado no funil de cadastro</li><li>Baixa conversão</li></ul>\n  </div>\n  <div class=\"obs\">ℹ️ <b>Sem contato ativo hoje.</b> O bot do canal foi desativado por estar desatualizado e, no momento, não há um canal de contato ativo com o cliente — uma das principais dores apontadas.</div>\n  <div class=\"obs\">ℹ️ <b>Base inativa.</b> 2.287 dos 5.299 clientes cadastrados (43,2%) não compram hoje. A visão do canal aponta a reativação dessa base como a principal frente de crescimento.</div>\n  <p class=\"refs\">Concorrentes de referência: Martins Atacado · Compra Agora (Unilever)</p>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f710269da4b15e92aaf45","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bBot","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 18px}\n.legend{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 20px}\n.lg{font-size:12px;border-radius:999px;padding:4px 12px;border:1px solid}\n.lg.g{background:#E7F0E9;color:#1E6B43;border-color:#BCD4C3}\n.lg.c{background:#FBF3DC;color:#8C6F2A;border-color:#E3D0A0}\n.lg.p{background:#F7E4E0;color:#A33B2E;border-color:#E5C0B8}\n.flows{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:16px;align-items:start}\n.menu{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:12px;overflow:hidden}\n.mh{background:#1E4D3B;color:#F5F0E1;padding:14px 18px;font-family:Georgia,serif;font-size:17px}\n.mh small{display:block;font-family:'Gill Sans','Trebuchet MS',sans-serif;font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:#CBB878;margin-top:3px}\n.cat{padding:12px 18px 4px;border-top:1px solid #EFE6CF}\n.cat>b{display:block;color:#1E4D3B;font-size:14.5px;margin-bottom:8px}\n.opt{display:flex;gap:10px;align-items:flex-start;padding:7px 0 7px 12px;border-left:3px solid #D9C289;margin:0 0 8px}\n.opt .q{font-size:13.5px;font-weight:600;color:#3B3A2E}\n.opt .a{font-size:12.5px;color:#6B6753;line-height:1.45;margin-top:2px}\n.tag{flex:0 0 auto;font-size:10.5px;letter-spacing:.5px;border-radius:999px;padding:3px 9px;margin-top:2px;white-space:nowrap;border:1px solid}\n.tag.g{background:#E7F0E9;color:#1E6B43;border-color:#BCD4C3}\n.tag.c{background:#FBF3DC;color:#8C6F2A;border-color:#E3D0A0}\n.tag.p{background:#F7E4E0;color:#A33B2E;border-color:#E5C0B8}\n.tag.n{background:#F5EEDC;color:#8C6F2A;border-color:#D9C289}\n.opt.pp{border-left-color:#CD8B80}\n.note{background:#1E4D3B;color:#F5F0E1;border-radius:10px;padding:18px 22px;line-height:1.55;margin-top:26px;font-size:14.5px}\n.note b{color:#E8CF8F}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:13px 17px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin:0 0 18px}\n.obs b{color:#3B3A2E}\n.obs.mt{margin:24px 0 0}\n.dec{margin-top:8px;background:#EAF2EC;border:1px solid #C9AF6B;border-left:4px solid #B8963E;border-radius:8px;padding:9px 12px;font-size:12.5px;line-height:1.5;color:#2F4A3C}\n.dec .dhd{display:block;font-size:10.5px;letter-spacing:1.2px;text-transform:uppercase;color:#8C6F2A;font-weight:700;margin-bottom:4px}\n.sysnote{font-size:12px;color:#6B6753;background:#F8F2E2;border:1px dashed #C9BC97;border-radius:6px;padding:7px 10px;margin:0 0 10px;line-height:1.5}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.opt{flex-wrap:wrap}}`,\n  render(d2){\n    const T={g:'🟢 autosserviço',c:'📋 coleta dados',p:'🎫 abre chamado',n:'✨ novo (projeto)'}\n    const COM=[\n      ['🧴 Produtos e Estoque',[\n        ['Não encontrei o produto no site','Explica que nem todo portfólio é revenda e envia o catálogo em anexo','g','Este é o grande problema do canal: existe uma divisão estratégica de portfólio — o mix restrito é decisão, não falha. A resposta precisa explicar isso bem.'],\n        ['Produto sem estoque — quando volta?','Orienta usar o “Avise-me quando chegar” na página do item','g','Automatizar o “avise-me quando chegar”.'],\n        ['Quais produtos posso revender?','Envia o catálogo completo de revenda','g','Entregar o catálogo em PDF e gerar PDF com preços e data de emissão.'],\n        ['Outras dúvidas','Encaminha para abrir chamado no portal de suporte','p']]],\n      ['🚚 Pedidos e Entregas',[\n        ['Rastrear meu pedido','Passo a passo no site; qualquer problema → chamado no portal (48h)','g','Tentar rastrear pelo Consulta Fácil. Cadeia do pedido: OMS → Magento → NF → Confirma Fácil (o webFor tem tudo isso).'],\n        ['Avaria, sobra ou item faltando','Checa a política e pede NF + telefone + itens + foto; análise em 48h','c','Automatizar 100% do processo, passo a passo, garantindo a coleta completa (NF, itens, quantidades, fotos) para processar a reclamação internamente. Estratégia: sempre tentar resolver com cupom — abaixo de R$ 500, cupom; acima, Nota de Devolução + coleta + entrada no CD (pode levar até 1 mês). Sugestão da Ale Schwab: oferecer 10% de desconto ou outra vantagem para o cliente não seguir com a desistência/reembolso.'],\n        ['Preciso da nota fiscal','2ª via em PDF/XML em Meus Pedidos','g','Já existe no site → automatizável: perguntar de qual pedido, o cliente seleciona, e enviamos direto no canal.'],\n        ['Cancelamento ou devolução','Após faturar não cancela; orienta recusar na entrega','g','É a desistência. Se a NF já estiver emitida no site, o cliente precisa recusar o pedido na entrega.'],\n        ['Outras dúvidas','Encaminha para abrir chamado no portal de suporte','p']]],\n      ['💳 Pagamentos e Financeiro',[\n        ['Formas de pagamento','PIX com 3% de desconto · cartão · boleto para pagar em 28 dias (sujeito a análise de crédito)','g','Impedir o boleto quando existe boleto pendente. O melhor meio para todos é o PIX.'],\n        ['Quero pagar com boleto a prazo','Solicitar análise de crédito dentro do portal (precisa histórico)','g','Existe um bureau de crédito: o cliente pode solicitar a análise e também solicitar aumento de limite, preenchendo um formulário. Alinhar isso com Sandro Santana também.'],\n        ['Boleto não aparece no checkout','Explica limite de crédito aprovado e alternativas','g','Duas causas: (1) falta a avaliação de crédito (formulário do Sandro); (2) limite insuficiente para o carrinho — ex.: limite de boleto de R$ 5.000 com carrinho de R$ 6.000 → o boleto não habilita. Podemos tentar automatizar o split de pagamento (parte no boleto até o limite, o restante em PIX/cartão).'],\n        ['Segunda via de boleto','Minha Conta → Crédito da empresa; retorno em 48h','g','Caso à parte: alinhar com Sandro Santana, que já está resolvendo a emissão de boletos.'],\n        ['Outras dúvidas','Encaminha para abrir chamado no portal de suporte','p']],'📌 <b>Contexto (Granado, 18/08).</b> 3 formas hoje — boleto 28 dias, cartão até 2x sem juros e PIX com 3% de desconto. Pedido no boleto pode ser cancelado pelo financeiro quando há boleto pendente (o JD sabe disso) — e hoje o cliente não é avisado desse cancelamento; precisamos avisar de alguma forma.'],\n      ['🛒 Jornada de compra',[\n        ['Comprar pelo canal — jornada completa','Item novo do projeto — não existia no bot original','n','Jornada de compra ponta a ponta dentro do atendimento: a partir do ID do cliente no Magento dá para montar o carrinho e fechar o pedido sem sair da conversa — da escolha dos produtos ao pedido concluído.']]],\n      ['❓ Dúvidas gerais',[\n        ['Qualquer outro assunto','“Nenhum especialista disponível por aqui” → chamado no portal','p','Pendência: extrair tudo o que já foi aberto no portal de chamados, para entender o que cai aqui. Vale para todos os nós “Outras dúvidas”.']]]]\n    const SEM=[\n      ['📝 Cadastro',[\n        ['Quero me cadastrar para revender','Link do formulário + material com dúvidas frequentes; preços só após aprovação','g','Vamos automatizar este fluxo: coletar todas as informações necessárias já na conversa e pedir que aguarde a validação. Oferecer: “gostaria que eu te chamasse por aqui quando o cadastro for efetivado?”'],\n        ['Meu cadastro foi rejeitado','Lista os motivos: CNAE (código de atividade da empresa) incompatível, dados divergentes, CNPJ inativo','g','Validação imediata. Pendência: listar todos os critérios de rejeição — os dados existem em alguns sistemas; é importante conseguir dar esse retorno na hora.'],\n        ['Meu cadastro está pendente','Análise média de 2 dias úteis; acompanhar em “Status da Conta”','g','Avaliar deixar o cliente fazer tudo enquanto pendente — somente não fatura. Com validação em tempo real, o volume de pendentes deve cair muito.'],\n        ['Quero alterar meus dados','Minha Conta → Dados da Empresa; endereço deve bater com a Receita Federal','g','Já existe formulário no site — consultar a possibilidade de um fluxo pró-ativo.']],'Sistemas envolvidos no cadastro: <b>Magento</b> (portal B2B), <b>WebCli</b> e <b>JD</b> (ERP JD Edwards).'],\n      ['🛒 Compra',[\n        ['Como faço para comprar?','Login no portal + cupom BEMVINDO10 na 1ª compra','g','Primeira pergunta: tem cadastro? Sim/não diverge o fluxo. Para quem não tem, criar o perfil B2B e fazer o cadastro ali mesmo.<br><br>Finalizar o pedido direto no canal de atendimento: a partir do ID do cliente no Magento dá para montar o carrinho — a experiência precisa ser ponta a ponta, da escolha ao pedido fechado, sem sair da conversa.'],\n        ['Outras dúvidas sobre o portal','Encaminha para abrir chamado no portal de suporte','p']]]]\n    const menu=(title,sub,cats)=>`<div class=\"menu\"><div class=\"mh\">${title}<small>${sub}</small></div>${cats.map(c=>`<div class=\"cat\"><b>${c[0]}</b>${c[2]?`<div class=\"sysnote\">${c[2]}</div>`:''}${c[1].map(o=>`<div class=\"opt${o[2]==='p'?' pp':''}\"><div style=\"flex:1\"><div class=\"q\">${o[0]}</div><div class=\"a\">${o[1]}</div>${o[3]?`<div class=\"dec\"><span class=\"dhd\">📌 Decisão (Granado, 18/08)</span>${o[3]}</div>`:''}</div><span class=\"tag ${o[2]}\">${T[o[2]]}</span></div>`).join('')}</div>`).join('')}</div>`\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Auto-atendimento · roteiro do bot (desativado)</p>\n  <h2>O bot do canal, mapeado</h2>\n  <p class=\"lead\">Este é o roteiro do bot de atendimento: o cliente escolhia opções num menu e recebia respostas prontas. Há dois menus — um para quem já tem cadastro, outro para quem não tem. O mapa abaixo, extraído do documento do bot, mostra cada caminho e como ele termina.</p>\n  <div class=\"obs\">ℹ️ <b>Importante.</b> Este bot está desativado atualmente — a empresa optou por desligá-lo por estar desatualizado. O roteiro abaixo mostra como ele funcionava e foi desenhado. Os cards <b>📌 Decisão</b> são anotações novas do projeto, em coleta com a Granado — não fazem parte do material original.</div>\n  <div class=\"obs\">ℹ️ <b>Observação.</b> O bot era um menu de respostas prontas: não consultava sistemas — pedido, cadastro, rastreio ou boleto — e não executava ações. A maioria dos caminhos terminava em “abrir chamado no portal”, respondido pelo time em até 48h.</div>\n  <div class=\"legend\"><span class=\"lg g\">🟢 autosserviço — instrução pronta</span><span class=\"lg c\">📋 coleta dados — e segue para o time</span><span class=\"lg p\">🎫 abre chamado — vai para o portal</span></div>\n  <div class=\"flows\">${menu('COM cadastro','menu para clientes ativos',COM)}${menu('SEM cadastro / pendente','menu para interessados',SEM)}</div>\n  <div class=\"obs mt\">A resolução em si acontece no atendimento humano, com as macros (aba 💬 Macros).</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f710298c697c206264689","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bMacros","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 20px}\n.grp{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;margin-bottom:10px;overflow:hidden}\n.grp summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:12px;padding:14px 18px;font-family:Georgia,serif;font-size:16.5px;color:#1E4D3B;user-select:none}\n.grp summary::-webkit-details-marker{display:none}\n.grp summary .n{margin-left:auto;background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:2px 11px;font-size:12px;font-family:'Gill Sans','Trebuchet MS',sans-serif}\n.grp summary .ar{transition:transform .2s;color:#8C6F2A;font-size:12px}\n.grp[open] summary .ar{transform:rotate(90deg)}\n.grp[open] summary{border-bottom:1px solid #EFE6CF;background:#FBF7EA}\n.m{padding:10px 18px 10px 30px;border-bottom:1px solid #F3EDDA;font-size:13.5px;line-height:1.5}\n.m:last-child{border-bottom:none}\n.m b{color:#1E4D3B}\n.m span{color:#6B6753}\n.note{background:#1E4D3B;color:#F5F0E1;border-radius:10px;padding:18px 22px;line-height:1.55;margin:0 0 22px;font-size:14.5px}\n.note b{color:#E8CF8F}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:13px 17px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin:0 0 22px}\n.obs b{color:#3B3A2E}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}}`,\n  render(d2){\n    const G=[\n['📝','Cadastro',[\n['Revenda — informações gerais','quem pode revender, análise em ~2 dias úteis, linhas disponíveis, mínimo e formas de pagamento'],\n['Cadastro em análise / status','análise média de 2 dias úteis; acompanhar em “Status da Conta”; dados devem bater com a Receita Federal'],\n['Cadastro realizado','confirma o envio, informa o prazo e ensina a redefinir a senha no 1º acesso'],\n['Cadastro ativo','boas-vindas com as regras principais + cupom BEMVINDO10'],\n['Cadastro reprocessado / erro','erro identificado na análise; reprocessado com retorno em até 48h'],\n['Cadastro rejeitado — CNAE','motivos da rejeição e exemplos de CNAEs aceitos (CNAE = código da atividade da empresa na Receita Federal)'],\n['Tempo de abertura','reprovação de empresa com menos de 6 meses de abertura'],\n['Erro de cadastro no site','instabilidade sistêmica no processamento; aviso quando normalizar'],\n['Alteração cadastral','mudanças feitas na Receita Federal → formulário de atualização'],\n['Atualização de endereço','só após atualizar na Receita Federal; não muda com pedido finalizado'],\n['Endereço não muda','dados vêm automaticamente da Receita; transportadora confirma por telefone'],\n['Dados alterados','atualização concluída; aguardar 24h antes do próximo pedido'],\n['Novo e-mail / e-mail alterado','troca do e-mail de cadastro + redefinição de senha'],\n['Redefinir senha','fluxo “Esqueci minha senha” no portal'],\n['CNPJ e pedido','pede CNPJ ou nº do pedido para localizar o caso no sistema']]],\n['💰','Financeiro / Crédito',[\n['Limite / análise de crédito','formulário próprio; exige histórico de compras e cadastro ativo'],\n['Limite liberado','boleto disponível em até 24h; pedido deve caber no limite; prazo de 28 dias'],\n['Boleto não aparece','só aparece com limite aprovado e pedido dentro do limite; alternativas PIX/cartão'],\n['Boleto / 2ª via de fatura','Minha Conta → Crédito da empresa; retorno em 48h'],\n['Cancelado pelo financeiro','pedido cancelado em revisão; refazer com PIX ou cartão'],\n['Encaminhar para o financeiro','roteia cobranças para o setor de crédito e cobrança'],\n['Pedido entregue sem pagamento','cobrança formal com dados para regularização do valor']]],\n['💳','Pagamento',[\n['Bloqueio de cartão','aguardar ~24h, tentar de novo e checar com a operadora'],\n['Dados do erro de pagamento','pede a forma de pagamento e um print do erro'],\n['Reembolso realizado','compensação em até 24h'],\n['Estorno realizado','valor visível em até 48h, conforme a operadora'],\n['Dados para reembolso','pede dados bancários vinculados ao CNPJ do cadastro'],\n['Valor muda na sacola','efeito de cache; vale o valor da sacola, já com impostos'],\n['Erro de preço','preços podem levar minutos para atualizar após a aprovação do cadastro']]],\n['🚚','Logística / Entrega',[\n['Data de entrega / rastreamento','informa a previsão; rastreio pelo site temporariamente indisponível'],\n['Faturamento do pedido','em faturamento/expedição; NF e rastreio em “Meus Pedidos”'],\n['Pedido entregue','sistema aponta entrega; verifica com a transportadora se necessário'],\n['Aguardando logística','caso em verificação urgente com o setor logístico / transportadora'],\n['Pedido atrasado + cupom','nova previsão de entrega + cupom B2B5 (5%) como retratação'],\n['Pedido extraviado','estorno + “Comprar novamente” + cupom B2B10 (10%)'],\n['Problemas com a entrega','pede retorno; reforça que só se entrega no endereço da Receita Federal'],\n['Tentativa de entrega','pede confirmação de local e horário para reentrega'],\n['Alinhar entrega','recusa endereço alternativo; agenda nova tentativa com telefone de contato'],\n['Entrega não realizada por avaria','carga avariada no transporte → estorno + cupom B2B10'],\n['Corte de pedido','itens indisponíveis; cliente escolhe aguardar ou faturar com corte']]],\n['🧾','Fiscal / SEFAZ',[\n['Pedido retido na SEFAZ','a SEFAZ (fazenda estadual) retém a mercadoria até o cliente pagar o ICMS antecipado (imposto estadual)'],\n['Mercadoria ainda retida','pendência de ICMS; risco de apreensão e leilão'],\n['Liberação SEFAZ','confirmação de que a liberação está em andamento'],\n['IE não habilitada','IE (Inscrição Estadual) irregular impede o faturamento; regularizar em 48h ou o pedido é cancelado'],\n['Cancelado — IE desabilitada','pedido cancelado; refazer após regularizar na SEFAZ'],\n['Cancelado — IE ativa não informada','ajuste cadastral + estorno + refazer o pedido'],\n['CNAE “dispensado”','status fiscal impede o faturamento; orientação ao contador'],\n['Cobranças adicionais / impostos','faturamento sai do RJ; tributos estaduais podem incidir no destino'],\n['Encaminhar ao fiscal / manifesto de recusa','roteia ao setor fiscal; pede manifesto “Operação não realizada” em sinistros']]],\n['📸','Avaria / Devolução',[\n['Avaria — fluxo completo','política de devolução + nota fiscal (NF), telefone, itens e foto → análise em 48h → devolução ou cupom'],\n['Cupom por avaria','cupom no valor dos itens, sem validade, respeitando o pedido mínimo'],\n['Cupom ou nota de devolução','cliente escolhe: devolução com NF ou crédito em cupom'],\n['Emitir NF de devolução','nota apenas dos itens recolhidos; fiscal analisa e a logística coleta'],\n['NF de devolução recebida','análise fiscal → coleta → baixa da fatura ou estorno'],\n['NF em análise fiscal/logística','documento em validação; reembolso após a conferência'],\n['Troca','não existe troca no B2B; apenas devolução + estorno/reembolso'],\n['Arrependimento de compra','até 7 dias corridos do recebimento; pede dados para análise'],\n['Cancelamento de pedido','processado com estorno; recusar a entrega se o pedido chegar']]],\n['🧭','Roteamentos',[\n['SAC consumidor final','compras fora do B2B → WhatsApp/e-mail/site do SAC'],\n['Venda corporativa','kits e presentes institucionais → e-mail de vendas corporativas'],\n['Amenities (hotelaria)','orçamento via e-mail do setor de amenities'],\n['Exportação','vendas fora do Brasil → setor de exportação'],\n['Fornecedor / parceria','propostas → e-mail de compras; registradas no banco de fornecedores'],\n['Imprensa / RP','parcerias de comunicação → e-mail de RP'],\n['Currículo','candidaturas → e-mail de seleção (RH)'],\n['Franquia','a Granado não franqueia — lojas 100% próprias; a alternativa é a revenda B2B'],\n['Contato comercial / representante','pedidos a partir de R$ 50 mil vão para o time comercial da região']]],\n['🗂️','Outros — pedido, cupons & conversa',[\n['Como fazer pedido','passo a passo completo da compra no portal'],\n['Pedido mínimo','subtotal de produtos sem impostos; tabela por região (SE/S 400 … Norte 750)'],\n['Não consegue fechar a compra','checa cadastro ativo e pedido mínimo; pede print do erro'],\n['Erro de login','redefinição de senha, aba anônima, limpeza de cache'],\n['Nota fiscal (2ª via)','download em PDF/XML em “Meus Pedidos”'],\n['Cupom não aplica','não cumulativo com itens que já estão em promoção'],\n['Cupom não entra','verificar o mínimo da promoção sobre o subtotal'],\n['Cupom de 1ª compra travado','libera o B2B10 como alternativa ao BEMVINDO10'],\n['Preço tabelado / cotação','preços tabelados com campanhas quinzenais; não se envia cotação por e-mail'],\n['Produto esgotado / reposição','sem previsão; usar o “Avise-me” na página do produto'],\n['Produto descontinuado','sem reposição futura; sugere similares'],\n['Linhas para revenda / catálogo','só linhas selecionadas; pedido de novas linhas via formulário'],\n['Preço de revenda','preço final a critério do lojista; referência nos sites oficiais'],\n['Sacolas e amostras','não disponíveis para revenda'],\n['Conversa','saudação, finalização, inatividade, WhatsApp sem resposta, tratativa interna, caso duplicado']]]]\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Atendimento humano · respostas prontas</p>\n  <h2>As macros — onde a resolução acontece hoje</h2>\n  <p class=\"lead\">Macro é uma resposta pronta que o atendente copia e adapta ao responder um chamado. É com este repertório de ~70 macros que o time humano responde aos chamados do portal — abaixo, todas agrupadas por tema, com o resumo de cada uma. Elas mostram o que o futuro auto-atendimento precisará cobrir.</p>\n  <div class=\"obs\">ℹ️ <b>Como funciona hoje.</b> Toda resolução real é humana: um atendente lê o chamado, escolhe a macro e responde. O prazo de resposta prometido (SLA) é de 48h — inclusive para consultas simples, como status de cadastro, 2ª via de boleto ou rastreio. As macros também carregam as regras de negócio reais (prazos, limites, impostos, cupons).</div>\n  ${G.map((g,i)=>`<details class=\"grp\"${i===0?' open':''}><summary><span class=\"ar\">▶</span> ${g[0]} ${g[1]}<span class=\"n\">${g[2].length} macros</span></summary>${g[2].map(m=>`<div class=\"m\"><b>${m[0]}</b> — <span>${m[1]}</span></div>`).join('')}</details>`).join('')}\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f710269da4b15e92aaf4a","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bTabul","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 22px}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px}\n.cat{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:16px 18px;box-sizing:border-box}\n.ch{display:flex;align-items:baseline;gap:10px;margin-bottom:10px}\n.ch b{font-family:Georgia,serif;color:#1E4D3B;font-size:16.5px}\n.ch .n{margin-left:auto;background:#1E4D3B;color:#F5F0E1;border-radius:999px;padding:2px 10px;font-size:11.5px}\n.chips{display:flex;flex-wrap:wrap;gap:6px}\n.chip{background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:4px 11px;font-size:12px;line-height:1.3}\n.tot{background:#1E4D3B;color:#F5F0E1;border-radius:10px;padding:14px 20px;margin:0 0 20px;display:flex;gap:26px;flex-wrap:wrap;font-size:14px}\n.tot b{font-family:Georgia,serif;font-size:22px;color:#E8CF8F;display:block}\n.chip .d{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:6px;vertical-align:1px}\n.d.a{background:#4C7A5E}.d.s{background:#B8963E}.d.h{background:#A8A28C}\n.leg{display:flex;gap:14px;flex-wrap:wrap;font-size:12.5px;color:#6B6753;margin:0 0 16px;align-items:center}\n.leg .d{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:6px}\n.fine{font-size:12px;color:#8A8672;margin-top:16px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}}`,\n  render(d2){\n    const C=[\n['Pedidos',[['Alteração de pedido','s'],['Cancelamento','s'],['Como fazer pedido','a'],['Nota fiscal','a'],['Pedido incompleto/excesso/devolução','s'],['Pedido mínimo','a'],['Pedido retido','s'],['Produto com avaria','s'],['Produto divergente','s'],['Status do pedido','a']]],\n['Plataforma / Site',[['Boleto não aparece','a'],['Checkout','s'],['Erro no site','s'],['Login','a'],['Pedido não finaliza','s'],['Preço não aparece','a'],['Produto não aparece','a'],['Senha','a'],['Site lento','s'],['Outros','h']]],\n['Financeiro',[['Análise de crédito','s'],['Aumento de limite','s'],['Boleto','a'],['Cobrança','h'],['Divergência de valores','s'],['Inadimplência','h'],['Limite de crédito','a'],['Outros','h']]],\n['Pagamento',[['Boleto faturado','a'],['Cartão','s'],['Estorno','s'],['Pagamento recusado','s'],['Parcelamento','a'],['PIX','a'],['Reembolso','s'],['Outros','h']]],\n['Frete e Entrega',[['Avaria no transporte','s'],['Divergência de entrega','s'],['Endereço de entrega','s'],['Pedido atrasado','s'],['Pedido não entregue','s'],['Prazo de entrega','a'],['Outros','h']]],\n['Cadastro',[['Alteração de dados cadastrais','s'],['Erro no cadastro','s'],['Novo cadastro','a'],['Status do cadastro','a'],['Outros','h']]],\n['Promoções',[['Cupom','a'],['Desconto não aplicado','s'],['Divergência de desconto','s'],['Primeira compra','a'],['Promoção vigente','a']]],\n['Revenda',[['Carta de autorização / Fispq','h'],['Marketplace','h'],['Quem pode revender','a'],['Regras de revenda','a'],['Outros','h']]],\n['Produtos',[['Produto descontinuado','a'],['Produto fora do catálogo/linha/portfólio','a'],['Outros','h']]],\n['Comercial',[['Atendimento comercial','h'],['Negociação / Cotação','h']]]]\n    const total=C.reduce((s,c)=>s+c[1].length,0)\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Classificação oficial de chamados</p>\n  <h2>Tabulações do atendimento</h2>\n  <p class=\"lead\">Tabulação é a etiqueta que o atendente marca ao encerrar um chamado, dizendo do que ele tratava — é assim que a operação mede o volume de cada assunto. Abaixo, a classificação oficial da planilha de tabulações: 63 motivos em 10 categorias.</p>\n  <div class=\"tot\"><span><b>${total}</b>motivos</span><span><b>${C.length}</b>categorias</span><span style=\"align-self:center;max-width:420px;font-size:13px;opacity:.9\">Pedidos e Plataforma/Site são as categorias mais ramificadas — 10 motivos cada.</span></div>\n  <div class=\"leg\"><span><span class=\"d a\"></span>consulta simples — respondível na hora por um auto-atendimento com acesso aos sistemas</span><span><span class=\"d s\"></span>exige análise ou anexos — parcialmente automatizável</span><span><span class=\"d h\"></span>segue com o time humano</span></div>\n  <div class=\"grid\">${C.map(c=>`<div class=\"cat\"><div class=\"ch\"><b>${c[0]}</b><span class=\"n\">${c[1].length}</span></div><div class=\"chips\">${c[1].map(m=>`<span class=\"chip\"><span class=\"d ${m[1]}\"></span>${m[0]}</span>`).join('')}</div></div>`).join('')}</div>\n  <div class=\"fine\">Classificação indicativa, derivada do fluxo do bot e das macros — não é meta nem promessa.</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f71026c713f0dc1c3083e","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bProdutos","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 22px}\nh3.sec{font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:32px 0 12px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;margin-bottom:8px}\n.st{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:16px 18px}\n.st .lbl{font-size:11px;letter-spacing:1.8px;text-transform:uppercase;color:#8C6F2A;margin-bottom:6px}\n.st .val{font-family:Georgia,serif;font-size:26px;color:#1E4D3B}\n.st .sub{font-size:12.5px;color:#6B6753;margin-top:5px}\n.chart{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:20px 22px}\n.cexp{font-size:13px;color:#6B6753;margin:-2px 0 12px;line-height:1.5}\n.brow{display:grid;grid-template-columns:130px 1fr 44px;align-items:center;gap:12px;margin-bottom:9px}\n.blab{font-size:13px;color:#3B3A2E;text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.btrack{background:#EFE6CF;border-radius:999px;height:16px;overflow:hidden}\n.bfill{height:100%;background:linear-gradient(90deg,#1E4D3B,#2A6148);border-radius:999px;min-width:4px}\n.brow.ph .bfill{background:linear-gradient(90deg,#B8963E,#D9A93D)}\n.bn{font-size:13px;font-weight:700;color:#1E4D3B}\n.cnote{font-size:12px;color:#8A8672;margin-top:12px}\n.prods{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px}\n.p{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:8px}\n.p .nm{font-size:13.5px;font-weight:600;color:#3B3A2E;line-height:1.4;min-height:38px}\n.p .ft{display:flex;align-items:center;justify-content:space-between}\n.p .ln{background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:3px 10px;font-size:11px}\n.p .pr{font-family:Georgia,serif;font-size:16px;color:#1E4D3B;font-weight:700}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.brow{grid-template-columns:96px 1fr 38px}.blab{font-size:11.5px}}`,\n  render(d2){\n    const L=[['Terrapeutics',95],['Bebê',71],['Pink',56],['Glicerina',30],['Pet',20],['Tradicional',20],['Origens (Phebo)',17,1],['Antisséptica',14],['Sem linha',12],['Tratamento',4],['Barbearia',4]]\n    const max=95\n    const P=[\n['Sabonete Líquido Bebê Erva-doce 250ml','Bebê','R$ 16,09'],\n['Creme Contra Assaduras Dermocalmante 300g','Bebê','R$ 39,69'],\n['Hidratante Suave para Bebê Tradicional 120ml','Bebê','R$ 14,26'],\n['Repelente Longa Duração Bebê Peles Sensíveis 110ml','Bebê','R$ 30,91'],\n['Sabonete Líquido de Glicerina Rosa 300ml','Glicerina','R$ 15,08'],\n['Refil Sabonete Líquido Phebo Lima da Pérsia 320ml','Phebo · Glicerina','R$ 12,30'],\n['Kit Terrapeutics — Mix Sabonetes Calêndula e Castanha 6x90g','Terrapeutics','R$ 20,22'],\n['Vela Perfumada Calêndula 180g','Terrapeutics','R$ 53,98'],\n['Desodorante Aerossol para Pés Tradicional 100ml','Tradicional','R$ 17,40']]\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Catálogo habilitado no portal B2B</p>\n  <h2>343 SKUs à venda para revenda</h2>\n  <p class=\"lead\">Estes são os 343 produtos habilitados hoje no portal de revenda, segundo a planilha de produtos ativos — uma seleção do portfólio Granado | Phebo, com linhas específicas liberadas para atacado e preço tabelado. SKU = um produto específico do catálogo (cada tamanho ou versão conta como um).</p>\n  <div class=\"stats\">\n    <div class=\"st\"><div class=\"lbl\">SKUs habilitados</div><div class=\"val\">343</div><div class=\"sub\">todos ativos no portal</div></div>\n    <div class=\"st\"><div class=\"lbl\">Faixa de preço</div><div class=\"val\">R$ 1 – 238</div><div class=\"sub\">preço médio R$ 25,99 (sem impostos)</div></div>\n    <div class=\"st\"><div class=\"lbl\">Sem estoque B2B</div><div class=\"val\">64 SKUs</div><div class=\"sub\">aparecem no site, mas estão indisponíveis para compra no atacado agora</div></div>\n    <div class=\"st\"><div class=\"lbl\">Marcas</div><div class=\"val\">260 · 34</div><div class=\"sub\">Granado · Phebo (7 materiais de vendedor, 42 sem marca)</div></div>\n  </div>\n  <h3 class=\"sec\">SKUs por linha</h3>\n  <p class=\"cexp\">Cada barra mostra quantos produtos cada linha (família de produtos) tem no catálogo de revenda — Terrapeutics é a maior, com 95.</p>\n  <div class=\"chart\">\n    ${L.map(l=>`<div class=\"brow${l[2]?' ph':''}\"><span class=\"blab\">${l[0]}</span><div class=\"btrack\"><div class=\"bfill\" style=\"width:${Math.round(l[1]/max*100)}%\"></div></div><span class=\"bn\">${l[1]}</span></div>`).join('')}\n    <div class=\"cnote\">Barras douradas = linha Phebo. A linha Tradicional soma 20 SKUs Granado + 20 Phebo no total de marcas.</div>\n  </div>\n  <h3 class=\"sec\">Amostra do catálogo</h3>\n  <div class=\"prods\">${P.map(p=>`<div class=\"p\"><div class=\"nm\">${p[0]}</div><div class=\"ft\"><span class=\"ln\">${p[1]}</span><span class=\"pr\">${p[2]}</span></div></div>`).join('')}</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f7102e811f18f500e5fbc","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bRegras","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 22px}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}\n.r{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:18px 20px;box-sizing:border-box}\n.r .hd{display:flex;align-items:center;gap:10px;margin-bottom:10px}\n.r .hd .em{font-size:22px}\n.r .hd b{font-family:Georgia,serif;color:#1E4D3B;font-size:17px}\n.r ul{margin:0;padding-left:18px;font-size:13.5px;line-height:1.65;color:#4A4A3C}\n.r li b{color:#1E4D3B}\ntable.min{width:100%;border-collapse:collapse;font-size:13px;margin:4px 0 8px}\ntable.min td{padding:5px 8px;border-bottom:1px solid #F3EDDA}\ntable.min td:last-child{text-align:right;font-weight:700;color:#1E4D3B;font-family:Georgia,serif}\n.cup{display:flex;flex-direction:column;gap:8px}\n.cup .c{display:flex;align-items:center;gap:10px;font-size:13px;color:#4A4A3C}\n.cup code{background:#EFE6CF;border:1px dashed #B8963E;color:#5B4A1E;border-radius:6px;padding:3px 9px;font-weight:700;font-size:12.5px}\n.foot{font-size:12px;color:#8A8672;margin-top:20px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}}`,\n  render(d2){\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Regras de negócio · extraídas do bot, das macros e do manual</p>\n  <h2>As regras do jogo no B2B</h2>\n  <p class=\"lead\">Tudo o que o atendimento precisa saber de cor — e que o futuro auto-atendimento precisa aplicar sem errar. Regras reunidas do fluxo do bot, das macros e do Manual do Revendedor 2025.</p>\n  <div class=\"grid\">\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">📋</span><b>Cadastro</b></div><ul>\n      <li><b>CNPJ ativo</b> (MEI ok) com <b>CNAE</b> — o código de atividade da empresa — compatível: cosméticos, higiene ou farma</li>\n      <li>Dados idênticos aos da <b>Receita Federal</b></li>\n      <li>Empresa com <b>≥ 6 meses</b> de abertura</li>\n      <li>Análise em <b>até 5 dias úteis</b> (regra oficial do site)</li>\n      <li>Aceita: farmácias, perfumarias, salões, barbearias, distribuidoras, mercearias, lojas de conveniência, materno-infantis e pet shops</li>\n      <li>Preços só aparecem <b>após a aprovação</b></li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">📦</span><b>Pedido mínimo por região</b></div>\n      <table class=\"min\"><tr><td>Sudeste / Sul</td><td>R$ 400</td></tr><tr><td>Centro-Oeste</td><td>R$ 450</td></tr><tr><td>Norte — Tocantins</td><td>R$ 560</td></tr><tr><td>Nordeste</td><td>R$ 580</td></tr><tr><td>Norte — demais estados</td><td>R$ 750</td></tr></table>\n      <ul><li>Calculado sobre o <b>subtotal dos produtos</b>, sem impostos</li><li><b>Obrigatório</b>: sem atingir o mínimo não dá para finalizar o pedido</li><li><b>Frete sempre grátis</b> — todo pedido válido já atinge o mínimo</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">💳</span><b>Pagamento</b></div><ul>\n      <li><b>PIX</b> com 3% de desconto</li>\n      <li><b>Cartão</b> em até 2x sem juros</li>\n      <li><b>Boleto faturado 28 dias</b> — mediante análise de crédito: histórico de compras + cadastro ativo + pedido dentro do limite</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">🎟️</span><b>Cupons</b></div><div class=\"cup\">\n      <div class=\"c\"><code>BEMVINDO10</code> 10% na 1ª compra — não cumulativo</div>\n      <div class=\"c\"><code>B2B10</code> 10% — compensação por extravio/avaria</div>\n      <div class=\"c\"><code>B2B5</code> 5% — retratação por atraso na entrega</div></div></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">🚚</span><b>Entrega</b></div><ul>\n      <li>Entrega <b>exclusivamente no endereço da Receita Federal</b></li>\n      <li>Mudança de endereço exige atualizar a RF primeiro</li>\n      <li>Pedido pode ser <b>retido na SEFAZ</b> (fazenda estadual) — o imposto ICMS antecipado fica por conta do cliente</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">📸</span><b>Avaria &amp; devolução</b></div><ul>\n      <li>Nota fiscal (NF) + telefone + lista de itens + <b>foto</b> → análise em <b>48h</b></li>\n      <li>Prazo p/ solicitar devolução: <b>7 dias corridos</b> do recebimento</li>\n      <li>NF de devolução = cópia da NF original + <b>CFOP de devolução</b>, só com os itens a devolver</li>\n      <li>Reembolso conforme o pagamento: boleto → abatimento em fatura ou cupom · cartão → estorno em até 2 faturas ou cupom · PIX → estorno em até 7 dias úteis ou cupom</li>\n      <li>Reembolso condicionado ao retorno da mercadoria ao CD e conferência</li>\n      <li><b>Não existe troca</b> no B2B</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">✋</span><b>Cancelamento</b></div><ul>\n      <li><b>Impossível após o faturamento</b></li>\n      <li>Recusar na entrega devolve ao CD e gera estorno</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">🧭</span><b>Fora de escopo → roteamentos</b></div><ul>\n      <li><b>SAC</b> consumidor final · <b>vendas corporativas</b> · <b>amenities</b>/hotelaria · <b>exportação</b> · fornecedores · RH · imprensa/RP · fiscal · cobrança</li>\n      <li><b>Franquia não existe</b> — lojas 100% próprias</li></ul></div>\n    <div class=\"r\"><div class=\"hd\"><span class=\"em\">📞</span><b>Canais &amp; SLA</b></div><ul>\n      <li>Portal de suporte (Salesforce), WhatsApp e e-mail oficiais do B2B</li>\n      <li>Prazo padrão de resposta (SLA): <b>48h</b></li>\n      <li>Pedidos <b>≥ R$ 50 mil</b> → negociação com o time comercial</li></ul></div>\n  </div>\n  <p class=\"foot\">Fontes: árvore do bot, macros do atendimento e Manual do Revendedor 2025. Regras confirmadas nas páginas oficiais do portal (regras do site, cadastro rejeitado e política de devolução).</p>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a7f7102e811f18f500e5fc3","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bShell","package":"granado-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:`.shell{position:relative;display:grid;grid-template-rows:auto auto 1fr;height:100%;background:#F5F0E1;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;color:#2B2B23}\n.hdr{background:#1E4D3B;color:#F5F0E1;padding:18px 28px 12px;border-bottom:3px solid #B8963E}\n.brand{font-family:Georgia,'Times New Roman',serif;font-size:26px;letter-spacing:.5px}\n.brand .sep{color:#B8963E;margin:0 6px}\n.brand .b2b{font-size:15px;color:#E8CF8F;letter-spacing:2px;margin-left:10px;text-transform:uppercase}\n.sub{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#A8C3B2;margin-top:5px}\n.tabs{display:flex;gap:2px;background:#163A2C;padding:0 18px;overflow-x:auto;scrollbar-width:thin}\n.tabbtn{flex:0 0 auto;background:none;border:none;color:#BFD4C6;font-family:inherit;font-size:14px;padding:13px 15px 11px;cursor:pointer;border-bottom:3px solid transparent;white-space:nowrap;letter-spacing:.3px}\n.tabbtn:hover{color:#fff;background:rgba(255,255,255,.05)}\n.tabbtn.active{color:#fff;border-bottom-color:#D9A93D;background:rgba(255,255,255,.07);font-weight:600}\n.b2b-content{overflow-y:auto;overflow-x:hidden;background:#F5F0E1}\n.gatewrap{position:absolute;inset:0;z-index:60}\n@media(max-width:640px){.hdr{padding:13px 16px 9px}.brand{font-size:20px}.brand .b2b{font-size:12px;margin-left:6px}.sub{font-size:9.5px;letter-spacing:1.8px}.tabs{padding:0 8px}.tabbtn{font-size:13px;padding:11px 11px 9px}}`,\n  render(d2){\n    const P='granado-app@'\n    const TABS=[['canal','📊','Canal B2B',P+'kB2bCanal'],['progresso','✅','Progresso',P+'kB2bProgresso'],['bot','🤖','Bot (desativado)',P+'kB2bBot'],['alvo','🧭','Fluxo alvo',P+'kB2bFluxoAlvo'],['preco','💲','Precificação',P+'kB2bPrecificacao'],['macros','💬','Macros',P+'kB2bMacros'],['tabul','🏷️','Tabulações',P+'kB2bTabul'],['prod','🧴','Produtos',P+'kB2bProdutos'],['regras','📜','Regras',P+'kB2bRegras'],['seq','🎯','Sequencer',P+'kB2bSequencer'],['kpis','📈','KPIs',P+'kB2bKpis'],['integ','🔌','Integrações',P+'kB2bIntegracoes'],['sfmc','🔗','Integração SFMC',P+'kB2bSfmc']]\n    const un=d2.state.unlocked||(typeof sessionStorage!=='undefined'&&sessionStorage.getItem('b2bOverviewUnlocked')==='1')\n    const act=d2.state.tab||'canal'\n    const t=TABS.find(x=>x[0]===act)||TABS[0]\n    return `<div class=\"shell\">\n  <header class=\"hdr\"><div class=\"brand\">Granado<span class=\"sep\">|</span>Phebo<span class=\"b2b\">B2B</span></div><div class=\"sub\">Atendimento B2B · Visão compartilhada · Material interno</div></header>\n  <nav class=\"tabs\">${TABS.map(x=>`<button class=\"tabbtn${x[0]===act?' active':''}\" data-id=\"${x[0]}\">${x[1]} ${x[2]}</button>`).join('')}</nav>\n  <div class=\"b2b-content\">${d2.render(t[3],{},{key:act,style:'height:auto;display:block'})}</div>\n  ${un?'':`<div class=\"gatewrap\">${d2.render(P+'kB2bGate',{},{key:'gate',style:'height:100%;display:block',onUnlock:()=>{d2.state.unlocked=true}})}</div>`}\n</div>`\n  },\n  mounted(d2){\n    d2.on('.tabbtn','click',(e,el)=>{ d2.state.tab=el.dataset.id; const c=d2.$('.b2b-content'); if(c)c.scrollTop=0 })\n    d2.on(window,'b2bUnlock',()=>{ d2.state.unlocked=true })\n  }\n}"}}}}}}}},{"_id":"6a7f710398c697c206264694","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bGate","package":"granado-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:`.gate{position:absolute;inset:0;background:radial-gradient(1200px 700px at 50% -10%,#2A6148 0%,#1E4D3B 55%,#143528 100%);display:flex;align-items:center;justify-content:center;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;z-index:50}\n.gcard{background:#F5F0E1;border:1px solid #B8963E;border-radius:12px;padding:36px 34px 28px;width:min(370px,86vw);text-align:center;box-shadow:0 24px 70px rgba(0,0,0,.4);box-sizing:border-box}\n.gbrand{font-family:Georgia,'Times New Roman',serif;font-size:25px;color:#1E4D3B}\n.gbrand .sep{color:#B8963E;margin:0 6px}\n.gsub{font-size:10.5px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:8px 0 24px}\n.ginput{width:100%;box-sizing:border-box;padding:12px 14px;border:1px solid #C9BC97;border-radius:8px;background:#fff;font-size:15px;text-align:center;outline:none;font-family:inherit}\n.ginput:focus{border-color:#1E4D3B;box-shadow:0 0 0 3px rgba(30,77,59,.12)}\n.gbtn{width:100%;margin-top:12px;padding:12px;border:none;border-radius:8px;background:#1E4D3B;color:#F5F0E1;font-size:13px;letter-spacing:2px;text-transform:uppercase;cursor:pointer;font-family:inherit}\n.gbtn:hover{background:#163A2C}\n.gerr{display:none;color:#A33B2E;font-size:13px;margin-top:12px}\n.gfoot{margin-top:20px;font-size:11px;color:#8A8672;letter-spacing:.5px}\n.shake{animation:sh .4s}\n@keyframes sh{0%,100%{transform:translateX(0)}20%{transform:translateX(-9px)}40%{transform:translateX(9px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}`,\n  render(d2){\n    return `<div class=\"gate\"><div class=\"gcard\">\n  <div class=\"gbrand\">Granado<span class=\"sep\">|</span>Phebo</div>\n  <div class=\"gsub\">Atendimento B2B · Material interno</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 class=\"gfoot\">Visão compartilhada do atendimento B2B</div>\n</div></div>`\n  },\n  mounted(d2){\n    const check=()=>{\n      const inp=d2.$('.ginput'); if(!inp) return\n      if(inp.value==='Granado@2026@data2'){\n        if(typeof sessionStorage!=='undefined') sessionStorage.setItem('b2bOverviewUnlocked','1')\n        try{window.dispatchEvent(new CustomEvent('b2bUnlock'))}catch(err){}\n        d2.emit('onUnlock')\n      } else {\n        const c=d2.$('.gcard'), er=d2.$('.gerr')\n        if(er) er.style.display='block'\n        if(c){ c.classList.remove('shake'); void c.offsetWidth; c.classList.add('shake') }\n        if(inp.select) inp.select()\n      }\n    }\n    d2.on('.gbtn','click',check)\n    d2.on('.ginput','keydown',(e)=>{ if(e.key==='Enter') check() })\n  }\n}"}}}}}}}},{"_id":"6a846ce0e63cca502f90196d","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bSequencer","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:900px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:14.5px;line-height:1.55;color:#4A4A3C;margin:0 0 10px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13px;line-height:1.55;color:#4A4A3C;margin:0 0 8px}\n.sav{display:inline-block;font-size:11.5px;color:#8C6F2A;letter-spacing:1px;text-transform:uppercase;margin:4px 0 0}\n.ghead{display:flex;align-items:baseline;gap:10px;font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:30px 0 10px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.ghead small{font-family:'Gill Sans','Trebuchet MS',sans-serif;font-size:11px;color:#8C6F2A;letter-spacing:1.5px;text-transform:uppercase}\n.ghead .gn{margin-left:auto;font-family:'Gill Sans','Trebuchet MS',sans-serif;font-size:12px;color:#8A8672}\n.sq{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:14px 16px;margin-bottom:10px}\n.sq .top{display:flex;gap:8px;align-items:center;flex-wrap:wrap}\n.sq .ti{font-weight:600;font-size:14.5px;color:#1E4D3B;flex:1;min-width:180px}\n.badge{font-size:11px;border-radius:999px;padding:2px 9px;border:1px solid;white-space:nowrap}\n.badge.sc{background:#EFE6CF;border-color:#D9C289;color:#5B4A1E;font-weight:700}\n.badge.man{background:#EAF2EC;border-color:#B8963E;color:#8C6F2A}\n.sq .de{font-size:12.5px;color:#6B6753;margin:5px 0 10px;line-height:1.45}\n.dim{display:flex;align-items:center;gap:6px;margin:7px 0;flex-wrap:wrap}\n.dim .dl{font-size:12px;color:#4A4A3C;width:132px}\n.sq-chip{border:1px solid #C9BC97;background:#fff;color:#5B4A1E;border-radius:999px;min-width:36px;padding:6px 8px;font-size:13px;cursor:pointer;text-align:center;font-family:inherit;line-height:1}\n.sq-chip.on{background:#1E4D3B;border-color:#1E4D3B;color:#F5F0E1;font-weight:700}\n.sq-chip.ov.on{background:#B8963E;border-color:#B8963E;color:#fff}\n.empty{font-size:13px;color:#8A8672;padding:8px 0}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.dim .dl{width:100%}}`,\n  render(d2){\n    const ds=d2.data('rows')||d2.data('b2b-sequencerDataSet')\n    if(!ds) return `<div class=\"wrap\"><p class=\"eyebrow\">Priorização</p><h2>Sequencer de entregáveis</h2><p class=\"lead\">Carregando dados…</p></div>`\n    const rows=(ds.records()||[]).slice()\n    const S=r=>(r.cliente||0)+(r.negocio||0)-(r.esforco||0)\n    const isRated=r=>(r.cliente||0)>0||(r.negocio||0)>0||(r.esforco||0)>0\n    const rated=rows.filter(isRated)\n    const unrated=rows.filter(r=>!isRated(r))\n    rated.sort((a,b)=>S(b)-S(a)||(a.esforco||0)-(b.esforco||0)||String(a.titulo||'').localeCompare(String(b.titulo||'')))\n    const n=rated.length\n    const sug={}\n    rated.forEach((r,i)=>{sug[String(r._id)]=n?Math.min(3,Math.floor(i*3/n)+1):1})\n    const groups={1:[],2:[],3:[]}\n    rated.forEach(r=>{const ov=r.grupo_override||0;groups[ov>0?ov:sug[String(r._id)]].push(r)})\n    const chip=(id,f,v,cur,lab,ov)=>`<button class=\"sq-chip${ov?' ov':''}${cur===v?' on':''}\" data-id=\"${id}\" data-f=\"${f}\" data-v=\"${v}\">${lab}</button>`\n    const card=r=>{\n      const s=S(r), ov=r.grupo_override||0, id=String(r._id)\n      return `<div class=\"sq\" data-key=\"${id}\">\n        <div class=\"top\"><div class=\"ti\">${r.titulo||''}</div>${isRated(r)?`<span class=\"badge sc\">score ${s>0?'+':''}${s}</span>`:''}${ov>0?`<span class=\"badge man\">manual</span>`:''}</div>\n        <div class=\"de\">${r.descricao||''}</div>\n        <div class=\"dim\"><span class=\"dl\">❤️ Agrada o cliente</span>${[1,2,3].map(v=>chip(id,'cliente',v,r.cliente||0,v)).join('')}</div>\n        <div class=\"dim\"><span class=\"dl\">💰 Agrada o negócio</span>${[1,2,3].map(v=>chip(id,'negocio',v,r.negocio||0,v)).join('')}</div>\n        <div class=\"dim\"><span class=\"dl\">🔨 Esforço</span>${[1,2,3].map(v=>chip(id,'esforco',v,r.esforco||0,v)).join('')}</div>\n        <div class=\"dim\"><span class=\"dl\">Grupo de entrega</span>${chip(id,'grupo_override',0,ov,'Auto',1)}${[1,2,3].map(v=>chip(id,'grupo_override',v,ov,v,1)).join('')}</div>\n      </div>`\n    }\n    const sec=(t,sub,list)=>list.length?`<div class=\"ghead\">${t}<small>${sub}</small><span class=\"gn\">${list.length} itens</span></div>${list.map(card).join('')}`:''\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Priorização · preenchido em reunião</p>\n  <h2>Sequencer de entregáveis</h2>\n  <p class=\"lead\">Esta é a lista dos entregáveis do projeto, derivados das decisões 📌 coletadas na Granado. Para cada item, o time dá três notas de 1 a 3: ❤️ quanto agrada o cliente e 💰 quanto agrada o negócio (1 = pouco, 3 = muito), e 🔨 esforço para construir (1 = fácil, 3 = difícil).</p>\n  <div class=\"obs\">ℹ️ O <b>score</b> sugerido é ❤️ + 💰 − 🔨, e a lista se reordena sozinha em três grupos de entrega (Grupo 1 = primeiro). A sugestão pode ser ajustada: use \"Grupo de entrega\" para forçar o grupo de um item — ele fica marcado como <b>manual</b>. Cada toque salva automaticamente; itens ainda sem notas ficam em \"A avaliar\", no fim.</div>\n  ${ds.mutating?`<span class=\"sav\">salvando…</span>`:''}\n  ${sec('Grupo 1','entrega primeiro',groups[1])}\n  ${sec('Grupo 2','na sequência',groups[2])}\n  ${sec('Grupo 3','por último',groups[3])}\n  ${sec('A avaliar','ainda sem notas',unrated)}\n  ${rows.length?'':'<p class=\"empty\">Nenhum item carregado ainda — recarregue a página.</p>'}\n</div>`\n  },\n  mounted(d2){\n    const get=()=>d2.data('rows')||d2.data('b2b-sequencerDataSet')\n    const ds0=get(); if(ds0&&ds0.refresh) ds0.refresh()\n    let saveQ=Promise.resolve()\n    d2.on('.sq-chip','click',(e,el)=>{\n      const ds=get(); if(!ds) return\n      const id=el.dataset.id, f=el.dataset.f, v=parseInt(el.dataset.v,10)\n      const rec=(ds.records()||[]).find(r=>String(r._id)===String(id)); if(!rec) return\n      const cur=rec[f]||0\n      const next=(f!=='grupo_override'&&cur===v)?0:v\n      if(next===cur) return\n      saveQ=saveQ.then(()=>get().update({_id:rec._id,[f]:next})).catch(err=>{d2.log('sequencer: falha ao salvar',err)})\n    })\n  }\n}"}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}}}}}}}},{"_id":"6a846ec141c82a824f580456","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bKpis","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:780px;margin:0 0 14px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13px;line-height:1.55;color:#4A4A3C;margin:0 0 22px}\n.obs b{color:#3B3A2E}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:14px}\n.k{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:18px 20px;box-sizing:border-box}\n.k .hd{display:flex;align-items:center;gap:10px;margin-bottom:8px}\n.k .hd .em{font-size:22px}\n.k .hd b{font-family:Georgia,serif;color:#1E4D3B;font-size:17px}\n.k .what{font-size:13.5px;color:#4A4A3C;line-height:1.55;margin-bottom:8px}\n.k .how{font-size:12.5px;color:#6B6753;line-height:1.5;background:#F8F2E2;border:1px dashed #C9BC97;border-radius:6px;padding:8px 10px}\n.k .how b{color:#5B4A1E}\n.k .base{font-size:12px;color:#8C6F2A;margin-top:8px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}}`,\n  render(d2){\n    const K=[\n['🛒','Conversão','Quantas interações no canal terminam em pedido fechado.','Pedidos concluídos no canal ÷ clientes que interagiram no período.','Hoje não há canal ativo — a primeira medição vira a linha de base.'],\n['💵','Incremento de receita','Receita adicional que o canal gera: pedidos fechados na conversa e clientes inativos reativados.','Receita dos pedidos originados no canal + receita de clientes reativados após contato ativo.','Referência: 2.287 clientes inativos na base.'],\n['⏱️','Desoneração de horas do negócio','Horas de trabalho humano que o canal economiza — calculadas por chamado aberto.','Chamados evitados (resolvidos no canal sem virar chamado no portal) × tempo médio de tratamento de um chamado.','Requer medir o tempo médio de tratamento por chamado no portal atual.'],\n['🔁','Retenção','Quantos clientes seguem comprando — a base ativa do canal.','% da base cadastrada que comprou no período, acompanhado mês a mês.','Baseline conhecido: 56,8% de base ativa (3.012 de 5.299).'],\n['😊','Satisfação do cliente','Como o cliente avalia o atendimento recebido no canal.','Nota rápida ao fim da conversa (ex.: 1 a 5); acompanhar o % de notas altas.','Hoje não há medição — a primeira coleta define a base.'],\n['💳','Redução de inadimplência','Efeito do bloqueio de boleto com pendência e dos avisos ao cliente.','Boletos vencidos sem pagamento ÷ boletos emitidos, comparando antes e depois da implantação.',''],\n['⚡','Redução do tempo de resposta','Quanto tempo o cliente espera por uma resposta útil.','Tempo até a primeira resposta resolutiva, medido por assunto.','Hoje o prazo padrão (SLA) é 48h; consultas simples devem cair para resposta imediata.']]\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Indicadores · acompanhamento da implantação</p>\n  <h2>KPIs sugeridos</h2>\n  <p class=\"lead\">Indicadores para acompanhar o auto-atendimento durante a implantação: o que cada um acompanha e uma sugestão simples de como medir.</p>\n  <div class=\"obs\">ℹ️ <b>Sugestões de partida.</b> As fórmulas e as fontes de dados exatas serão fechadas com a Granado quando as integrações estiverem definidas. Onde já existe número conhecido, ele aparece como linha de base.</div>\n  <div class=\"grid\">${K.map(k=>`<div class=\"k\"><div class=\"hd\"><span class=\"em\">${k[0]}</span><b>${k[1]}</b></div><div class=\"what\">${k[2]}</div><div class=\"how\"><b>Como medir:</b> ${k[3]}</div>${k[4]?`<div class=\"base\">${k[4]}</div>`:''}</div>`).join('')}</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a8492dbd03c638697ccf8b8","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bIntegracoes","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:980px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:14.5px;line-height:1.55;color:#4A4A3C;margin:0 0 14px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13px;line-height:1.55;color:#4A4A3C;margin:0 0 16px}\n.obs b{color:#3B3A2E}\n.pills{display:flex;gap:7px;flex-wrap:wrap;margin:0 0 18px}\n.iz-fil{border:1px solid #C9BC97;background:#FFFDF6;color:#5B4A1E;border-radius:999px;padding:7px 13px;font-size:12.5px;cursor:pointer;font-family:inherit}\n.iz-fil.on{background:#1E4D3B;border-color:#1E4D3B;color:#F5F0E1;font-weight:600}\n.sav{display:inline-block;font-size:11px;color:#8C6F2A;letter-spacing:1px;text-transform:uppercase;margin:0 0 8px}\n.ghead{display:flex;align-items:baseline;gap:10px;font-family:Georgia,serif;color:#1E4D3B;font-size:18px;margin:24px 0 10px;border-bottom:2px solid #D9C289;padding-bottom:5px}\n.ghead .gn{margin-left:auto;font-family:'Gill Sans','Trebuchet MS',sans-serif;font-size:12px;color:#8A8672}\n.ic{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:14px 16px;margin-bottom:10px}\n.ic .ht{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:8px}\n.iz-nome{flex:1;min-width:220px;border:1px solid transparent;border-radius:7px;background:transparent;padding:6px 8px;font-family:inherit;font-size:14.5px;font-weight:600;color:#1E4D3B;outline:none}\n.iz-nome:hover{border-color:#E3D9BD;background:#fff}\n.iz-nome:focus{border-color:#1E4D3B;background:#fff}\n.sysb{font-size:10.5px;letter-spacing:.8px;text-transform:uppercase;background:#1E4D3B;color:#F5F0E1;border-radius:999px;padding:3px 11px;white-space:nowrap}\n.fl{font-size:10.5px;letter-spacing:1px;text-transform:uppercase;color:#8C6F2A;margin:8px 0 3px}\n.iz-ta{width:100%;box-sizing:border-box;border:1px dashed #C9BC97;border-radius:7px;background:#FBF7EA;padding:8px 10px;font-family:inherit;font-size:12.8px;line-height:1.5;color:#4A4A3C;resize:vertical;outline:none}\n.iz-ta:focus{border-color:#1E4D3B;border-style:solid;background:#fff}\n.chiprow{display:flex;align-items:center;gap:6px;flex-wrap:wrap;margin:7px 0 0}\n.chiprow .cl{font-size:11px;color:#8A8672;width:86px}\n.iz-chip{border:1px solid #C9BC97;background:#fff;color:#5B4A1E;border-radius:999px;padding:4px 11px;font-size:11.5px;cursor:pointer;font-family:inherit;line-height:1.2}\n.iz-chip.on{background:#1E4D3B;border-color:#1E4D3B;color:#F5F0E1;font-weight:600}\n.iz-chip.crit.on{background:#B8963E;border-color:#B8963E;color:#fff}\n.add{background:#F1EBD9;border:1px solid #D5CBAA;border-radius:10px;padding:14px 16px;margin:0 0 6px}\n.add>b{display:block;font-family:Georgia,serif;color:#1E4D3B;font-size:15px;margin-bottom:9px}\n.frow{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px}\n.ai{flex:1;min-width:170px;box-sizing:border-box;border:1px solid #C9BC97;border-radius:7px;background:#fff;padding:9px 11px;font-family:inherit;font-size:13px;color:#2B2B23;outline:none}\ntextarea.ai{width:100%;resize:vertical;margin-bottom:8px}\n.ai:focus{border-color:#1E4D3B}\n.ab{border:none;background:#1E4D3B;color:#F5F0E1;border-radius:8px;padding:10px 18px;font-size:12.5px;letter-spacing:1px;text-transform:uppercase;cursor:pointer;font-family:inherit}\n.ab:hover{background:#163A2C}\n.empty{font-size:13px;color:#8A8672;padding:8px 0}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.chiprow .cl{width:100%}}`,\n  render(d2){\n    const ds=d2.data('b2b-integracoesDataSet')\n    if(!ds) return `<div class=\"wrap\"><p class=\"eyebrow\">Integrações</p><h2>Mapa de integrações</h2><p class=\"lead\">Carregando…</p></div>`\n    const all=(ds.records()||[]).slice()\n    const esc=s=>String(s||'').replace(/</g,'&lt;').replace(/\"/g,'&quot;')\n    const ORD=['Magento','JD Edwards','WebCli','OMS','Consulta Fácil / Confirma Fácil','Receita Federal','SEFAZ','Bureau de crédito','Pipefy','Salesforce','Canal de atendimento']\n    const by={}\n    all.forEach(r=>{const s=r.sistema||'Outros';(by[s]=by[s]||[]).push(r)})\n    const sistemas=ORD.filter(s=>by[s]).concat(Object.keys(by).filter(s=>ORD.indexOf(s)<0).sort())\n    const fs=d2.state.fs||''\n    const CAP=['leitura','escrita','leitura + escrita']\n    const CRIT=['essencial','importante','desejável']\n    const STAT=['a levantar','confirmada','tem API','sem API']\n    const chips=(r,f,list,cls)=>list.map(v=>`<button class=\"iz-chip${cls?' '+cls:''}${(r[f]||'')===v?' on':''}\" data-id=\"${r._id}\" data-f=\"${f}\" data-v=\"${esc(v)}\">${v}</button>`).join('')\n    const card=r=>`<div class=\"ic\" data-key=\"${r._id}\">\n      <div class=\"ht\"><input class=\"iz-nome iz-txt\" data-id=\"${r._id}\" data-f=\"nome\" value=\"${esc(r.nome)}\"/><span class=\"sysb\">${esc(r.sistema)}</span></div>\n      <div class=\"fl\">Para que serve</div><textarea class=\"iz-ta iz-txt\" data-id=\"${r._id}\" data-f=\"objetivo\" rows=\"2\">${esc(r.objetivo)}</textarea>\n      <div class=\"fl\">Dados / endpoints necessários</div><textarea class=\"iz-ta iz-txt\" data-id=\"${r._id}\" data-f=\"dados\" rows=\"2\">${esc(r.dados)}</textarea>\n      <div class=\"chiprow\"><span class=\"cl\">Capacidade</span>${chips(r,'capacidade',CAP)}</div>\n      <div class=\"chiprow\"><span class=\"cl\">Criticidade</span>${chips(r,'criticidade',CRIT,'crit')}</div>\n      <div class=\"chiprow\"><span class=\"cl\">Status TI</span>${chips(r,'status',STAT)}</div>\n      <div class=\"fl\">Anotações</div><textarea class=\"iz-ta iz-txt\" data-id=\"${r._id}\" data-f=\"anotacoes\" rows=\"2\" placeholder=\"Comentários da conversa com TI…\">${esc(r.anotacoes)}</textarea>\n    </div>`\n    const shown=fs?sistemas.filter(s=>s===fs):sistemas\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Integrações · levantamento para TI</p>\n  <h2>Mapa de integrações necessárias</h2>\n  <p class=\"lead\">Uma linha por integração que o auto-atendimento precisa, agrupadas por sistema externo. Levantadas a partir das decisões 📌 e dos fluxos mapeados — a lista é viva: edite os textos direto no card (salvam ao sair do campo), marque capacidade/criticidade/status e adicione integrações novas.</p>\n  <div class=\"obs\">ℹ️ <b>Para a conversa com TI:</b> \"Status TI\" marca o resultado do levantamento — <b>a levantar</b> (ainda não discutida), <b>confirmada</b> (TI valida o caminho), <b>tem API</b> (interface pronta) ou <b>sem API</b> (precisa alternativa). Criticidade: essencial = sem ela o fluxo não existe.</div>\n  <div class=\"pills\"><button class=\"iz-fil${fs===''?' on':''}\" data-s=\"\">Todos (${all.length})</button>${sistemas.map(s=>`<button class=\"iz-fil${fs===s?' on':''}\" data-s=\"${esc(s)}\">${s} (${by[s].length})</button>`).join('')}</div>\n  ${ds.mutating?'<span class=\"sav\">salvando…</span>':''}\n  <div class=\"add\" data-key=\"add-integ\"><b>Nova integração</b><div class=\"frow\"><input class=\"ai\" id=\"iz-serv\" placeholder=\"Sistema externo (ex.: Magento)\"/><input class=\"ai\" id=\"iz-nome\" placeholder=\"Nome da integração\"/></div><textarea class=\"ai\" id=\"iz-obj\" rows=\"2\" placeholder=\"Para que serve (1 frase)\"></textarea><button class=\"iz-add ab\">➕ Adicionar</button></div>\n  ${shown.map(s=>`<div class=\"ghead\">${s}<span class=\"gn\">${by[s].length} integrações</span></div>${by[s].map(card).join('')}`).join('')}\n  ${all.length?'':'<p class=\"empty\">Carregando itens…</p>'}\n</div>`\n  },\n  mounted(d2){\n    const get=()=>d2.data('b2b-integracoesDataSet')\n    const ds0=get(); if(ds0&&ds0.refresh)ds0.refresh()\n    let q=Promise.resolve()\n    const save=(id,f,v)=>{q=q.then(()=>get().update({_id:id,[f]:v})).catch(err=>d2.log('integracoes: falha ao salvar',err))}\n    d2.on('.iz-fil','click',(e,el)=>{d2.state.fs=el.dataset.s})\n    d2.on('.iz-chip','click',(e,el)=>save(el.dataset.id,el.dataset.f,el.dataset.v))\n    d2.on('.iz-txt','blur',(e,el)=>{\n      const ds=get(); if(!ds)return\n      const r=(ds.records()||[]).find(x=>String(x._id)===String(el.dataset.id)); if(!r)return\n      const f=el.dataset.f\n      if((r[f]||'')!==el.value) save(r._id,f,el.value)\n    })\n    d2.on('.iz-add','click',()=>{\n      const ds=get(); if(!ds)return\n      const V=id=>{const el=d2.$('#'+id);return el?String(el.value).trim():''}\n      const sis=V('iz-serv')||'Outros', nome=V('iz-nome'), obj=V('iz-obj')\n      if(!nome) return\n      const key=nome.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,40)+'-'+Date.now().toString(36)\n      q=q.then(()=>get().create({key:key,sistema:sis,nome:nome,capacidade:'leitura',objetivo:obj,dados:'',criticidade:'importante',status:'a levantar',anotacoes:''})).catch(err=>d2.log('integracoes: falha ao adicionar',err))\n      ;['iz-serv','iz-nome','iz-obj'].forEach(id=>{const el=d2.$('#'+id);if(el)el.value=''})\n    })\n  }\n}"}}}}}}}},{"_id":"6a84a26786515e6927b4103d","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granadoEscuta","package":"granado-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 CAT = {\n  entrega: { label: 'Entrega & logística', cor: '#B4552D' },\n  'pos-venda': { label: 'Pós-venda', cor: '#8E5A9E' },\n  atendimento: { label: 'Atendimento / SAC', cor: '#C0392B' },\n  preco: { label: 'Preço', cor: '#B78A2E' },\n  qualidade: { label: 'Qualidade do produto', cor: '#2E7D6B' },\n  disponibilidade: { label: 'Disponibilidade', cor: '#3A6EA5' },\n  'site-app': { label: 'Site & app', cor: '#5B6770' },\n  fidelidade: { label: 'Clube de fidelidade', cor: '#A8763E' },\n  evento: { label: 'Eventos & experiências', cor: '#7A8B3C' },\n  reputacao: { label: 'Reputação da marca', cor: '#8B2E3F' },\n  fraude: { label: 'Golpe / perfil falso', cor: '#6E2C2C' },\n  duvida: { label: 'Dúvida', cor: '#6B7280' },\n  elogio: { label: 'Elogio', cor: '#2E7D32' },\n  outro: { label: 'Outro', cor: '#6B7280' }\n}\nconst URG = {\n  alta: { label: 'Alta', cor: '#B3261E' },\n  media: { label: 'Média', cor: '#B78A2E' },\n  baixa: { label: 'Baixa', cor: '#5B6770' }\n}\nconst STATUS = {\n  novo: { label: 'Não tratado', cor: '#B3261E' },\n  em_atendimento: { label: 'Em atendimento', cor: '#B78A2E' },\n  respondido: { label: 'Respondido', cor: '#2E7D6B' },\n  arquivado: { label: 'Arquivado', cor: '#8A8A8A' }\n}\nconst PLAT = {\n  instagram: { label: 'Instagram', curto: 'IG', cor: '#B4407A', handle: '@granado' },\n  tiktok: { label: 'TikTok', curto: 'TT', cor: '#1B2B24', handle: '@granadopharmacias' },\n  reclameaqui: { label: 'Reclame AQUI', curto: 'RA', cor: '#2E7D6B', handle: 'granado-pharmacias' }\n}\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n  .replace(/\"/g, '&quot;').replace(/'/g, '&#39;')\n\nconst catInfo = (k) => CAT[k] || { label: k || 'Sem categoria', cor: '#6B7280' }\nconst urgInfo = (k) => URG[k] || URG.baixa\nconst stInfo = (k) => STATUS[k] || STATUS.novo\nconst platInfo = (k) => PLAT[k] || { label: k || 'Outro canal', curto: '??', cor: '#6B7280', handle: '' }\n\nfunction dia (iso) {\n  if (!iso) return ''\n  const d = new Date(iso)\n  return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit' })\n}\nfunction dataHora (iso) {\n  if (!iso) return ''\n  const d = new Date(iso)\n  return d.toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: '2-digit' }) +\n    ' · ' + d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })\n}\nfunction num (n) { return (n || 0).toLocaleString('pt-BR') }\nfunction iniciais (nome, autor) {\n  const base = (nome || autor || '?').trim()\n  const p = base.split(/[\\s._-]+/).filter(Boolean)\n  return ((p[0] || '?')[0] + (p[1] ? p[1][0] : '')).toUpperCase()\n}\nfunction prioridade (c) {\n  const peso = { alta: 60, media: 30, baixa: 10 }[c.urgencia] || 10\n  const eng = Math.min(25, (c.curtidas || 0) * 3 + (c.respostas || 0) * 5)\n  const canal = c.plataforma === 'reclameaqui' ? 12 : 0\n  const semResposta = (c.statusOrigem || '').toUpperCase().indexOf('NAO RESPONDIDA') === 0 ? 10 : 0\n  const dias = (Date.now() - new Date(c.data || Date.now()).getTime()) / 86400000\n  const fresco = Math.max(0, 15 - dias * 0.4)\n  return Math.round(peso + eng + canal + semResposta + fresco)\n}\nfunction agrupar (arr) {\n  const m = {}\n  ;(arr || []).forEach(x => { m[x._id] = x.n })\n  return m\n}\n\nexport default {\n  css: `\n:host { display:block; height:100%; }\n* { box-sizing:border-box; }\n.wrap {\n  height:100%; display:flex; overflow:hidden;\n  background:#F2EFE7; color:#1B2B24;\n  font-family:'Inter','Helvetica Neue',Helvetica,Arial,sans-serif;\n  font-size:14px; line-height:1.45; -webkit-font-smoothing:antialiased;\n}\n.serif { font-family:'Playfair Display',Georgia,'Times New Roman',serif; }\n\n.side {\n  width:244px; flex:0 0 244px; background:#0E3B2C; color:#E9E4D6;\n  display:flex; flex-direction:column; padding:26px 18px; gap:22px; overflow-y:auto;\n}\n.brand { display:flex; flex-direction:column; gap:4px; }\n.brand .mark { font-size:19px; letter-spacing:.18em; text-transform:uppercase; color:#F0EADA; }\n.brand .sub { font-size:10.5px; letter-spacing:.22em; text-transform:uppercase; color:#7FA292; }\n.fontes { display:flex; flex-direction:column; gap:7px; }\n.fonte { display:flex; align-items:center; gap:9px; padding:9px 11px; border:1px solid rgba(233,228,214,.16); border-radius:10px; background:rgba(255,255,255,.03); }\n.fonte .dot { width:7px; height:7px; border-radius:50%; flex:0 0 auto; }\n.fonte .dot.on { background:#5FCB8E; box-shadow:0 0 0 3px rgba(95,203,142,.16); }\n.fonte .dot.off { background:#C8A34A; box-shadow:0 0 0 3px rgba(200,163,74,.14); }\n.fonte .txt { min-width:0; }\n.fonte .h { font-size:12.5px; color:#F0EADA; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }\n.fonte .p { font-size:10px; color:#7FA292; letter-spacing:.06em; text-transform:uppercase; }\n.nav { display:flex; flex-direction:column; gap:3px; }\n.nav button {\n  all:unset; cursor:pointer; padding:10px 12px; border-radius:9px; font-size:13.5px; color:#B9CEC3;\n  display:flex; align-items:center; justify-content:space-between; gap:8px; transition:background .16s,color .16s;\n}\n.nav button:hover { background:rgba(255,255,255,.06); color:#F0EADA; }\n.nav button.on { background:#C8A34A; color:#1B2B24; font-weight:600; }\n.nav .pill { font-size:11px; padding:1px 7px; border-radius:99px; background:rgba(255,255,255,.12); color:inherit; }\n.nav button.on .pill { background:rgba(27,43,36,.18); }\n.side .foot { margin-top:auto; font-size:10.5px; color:#6F8E80; line-height:1.6; letter-spacing:.03em; padding-top:8px; }\n.side .foot b { color:#9BB6A9; font-weight:500; }\n\n.main { flex:1; min-width:0; overflow-y:auto; padding:28px 32px 60px; }\n.top { display:flex; align-items:flex-end; justify-content:space-between; gap:20px; margin-bottom:22px; flex-wrap:wrap; }\n.top h1 { font-size:27px; margin:0; font-weight:500; letter-spacing:-.01em; }\n.top p { margin:5px 0 0; color:#5C6F66; font-size:13px; }\n.btn {\n  all:unset; cursor:pointer; padding:9px 16px; border-radius:8px; font-size:13px; font-weight:500;\n  background:#0E3B2C; color:#F0EADA; transition:opacity .16s, transform .08s; white-space:nowrap;\n}\n.btn:hover { opacity:.88; }\n.btn:active { transform:translateY(1px); }\n.btn.ghost { background:transparent; color:#0E3B2C; border:1px solid rgba(14,59,44,.25); }\n.btn.gold { background:#C8A34A; color:#1B2B24; }\n.btn.sm { padding:6px 11px; font-size:12px; border-radius:7px; }\n.btn[disabled] { opacity:.5; cursor:default; }\n\n.grid { display:grid; gap:14px; }\n.kpis { grid-template-columns:repeat(auto-fit,minmax(184px,1fr)); margin-bottom:18px; }\n.card { background:#FBFAF6; border:1px solid #E2DCCC; border-radius:14px; padding:18px; }\n.kpi .lab { font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:#7B8A82; }\n.kpi .val { font-size:31px; margin-top:7px; letter-spacing:-.02em; }\n.kpi .hint { font-size:12px; color:#7B8A82; margin-top:3px; }\n.kpi.alerta .val { color:#B3261E; }\n.kpi.gold .val { color:#A8763E; }\n\n.sec { margin-top:22px; }\n.sec h2 { font-size:15px; font-weight:600; margin:0 0 12px; letter-spacing:.01em; display:flex; align-items:center; gap:9px; }\n.sec h2 .n { font-size:11.5px; color:#7B8A82; font-weight:400; }\n\n.bars { display:flex; flex-direction:column; gap:9px; }\n.bar { display:grid; grid-template-columns:150px 1fr 42px; align-items:center; gap:11px; font-size:12.5px; }\n.bar .lbl { color:#41574D; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.bar .track { height:9px; background:#EAE5D6; border-radius:99px; overflow:hidden; }\n.bar .fill { height:100%; border-radius:99px; }\n.bar .qt { text-align:right; color:#5C6F66; font-variant-numeric:tabular-nums; }\n.bar.clickable { cursor:pointer; }\n.bar.clickable:hover .lbl { text-decoration:underline; }\n\n.two { display:grid; grid-template-columns:1.35fr 1fr; gap:14px; align-items:start; }\n@media (max-width:1080px){ .two { grid-template-columns:1fr; } }\n\n.postrow { display:flex; gap:12px; align-items:center; padding:10px; border-radius:11px; cursor:pointer; transition:background .15s; }\n.postrow:hover { background:#F3F0E6; }\n.postrow img { width:52px; height:52px; border-radius:9px; object-fit:cover; flex:0 0 auto; background:#E5E0D2; }\n.postrow .t { flex:1; min-width:0; }\n.postrow .cap { font-size:12.5px; color:#334840; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }\n.postrow .meta { font-size:11px; color:#8A968F; margin-top:3px; }\n.pct { font-size:15px; font-weight:600; font-variant-numeric:tabular-nums; flex:0 0 auto; }\n.pct.hi { color:#B3261E; } .pct.mid { color:#B78A2E; } .pct.lo { color:#5C6F66; }\n\n.filters { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:11px; }\n.chip {\n  all:unset; cursor:pointer; padding:6px 12px; border-radius:99px; font-size:12.5px;\n  background:#FBFAF6; border:1px solid #E2DCCC; color:#41574D; transition:all .15s;\n  display:inline-flex; align-items:center; gap:6px;\n}\n.chip:hover { border-color:#C8A34A; }\n.chip.on { background:#0E3B2C; color:#F0EADA; border-color:#0E3B2C; }\n.chip .cdot { width:7px; height:7px; border-radius:50%; }\n.search { flex:1; min-width:180px; }\n.search input {\n  width:100%; padding:8px 13px; border-radius:8px; border:1px solid #E2DCCC; background:#FBFAF6;\n  font-family:inherit; font-size:13px; color:#1B2B24; outline:none;\n}\n.search input:focus { border-color:#C8A34A; }\n\n.caso { background:#FBFAF6; border:1px solid #E2DCCC; border-radius:13px; padding:15px 16px; display:flex; gap:13px; cursor:pointer; transition:border-color .15s, box-shadow .15s; }\n.caso:hover { border-color:#C8A34A; box-shadow:0 3px 14px rgba(27,43,36,.07); }\n.caso.sel { border-color:#0E3B2C; box-shadow:0 3px 14px rgba(27,43,36,.1); }\n.av { width:38px; height:38px; border-radius:50%; flex:0 0 auto; display:grid; place-items:center; font-size:12px; font-weight:600; color:#F0EADA; background:#3C6252; }\n.caso .body { flex:1; min-width:0; }\n.caso .who { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }\n.caso .who b { font-size:13.5px; font-weight:600; }\n.caso .who span { font-size:11.5px; color:#8A968F; }\n.caso .tit { font-size:13.5px; font-weight:600; margin-top:3px; }\n.caso .txt { margin-top:5px; font-size:13px; color:#2B3D35; overflow:hidden; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; }\n.tags { display:flex; gap:6px; margin-top:9px; flex-wrap:wrap; align-items:center; }\n.tag { font-size:11px; padding:2.5px 9px; border-radius:99px; letter-spacing:.02em; white-space:nowrap; }\n.tag.cat { color:#fff; }\n.tag.urg { border:1px solid currentColor; background:transparent; }\n.tag.st { background:#EDE8DA; color:#5C6F66; }\n.tag.plat { color:#fff; font-weight:500; }\n.tag.orig { background:#FBECEA; color:#B3261E; border:1px solid #F0D9D4; }\n.score { flex:0 0 auto; text-align:right; }\n.score .n { font-size:19px; font-weight:600; font-variant-numeric:tabular-nums; }\n.score .l { font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:#8A968F; }\n\n.list { display:flex; flex-direction:column; gap:9px; }\n.empty { padding:34px; text-align:center; color:#8A968F; font-size:13px; }\n\n.scrim { position:fixed; inset:0; background:rgba(14,32,25,.42); backdrop-filter:blur(1.5px); z-index:40; }\n.drawer {\n  position:fixed; top:0; right:0; bottom:0; width:min(540px,94vw); background:#FBFAF6; z-index:41;\n  box-shadow:-18px 0 44px rgba(14,32,25,.2); display:flex; flex-direction:column;\n  animation:slide .22s cubic-bezier(.2,.8,.3,1);\n}\n@keyframes slide { from { transform:translateX(26px); opacity:.5 } to { transform:none; opacity:1 } }\n.drawer .dh { padding:20px 22px 14px; border-bottom:1px solid #E8E2D2; display:flex; justify-content:space-between; align-items:flex-start; gap:12px; }\n.drawer .dh h3 { margin:0; font-size:17px; font-weight:500; }\n.drawer .db { padding:20px 22px 30px; overflow-y:auto; flex:1; }\n.drawer .df { padding:14px 22px; border-top:1px solid #E8E2D2; display:flex; gap:8px; flex-wrap:wrap; background:#F6F3EA; }\n.x { all:unset; cursor:pointer; font-size:20px; line-height:1; color:#8A968F; padding:2px 6px; }\n.x:hover { color:#1B2B24; }\n.quote { background:#F2EFE4; border-left:3px solid #C8A34A; padding:13px 15px; border-radius:0 9px 9px 0; font-size:13.5px; color:#243830; white-space:pre-wrap; max-height:260px; overflow-y:auto; }\n.kv { display:grid; grid-template-columns:126px 1fr; gap:7px 12px; font-size:12.5px; margin-top:16px; }\n.kv .k { color:#8A968F; }\n.mini { display:flex; gap:11px; align-items:center; margin-top:16px; padding:11px; background:#F2EFE4; border-radius:11px; cursor:pointer; }\n.mini img { width:46px; height:46px; border-radius:8px; object-fit:cover; background:#E5E0D2; }\n.mini .c { font-size:12px; color:#41574D; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }\n.mini .m { font-size:11px; color:#8A968F; margin-top:2px; }\ntextarea.resp {\n  width:100%; min-height:132px; margin-top:9px; padding:13px; border-radius:10px; border:1px solid #E2DCCC;\n  background:#fff; font-family:inherit; font-size:13.5px; line-height:1.55; color:#1B2B24; resize:vertical; outline:none;\n}\ntextarea.resp:focus { border-color:#C8A34A; }\n.lbl-s { font-size:11px; letter-spacing:.13em; text-transform:uppercase; color:#7B8A82; margin-top:20px; display:block; }\n.load { display:inline-flex; align-items:center; gap:7px; font-size:12.5px; color:#7B8A82; }\n.spin { width:12px; height:12px; border:2px solid #D8D0BC; border-top-color:#0E3B2C; border-radius:50%; animation:sp .7s linear infinite; }\n@keyframes sp { to { transform:rotate(360deg) } }\n\n.ai { background:linear-gradient(180deg,#0E3B2C,#14503B); color:#EDE7D8; border-radius:14px; padding:20px 22px; }\n.ai h3 { margin:0 0 4px; font-size:16px; font-weight:500; }\n.ai .tagline { font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#8CB3A1; }\n.ai p { font-size:13.5px; line-height:1.62; color:#DCD6C6; margin:12px 0 0; }\n.ai .rec { margin-top:13px; padding:12px 14px; border-radius:10px; background:rgba(200,163,74,.14); border-left:3px solid #C8A34A; font-size:13px; color:#F0EADA; }\n\n.alerta { display:flex; gap:12px; padding:14px 16px; border-radius:12px; background:#FBF1EF; border:1px solid #F0D9D4; }\n.alerta.media { background:#FBF6EA; border-color:#EFE2C4; }\n.alerta .ic { width:9px; height:9px; border-radius:50%; background:#B3261E; margin-top:6px; flex:0 0 auto; }\n.alerta.media .ic { background:#B78A2E; }\n.alerta .t { font-size:13.5px; font-weight:600; }\n.alerta .d { font-size:12.5px; color:#5C6F66; margin-top:3px; }\n.alerta .go { margin-left:auto; align-self:center; }\n\n.repcard { display:flex; gap:16px; align-items:center; }\n.repnota { width:74px; height:74px; border-radius:50%; display:grid; place-items:center; flex:0 0 auto; background:#F2EFE4; border:2px solid #C8A34A; }\n.repnota b { font-size:22px; font-family:'Playfair Display',Georgia,serif; }\n.repnota span { font-size:9px; letter-spacing:.1em; color:#8A968F; text-transform:uppercase; }\n.repinfo { min-width:0; }\n.repinfo .rt { font-size:15px; font-weight:600; }\n.repinfo .rd { font-size:12.5px; color:#5C6F66; margin-top:3px; }\n\n.tema { border:1px solid #E2DCCC; border-radius:13px; overflow:hidden; background:#FBFAF6; }\n.tema .th { display:flex; align-items:center; gap:11px; padding:14px 16px; cursor:pointer; }\n.tema .th:hover { background:#F5F2E8; }\n.tema .sw { width:10px; height:10px; border-radius:3px; flex:0 0 auto; }\n.tema .tt { flex:1; font-size:14px; font-weight:600; }\n.tema .tn { font-size:12.5px; color:#5C6F66; font-variant-numeric:tabular-nums; }\n.tema .tb { padding:0 16px 14px; display:flex; flex-direction:column; gap:8px; }\n.tema .ex { font-size:12.5px; color:#41574D; padding:9px 12px; background:#F2EFE4; border-radius:9px; cursor:pointer; }\n.tema .ex:hover { background:#EDE8DA; }\n.tema .ex b { color:#1B2B24; }\n\n.postgrid { display:grid; grid-template-columns:repeat(auto-fill,minmax(215px,1fr)); gap:13px; }\n.pcard { background:#FBFAF6; border:1px solid #E2DCCC; border-radius:13px; overflow:hidden; cursor:pointer; transition:box-shadow .16s, transform .16s; }\n.pcard:hover { box-shadow:0 5px 18px rgba(27,43,36,.1); transform:translateY(-1px); }\n.pcard .im { width:100%; height:150px; object-fit:cover; display:block; background:#E5E0D2; }\n.pcard .pb { padding:12px 13px 14px; }\n.pcard .pc { font-size:12px; color:#41574D; height:33px; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }\n.pcard .pm { display:flex; justify-content:space-between; align-items:baseline; margin-top:9px; font-size:11.5px; color:#8A968F; }\n\n.tester { display:grid; gap:10px; }\n.tester textarea {\n  width:100%; min-height:78px; padding:12px; border-radius:10px; border:1px solid #E2DCCC; background:#fff;\n  font-family:inherit; font-size:13.5px; color:#1B2B24; resize:vertical; outline:none;\n}\n.tester textarea:focus { border-color:#C8A34A; }\n.res { display:flex; gap:8px; flex-wrap:wrap; align-items:center; }\n\n.toast {\n  position:fixed; left:50%; bottom:26px; transform:translateX(-50%); z-index:50;\n  background:#0E3B2C; color:#F0EADA; padding:12px 20px; border-radius:10px; font-size:13px;\n  box-shadow:0 10px 30px rgba(14,32,25,.25); animation:up .2s ease;\n}\n@keyframes up { from { transform:translate(-50%,8px); opacity:0 } to { transform:translate(-50%,0); opacity:1 } }\n\n.center { display:grid; place-items:center; height:100%; width:100%; gap:12px; text-align:center; }\n.dotline { display:flex; gap:5px; }\n.dotline i { width:7px; height:7px; border-radius:50%; background:#0E3B2C; animation:bl 1s infinite; }\n.dotline i:nth-child(2){ animation-delay:.15s } .dotline i:nth-child(3){ animation-delay:.3s }\n@keyframes bl { 0%,100%{opacity:.25} 50%{opacity:1} }\n\n/* o player aplica display:flex em toda div dentro do shadow root — devolvemos block onde o empilhamento importa */\n.card, .sec:not(.two), .list, .grid > .card,\n.kpi .lab, .kpi .val, .kpi .hint,\n.brand .mark, .brand .sub, .fonte .txt, .fonte .h, .fonte .p, .side .foot,\n.top > div, .top h1, .top p,\n.bar .lbl, .bar .track, .bar .qt,\n.postrow .t, .postrow .meta,\n.caso .body, .caso .tit, .score, .score .n, .score .l,\n.alerta > div, .alerta .t, .alerta .d,\n.repinfo, .repinfo .rt, .repinfo .rd,\n.tema, .tema .tt, .tema .tn, .tema .ex,\n.pcard, .pcard .pb,\n.ai, .ai p, .ai .rec, .ai .tagline,\n.quote, .kv > div, .mini > div, .mini .m,\n.drawer .db, .drawer .dh > div, .empty, .lbl-s, .res > div, .search { display:block; }\n`,\n\n  render (d2) {\n    const s = d2.state\n    if (!s.pronto) {\n      return `<div class=\"wrap\"><div class=\"center\">\n        <div class=\"serif\" style=\"font-size:20px;letter-spacing:.14em;text-transform:uppercase\">Granado · Escuta</div>\n        <div class=\"dotline\"><i></i><i></i><i></i></div>\n        <div style=\"font-size:12.5px;color:#7B8A82\">${s.erro ? esc(s.erro) : 'carregando o monitoramento dos canais…'}</div>\n      </div></div>`\n    }\n    const casos = s.casos || []\n    const posts = s.posts || []\n    const naoTratados = casos.filter(c => (c.status || 'novo') === 'novo').length\n    const altas = casos.filter(c => c.urgencia === 'alta').length\n    const tab = s.tab || 'radar'\n    const nav = [\n      ['radar', 'Radar', ''],\n      ['fila', 'Fila de casos', String(naoTratados)],\n      ['temas', 'Temas', String((s.porCategoria || []).length)],\n      ['posts', 'Publicações', String(posts.length)]\n    ]\n    const porPlat = agrupar(s.porPlataforma)\n    const fontes = [\n      ['instagram', porPlat.instagram ? `${num(porPlat.instagram)} comentários` : 'sem dados', !!porPlat.instagram],\n      ['tiktok', porPlat.tiktok ? `${num(porPlat.tiktok)} comentários` : 'aguardando conexão', !!porPlat.tiktok],\n      ['reclameaqui', porPlat.reclameaqui ? `${num(porPlat.reclameaqui)} reclamações` : 'sem dados', !!porPlat.reclameaqui]\n    ]\n    return `<div class=\"wrap\">\n      <aside class=\"side\">\n        <div class=\"brand\">\n          <div class=\"mark serif\">Granado</div>\n          <div class=\"sub\">Escuta social</div>\n        </div>\n        <div class=\"fontes\">\n          ${fontes.map(([k, txt, on]) => {\n            const p = platInfo(k)\n            return `<div class=\"fonte\" data-key=\"${k}\">\n              <div class=\"dot ${on ? 'on' : 'off'}\"></div>\n              <div class=\"txt\"><div class=\"h\">${esc(p.handle)}</div><div class=\"p\">${esc(p.label)} · ${esc(txt)}</div></div>\n            </div>`\n          }).join('')}\n        </div>\n        <nav class=\"nav\">\n          ${nav.map(([k, l, n]) => `<button data-tab=\"${k}\" class=\"${tab === k ? 'on' : ''}\">${l}${n && n !== '0' ? `<span class=\"pill\">${n}</span>` : ''}</button>`).join('')}\n        </nav>\n        <div class=\"foot\">\n          <b>${num(s.totalComentarios)}</b> manifestações monitoradas<br>\n          <b>${num(casos.length)}</b> negativas em aberto<br>\n          última varredura: <b>${s.ultimaVarredura ? dataHora(s.ultimaVarredura) : '—'}</b>\n        </div>\n      </aside>\n      <main class=\"main\">${\n        tab === 'radar' ? viewRadar(d2, s, casos, posts, naoTratados, altas)\n        : tab === 'fila' ? viewFila(d2, s, casos, posts)\n        : tab === 'temas' ? viewTemas(d2, s, casos)\n        : viewPosts(d2, s, posts)\n      }</main>\n      ${s.aberto ? drawer(d2, s, casos, posts) : ''}\n      ${s.toast ? `<div class=\"toast\">${esc(s.toast)}</div>` : ''}\n    </div>`\n  },\n\n  mounted (d2, root) {\n    d2.state.tab = d2.state.tab || 'radar'\n    carregar(d2)\n\n    const aoTopo = () => requestAnimationFrame(() => { const m = d2.$('.main'); if (m) m.scrollTop = 0 })\n    d2.on('[data-tab]', 'click', (e) => {\n      d2.state.tab = e.target.closest('[data-tab]').dataset.tab\n      d2.state.aberto = null\n      aoTopo()\n    })\n    d2.on('[data-caso]', 'click', (e) => {\n      d2.state.aberto = e.target.closest('[data-caso]').dataset.caso\n      d2.state.respRascunho = null\n    })\n    d2.on('[data-fechar]', 'click', () => { d2.state.aberto = null })\n    d2.on('[data-filtro-urg]', 'click', (e) => {\n      const v = e.target.closest('[data-filtro-urg]').dataset.filtroUrg\n      d2.state.fUrg = d2.state.fUrg === v ? null : v\n    })\n    d2.on('[data-filtro-cat]', 'click', (e) => {\n      const v = e.target.closest('[data-filtro-cat]').dataset.filtroCat\n      d2.state.fCat = d2.state.fCat === v ? null : v\n    })\n    d2.on('[data-filtro-st]', 'click', (e) => {\n      const v = e.target.closest('[data-filtro-st]').dataset.filtroSt\n      d2.state.fSt = d2.state.fSt === v ? null : v\n    })\n    d2.on('[data-filtro-plat]', 'click', (e) => {\n      const v = e.target.closest('[data-filtro-plat]').dataset.filtroPlat\n      d2.state.fPlat = d2.state.fPlat === v ? null : v\n    })\n    d2.on('[data-busca]', 'input', (e) => { d2.state.busca = e.target.value })\n    d2.on('[data-ir-fila]', 'click', (e) => {\n      const el = e.target.closest('[data-ir-fila]')\n      const cat = el.dataset.irFila\n      const plat = el.dataset.plat\n      const urg = el.dataset.urg\n      d2.setState({ fCat: cat || null, fPlat: plat || null, fUrg: urg || null, fSt: el.dataset.st || null, tab: 'fila' })\n      aoTopo()\n    })\n    d2.on('[data-toggle-tema]', 'click', (e) => {\n      const k = e.target.closest('[data-toggle-tema]').dataset.toggleTema\n      d2.state.temaAberto = d2.state.temaAberto === k ? null : k\n    })\n    d2.on('[data-sugerir]', 'click', () => sugerir(d2))\n    d2.on('[data-resp]', 'input', (e) => { d2.state.respRascunho = e.target.value })\n    d2.on('[data-status]', 'click', (e) => salvarStatus(d2, e.target.closest('[data-status]').dataset.status))\n    d2.on('[data-recarregar]', 'click', () => carregar(d2))\n    d2.on('[data-varrer]', 'click', () => varrer(d2))\n    d2.on('[data-diagnostico]', 'click', () => diagnostico(d2))\n    d2.on('[data-teste-txt]', 'input', (e) => { d2.state.testeTxt = e.target.value })\n    d2.on('[data-testar]', 'click', () => testar(d2))\n    d2.on('[data-abrir]', 'click', (e) => {\n      const link = e.target.closest('[data-abrir]').dataset.abrir\n      if (link) window.open(link, '_blank', 'noopener')\n    })\n  }\n}\n\n/* ============================ dados ============================ */\n\nfunction toast (d2, msg) {\n  d2.state.toast = msg\n  setTimeout(() => { d2.state.toast = null }, 4200)\n}\n\nasync function carregar (d2) {\n  try {\n    const r = await d2.rush('scr-dados').invoke({})\n    const dados = r && r.data ? r.data : r\n    const casos = (dados.casos || []).map(c => ({ ...c, score: prioridade(c) }))\n      .sort((a, b) => b.score - a.score)\n    const datas = (dados.posts || []).map(p => p.data).filter(Boolean).sort()\n    const alertas = (dados.alertas || []).slice()\n      .sort((a, b) => new Date(b.criadoEm || 0) - new Date(a.criadoEm || 0))\n    const ultima = alertas.length ? alertas[0].criadoEm : null\n    const lote = alertas.filter(a => a.criadoEm === ultima)\n    const contaRA = (dados.conta || []).find(c => c.plataforma === 'reclameaqui') || null\n    d2.setState({\n      pronto: true,\n      erro: null,\n      casos,\n      posts: (dados.posts || []).slice().sort((a, b) => new Date(b.data) - new Date(a.data)),\n      totalComentarios: dados.totalComentarios || 0,\n      porSentimento: dados.porSentimento || [],\n      porCategoria: dados.porCategoria || [],\n      porUrgencia: dados.porUrgencia || [],\n      porPlataforma: dados.porPlataforma || [],\n      negPorPlataforma: dados.negPorPlataforma || [],\n      alertas: lote,\n      ultimaVarredura: ultima,\n      contaRA,\n      periodo: datas.length ? `${dia(datas[0])} – ${dia(datas[datas.length - 1])}` : '—'\n    })\n  } catch (e) {\n    d2.setState({ erro: 'não consegui carregar os dados: ' + (e && e.message ? e.message : e) })\n  }\n}\n\nasync function varrer (d2) {\n  d2.state.varrendo = true\n  try {\n    const r = await d2.rush('scr-varredura').invoke({})\n    const out = (r && r.data ? r.data : r) || {}\n    await carregar(d2)\n    d2.state.varrendo = false\n    const novos = out.novosReclameAqui || 0\n    toast(d2, novos > 0\n      ? `Varredura concluída — ${novos} nova(s) reclamação(ões) no Reclame AQUI, ${out.classificados || 0} item(ns) classificado(s).`\n      : 'Varredura concluída — nenhuma reclamação nova desde a última checagem.')\n  } catch (e) {\n    d2.setState({ varrendo: false })\n    toast(d2, 'A varredura falhou. Veja os logs do listener scr-varredura.')\n  }\n}\n\nasync function sugerir (d2) {\n  const c = (d2.state.casos || []).find(x => x.id === d2.state.aberto)\n  if (!c) return\n  d2.state.gerando = true\n  try {\n    const r = await d2.rush('scr-sugerir-resposta').invoke({\n      texto: c.texto, autor: c.autor, categoria: c.categoria,\n      plataforma: c.plataforma, titulo: c.titulo || '', local: c.local || ''\n    })\n    const out = r && r.data ? r.data : r\n    d2.setState({ gerando: false, respRascunho: (out.resposta || '').trim() })\n  } catch (e) {\n    d2.setState({ gerando: false, respRascunho: 'não foi possível gerar agora — tente de novo.' })\n  }\n}\n\nasync function salvarStatus (d2, status) {\n  const c = (d2.state.casos || []).find(x => x.id === d2.state.aberto)\n  if (!c) return\n  d2.state.salvando = true\n  const resposta = d2.state.respRascunho != null ? d2.state.respRascunho : (c.resposta || '')\n  try {\n    await d2.rush('scr-atualizar-caso').invoke({\n      id: c.id, status, responsavel: 'Time social Granado', resposta\n    })\n    const casos = (d2.state.casos || []).map(x => x.id === c.id ? { ...x, status, resposta } : x)\n    d2.setState({ salvando: false, casos, aberto: status === 'novo' ? d2.state.aberto : null })\n    toast(d2, status === 'respondido' ? 'Caso marcado como respondido.' : status === 'arquivado' ? 'Caso arquivado.' : 'Caso assumido pelo time.')\n  } catch (e) {\n    d2.setState({ salvando: false })\n  }\n}\n\nasync function diagnostico (d2) {\n  d2.state.diagnosticando = true\n  const casos = d2.state.casos || []\n  const cats = (d2.state.porCategoria || []).slice(0, 6)\n    .map(c => `${catInfo(c._id).label}: ${c.n}`).join(', ')\n  const plats = (d2.state.negPorPlataforma || [])\n    .map(p => `${platInfo(p._id).label}: ${p.n}`).join(', ')\n  const amostra = casos.slice(0, 22).map(c => `(${platInfo(c.plataforma).label}/${catInfo(c.categoria).label}) ${c.titulo ? c.titulo + ' — ' : ''}${(c.texto || '').slice(0, 400)}`).join(' | ').slice(0, 5200)\n  const resumo = `${d2.state.totalComentarios} manifestações monitoradas nos canais da Granado, ${casos.length} negativas. Por canal: ${plats}. Temas: ${cats}. Urgência alta: ${casos.filter(c => c.urgencia === 'alta').length}.`\n  try {\n    const r = await d2.rush('scr-gerar-insight').invoke({ resumo, amostra })\n    const out = r && r.data ? r.data : r\n    let ins = out.insight\n    if (typeof ins === 'string') { try { ins = JSON.parse(ins) } catch (_) { ins = { titulo: 'Diagnóstico', corpo: ins, recomendacao: '' } } }\n    d2.setState({ diagnosticando: false, diagnostico: ins })\n  } catch (e) {\n    d2.setState({ diagnosticando: false })\n  }\n}\n\nasync function testar (d2) {\n  const t = (d2.state.testeTxt || '').trim()\n  if (!t) return\n  d2.state.testando = true\n  try {\n    const r = await d2.rush('scr-classificar').invoke({ texto: t })\n    const out = r && r.data ? r.data : r\n    let a = out.analise\n    if (typeof a === 'string') { try { a = JSON.parse(a) } catch (_) { a = null } }\n    d2.setState({ testando: false, teste: a })\n  } catch (e) {\n    d2.setState({ testando: false, teste: null })\n  }\n}\n\n/* ============================ views ============================ */\n\nfunction viewRadar (d2, s, casos, posts, naoTratados, altas) {\n  const total = s.totalComentarios || 1\n  const pctNeg = Math.round((casos.length / total) * 100)\n  const cats = (s.porCategoria || []).filter(c => c._id && c._id !== 'duvida' && c._id !== 'elogio').slice(0, 8)\n  const maxCat = Math.max(1, ...cats.map(c => c.n))\n  const criticos = posts.filter(p => (p.monitorados || 0) >= 8 && (p.pctNegativo || 0) >= 40)\n    .sort((a, b) => b.pctNegativo - a.pctNegativo).slice(0, 5)\n  const dg = s.diagnostico\n  const alertas = s.alertas || []\n  const ra = s.contaRA\n  const negPlat = agrupar(s.negPorPlataforma)\n  return `\n    <div class=\"top\">\n      <div>\n        <h1 class=\"serif\">Radar de reclamações</h1>\n        <p>Instagram · TikTok · Reclame AQUI — varredura diária às 9h, com triagem e resposta sugerida por IA</p>\n      </div>\n      <div style=\"display:flex;gap:8px\">\n        <button class=\"btn ghost\" data-recarregar>Atualizar painel</button>\n        <button class=\"btn\" data-varrer ${s.varrendo ? 'disabled' : ''}>${s.varrendo ? 'varrendo os canais…' : 'Rodar varredura agora'}</button>\n      </div>\n    </div>\n\n    ${alertas.length ? `<div class=\"sec\">\n      <h2>Alertas para o time <span class=\"n\">gerados na varredura de ${s.ultimaVarredura ? dataHora(s.ultimaVarredura) : '—'}</span></h2>\n      <div class=\"list\">\n        ${alertas.map(a => `<div class=\"alerta ${a.severidade === 'alta' ? '' : 'media'}\" data-key=\"${esc(a._id || a.alertaId)}\">\n          <div class=\"ic\"></div>\n          <div><div class=\"t\">${esc(a.titulo)}</div><div class=\"d\">${esc(a.mensagem)}</div></div>\n          ${botaoAlerta(a)}\n        </div>`).join('')}\n      </div>\n    </div>` : ''}\n\n    <div class=\"grid kpis\" style=\"margin-top:18px\">\n      <div class=\"card kpi\"><div class=\"lab\">Manifestações monitoradas</div><div class=\"val serif\">${num(total)}</div><div class=\"hint\">${num(posts.length)} publicações + Reclame AQUI</div></div>\n      <div class=\"card kpi alerta\"><div class=\"lab\">Negativas</div><div class=\"val serif\">${num(casos.length)}</div><div class=\"hint\">${pctNeg}% do volume total</div></div>\n      <div class=\"card kpi alerta\"><div class=\"lab\">Urgência alta</div><div class=\"val serif\">${num(altas)}</div><div class=\"hint\">exigem resposta hoje</div></div>\n      <div class=\"card kpi gold\"><div class=\"lab\">Sem tratamento</div><div class=\"val serif\">${num(naoTratados)}</div><div class=\"hint\">na fila de atendimento</div></div>\n    </div>\n\n    <div class=\"sec two\">\n      <div class=\"card\">\n        <h2>Negativas por canal</h2>\n        <div class=\"bars\">\n          ${['instagram', 'tiktok', 'reclameaqui'].map(k => {\n            const p = platInfo(k); const n = negPlat[k] || 0\n            const maxP = Math.max(1, ...Object.values(negPlat))\n            return `<div class=\"bar clickable\" data-ir-fila=\"\" data-plat=\"${k}\" data-key=\"${k}\">\n              <div class=\"lbl\">${esc(p.label)}</div>\n              <div class=\"track\"><div class=\"fill\" style=\"width:${Math.round((n / maxP) * 100)}%;background:${p.cor}\"></div></div>\n              <div class=\"qt\">${num(n)}</div>\n            </div>`\n          }).join('')}\n        </div>\n        ${ra ? `<div style=\"margin-top:18px;padding-top:16px;border-top:1px solid #EDE8DA\">\n          <div class=\"repcard\">\n            <div class=\"repnota\"><b>${esc(ra.nota || '—')}</b><span>de 10</span></div>\n            <div class=\"repinfo\">\n              <div class=\"rt\">Reputação ${esc(ra.reputacao || '—')} no Reclame AQUI</div>\n              <div class=\"rd\">${num(ra.totalReclamacoes)} reclamações nos últimos 6 meses · índice de resposta ${esc(ra.indiceResposta || '—')}%</div>\n              <div style=\"margin-top:9px\"><button class=\"btn sm ghost\" data-abrir=\"${esc(ra.link)}\">abrir perfil no Reclame AQUI</button></div>\n            </div>\n          </div>\n        </div>` : ''}\n      </div>\n      <div class=\"card\">\n        <h2>Do que os clientes estão reclamando <span class=\"n\">${num(casos.length)} manifestações</span></h2>\n        <div class=\"bars\">\n          ${cats.map(c => {\n            const i = catInfo(c._id)\n            return `<div class=\"bar clickable\" data-ir-fila=\"${esc(c._id)}\" data-key=\"${esc(c._id)}\">\n              <div class=\"lbl\">${esc(i.label)}</div>\n              <div class=\"track\"><div class=\"fill\" style=\"width:${Math.round((c.n / maxCat) * 100)}%;background:${i.cor}\"></div></div>\n              <div class=\"qt\">${num(c.n)}</div>\n            </div>`\n          }).join('')}\n        </div>\n      </div>\n    </div>\n\n    <div class=\"sec\">\n      <div class=\"card\">\n        <h2>Publicações em crise <span class=\"n\">% de comentários negativos no Instagram</span></h2>\n        <div>\n          ${criticos.length === 0 ? '<div class=\"empty\">nenhuma publicação acima de 40%.</div>' : criticos.map(p => `\n            <div class=\"postrow\" data-abrir=\"${esc(p.link)}\" data-key=\"${esc(p.postId)}\">\n              <img src=\"${esc(p.thumb)}\" alt=\"\">\n              <div class=\"t\">\n                <div class=\"cap\">${esc((p.legenda || '').slice(0, 110))}</div>\n                <div class=\"meta\">${dia(p.data)} · ${num(p.negativos)} de ${num(p.monitorados)} comentários lidos</div>\n              </div>\n              <div class=\"pct ${p.pctNegativo >= 60 ? 'hi' : p.pctNegativo >= 35 ? 'mid' : 'lo'}\">${p.pctNegativo}%</div>\n            </div>`).join('')}\n        </div>\n      </div>\n    </div>\n\n    <div class=\"sec two\">\n      <div class=\"ai\">\n        <div class=\"tagline\">Diagnóstico por IA</div>\n        <h3 class=\"serif\">${dg ? esc(dg.titulo || 'Leitura do período') : 'O que esses números querem dizer'}</h3>\n        ${dg ? `<p>${esc(dg.corpo || '')}</p>${dg.recomendacao ? `<div class=\"rec\">${esc(dg.recomendacao)}</div>` : ''}` :\n          `<p>A IA lê as manifestações negativas dos três canais e devolve um diagnóstico executivo com a ação recomendada para as próximas 48 horas.</p>`}\n        <div style=\"margin-top:15px\">\n          <button class=\"btn gold\" data-diagnostico ${s.diagnosticando ? 'disabled' : ''}>${s.diagnosticando ? 'analisando…' : (dg ? 'Gerar novamente' : 'Gerar diagnóstico')}</button>\n        </div>\n      </div>\n      <div class=\"card\">\n        <h2>Classificar uma manifestação nova</h2>\n        <div class=\"tester\">\n          <textarea data-teste-txt placeholder=\"Cole um comentário ou uma reclamação e veja como o monitoramento classifica…\">${esc(s.testeTxt || '')}</textarea>\n          <div><button class=\"btn\" data-testar ${s.testando ? 'disabled' : ''}>${s.testando ? 'classificando…' : 'Classificar com IA'}</button></div>\n          ${s.teste ? `<div class=\"res\">\n            <span class=\"tag cat\" style=\"background:${catInfo(s.teste.categoria).cor}\">${esc(catInfo(s.teste.categoria).label)}</span>\n            <span class=\"tag urg\" style=\"color:${urgInfo(s.teste.urgencia).cor}\">urgência ${esc(urgInfo(s.teste.urgencia).label)}</span>\n            <span class=\"tag st\">${esc(s.teste.sentimento || '')}</span>\n            <div style=\"font-size:12.5px;color:#5C6F66;width:100%\">${esc(s.teste.justificativa || s.teste.resumo || '')}</div>\n          </div>` : ''}\n        </div>\n      </div>\n    </div>`\n}\n\nfunction botaoAlerta (a) {\n  if (a.tipo === 'urgencia') return `<div class=\"go\"><button class=\"btn sm ghost\" data-ir-fila=\"\" data-urg=\"alta\" data-st=\"novo\">ver casos</button></div>`\n  if (a.tipo === 'reclameaqui') return `<div class=\"go\"><button class=\"btn sm ghost\" data-ir-fila=\"\" data-plat=\"reclameaqui\">ver reclamações</button></div>`\n  if (a.tipo === 'tema') return `<div class=\"go\"><button class=\"btn sm ghost\" data-ir-fila=\"${esc(a.categoria)}\">ver tema</button></div>`\n  return ''\n}\n\nfunction filtrar (s, casos) {\n  const q = (s.busca || '').toLowerCase().trim()\n  return casos.filter(c =>\n    (!s.fUrg || c.urgencia === s.fUrg) &&\n    (!s.fCat || c.categoria === s.fCat) &&\n    (!s.fSt || (c.status || 'novo') === s.fSt) &&\n    (!s.fPlat || c.plataforma === s.fPlat) &&\n    (!q || (c.texto || '').toLowerCase().includes(q) || (c.autor || '').toLowerCase().includes(q) || (c.titulo || '').toLowerCase().includes(q))\n  )\n}\n\nfunction viewFila (d2, s, casos, posts) {\n  const lista = filtrar(s, casos)\n  const cats = (s.porCategoria || []).filter(c => c._id).slice(0, 8)\n  const negPlat = agrupar(s.negPorPlataforma)\n  return `\n    <div class=\"top\">\n      <div>\n        <h1 class=\"serif\">Fila de casos</h1>\n        <p>ordenada por prioridade — urgência, canal, engajamento e tempo sem resposta</p>\n      </div>\n      <div style=\"font-size:12.5px;color:#5C6F66\">${num(lista.length)} de ${num(casos.length)} casos</div>\n    </div>\n\n    <div class=\"filters\">\n      <div class=\"search\"><input data-busca placeholder=\"buscar por texto, título ou @perfil…\" value=\"${esc(s.busca || '')}\"></div>\n      ${['instagram', 'tiktok', 'reclameaqui'].map(k => `<button class=\"chip ${s.fPlat === k ? 'on' : ''}\" data-filtro-plat=\"${k}\"><span class=\"cdot\" style=\"background:${platInfo(k).cor}\"></span>${esc(platInfo(k).label)} <span style=\"opacity:.6\">${num(negPlat[k] || 0)}</span></button>`).join('')}\n    </div>\n    <div class=\"filters\">\n      ${['alta', 'media', 'baixa'].map(u => `<button class=\"chip ${s.fUrg === u ? 'on' : ''}\" data-filtro-urg=\"${u}\">urgência ${URG[u].label.toLowerCase()}</button>`).join('')}\n      ${['novo', 'em_atendimento', 'respondido'].map(st => `<button class=\"chip ${s.fSt === st ? 'on' : ''}\" data-filtro-st=\"${st}\">${STATUS[st].label.toLowerCase()}</button>`).join('')}\n    </div>\n    <div class=\"filters\">\n      ${cats.map(c => `<button class=\"chip ${s.fCat === c._id ? 'on' : ''}\" data-filtro-cat=\"${esc(c._id)}\">${esc(catInfo(c._id).label)} <span style=\"opacity:.6\">${c.n}</span></button>`).join('')}\n    </div>\n\n    <div class=\"list\">\n      ${lista.length === 0 ? '<div class=\"card empty\">nenhum caso com esses filtros.</div>' : lista.slice(0, 120).map(c => cardCaso(c, s)).join('')}\n      ${lista.length > 120 ? `<div class=\"empty\">mostrando os 120 casos mais prioritários de ${num(lista.length)}.</div>` : ''}\n    </div>`\n}\n\nfunction cardCaso (c, s) {\n  const ci = catInfo(c.categoria); const ui = urgInfo(c.urgencia); const si = stInfo(c.status); const pi = platInfo(c.plataforma)\n  const ra = c.plataforma === 'reclameaqui'\n  const semResposta = ra && (c.statusOrigem || '').toUpperCase().indexOf('NAO RESPONDIDA') === 0\n  return `<div class=\"caso ${s.aberto === c.id ? 'sel' : ''}\" data-caso=\"${esc(c.id)}\" data-key=\"${esc(c.id)}\">\n    <div class=\"av\" style=\"background:${pi.cor}\">${ra ? 'RA' : esc(iniciais(c.nome, c.autor))}</div>\n    <div class=\"body\">\n      <div class=\"who\">\n        <b>${ra ? esc(c.local || 'Consumidor') : '@' + esc(c.autor)}</b>\n        <span>${dataHora(c.data)}</span>\n        ${c.curtidas ? `<span>· ${num(c.curtidas)} curtidas</span>` : ''}\n      </div>\n      ${ra && c.titulo ? `<div class=\"tit\">${esc(c.titulo)}</div>` : ''}\n      <div class=\"txt\">${esc(c.texto)}</div>\n      <div class=\"tags\">\n        <span class=\"tag plat\" style=\"background:${pi.cor}\">${esc(pi.label)}</span>\n        <span class=\"tag cat\" style=\"background:${ci.cor}\">${esc(ci.label)}</span>\n        <span class=\"tag urg\" style=\"color:${ui.cor}\">urgência ${esc(ui.label.toLowerCase())}</span>\n        <span class=\"tag st\" style=\"color:${si.cor}\">${esc(si.label.toLowerCase())}</span>\n        ${semResposta ? '<span class=\"tag orig\">sem resposta da empresa</span>' : ''}\n      </div>\n    </div>\n    <div class=\"score\"><div class=\"n\">${c.score}</div><div class=\"l\">prior.</div></div>\n  </div>`\n}\n\nfunction viewTemas (d2, s, casos) {\n  const cats = (s.porCategoria || []).filter(c => c._id)\n  return `\n    <div class=\"top\">\n      <div>\n        <h1 class=\"serif\">Temas de insatisfação</h1>\n        <p>o que a IA identificou por trás das ${num(casos.length)} manifestações negativas dos três canais</p>\n      </div>\n    </div>\n    <div class=\"list\">\n      ${cats.map(c => {\n        const i = catInfo(c._id)\n        const ex = casos.filter(x => x.categoria === c._id).slice(0, 4)\n        const aberto = s.temaAberto === c._id\n        return `<div class=\"tema\" data-key=\"${esc(c._id)}\">\n          <div class=\"th\" data-toggle-tema=\"${esc(c._id)}\">\n            <div class=\"sw\" style=\"background:${i.cor}\"></div>\n            <div class=\"tt\">${esc(i.label)}</div>\n            <div class=\"tn\">${num(c.n)} manifestações</div>\n            <button class=\"btn sm ghost\" data-ir-fila=\"${esc(c._id)}\">ver na fila</button>\n          </div>\n          ${aberto ? `<div class=\"tb\">${ex.map(x => `<div class=\"ex\" data-caso=\"${esc(x.id)}\" data-key=\"${esc(x.id)}\"><b>${platInfo(x.plataforma).label}</b> · ${esc((x.titulo || x.texto || '').slice(0, 220))}${(x.titulo || x.texto || '').length > 220 ? '…' : ''}</div>`).join('')}</div>` : ''}\n        </div>`\n      }).join('')}\n    </div>`\n}\n\nfunction viewPosts (d2, s, posts) {\n  return `\n    <div class=\"top\">\n      <div>\n        <h1 class=\"serif\">Publicações monitoradas</h1>\n        <p>${num(posts.length)} posts de @granado no Instagram com leitura de sentimento nos comentários</p>\n      </div>\n    </div>\n    <div class=\"postgrid\">\n      ${posts.map(p => `<div class=\"pcard\" data-abrir=\"${esc(p.link)}\" data-key=\"${esc(p.postId)}\">\n        <img class=\"im\" src=\"${esc(p.thumb)}\" alt=\"\">\n        <div class=\"pb\">\n          <div class=\"pc\">${esc((p.legenda || '').slice(0, 110))}</div>\n          <div class=\"pm\">\n            <span>${dia(p.data)} · ${num(p.curtidas)} curtidas</span>\n            <span class=\"pct ${p.pctNegativo >= 60 ? 'hi' : p.pctNegativo >= 35 ? 'mid' : 'lo'}\" style=\"font-size:12.5px\">${p.pctNegativo || 0}% neg.</span>\n          </div>\n        </div>\n      </div>`).join('')}\n    </div>`\n}\n\nfunction drawer (d2, s, casos, posts) {\n  const c = casos.find(x => x.id === s.aberto)\n  if (!c) return ''\n  const p = posts.find(x => x.postId === c.postId)\n  const ci = catInfo(c.categoria); const ui = urgInfo(c.urgencia); const si = stInfo(c.status); const pi = platInfo(c.plataforma)\n  const ra = c.plataforma === 'reclameaqui'\n  const resp = s.respRascunho != null ? s.respRascunho : (c.resposta || '')\n  return `<div class=\"scrim\" data-fechar></div>\n  <aside class=\"drawer\">\n    <div class=\"dh\">\n      <div>\n        <h3 class=\"serif\">${ra ? esc(c.titulo || 'Reclamação') : '@' + esc(c.autor)}</h3>\n        <div style=\"font-size:12px;color:#8A968F\">${esc(pi.label)} · ${dataHora(c.data)} · prioridade ${c.score}</div>\n      </div>\n      <button class=\"x\" data-fechar>×</button>\n    </div>\n    <div class=\"db\">\n      <div class=\"quote\">${esc(c.texto)}</div>\n      <div class=\"tags\" style=\"margin-top:12px\">\n        <span class=\"tag plat\" style=\"background:${pi.cor}\">${esc(pi.label)}</span>\n        <span class=\"tag cat\" style=\"background:${ci.cor}\">${esc(ci.label)}</span>\n        <span class=\"tag urg\" style=\"color:${ui.cor}\">urgência ${esc(ui.label.toLowerCase())}</span>\n        <span class=\"tag st\" style=\"color:${si.cor}\">${esc(si.label.toLowerCase())}</span>\n      </div>\n      <div class=\"kv\">\n        <div class=\"k\">Leitura da IA</div><div>${esc(c.resumo || '—')}</div>\n        ${ra ? `<div class=\"k\">Status na origem</div><div>${esc(c.statusOrigem || '—')}</div>\n        <div class=\"k\">Consumidor</div><div>${esc(c.local || '—')}</div>` :\n        `<div class=\"k\">Engajamento</div><div>${num(c.curtidas)} curtidas · ${num(c.respostas)} respostas</div>`}\n        <div class=\"k\">Responsável</div><div>${esc(c.responsavel || '—')}</div>\n      </div>\n      ${ra && c.link ? `<div style=\"margin-top:14px\"><button class=\"btn sm ghost\" data-abrir=\"${esc(c.link)}\">abrir reclamação no Reclame AQUI</button></div>` : ''}\n      ${!ra && p ? `<div class=\"mini\" data-abrir=\"${esc(p.link)}\">\n        <img src=\"${esc(p.thumb)}\" alt=\"\">\n        <div><div class=\"c\">${esc((p.legenda || '').slice(0, 130))}</div>\n        <div class=\"m\">${dia(p.data)} · ${p.pctNegativo || 0}% dos comentários lidos são negativos</div></div>\n      </div>` : ''}\n      <label class=\"lbl-s\">Resposta ${ra ? 'oficial no Reclame AQUI' : 'pública'} sugerida</label>\n      <textarea class=\"resp\" data-resp placeholder=\"clique em “Gerar resposta” para a IA escrever na voz da Granado…\">${esc(resp)}</textarea>\n      <div style=\"margin-top:9px;display:flex;gap:8px;align-items:center\">\n        <button class=\"btn gold sm\" data-sugerir ${s.gerando ? 'disabled' : ''}>${s.gerando ? 'escrevendo…' : 'Gerar resposta com IA'}</button>\n        ${s.gerando ? '<span class=\"load\"><span class=\"spin\"></span>voz da marca</span>' : ''}\n      </div>\n    </div>\n    <div class=\"df\">\n      <button class=\"btn sm\" data-status=\"em_atendimento\" ${s.salvando ? 'disabled' : ''}>Assumir caso</button>\n      <button class=\"btn sm gold\" data-status=\"respondido\" ${s.salvando ? 'disabled' : ''}>Marcar como respondido</button>\n      <button class=\"btn sm ghost\" data-status=\"arquivado\" ${s.salvando ? 'disabled' : ''}>Arquivar</button>\n    </div>\n  </aside>`\n}\n\n"}}}}}}}},{"_id":"6a84a31a291306363fdc22a0","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bFluxoAlvo","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:820px;margin:0 0 14px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13px;line-height:1.55;color:#4A4A3C;margin:0 0 22px}\n.obs b{color:#3B3A2E}\nh3.sec{font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:34px 0 14px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.flow{max-width:560px;margin:0 auto}\n.st{position:relative;border-radius:10px;padding:13px 16px;margin:0 auto;text-align:center}\n.st b{display:block;font-size:15px;margin-bottom:2px}\n.st span{font-size:12.5px;opacity:.85}\n.st.e{background:#E1EFE6;border:1px solid #BBD4C4;color:#1E4D3B}\n.st.a{background:#F7ECD6;border:1px solid #E3CE9E;color:#8C6F2A}\n.st.sys{background:#E7E4F5;border:1px solid #C7C0E6;color:#4B3F86}\n.st.core{background:#DCEBC8;border:2px solid #8FA86A;color:#3C5220}\n.st.buy{background:#DEEAF6;border:1px solid #B6CDe6;color:#24507A}\n.conn{width:2px;height:20px;background:#C9BC97;margin:5px auto}\n.branch{display:flex;align-items:center;justify-content:center;gap:8px;margin:5px auto}\n.branch .ar{color:#B08A8A;font-size:16px}\n.bnote{font-size:12px;border-radius:8px;padding:6px 11px}\n.bnote.rej{background:#F4E3DF;border:1px solid #E0BEB5;color:#9C4436}\n.bnote.reg{background:#EFEBDD;border:1px solid #D5CBAA;color:#6B6753}\n.ptable{width:100%;border-collapse:collapse;font-size:13px;background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;overflow:hidden}\n.ptable th{background:#1E4D3B;color:#F5F0E1;text-align:left;padding:9px 12px;font-weight:600;font-size:12px;letter-spacing:.4px}\n.ptable td{padding:8px 12px;border-top:1px solid #F0E9D5;vertical-align:top}\n.ptable tr.grp td{background:#EFE6CF;color:#5B4A1E;font-weight:700;font-family:Georgia,serif;font-size:13px}\n.tg{font-size:11px;font-weight:700;border-radius:999px;padding:2px 9px}\n.tg.c{background:#E1EFE6;color:#1E6B43;border:1px solid #BBD4C4}\n.tg.i{background:#F5EEDC;color:#8C6F2A;border:1px solid #D9C289}\n.pm{font-family:Georgia,serif;color:#1E4D3B;font-weight:700}\n.stt{font-size:12px;color:#4A4A3C}\n.tbwrap{overflow-x:auto}\n.st3{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin:0 0 6px}\n.mini{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:14px 16px}\n.mini .v{font-family:Georgia,serif;font-size:24px;color:#1E4D3B}\n.mini .l{font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:#8C6F2A;margin-top:4px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.branch{flex-wrap:wrap}}`,\n  render(d2){\n    const F=[\n      ['e','Cliente no WhatsApp',''],\n      ['e','Bot pede o CNPJ','Valida formato e dígito'],\n      ['sys','Consulta CNPJ na API','Situação · CNAE · UF · IE'],\n      ['a','Situação ativa? CNAE compatível?','',['rej','Não → macro de rejeição']],\n      ['a','Já cadastrado e aprovado?','Checa no Magento',['reg','Não → fluxo de cadastro']],\n      ['core','Resolve UF + contribuinte (IE)','O coração da lógica'],\n      ['core','Mapeia 1 das 14 tabelas','C1–C9 (contribuinte) ou I1–I5 (isento)'],\n      ['buy','Cliente escolhe o produto','Busca por SKU ou catálogo'],\n      ['buy','Bot devolve o preço final da tabela','ICMS · ST · IPI já embutidos'],\n      ['buy','Monta o carrinho','Valida o pedido mínimo da região'],\n      ['e','Gera o link de checkout','PIX 3% · cartão 2x · boleto']\n    ]\n    const step=(s,i)=>`${i?'<div class=\"conn\"></div>':''}<div class=\"st ${s[0]}\"><b>${s[1]}</b>${s[2]?`<span>${s[2]}</span>`:''}</div>${s[3]?`<div class=\"branch\"><span class=\"ar\">→</span><span class=\"bnote ${s[3][0]}\">${s[3][1]}</span></div>`:''}`\n    const C=[\n      ['C1','GO · DF · MS · BA · CE · SE · PI · PE · MA · RR · AC · PB · ES · RO','26,51'],\n      ['C2','SP · SC','27,97'],['C3','MG','34,51'],['C4','RS','34,73'],['C5','PR','33,77'],\n      ['C6','RJ','32,30'],['C7','AL','32,66'],['C8','AP','32,08'],['C9','AM','24,78']]\n    const I=[\n      ['I1','GO · DF · MS · BA · CE · SE · PI · PE · MA · RR · AC · PB · ES · RO · AP','26,51'],\n      ['I2','SP · MG · SC · RS · PR','27,97'],['I3','RJ','28,48'],['I4','AL','25,91'],['I5','AM','24,78']]\n    const row=(t,tipo)=>`<tr><td><b class=\"pm\">${t[0]}</b></td><td><span class=\"tg ${tipo==='Contribuinte'?'c':'i'}\">${tipo}</span></td><td class=\"stt\">${t[1]}</td><td class=\"pm\">R$ ${t[2]}</td><td>339</td></tr>`\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Arquitetura-alvo · como o novo atendimento vai funcionar</p>\n  <h2>Fluxo alvo: do CNPJ ao checkout</h2>\n  <p class=\"lead\">Este é o desenho proposto para o novo auto-atendimento — diferente do bot antigo (desativado), que só dava respostas prontas. Aqui o atendimento identifica a empresa pelo CNPJ, descobre o preço correto para a região dela e leva até o pedido fechado, tudo na conversa.</p>\n  <div class=\"obs\">ℹ️ <b>A ideia central:</b> a partir do CNPJ o sistema descobre <b>o estado (UF)</b> e se a empresa é <b>contribuinte ou isenta de ICMS</b>. Esses dois dados apontam <b>uma entre 14 tabelas de preço</b>, cada uma já com os impostos embutidos — então o cliente vê o preço certo para o caso dele e segue direto para a compra.</div>\n  <h3 class=\"sec\">O caminho, passo a passo</h3>\n  <div class=\"flow\">${F.map(step).join('')}</div>\n  <h3 class=\"sec\">O motor de preços: 14 tabelas</h3>\n  <p class=\"lead\">O trabalho de consolidação reduziu <b>46 tabelas para 14</b> (−70%). Cada uma cobre um conjunto de estados e um perfil fiscal; todas com os mesmos 339 SKUs, com ICMS, ST e IPI já calculados no preço final.</p>\n  <div class=\"st3\">\n    <div class=\"mini\"><div class=\"v\">46 → 14</div><div class=\"l\">tabelas de preço (−70%)</div></div>\n    <div class=\"mini\"><div class=\"v\">9 + 5</div><div class=\"l\">contribuinte · isento</div></div>\n    <div class=\"mini\"><div class=\"v\">339</div><div class=\"l\">SKUs por tabela</div></div>\n  </div>\n  <div class=\"tbwrap\"><table class=\"ptable\">\n    <thead><tr><th>Tabela</th><th>Perfil</th><th>Estados</th><th>Preço médio</th><th>SKUs</th></tr></thead>\n    <tbody>\n      <tr class=\"grp\"><td colspan=\"5\">Contribuintes de ICMS — 9 tabelas</td></tr>\n      ${C.map(t=>row(t,'Contribuinte')).join('')}\n      <tr class=\"grp\"><td colspan=\"5\">Isentos / não contribuintes — 5 tabelas</td></tr>\n      ${I.map(t=>row(t,'Isento')).join('')}\n    </tbody>\n  </table></div>\n  <div class=\"obs\" style=\"margin-top:16px\">ℹ️ <b>Ajuste de consolidação registrado:</b> o SKU 7896512959867 foi arredondado de R$ 11,99 → R$ 12,49 nas tabelas C1 e I1, para unificar ES, RO e AP ao grupo maior. Fonte: planilha \"Tabelas B2B consolidadas\".</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a84a4e600cfd069693aeafe","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bPrecificacao","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:820px;margin:0 0 14px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin:0 0 22px}\n.obs b{color:#3B3A2E}\nh3.sec{font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:32px 0 14px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.layers{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:14px}\n.ly{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:16px 18px}\n.ly .n{width:26px;height:26px;border-radius:50%;background:#1E4D3B;color:#F5F0E1;font-family:Georgia,serif;display:flex;align-items:center;justify-content:center;font-size:15px;margin-bottom:9px}\n.ly b{color:#1E4D3B;font-size:15.5px;display:block;margin-bottom:5px}\n.ly p{font-size:13px;color:#4A4A3C;line-height:1.5;margin:0}\n.ly .tagf{display:inline-block;margin-top:9px;font-size:12px;background:#EFE6CF;border:1px dashed #C9BC97;color:#5B4A1E;border-radius:6px;padding:4px 9px}\n.tbwrap{overflow-x:auto}\n.ex{width:100%;border-collapse:collapse;font-size:13.5px;background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;overflow:hidden}\n.ex th{background:#1E4D3B;color:#F5F0E1;text-align:left;padding:9px 12px;font-size:12px;font-weight:600}\n.ex td{padding:9px 12px;border-top:1px solid #F0E9D5}\n.ex td.f{font-family:Georgia,serif;font-weight:700;color:#1E4D3B}\n.ex .bar{height:9px;border-radius:999px;background:linear-gradient(90deg,#1E4D3B,#4C7A5E);display:inline-block;vertical-align:middle;margin-right:8px}\n.tg{font-size:11px;font-weight:700;border-radius:999px;padding:2px 9px;white-space:nowrap}\n.tg.c{background:#E1EFE6;color:#1E6B43;border:1px solid #BBD4C4}\n.tg.i{background:#F5EEDC;color:#8C6F2A;border:1px solid #D9C289}\n.grid2{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px}\n.card{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:16px 18px}\n.card b.h{font-family:Georgia,serif;color:#1E4D3B;font-size:16px;display:block;margin-bottom:10px}\n.rate{display:flex;align-items:center;gap:10px;font-size:13.5px;color:#4A4A3C;padding:6px 0;border-bottom:1px solid #F3EDDA}\n.rate:last-child{border-bottom:none}\n.rate .p{font-family:Georgia,serif;font-weight:700;color:#1E4D3B;width:64px;flex:0 0 auto}\n.chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:6px}\n.chip{background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:4px 11px;font-size:12.5px}\n.calc{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:18px 20px}\n.calc .frow{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:14px}\n.calc label{display:flex;flex-direction:column;gap:5px;font-size:11px;color:#8C6F2A;letter-spacing:1px;text-transform:uppercase;flex:1;min-width:140px}\n.calc input,.calc select{border:1px solid #C9BC97;border-radius:7px;padding:10px 11px;font-family:inherit;font-size:14.5px;color:#2B2B23;background:#fff;outline:none}\n.calc input:focus,.calc select:focus{border-color:#1E4D3B;box-shadow:0 0 0 3px rgba(30,77,59,.1)}\n.seg{display:flex;border:1px solid #C9BC97;border-radius:7px;overflow:hidden;align-self:flex-start}\n.pc-perfil{border:none;background:#fff;color:#5B4A1E;padding:10px 16px;font-family:inherit;font-size:13.5px;cursor:pointer}\n.pc-perfil.on{background:#1E4D3B;color:#F5F0E1;font-weight:600}\n.out{background:#1E4D3B;color:#F5F0E1;border-radius:10px;padding:18px 20px;margin-top:4px}\n.out .ol{font-size:11px;letter-spacing:1.5px;text-transform:uppercase;color:#CBB878}\n.out .big{font-family:Georgia,serif;font-size:32px;margin:4px 0 2px}\n.out .meta{font-size:13px;color:#DCE7DF;line-height:1.6;margin-top:8px}\n.out .meta b{color:#E8CF8F}\n.out .st{margin-top:10px;background:rgba(255,255,255,.09);border-radius:7px;padding:8px 11px;font-size:12.5px;line-height:1.5}\n.out .warn{background:#5A3B33;border-radius:7px;padding:10px 12px;font-size:13px;line-height:1.5}\n.pfind{position:relative;margin-bottom:14px}\n.pfind input{width:100%;box-sizing:border-box;border:1px solid #C9BC97;border-radius:7px;padding:10px 12px;font-family:inherit;font-size:14px;background:#fff;outline:none}\n.pfind input:focus{border-color:#1E4D3B;box-shadow:0 0 0 3px rgba(30,77,59,.1)}\n.pres{margin-top:6px;max-height:230px;overflow-y:auto;border:1px solid #E3D9BD;border-radius:8px;background:#fff}\n.pr-item{display:flex;align-items:center;gap:10px;padding:8px 11px;border-bottom:1px solid #F3EDDA;cursor:pointer;font-size:13px}\n.pr-item:last-child{border-bottom:none}\n.pr-item:hover{background:#F5F0E1}\n.pr-item.sel{background:#E1EFE6}\n.pr-item .pn{flex:1;color:#3B3A2E;line-height:1.3}\n.pr-item .pl{font-size:10.5px;background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:1px 8px;white-space:nowrap}\n.pr-item .pp{font-family:Georgia,serif;font-weight:700;color:#1E4D3B;white-space:nowrap}\n.pcount{font-size:12px;color:#8A8672;padding:6px 2px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.rate .p{width:56px}}`,\n  render(d2){\n    const EX=[\n      ['Amazonas (Zona Franca)','Contribuinte','c','0% ICMS · sem ST','39,69',39.69],\n      ['Centro/Norte/NE — ex.: GO, BA, ES','Contribuinte','c','7% ICMS · sem ST','42,68',42.68],\n      ['Rio de Janeiro','Isento','i','37% ICMS · sem ST','46,15',46.15],\n      ['Rio de Janeiro','Contribuinte','c','37% ICMS + ST','55,99',55.99],\n      ['Alagoas','Contribuinte','c','7% ICMS + ST','59,11',59.11]]\n    const mx=59.11\n    const exrow=e=>`<tr><td>${e[0]}</td><td><span class=\"tg ${e[2]}\">${e[1]}</span></td><td>${e[3]}</td><td class=\"f\"><span class=\"bar\" style=\"width:${Math.round(e[5]/mx*70)}px\"></span>R$ ${e[4]}</td></tr>`\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Regra de precificação · como o preço é calculado</p>\n  <h2>Como o preço é formado</h2>\n  <p class=\"lead\">Todo produto tem um <b>preço base nacional</b>, igual para o país inteiro. O que muda o preço final que o cliente vê são os <b>impostos</b>, que dependem do <b>estado</b> dele e de ser <b>contribuinte ou isento de ICMS</b>. É por isso que existem 14 tabelas — cada uma já traz o preço final calculado para uma combinação.</p>\n  <div class=\"obs\">ℹ️ <b>A regra em uma frase:</b> preço final = preço base + ICMS (embutido no preço, com alíquota do estado) + ST (Substituição Tributária, um valor por produto cobrado do contribuinte em alguns estados). O IPI não incide nesta operação.</div>\n\n  <h3 class=\"sec\">A regra em 3 camadas</h3>\n  <div class=\"layers\">\n    <div class=\"ly\"><div class=\"n\">1</div><b>Preço base</b><p>O preço \"cheio\" do produto, sem impostos — tabelado e igual para todo o Brasil. É o ponto de partida.</p></div>\n    <div class=\"ly\"><div class=\"n\">2</div><b>ICMS por dentro</b><p>O imposto estadual entra <b>embutido no preço</b>: o valor final já contém o ICMS. A alíquota muda por estado — de 0% (Zona Franca de Manaus) a 20–37% no Rio de Janeiro.</p><span class=\"tagf\">ex.: base R$ 39,69 a 7% → R$ 42,68</span></div>\n    <div class=\"ly\"><div class=\"n\">3</div><b>Substituição Tributária (ST)</b><p>Em alguns estados o <b>contribuinte</b> paga um valor extra por produto (o imposto da cadeia é recolhido antecipado). Quem é <b>isento</b> não tem ST na tabela.</p><span class=\"tagf\">só contribuinte · MG, RS, PR, RJ, AL, AP</span></div>\n  </div>\n\n  <h3 class=\"sec\">O mesmo produto, preços diferentes</h3>\n  <p class=\"lead\">Um exemplo real (produto de preço base <b>R$ 39,69</b>) mostra por que o estado e o perfil fiscal importam tanto — o preço final vai de R$ 39,69 a R$ 59,11 só por causa de imposto.</p>\n  <div class=\"tbwrap\"><table class=\"ex\">\n    <thead><tr><th>Estado / região</th><th>Perfil</th><th>Impostos</th><th>Preço final</th></tr></thead>\n    <tbody>${EX.map(exrow).join('')}</tbody>\n  </table></div>\n\n  <h3 class=\"sec\">Alíquota de ICMS por estado</h3>\n  <div class=\"grid2\">\n    <div class=\"card\"><b class=\"h\">Quanto de ICMS por região</b>\n      <div class=\"rate\"><span class=\"p\">0%</span><span>Amazonas (Zona Franca de Manaus)</span></div>\n      <div class=\"rate\"><span class=\"p\">7%</span><span>Centro-Oeste, Norte e Nordeste (GO, DF, MS, BA, CE, SE, PI, PE, MA, RR, AC, PB, ES, RO), além de AL e AP</span></div>\n      <div class=\"rate\"><span class=\"p\">12%</span><span>SP, SC, MG, RS e PR</span></div>\n      <div class=\"rate\"><span class=\"p\">20–37%</span><span>Rio de Janeiro (conforme o item)</span></div>\n    </div>\n    <div class=\"card\"><b class=\"h\">Onde há Substituição Tributária</b>\n      <p style=\"font-size:13px;color:#4A4A3C;line-height:1.55;margin:0 0 10px\">A ST aparece apenas nas tabelas de <b>contribuinte</b> destes estados — soma um valor por produto. Nas tabelas de isento, não há ST.</p>\n      <div class=\"chips\"><span class=\"chip\">MG</span><span class=\"chip\">RS</span><span class=\"chip\">PR</span><span class=\"chip\">RJ</span><span class=\"chip\">AL</span><span class=\"chip\">AP</span></div>\n      <p style=\"font-size:12.5px;color:#8A8672;line-height:1.5;margin:12px 0 0\">Nos casos de RJ e de ST com base reduzida, o cálculo exato do imposto é mais elaborado — por isso os preços finais já vêm <b>pré-calculados</b> nas 14 tabelas, e o atendimento apenas lê o valor certo.</p>\n    </div>\n  </div>\n\n  <h3 class=\"sec\">Calculadora de preço</h3>\n  <p class=\"lead\">Simule o preço final: escolha um produto real (ou digite um preço base), o estado de entrega e o perfil fiscal do cliente. A calculadora aplica o ICMS por dentro (a ST, quando existe, é um valor por produto que se soma — ver a tabela da UF).</p>\n  ${this.calc(d2)}\n\n  <div class=\"obs\" style=\"margin-top:24px\">ℹ️ <b>Como isso vira preço na conversa:</b> pelo CNPJ o sistema descobre o estado (UF) e se a empresa é contribuinte ou isenta, escolhe 1 das 14 tabelas e lê o preço final já com os impostos — ver a aba 🧭 Fluxo alvo. Fonte: planilha \"Tabelas B2B consolidadas\".</div>\n</div>`\n  },\n  UF:{\n    AM:{a:[0],tc:'C9',ti:'I5'},\n    GO:{a:[0.07],tc:'C1',ti:'I1'},DF:{a:[0.07],tc:'C1',ti:'I1'},MS:{a:[0.07],tc:'C1',ti:'I1'},BA:{a:[0.07],tc:'C1',ti:'I1'},CE:{a:[0.07],tc:'C1',ti:'I1'},SE:{a:[0.07],tc:'C1',ti:'I1'},PI:{a:[0.07],tc:'C1',ti:'I1'},PE:{a:[0.07],tc:'C1',ti:'I1'},MA:{a:[0.07],tc:'C1',ti:'I1'},RR:{a:[0.07],tc:'C1',ti:'I1'},AC:{a:[0.07],tc:'C1',ti:'I1'},PB:{a:[0.07],tc:'C1',ti:'I1'},ES:{a:[0.07],tc:'C1',ti:'I1'},RO:{a:[0.07],tc:'C1',ti:'I1'},\n    AL:{a:[0.07],tc:'C7',ti:'I4',stC:true},AP:{a:[0.07],tc:'C8',ti:'I1',stC:true},\n    SP:{a:[0.12],tc:'C2',ti:'I2'},SC:{a:[0.12],tc:'C2',ti:'I2'},\n    MG:{a:[0.12],tc:'C3',ti:'I2',stC:true},RS:{a:[0.12],tc:'C4',ti:'I2',stC:true},PR:{a:[0.12],tc:'C5',ti:'I2',stC:true},\n    RJ:{a:[0.20,0.37],tc:'C6',ti:'I3',stC:true}\n  },\n  UFNAMES:{AC:'Acre',AL:'Alagoas',AM:'Amazonas',AP:'Amapá',BA:'Bahia',CE:'Ceará',DF:'Distrito Federal',ES:'Espírito Santo',GO:'Goiás',MA:'Maranhão',MG:'Minas Gerais',MS:'Mato Grosso do Sul',PB:'Paraíba',PE:'Pernambuco',PI:'Piauí',PR:'Paraná',RJ:'Rio de Janeiro',RO:'Rondônia',RR:'Roraima',RS:'Rio Grande do Sul',SC:'Santa Catarina',SE:'Sergipe',SP:'São Paulo'},\n  UNMAPPED:{MT:'Mato Grosso',PA:'Pará',RN:'Rio Grande do Norte',TO:'Tocantins'},\n  calc(d2){\n    const money=n=>'R$ '+n.toLocaleString('pt-BR',{minimumFractionDigits:2,maximumFractionDigits:2})\n    const base=parseFloat(String(d2.state.base==null?'39.69':d2.state.base).replace(',','.'))\n    const uf=d2.state.uf||'SP'\n    const perfil=d2.state.perfil||'contribuinte'\n    const opts=Object.keys(this.UFNAMES).sort((a,b)=>this.UFNAMES[a].localeCompare(this.UFNAMES[b])).map(k=>`<option value=\"${k}\"${k===uf?' selected':''}>${this.UFNAMES[k]} (${k})</option>`).join('')\n      +'<optgroup label=\"— não mapeados na planilha —\">'+Object.keys(this.UNMAPPED).sort((a,b)=>this.UNMAPPED[a].localeCompare(this.UNMAPPED[b])).map(k=>`<option value=\"${k}\"${k===uf?' selected':''}>${this.UNMAPPED[k]} (${k})</option>`).join('')+'</optgroup>'\n    let out\n    if(this.UNMAPPED[uf]){\n      out=`<div class=\"out\"><div class=\"warn\">⚠️ ${this.UNMAPPED[uf]} (${uf}) não está entre as 14 tabelas consolidadas. Confirmar a tabela/alíquota deste estado com a área fiscal antes de usar.</div></div>`\n    } else if(!(base>0)){\n      out=`<div class=\"out\"><div class=\"ol\">Preço final</div><div class=\"meta\">Informe um preço base válido para calcular.</div></div>`\n    } else {\n      const d=this.UF[uf]\n      const tabela=perfil==='isento'?d.ti:d.tc\n      const hasST=perfil==='contribuinte'&&!!d.stC\n      const prices=d.a.map(a=>a>=0.999?base:base/(1-a))\n      const aliqTxt=d.a.map(a=>Math.round(a*100)+'%').join(' ou ')\n      const priceTxt=prices.length>1?`${money(Math.min(...prices))} a ${money(Math.max(...prices))}`:money(prices[0])\n      out=`<div class=\"out\"><div class=\"ol\">Preço com ICMS embutido</div><div class=\"big\">${priceTxt}</div>\n        <div class=\"meta\">Tabela <b>${tabela}</b> · perfil <b>${perfil}</b> · ICMS <b>${d.a[0]===0?'isento (Zona Franca)':aliqTxt}</b>${d.a.length>1?' (varia por item)':''}</div>\n        ${hasST?`<div class=\"st\">➕ <b>Substituição Tributária:</b> este estado cobra ST do contribuinte — um valor por produto que se soma a este preço. Não é estimável só pelo preço base; consulte o valor de ST do SKU na tabela ${tabela}.</div>`:`<div class=\"st\">Sem ST neste caso — o preço acima já é o final.</div>`}</div>`\n    }\n    const ds=d2.data('b2b-precos-baseDataSet')\n    const prods=ds?(ds.records()||[]):[]\n    const q=(d2.state.q||'').toLowerCase().trim()\n    let picker=''\n    if(prods.length){\n      const filt=q?prods.filter(p=>String(p.nome||'').toLowerCase().includes(q)||String(p.sku||'').includes(q)):prods\n      const shown=filt.slice(0,40)\n      picker=`<div class=\"pfind\">\n        <input id=\"pc-q\" type=\"text\" placeholder=\"Buscar produto por nome ou SKU… (${prods.length} no catálogo)\" value=\"${String(d2.state.q||'').replace(/\"/g,'&quot;')}\"/>\n        ${q?`<div class=\"pres\">${shown.map(p=>`<div class=\"pr-item${String(d2.state.sku||'')===String(p.sku)?' sel':''}\" data-sku=\"${p.sku}\" data-base=\"${p.base}\"><span class=\"pn\">${String(p.nome||'').replace(/</g,'&lt;')}</span>${p.linha?`<span class=\"pl\">${p.linha}</span>`:''}<span class=\"pp\">${money(Number(p.base)||0)}</span></div>`).join('')||'<div class=\"pcount\">Nenhum produto encontrado.</div>'}${filt.length>40?`<div class=\"pcount\">Mostrando 40 de ${filt.length} — refine a busca.</div>`:''}</div>`:''}\n      </div>`\n    }\n    return `<div class=\"calc\" data-key=\"pc-calc\">\n      ${picker}\n      <div class=\"frow\">\n        <label>Preço base (R$)<input id=\"pc-base\" type=\"text\" inputmode=\"decimal\" value=\"${d2.state.base==null?'39,69':String(d2.state.base)}\"/></label>\n        <label>Estado de entrega<select id=\"pc-uf\">${opts}</select></label>\n        <label>Perfil fiscal<div class=\"seg\"><button class=\"pc-perfil${perfil==='contribuinte'?' on':''}\" data-p=\"contribuinte\">Contribuinte</button><button class=\"pc-perfil${perfil==='isento'?' on':''}\" data-p=\"isento\">Isento</button></div></label>\n      </div>\n      ${out}\n    </div>`\n  },\n  mounted(d2){\n    const ds=d2.data('b2b-precos-baseDataSet'); if(ds&&ds.refresh)ds.refresh()\n    d2.on('#pc-q','input',(e,el)=>{d2.state.q=el.value})\n    d2.on('.pr-item','click',(e,el)=>{d2.state.sku=el.dataset.sku;d2.state.base=String(el.dataset.base).replace('.',',')})\n    d2.on('#pc-base','input',(e,el)=>{d2.state.base=el.value;d2.state.sku=''})\n    d2.on('#pc-uf','change',(e,el)=>{d2.state.uf=el.value})\n    d2.on('.pc-perfil','click',(e,el)=>{d2.state.perfil=el.dataset.p})\n  }\n}"}}}}}}}},{"_id":"6a84b1cde1c15d8b3029dc8b","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bSfmc","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1000px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:820px;margin:0 0 14px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:12px 16px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin:0 0 22px}\n.obs b{color:#3B3A2E}\nh3.sec{font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:32px 0 12px;border-bottom:2px solid #D9C289;padding-bottom:6px}\n.flow{display:flex;align-items:stretch;gap:10px;flex-wrap:wrap;margin:0 0 8px}\n.fn{flex:1;min-width:150px;background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:13px 15px;font-size:13px;line-height:1.45;color:#4A4A3C}\n.fn b{display:block;color:#1E4D3B;font-size:14px;margin-bottom:3px}\n.fn.own{border:2px solid #8FA86A;background:#DCEBC8}\n.fn .em{font-size:20px}\n.ar{align-self:center;color:#8C6F2A;font-size:20px;flex:0 0 auto}\n.diag{background:#22322A;border-radius:12px;padding:22px 16px;margin:0 0 10px;overflow:hidden}\n.rowd{display:flex;align-items:stretch}\n.node{flex:0 0 auto;width:152px;background:#2E4438;border:1px solid #46604F;border-radius:10px;padding:12px 10px;text-align:center;color:#E9EEE8}\n.node.own{border-color:#B8963E;background:#38503F;box-shadow:0 0 0 2px rgba(184,150,62,.25)}\n.node .em{font-size:24px;display:block}\n.node b{display:block;font-size:13.5px;margin:4px 0 2px;color:#fff}\n.node .d{font-size:11px;color:#AABDB0;line-height:1.35;display:block}\n.rail{position:relative;flex:1;min-width:72px}\n.line{position:absolute;left:0;right:0;height:2px;background:#46604F}\n.line.out{top:38%}.line.in{top:62%}\n.tagd{position:absolute;font-size:9.5px;letter-spacing:.5px;text-transform:uppercase;color:#8F9E92}\n.tagd.out{top:calc(38% - 15px);left:6px}\n.tagd.in{top:calc(62% + 5px);left:6px}\n.chip{position:absolute;transform:translateY(-50%);font-size:11px;white-space:nowrap;padding:2px 8px;border-radius:999px;font-weight:600}\n.chip.out{top:38%;background:#3E7A54;color:#EAF6EE;border:1px solid #5FA475;animation:flowR 3.4s linear infinite}\n.chip.in{top:62%;background:#B8963E;color:#241c08;border:1px solid #D9B85A;animation:flowL 3.4s linear infinite}\n@keyframes flowR{0%{left:-6%;opacity:0}8%{opacity:1}92%{opacity:1}100%{left:100%;opacity:0}}\n@keyframes flowL{0%{left:100%;opacity:0}8%{opacity:1}92%{opacity:1}100%{left:-6%;opacity:0}}\n.leg2{display:flex;gap:16px;flex-wrap:wrap;margin-top:14px;font-size:12px;color:#AABDB0;justify-content:center}\n.leg2 .sw{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:6px;vertical-align:1px}\n@media (prefers-reduced-motion: reduce){.chip{animation:none;left:42% !important;opacity:1}}\n.card{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:18px 20px;margin-bottom:14px}\n.card .hd{display:flex;align-items:center;gap:9px;margin-bottom:6px}\n.card .hd .em{font-size:20px}\n.card .hd b{font-family:Georgia,serif;color:#1E4D3B;font-size:16.5px}\n.card .dir{margin-left:auto;font-size:11px;letter-spacing:.5px;text-transform:uppercase;background:#EFE6CF;border:1px solid #D9C289;color:#5B4A1E;border-radius:999px;padding:2px 10px}\n.card p{font-size:13.5px;color:#4A4A3C;line-height:1.55;margin:0 0 10px}\n.kv{display:flex;gap:8px;font-size:13px;margin:4px 0;flex-wrap:wrap}\n.kv .k{color:#8C6F2A;letter-spacing:.5px;text-transform:uppercase;font-size:11px;width:90px;flex:0 0 auto;padding-top:2px}\n.kv .v{color:#3B3A2E;flex:1;min-width:180px}\ncode.inl{background:#EFE6CF;border:1px solid #E3D0A0;border-radius:5px;padding:1px 6px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;color:#5B4A1E}\npre{background:#22322A;color:#E9EEE8;border-radius:9px;padding:14px 16px;overflow-x:auto;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;line-height:1.5;margin:8px 0 0}\npre .c{color:#9BB39F}\npre .k2{color:#E8CF8F}\nol.steps{margin:0;padding-left:20px;font-size:13.8px;line-height:1.7;color:#4A4A3C}\nol.steps b{color:#1E4D3B}\nul.plain{margin:6px 0 0;padding-left:18px;font-size:13.5px;line-height:1.6;color:#4A4A3C}\nul.plain b{color:#1E4D3B}\n.warn{background:#F1EBD9;border:1px dashed #C9A24B;border-radius:8px;padding:10px 13px;font-size:12.8px;line-height:1.5;color:#5B4A1E;margin-top:10px}\n.pend{font-size:11px;letter-spacing:.5px;text-transform:uppercase;background:#F7ECD6;border:1px solid #E3CE9E;color:#8C6F2A;border-radius:999px;padding:2px 9px;margin-left:8px}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.ar{display:none}.kv .k{width:100%}}`,\n  render(d2){\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Plano de integração · WhatsApp × Salesforce Marketing Cloud</p>\n  <h2>Webhook Data2 ↔ SFMC</h2>\n  <p class=\"lead\">Como conectar <b>um único número de WhatsApp</b> ao atendimento no Data2 e às campanhas do Salesforce Marketing Cloud (SFMC). Um número WABA tem um só dono de API — então o Data2 é o dono do número e <b>expõe um webhook</b> para o SFMC disparar campanhas, e <b>devolve os eventos</b> (respostas e status) para um webhook do SFMC.</p>\n  <div class=\"obs\">ℹ️ <b>Decisão de arquitetura.</b> Data2 = dono da WABA/número (atendimento + envio). SFMC = cérebro das campanhas (público, jornada, agendamento), que aciona o envio via webhook do Data2. Alternativa, se o SFMC precisar disparar 100% sozinho: usar <b>um número separado</b> só para marketing — aí não precisa deste webhook.</div>\n\n  <h3 class=\"sec\">Como funciona</h3>\n  <div class=\"diag\"><div class=\"rowd\">\n    <div class=\"node\"><span class=\"em\">🟣</span><b>SFMC</b><span class=\"d\">monta e agenda a campanha</span></div>\n    <div class=\"rail\">\n      <div class=\"line out\"></div><div class=\"line in\"></div>\n      <span class=\"tagd out\">campanha →</span><span class=\"tagd in\">← eventos</span>\n      <span class=\"chip out\">📤 template</span>\n      <span class=\"chip out\" style=\"animation-delay:1.7s\">📤 template</span>\n      <span class=\"chip in\" style=\"animation-delay:.8s\">📥 status</span>\n    </div>\n    <div class=\"node own\"><span class=\"em\">🟢</span><b>Data2</b><span class=\"d\">dono do número · dispara e recebe</span></div>\n    <div class=\"rail\">\n      <div class=\"line out\"></div><div class=\"line in\"></div>\n      <span class=\"tagd out\">envia →</span><span class=\"tagd in\">← resposta</span>\n      <span class=\"chip out\" style=\"animation-delay:.4s\">📤 msg</span>\n      <span class=\"chip out\" style=\"animation-delay:2.2s\">📤 msg</span>\n      <span class=\"chip in\" style=\"animation-delay:1.1s\">📥 resposta</span>\n      <span class=\"chip in\" style=\"animation-delay:2.9s\">📥 resposta</span>\n    </div>\n    <div class=\"node\"><span class=\"em\">💬</span><b>Cliente</b><span class=\"d\">recebe no WhatsApp e responde</span></div>\n  </div>\n  <div class=\"leg2\"><span><span class=\"sw\" style=\"background:#3E7A54\"></span>saída: campanha / envio</span><span><span class=\"sw\" style=\"background:#B8963E\"></span>retorno: resposta / status</span></div>\n  </div>\n  <p class=\"lead\" style=\"margin-top:12px\">O fluxo é de mão dupla: o SFMC aciona o envio (📤) e o Data2 devolve as respostas do cliente e os status de entrega/leitura (📥) para o webhook do SFMC atualizar a jornada e o contato.</p>\n\n  <h3 class=\"sec\">Direção A — SFMC dispara campanha <span class=\"pend\">a provisionar</span></h3>\n  <div class=\"card\">\n    <div class=\"hd\"><span class=\"em\">📤</span><b>Endpoint de envio (SFMC → Data2)</b><span class=\"dir\">SFMC chama</span></div>\n    <p>O SFMC chama este endpoint (por contato, ou em lote) para o Data2 enviar um template de WhatsApp aprovado. O envio real pela WABA é executado pelo Data2.</p>\n    <div class=\"kv\"><span class=\"k\">Método</span><span class=\"v\"><code class=\"inl\">POST</code></span></div>\n    <div class=\"kv\"><span class=\"k\">URL</span><span class=\"v\"><code class=\"inl\">https://sfmc-whatsapp.granado-app.data2apis.com/</code> <span style=\"color:#8A8672\">(a criar)</span></span></div>\n    <div class=\"kv\"><span class=\"k\">Auth</span><span class=\"v\">Header <code class=\"inl\">x-api-key: &lt;chave combinada&gt;</code></span></div>\n    <div class=\"kv\"><span class=\"k\">Content-Type</span><span class=\"v\"><code class=\"inl\">application/json</code></span></div>\n    <pre><span class=\"c\">// corpo da requisição (exemplo)</span>\n{\n  <span class=\"k2\">\"to\"</span>: \"5521999999999\",           <span class=\"c\">// telefone E.164, só dígitos</span>\n  <span class=\"k2\">\"template\"</span>: \"campanha_reativacao\", <span class=\"c\">// nome do template aprovado</span>\n  <span class=\"k2\">\"locale\"</span>: \"pt_BR\",\n  <span class=\"k2\">\"params\"</span>: [\"Maria\", \"BEMVINDO10\"],   <span class=\"c\">// preenche {{1}}, {{2}}…</span>\n  <span class=\"k2\">\"contactKey\"</span>: \"003xx000004TmiQ\",    <span class=\"c\">// id do contato no SFMC (volta nos eventos)</span>\n  <span class=\"k2\">\"campaignId\"</span>: \"reativacao-ago25\"\n}</pre>\n    <pre><span class=\"c\">// resposta</span>\n{ <span class=\"k2\">\"accepted\"</span>: true, <span class=\"k2\">\"messageId\"</span>: \"wamid.HBg...\", <span class=\"k2\">\"contactKey\"</span>: \"003xx000004TmiQ\" }</pre>\n    <div class=\"warn\">⚠️ Campanha = <b>template message</b> aprovado na Meta. Os templates vivem na WABA do <b>dono do número (Data2)</b> — o time de marketing cadastra/aprova os textos ali. Só é possível enviar templates já aprovados.</div>\n  </div>\n\n  <h3 class=\"sec\">Direção B — Data2 devolve os eventos <span class=\"pend\">a provisionar</span></h3>\n  <div class=\"card\">\n    <div class=\"hd\"><span class=\"em\">📥</span><b>Webhook de retorno (Data2 → SFMC)</b><span class=\"dir\">SFMC recebe</span></div>\n    <p>O SFMC informa uma URL de webhook; o Data2 posta ali cada evento — resposta do cliente e mudança de status da mensagem — para o SFMC atualizar a jornada.</p>\n    <div class=\"kv\"><span class=\"k\">SFMC provê</span><span class=\"v\">URL do webhook + um segredo (assinatura/validação)</span></div>\n    <div class=\"kv\"><span class=\"k\">Método</span><span class=\"v\"><code class=\"inl\">POST</code> do Data2 para a URL do SFMC</span></div>\n    <pre><span class=\"c\">// evento: cliente respondeu</span>\n{\n  <span class=\"k2\">\"type\"</span>: \"inbound_message\",\n  <span class=\"k2\">\"from\"</span>: \"5521999999999\",\n  <span class=\"k2\">\"contactKey\"</span>: \"003xx000004TmiQ\",\n  <span class=\"k2\">\"text\"</span>: \"quero aproveitar o cupom\",\n  <span class=\"k2\">\"timestamp\"</span>: \"2026-08-18T14:03:00Z\"\n}\n<span class=\"c\">// evento: status do template enviado</span>\n{ <span class=\"k2\">\"type\"</span>: \"status\", <span class=\"k2\">\"messageId\"</span>: \"wamid.HBg...\", <span class=\"k2\">\"status\"</span>: \"delivered\" }</pre>\n  </div>\n\n  <h3 class=\"sec\">Pré-requisitos</h3>\n  <ul class=\"plain\">\n    <li><b>Número WABA</b> provisionado na integração oficial de WhatsApp do Data2 (dono único do número).</li>\n    <li><b>Templates de marketing</b> cadastrados e aprovados na Meta, na WABA do Data2.</li>\n    <li><b>Chave de API</b> (x-api-key) combinada entre Data2 e SFMC para a Direção A.</li>\n    <li><b>URL de webhook do SFMC</b> + segredo, para a Direção B.</li>\n    <li>Opt-in de WhatsApp dos contatos (exigência da Meta para marketing).</li>\n  </ul>\n\n  <h3 class=\"sec\">Para o time de Salesforce (Marketing Cloud)</h3>\n  <ol class=\"steps\">\n    <li>Confirmar que o número de WhatsApp será <b>dono do Data2</b> (não conectar o mesmo número na WABA do SFMC — um número só tem um dono de API).</li>\n    <li>Enviar a <b>lista de templates</b> de campanha (texto + variáveis) para cadastro e aprovação na WABA do Data2 — informar o <code class=\"inl\">nome do template</code> e a ordem dos parâmetros <code class=\"inl\">{{1}}, {{2}}…</code>.</li>\n    <li>Na jornada/automação do SFMC, na etapa de envio, chamar a <b>Direção A</b> (<code class=\"inl\">POST</code> no endpoint do Data2) — via <i>Custom Activity</i>, <i>API Event</i> ou chamada REST — passando <code class=\"inl\">to</code>, <code class=\"inl\">template</code>, <code class=\"inl\">params</code> e o <code class=\"inl\">contactKey</code>.</li>\n    <li>Expor um <b>webhook no SFMC</b> para receber a Direção B e mapear <code class=\"inl\">contactKey</code> → contato, atualizando a jornada com a resposta e o status.</li>\n    <li>Combinar com o time Data2 a <b>x-api-key</b> (Direção A) e o <b>segredo</b> do webhook (Direção B).</li>\n    <li>Definir <b>volume/limites</b> esperados (a Meta tem tiers de mensagens e quality rating por número — vale planejar o ramp-up).</li>\n  </ol>\n\n  <div class=\"obs\" style=\"margin-top:24px\">ℹ️ <b>Status.</b> Este é o <b>plano e o contrato</b> da integração — os endpoints ainda serão provisionados. A conexão do número WhatsApp em si é operacional (Meta/BSP + time da plataforma Data2). Assim que a chave e as URLs estiverem combinadas, o webhook de recebimento pode ser criado no Data2 e testado ponta a ponta.</div>\n</div>`\n  }\n}"}}}}}}}},{"_id":"6a85ad8b62c02a327720ff11","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-guia-trocas","package":"granado-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 SCENARIOS = [\n  { id: 'loja-fisica', icon: 'store', label: 'Comprei na loja', title: 'Comprei presencialmente na loja',\n    intro: 'Você comprou um produto Granado dentro de uma das nossas lojas físicas e quer trocar.',\n    badges: [{ text: '30 dias corridos', type: 'info' }, { text: 'Pode trocar (com exceções)', type: 'warn' }],\n    steps: [\n      { title: 'Guarde o comprovante', desc: 'Guarde o Cupom Fiscal ou o Cupom de Troca emitido na loja no momento da compra.' },\n      { title: 'Volte em até 30 dias', desc: 'Leve o produto de volta até 30 dias corridos depois da compra, sem uso e sem violar a embalagem.' },\n      { title: 'Apresente o comprovante', desc: 'Mostre o Cupom Fiscal ou o Cupom de Troca ao time da loja.' },\n      { title: 'Troca na hora', desc: 'Se tudo estiver certo, a troca é feita no mesmo atendimento — direto na loja.' }\n    ],\n    notes: [\n      { type: 'info', text: 'Esse fluxo vale só para quem comprou presencialmente na loja. Comprou pelo site (entrega em casa ou retirada em loja)? Veja a aba \"Fiz um pedido online\".' },\n      { type: 'exception', text: 'Produtos em promoção e produtos personalizados não podem ser trocados — a não ser que tenham defeito de fabricação.' },\n      { type: 'legal', text: 'Trocar só porque \"não gostou\" não é uma exigência da lei para compras em loja física — é um benefício que a Granado oferece por política própria.' }\n    ],\n    contact: { phone: '0800 940 6730', hours: 'seg. a sex., 9h às 18h', email: 'sac@granado.com.br' }\n  },\n  { id: 'presente', icon: 'gift', label: 'Recebi de presente', title: 'Ganhei de presente (loja física)',\n    intro: 'Alguém comprou um produto Granado numa loja física e te deu de presente.',\n    badges: [{ text: '30 dias corridos', type: 'info' }, { text: 'Depende do cupom', type: 'warn' }],\n    steps: [\n      { title: 'No momento da compra', desc: 'O vendedor pergunta a quem está comprando se quer levar o Cupom de Troca — é opcional.' },\n      { title: 'Se você tem o cupom', desc: 'Você troca o produto normalmente, dentro de 30 dias corridos, do mesmo jeito que numa compra própria.' },\n      { title: 'Se você não tem o cupom', desc: 'Fica mais difícil comprovar a compra, e a troca pode não ser possível.' }\n    ],\n    notes: [{ type: 'info', text: 'Dica para quem vai comprar um presente: peça sempre o Cupom de Troca na hora da compra e entregue junto com o produto.' }]\n  },\n  { id: 'logistica', icon: 'box', label: 'Não recebi', title: 'Pedido não entregue / devolvido pela transportadora',\n    intro: 'Isso não é exatamente uma troca — é o que acontece quando a entrega não é concluída.',\n    badges: [{ text: 'Não é uma troca', type: 'neutral' }, { text: 'Prazo varia', type: 'neutral' }],\n    steps: [\n      { title: 'Até 3 tentativas de entrega', desc: 'A transportadora tenta entregar o pedido em até 3 vezes, sempre em dias úteis.' },\n      { title: 'Retenção nos Correios', desc: 'Quando o envio é pelos Correios, o pacote fica retido na base por 7 dias antes de voltar para a Granado.' },\n      { title: 'Áreas rurais ou de difícil acesso', desc: 'Nesses casos, o pacote fica retido por 7 dias corridos no posto mais próximo.' },\n      { title: 'Se o pacote volta para o centro de distribuição', desc: 'Sem chamado aberto: cancelamento e reembolso automáticos. Com chamado aberto: e-mail confirmando reposição ou reembolso.' },\n      { title: 'Mudou de endereço depois da compra?', desc: 'A devolução acontece automaticamente — contate o SAC para resolver.' }\n    ]\n  },\n  { id: 'acessorios', icon: 'bag', label: 'Granado Acessórios', title: 'Comprei na Granado Acessórios (bolsas e acessórios)',\n    highlight: 'A Granado Acessórios é uma marca e um site diferentes da Granado Farmácias — as regras de troca são outras.',\n    intro: 'Você comprou um produto no site da Granado Acessórios.',\n    badges: [{ text: 'Troca: 10 dias', type: 'info' }, { text: 'Devolução: 7 dias corridos', type: 'info' }],\n    steps: [\n      { title: 'Prazo para troca', desc: 'Até 10 dias depois da entrega do produto.' },\n      { title: 'Prazo para devolução', desc: 'Até 7 dias corridos.' },\n      { title: 'Condições do produto', desc: 'Sem uso, sem sinais de desgaste, com a embalagem original e a nota fiscal.' },\n      { title: 'Frete', desc: 'A primeira troca ou devolução é grátis. A partir da segunda solicitação, o frete é pago pelo cliente.' },\n      { title: 'Reembolso', desc: 'Via PIX, em até 48h úteis. No cartão, em até 3 dias úteis.' }\n    ],\n    notes: [\n      { type: 'exception', text: 'Produtos personalizados não podem ser trocados nem cancelados.' },\n      { type: 'info', text: 'Comprou pela Amazon, Shopee ou outro marketplace? A troca e a devolução são resolvidas direto na plataforma onde você comprou, não com a Granado.' }\n    ]\n  },\n  { id: 'b2b', icon: 'building', label: 'Canal B2B', title: 'Canal B2B (vendas para empresas e revenda)',\n    intro: 'O canal B2B tem uma política de devolução própria, separada da política para o consumidor final.',\n    badges: [{ text: 'Fluxo diferente do varejo', type: 'neutral' }, { text: 'Conteúdo incompleto', type: 'warn' }],\n    incomplete: true,\n    notes: [{ type: 'warn', text: 'Esse fluxo é mais restrito e negociado caso a caso — não é o mesmo processo usado no varejo. O conteúdo completo desta política ainda precisa ser levantado com o time comercial B2B.' }]\n  }\n]\n\nconst TABS = [\n  { id: 'loja-fisica', icon: 'store', label: 'Comprei na loja' },\n  { id: 'presente', icon: 'gift', label: 'Recebi de presente' },\n  { id: 'pedido-online', icon: 'monitor', label: 'Fiz um pedido online' },\n  { id: 'logistica', icon: 'box', label: 'Não recebi' },\n  { id: 'acessorios', icon: 'bag', label: 'Granado Acessórios' },\n  { id: 'b2b', icon: 'building', label: 'Canal B2B' }\n]\n\nconst ORIGINS = {\n  cd: { label: 'CD / Darkstore', icon: 'warehouse', desc: 'O pedido foi separado num centro de distribuição ou darkstore — mesmo que a entrega tenha sido em casa ou a retirada numa loja.', channelLabel: 'Portal self-service', channelDetail: 'granado.troque.app.br', routingClass: 'routing-portal' },\n  loja: { label: 'Loja física', icon: 'store', desc: 'O pedido foi separado numa loja física — mesmo que a entrega tenha sido em casa ou a retirada numa loja diferente.', channelLabel: 'SAC', channelDetail: '0800 940 6730 · sac@granado.com.br', routingClass: 'routing-sac' }\n}\n\nconst REASONS = {\n  arrependimento: { label: 'Não gostei / comprei errado', badge: '7 dias corridos', badgeType: 'info',\n    windowText: 'Você tem 7 dias corridos a partir do RECEBIMENTO do produto para desistir da compra.',\n    notes: [\n      { type: 'exception', text: 'Itens em promoção não entram nesse direito de devolução — a não ser que tenham defeito de fabricação.' },\n      { type: 'legal', text: 'Esse prazo de 7 dias é o \"direito de arrependimento\", garantido por lei para compras feitas fora de loja física (art. 49 do Código de Defesa do Consumidor).' }\n    ]\n  },\n  defeito: { label: 'Defeito ou avaria no transporte', badge: 'Dentro da garantia', badgeType: 'ok',\n    windowText: 'Você pode pedir a troca em qualquer momento dentro da garantia do produto — não fica restrito aos 7 dias do arrependimento.',\n    notes: [{ type: 'info', text: 'Esse fluxo é diferente do \"arrependimento\": aqui o problema é do produto, então não há limite de 7 dias.' }]\n  }\n}\n\nconst LANES = {\n  sac: { label: 'SAC' }, loja: { label: 'Loja Física' }, dark: { label: 'Dark Store' },\n  logistica: { label: 'Logística / CD' }, digital: { label: 'Canais Digitais' },\n  financeiro: { label: 'Contas a Receber' }, fiscal: { label: 'Fiscal' }\n}\n\nconst OPS_SECTIONS = [\n  { id: 'canais-abertura', title: 'Canais de abertura do chamado (SAC)', lanes: ['sac'], nodes: [\n    { kind: 'step', text: 'Cliente contata por e-mail ou WhatsApp → chamado é criado automaticamente no Service Cloud.' },\n    { kind: 'decision', text: 'Cliente contata por telefone — o atendente decide onde tratar', branches: ['Tipo 3 (retire em loja/expressa) ou marca Care → abre direto no Service Cloud', 'Tipo 1 (CD) ou Tipo 2 (Dark) → decide entre Service Cloud ou Troquecommerce'] },\n    { kind: 'warning', text: 'Regra dura: devoluções de Care, Fidelidade e tipo 3 NÃO podem ser tratadas pelo Troquecommerce — só pelo Service Cloud.', gapRef: '49' },\n    { kind: 'step', text: 'SAC avalia os dados da solicitação: tipo de entrega, prazo de devolução (até 7 dias após o pedido) e se foi enviada foto dos itens.' },\n    { kind: 'step', text: 'Melhoria já implementada: criação de um e-mail único pra \"operação Omni\" (em vez do e-mail pessoal de cada atendente espalhado pelas lojas) — já em uso.', meeting: true },\n    { kind: 'warning', text: 'Ainda assim, funcionários de loja às vezes pedem cancelamento direto por e-mail ao atendente do SAC, sem abrir chamado formal — e às vezes desfazem o pedido pouco depois, sem nenhum registro. Uma iniciativa do time de TI já está em andamento pra que toda abertura feita na loja gere automaticamente um registro do lado do SAC também.', meeting: true }\n  ]},\n  { id: 'motivo-devolucao', title: 'Motivo da devolução', lanes: ['sac'], nodes: [\n    { kind: 'decision', text: 'Motivo é Qualidade?', branches: ['Sim → gera coleta pra fábrica + abre reclamação de mercado'] },\n    { kind: 'decision', text: 'Motivo é Normal — precisa de reposição do produto?', branches: ['Não → gera coleta pro CD (ou pra matriz/Canais Digitais, se tipo 3 e não lacrado)', 'Sim → segue para: é troca ou extravio?'] },\n    { kind: 'decision', text: 'Se for Troca', branches: ['Marca Care → pedido de bonificação via Wepipe (integra com Shopify, processa no SAP e gera NF)', 'Marca Granado/Phebo → solicita troca ao CD correspondente'] },\n    { kind: 'decision', text: 'Se for Extravio', branches: ['Pedido de bonificação no Magento (ou no Wepipe, se for marca Care)'] }\n  ]},\n  { id: 'sem-origem', title: 'Defeito sem saber a origem da compra', lanes: ['sac'], nodes: [\n    { kind: 'step', text: 'Cliente tem um produto com problema mas não sabe de qual pedido ele veio — achou, ganhou de alguém, ou perdeu essa informação.' },\n    { kind: 'warning', text: 'Sem código de pedido, não há como confirmar a origem da compra — inclusive existe o risco de o item ser produto roubado. Não existe portal self-service pra esse caso; é tratado manualmente pelo SAC, caso a caso.' }\n  ]},\n  { id: 'tipo3', title: 'Regra do tipo 3 — retirada em loja / entrega expressa', lanes: ['sac', 'loja', 'digital'], nodes: [\n    { kind: 'decision', text: 'Produto está lacrado?', branches: ['Não → gera coleta direto pra matriz/Canais Digitais via transportadora (Correios)', 'Sim → SAC confirma com o cliente: devolver na loja de retirada ou enviar pelos Correios?'] },\n    { kind: 'step', text: 'Se Correios → gera coleta pra loja de retirada no site da transportadora.', meeting: true },\n    { kind: 'warning', text: 'A loja NÃO aceita a devolução sem comunicação prévia do SAC + OK do time Digital — mesmo com o cliente já presente na loja.', gapRef: '22' },\n    { kind: 'step', text: 'Só depois dessa confirmação, o cliente é orientado a ir na loja devolver.' },\n    { kind: 'warning', text: 'Falta integração entre os sistemas de tratativa do SAC e a ferramenta de coleta dos Correios.', gapRef: '21' },\n    { kind: 'decision', text: 'A loja também pode recusar um pedido de \"retirada em loja\" se o estoque físico não bate com o sistema (\"satélite\") — o estorno automático depende da combinação marca + forma de pagamento.', branches: ['Marca Granado/Phebo + cartão de crédito ou PIX à vista via Adyen → estorno automático e imediato (o pedido nem é faturado)', 'Marca Care, mesmo pago via Adyen → SEM automação — alguém precisa ir manualmente devolver o valor ao cliente', 'Pagamento via PagBrasil (usado em PIX parcelado) → SEM automação nenhuma, pra qualquer marca'] },\n    { kind: 'warning', text: 'O cliente não é avisado ativamente quando isso acontece — só percebe que o pedido não completa, dependendo do tempo de resposta da loja.', meeting: true },\n    { kind: 'step', text: 'Esclarecimento: um carrinho com item de marca Granado + item de marca Phebo misturados não retorna loja pra \"retirar em loja\" — isso PARECE um bug, mas é comportamento correto e esperado: nenhuma loja teria estoque das duas marcas prontas pro mesmo picking, então é normal nenhuma aparecer disponível. O mesmo pedido funciona no Super Express porque, no teste, caiu pra uma Darkstore, que atende as duas marcas juntas (a entrega expressa em si é atendida tanto por Darkstores quanto por lojas físicas, conforme a cobertura de CEP de cada unidade).' },\n    { kind: 'step', text: 'Melhoria já implementada (cerca de 1 mês): o cliente recebe um aviso de que tem 7 dias corridos pra retirar o pedido — mas isso é só uma notificação, não um cancelamento automático. Na prática, o pedido fica parado até cerca de 30 dias, quando a gerente da Darkstore avisa informalmente o time Digital (fora do processo oficial), que só então processa tudo manualmente (SAP + planilha do financeiro).', meeting: true }\n  ]},\n  { id: 'tipo3-pos-retirada', title: 'Retirada em loja — depois que o cliente já retirou (lacrado vs. aberto)', lanes: ['sac', 'loja', 'logistica', 'fiscal'], nodes: [\n    { kind: 'decision', text: 'O cliente já retirou o pedido e não gostou. O item está lacrado ou já foi aberto?', branches: ['Lacrado → fluxo A', 'Aberto (cliente já usou/abriu) → fluxo B'] },\n    { kind: 'step', text: 'Fluxo A (lacrado): o SAC abre chamado e manda a coleta pra CASA do cliente.' },\n    { kind: 'step', text: 'Fluxo A: o item físico volta pra LOJA de origem — não pro escritório do SAC.' },\n    { kind: 'warning', text: 'Fluxo A: o SAC depende de a loja avisar por e-mail ou WhatsApp que o item chegou — não existe SLA formal pra esse aviso; o atendente precisa cobrar ativamente.', meeting: true },\n    { kind: 'step', text: 'Enquanto o item lacrado está devolvido na loja mas ainda não processado no SAP (aguardando o SAC), a loja consegue vender outra unidade do mesmo produto pelo PDV com estoque negativo e \"dar baixa\" depois — mas isso só é possível manualmente, e alguém no time questionou se vale a pena esse item passar pelo SAC nesse caso, já que quem devolve o estoque é o SAC mesmo assim.', meeting: true },\n    { kind: 'step', text: 'Fluxo A: só depois da confirmação, o SAC solicita o estorno ao financeiro (quem processa é o financeiro, não o SAC) e faz a devolução manual no SAP pra gerar a nota de devolução/DANFE — isso libera o item de volta pro estoque digital da loja.' },\n    { kind: 'step', text: 'Fluxo B (aberto): o SAC manda a coleta, mas esse item vai pro ESCRITÓRIO do SAC — não pra loja.' },\n    { kind: 'warning', text: 'Fluxo B: não existe processo formal de descarte/qualidade pra esses itens hoje — eles se acumulam nas mesas do time, e é comum que sejam usados informalmente por quem trabalha ali (ex.: perfumes devolvidos ficam disponíveis pra uso). Isso é arriscado: já houve um caso real de um produto devolvido que continha urina.', meeting: true },\n    { kind: 'warning', text: 'Reforço com números concretos: pessoa que trabalha há 10 meses no time nunca participou de um descarte formal — os itens só se acumulam. Relato de dois armários lotados, picos maiores em campanhas (Semana Granada), com sacos de correio \"maiores que a pessoa carregando\" (uma vez, cerca de 50 caixas em um só saco). O processo correto seria mandar pra baixa por pré-destruição na fábrica, mas isso não está acontecendo de fato.', meeting: true },\n    { kind: 'step', text: 'O time de Qualidade só quer receber de volta casos de reclamação real de segurança (corpo estranho, reação alérgica, suspeita de adulteração — que exigem laudo pra ANVISA). Tudo o resto deveria ser descartado, mas não há mecanismo pra isso ainda. Existe um processo de \"baixa por pré-destruição\" no CD/SAP (hoje usado pra lotes vencendo) que poderia ser reaproveitado — ainda não conectado a esse fluxo.' },\n    { kind: 'warning', text: 'BUG conhecido: pra emitir a nota de devolução (DANFE) desse item, o atendente precisa rodar a remessa de devolução no SAP, que também cancela a nota fiscal original — e essa transação devolve o item digitalmente pro estoque da LOJA de origem, mesmo com o item físico parado, aberto, no escritório do SAC. Isso cria estoque fantasma, só percebido na próxima contagem de inventário da loja.', meeting: true },\n    { kind: 'warning', text: 'Importante: não processar a devolução no SAP pra esses itens abertos não é pura negligência — é um workaround deliberado. Se processassem, o SAP creditaria automaticamente o estoque da loja errado (o próprio bug de estoque fantasma acima). \"Não fazer nada\" evita um problema maior, mesmo sendo uma solução imperfeita.', meeting: true },\n    { kind: 'warning', text: 'Confirmação oficial (dor #24 do relatório de auditoria): esses itens abertos ficam sem destino definido e sem rastreio sistêmico, existindo só fisicamente. O fix proposto é alinhar que voltem pro estoque da loja de origem e sigam pro CD visando devolução pra Ponteland — ainda não seguido na prática.', gapRef: '24' },\n    { kind: 'step', text: 'É comum o item chegar ao escritório sem identificação clara — às vezes só o nome do atendente, às vezes CPF, às vezes número de pedido, às vezes um código de coleta que não bate com nada. O atendente precisa investigar manualmente qual pedido é aquele item.' },\n    { kind: 'warning', text: 'Resultado prático: um pedido que o cliente queria com urgência (por isso escolheu retirada ou entrega expressa) pode levar cerca de 1 mês até resolver o estorno ou a troca.', meeting: true }\n  ]},\n  { id: 'entrega-expressa-cep', title: 'Entrega Expressa — cobertura de CEP (parceiro terceirizado)', lanes: ['logistica', 'digital'], nodes: [\n    { kind: 'step', text: 'Na Entrega Expressa o cliente não escolhe a loja — o sistema atribui de acordo com o CEP dele (diferente do \"retiro em loja\", onde o cliente escolhe a loja).', meeting: true },\n    { kind: 'step', text: 'O time de logística sobe manualmente uma planilha de CEPs cobertos por loja pra entrega expressa (parceiro terceirizado \"EuEntrego\"). Já tentaram automatizar essa cobertura e não funcionou.' },\n    { kind: 'warning', text: 'Muitos pedidos ficam \"fora de abrangência\" — por erro no CEP cadastrado, pela loja ter deixado de ser atendida pela transportadora, ou por mudança no raio de cobertura — e ninguém é notificado automaticamente. O pedido fica parado, sem ser roteado pra nenhum entregador, até a loja ou o cliente reclamarem. Essa tabela de frete/raio nasce no momento da ativação do canal SFS/Retire (ver seção \"Como o canal SFS/Retire é ativado\") — se não for criada/atualizada direito ali, ou a cobertura real da transportadora mudar depois, é exatamente isso que gera esse problema.', meeting: true },\n    { kind: 'step', text: 'Quando descoberto, o time oferece ao cliente duas opções: retirar na loja ou cancelar o pedido.' },\n    { kind: 'step', text: 'Se o cliente pagou frete expresso, isso se torna mais um caso de estorno — às vezes parcial. O caso do estorno parcial em cartão-presente (seção Contas a Receber) aconteceu exatamente numa situação assim, de pedido fora de abrangência.', meeting: true },\n    { kind: 'warning', text: 'Caso novo: o EuEntrego às vezes marca o pedido como ENTREGUE no sistema mesmo o produto tendo se perdido/extraviado — o cliente nunca recebe e precisa abrir chamado no SAC contestando. Isso aciona a mesma dor oficial #25 (extravio impede cancelamento no SAP).', gapRef: '25' }\n  ]},\n  { id: 'checkout-validacao', title: 'Validação no checkout — estoque vs. cobertura de entrega', lanes: ['digital', 'logistica'], nodes: [\n    { kind: 'step', text: 'Na tela de escolha de frete, o sistema consulta o SAP e o CEP/local pra saber as opções de entrega disponíveis.' },\n    { kind: 'warning', text: 'Ele NÃO valida se o produto específico tem estoque suficiente pra aquela operação — testado ao vivo pedindo 80 unidades de um item sem estoque, e mesmo assim o checkout mostrou normalmente as opções de retirada em loja e frete expresso. Duas explicações prováveis (ver seção \"Como o canal SFS/Retire é ativado\"): (1) na ativação de Care, se o TI terminar os passos dele DEPOIS da liberação no Shopify, a modalidade fica disponível sem integrar direito o estoque com o OMS; (2) o gatilho de inativação por produto de vencimento curto (estoque só no CD) é manual — se ninguém lembrar de acionar, o produto continua \"disponível\" mesmo sem estoque real de loja.', live: true },\n    { kind: 'step', text: 'São duas checagens sistêmicas diferentes: (a) o produto existe e está vendável no cadastro do e-commerce; (b) o CEP está dentro da área de cobertura de entrega. Uma pode falhar sem a outra falhar — são integrações separadas.' },\n    { kind: 'warning', text: 'Existe também um limite de 10 unidades por item no site (pra evitar caracterização de revenda, por questão fiscal) — mas essa trava também já falhou sob alta demanda (ex.: campanha \"Semana Granada\", com clientes comprando 15-20 unidades de sabonetes em promoção).', meeting: true }\n  ]},\n  { id: 'loja-excecao', title: 'Loja física — atendimento direto (exceção)', lanes: ['loja', 'digital'], nodes: [\n    { kind: 'warning', text: 'Isso é uma EXCEÇÃO, não o padrão: a devolução deveria ser sempre encaminhada ao SAC. A loja só aceita direto em casos excepcionais (ex.: cliente fiel).' },\n    { kind: 'warning', text: 'Existe uma forma de fazer a troca dentro do sistema da própria loja, mas ela não é usada na prática — a barreira de \"loja não pode aceitar sem autorização do SAC\" é parcialmente comportamental/procedimental, não só técnica: o time da loja simplesmente manda o cliente pra casa em vez de usar esse caminho.', meeting: true },\n    { kind: 'step', text: 'Se aceitar excepcionalmente, a loja avisa o CD e os Canais Digitais por e-mail.' },\n    { kind: 'decision', text: 'É troca e o item está lacrado?', branches: ['Sim → realiza a troca direto no PDV', 'Não → orienta o cliente a contatar o SAC'] },\n    { kind: 'step', text: 'Se for devolução (não troca) → cai no fluxo dos Canais Digitais.', gapRef: '5' }\n  ]},\n  { id: 'risco-fraude-cancelamento', title: 'Risco de fraude — pedido cancelado digitalmente mas retirado e pago na loja', lanes: ['loja', 'digital'], nodes: [\n    { kind: 'step', text: 'A loja tenta contato com o cliente por WhatsApp pedindo pra retirar o pedido represado; sem resposta, pede pro time digital cancelar.' },\n    { kind: 'step', text: 'O time digital processa o cancelamento no sistema.' },\n    { kind: 'warning', text: 'Só que a loja processa a retirada e recebe pagamento informal do cliente quase ao mesmo tempo — por falta de comunicação a tempo entre loja e time digital, o pedido acaba cancelado digitalmente enquanto o cliente já está com o produto e já pagou.', meeting: true },\n    { kind: 'warning', text: 'Resultado: fica irregular — sem baixa de estoque, sem nota fiscal. Teoricamente o cliente poderia contestar a cobrança no cartão alegando arrependimento, mesmo já tendo o produto em mãos.', meeting: true },\n    { kind: 'warning', text: 'Outro gatilho pro mesmo risco: quando o pedido fica travado no fiscal sem faturar (ver seção Assimetria de Faturamento), a loja sabe mas não pode liberar oficialmente — se o cliente já pagou e aparece pra buscar, a loja entrega informalmente mesmo sem nota fiscal.', meeting: true }\n  ]},\n  { id: 'loja-venda-propria', title: 'Loja física — devolução de venda feita na própria loja', lanes: ['loja', 'financeiro'], nodes: [\n    { kind: 'decision', text: 'Foi solicitado no mesmo dia da compra?', branches: ['Sim, cartão → estorna direto no PDV', 'Sim, dinheiro → devolve o dinheiro em mãos', 'Não, cartão → abre solicitação de estorno no Pipefy (data da transação, NSU, valor, autorização)', 'Não, dinheiro → sem regra clara — avaliar com Supervisor + Op Varejo caso a caso'], gapRef: ['45', '50'] },\n    { kind: 'step', text: 'Sempre, ao final: registra a devolução no PDV (Linx) com CPF, NF, vendedor e itens.' },\n    { kind: 'step', text: 'Valida no relatório Movimento Diário e confere se o cupom foi integrado ao SAP S4.' }\n  ]},\n  { id: 'faturamento-assimetria', title: 'Assimetria de faturamento entre retirada em loja e entrega expressa', lanes: ['fiscal', 'logistica', 'digital'], nodes: [\n    { kind: 'step', text: 'Pagamento aprovado não significa pedido faturado — o pedido pode ficar dias parado no SAP sem faturar, por motivo que nem o time interno sabe identificar caso a caso.' },\n    { kind: 'step', text: 'Existe um e-mail diário automático avisando o time Fiscal de pedidos parados no SAP sem faturar — alguém força manualmente o faturamento.' },\n    { kind: 'decision', text: 'O que o faturamento libera depende do tipo de entrega', branches: ['Retirada em loja: fatura → entra na fila de separação → só depois a loja marca \"pronto pra retirar\"', 'Entrega expressa: é o INVERSO — só depois de faturado é que libera a separação/coleta pro courier; enquanto não fatura, a entrega nem começa'] },\n    { kind: 'warning', text: 'Exemplo real: cliente pagou R$20 de frete esperando 2 dias, o pedido ficou 5 dias parado sem faturar, só no 6º dia gerou a coleta.', meeting: true },\n    { kind: 'warning', text: 'Escala relatada: já viram 150 pedidos parados por pendência fiscal em um único dia.', meeting: true },\n    { kind: 'warning', text: 'A loja SABE que o pedido está represado no fiscal — só não pode liberar oficialmente enquanto não fatura. Na prática, se o cliente já pagou e aparece pra buscar, a loja entrega informalmente MESMO SEM NOTA FISCAL. Isso é outro gatilho pro mesmo risco de fraude/chargeback já mapeado na seção \"Risco de fraude — pedido cancelado digitalmente mas retirado e pago na loja\" — não é um caso isolado.', meeting: true }\n  ]},\n  { id: 'nf-faturada', title: 'Nota fiscal já faturada', lanes: ['fiscal', 'financeiro'], nodes: [\n    { kind: 'decision', text: 'Está dentro do prazo de cancelamento?', branches: ['Cupom Fiscal, até 30 min → cancela a NF direto', 'DANFE, até 24h → cancela a NF direto'] },\n    { kind: 'step', text: 'Fora do prazo: cria ordem de devolução no SAP → gera remessa → registra entrada no estoque → emite nota de devolução.', gapRef: ['15', '16'] },\n    { kind: 'step', text: 'Se o pedido já foi entregue ao cliente, solicita coleta via SAT.' },\n    { kind: 'step', text: 'Em paralelo, Contas a Receber é avisado pra não emitir (ou cancelar) o boleto dessa venda.' },\n    { kind: 'warning', text: 'A maioria das devoluções fica parada na área Fiscal por recorrência de rejeição das notas geradas (MIRO/SEFAZ), e o SLA dessa área pra resolver é demorado.', gapRef: ['42', '43'] }\n  ]},\n  { id: 'logistica-cd', title: 'Logística / CD — o item devolvido', lanes: ['logistica', 'financeiro'], nodes: [\n    { kind: 'step', text: 'Primeiro verifica se a devolução está de acordo com o que o SAC solicitou — senão, devolve pro SAC avaliar de novo.' },\n    { kind: 'decision', text: 'É reembolso?', branches: ['Sim → gera ordem de devolução referenciando o pedido original + alimenta planilha de estornos pra Contas a Receber'] },\n    { kind: 'decision', text: 'É troca?', branches: ['Marca Granado/Phebo → conferência, contagem de itens e controle de qualidade → decide se o produto é revendável', 'Marca Care → segue fluxo próprio (ver aviso abaixo)'] },\n    { kind: 'warning', text: 'A conferência manual no CD não é rápida como um \"caminho feliz\" sugeriria — a fila pode acumular por MESES. Quem confere ainda precisa preencher outra planilha manual própria (distinta das já conhecidas do SAC e dos Canais Digitais) pra avisar o financeiro.', meeting: true },\n    { kind: 'warning', text: 'Mesmo pra Adyen + marca Granado/Phebo, o financeiro ainda faz um input MANUAL na Adyen pra liberar o estorno — a automação real do Adyen é específica do cenário de recusa da loja (dor oficial #8), não do fluxo padrão via CD.', gapRef: '8' },\n    { kind: 'warning', text: 'Marca Care: estornos pendentes não são integrados automaticamente ao Contas a Receber — processo manual. Segundo o relatório oficial de auditoria (dor #9): o motivo raiz foi falta de acesso às transações certas no SAP S4 — o acesso já foi liberado, e a regularização das cerca de 50 devoluções pendentes (base 03/08/2026) está em andamento.', gapRef: '1' },\n    { kind: 'decision', text: 'Produto é revendável?', branches: ['Sim → reintegra ao estoque (fluxo depende de onde a compra foi originada: SAP B1 ou S4)', 'Não → segue fluxo de baixa/transferência entre unidades internas (envolve \"Dark\" e \"Jesmond\"/\"Ponteland\" — processo de bastidor de transferência de estoque)'], gapRef: ['44', '14'] },\n    { kind: 'warning', text: 'Depois de ajustar uma ordem de devolução, é preciso salvar a remessa manualmente pra integrar ao WMS — na emissão inicial isso já é automático; só a remessa ajustada exige esse passo manual.', gapRef: '46' },\n    { kind: 'warning', text: 'Processo de lançar devolução no SAP S4 é moroso, e não há certeza se existe manual documentando os passos — alta dependência de TI.', gapRef: '11' },\n    { kind: 'step', text: 'Nota de contexto: as dores relatadas no retrabalho de loja pro fluxo Jesmond → Ponteland (preenchimento de telas duplicadas, aprovação lenta, mensagens de erro confusas, dificuldade de localizar NF de origem etc.) colapsam oficialmente em cerca de 9 requisitos reais (R1 a R9, no relatório de auditoria) — resolver os requisitos de base resolveria a maioria delas de uma vez.' }\n  ]},\n  { id: 'canais-digitais', title: 'Canais Digitais — cancelamento e estorno da venda online', lanes: ['digital'], nodes: [\n    { kind: 'decision', text: 'É troca de produto ainda não aberto/lacrado?', branches: ['Sim → simples: refaz o pedido no Magento/Shopify como bonificação'] },\n    { kind: 'decision', text: 'É devolução — veio de e-commerce normal ou de pontos de fidelidade (Loyalty/CRM)?', branches: ['E-commerce, marca Granado/Phebo → cancela no Magento', 'E-commerce, marca Care → cancela na Shopify', 'Fidelidade, marca Granado/Phebo → cancela no Magento', 'Fidelidade, marca Care → devolve os pontos manualmente no clube de fidelidade + confirma por e-mail ao cliente'] },\n    { kind: 'warning', text: 'Novo bug: quando a loja recusa um pedido da marca Care, o Shopify não mostra isso pro cliente — ele fica sem saber que o pedido foi recusado. Em contraste, o cancelamento via OMS + Magento (Granado/Phebo) já é automático e funciona bem.', meeting: true },\n    { kind: 'step', text: 'Depois: identifica a NF do pedido → gera ordem de venda de devolução referenciando ela no SAP.' },\n    { kind: 'warning', text: 'Alimenta uma planilha MANUAL de estornos, compartilhada com o SAC e enviada diariamente por e-mail pro Contas a Receber — ponto de erro conhecido.', gapRef: '7' },\n    { kind: 'warning', text: 'Na prática hoje existem pelo menos duas planilhas manuais paralelas alimentando o mesmo processo de estorno — uma do lado do SAC (retirada/entrega expressa) e outra, ainda maior, do lado dos Canais Digitais — cada uma preenchida por uma pessoa diferente, sem integração entre elas.', meeting: true },\n    { kind: 'warning', text: 'O SLA de retorno da área de Canais Digitais costuma ser longo, exigindo cobranças repetidas do SAC.', gapRef: '23' },\n    { kind: 'warning', text: 'Todo cancelamento/devolução é registrado com motivo genérico \"desistência do cliente\", independente do motivo real — perde rastreabilidade e impede relatório preventivo.', gapRef: '10' },\n    { kind: 'warning', text: 'Não existe controle unificado das devoluções feitas pelo time de Canais Digitais — risco de duplicidade, baixa rastreabilidade.', gapRef: '12' },\n    { kind: 'warning', text: 'Quando o pedido é extraviado, não é possível cancelar no SAP S4 — só dá pra estornar, ficando a venda em aberto sem contrapartida da adquirente.', gapRef: '25' },\n    { kind: 'warning', text: 'Extravio em entrega expressa: o SAT precisa abrir investigação, mas o SLA até Canais Digitais receber a solicitação é alto, perdendo a janela de investigação. Fix proposto: SAC comunicar Digital e SAT em paralelo via Service Cloud.', gapRef: '47' }\n  ]},\n  { id: 'contas-receber', title: 'Contas a Receber — conciliação do estorno', lanes: ['financeiro'], nodes: [\n    { kind: 'decision', text: 'Pagamento foi via adquirente Cielo?', branches: ['Sim → acessa a plataforma da Cielo direto, busca a venda e avisa por e-mail que o estorno foi solicitado', 'Outras adquirentes → preenche planilha de estornos da filial, envia pra Cielo processar, baixa carta de confirmação de cada estorno'], gapRef: ['19', '20'] },\n    { kind: 'decision', text: 'Qual foi a forma de pagamento (caso mais amplo, não só Cielo)?', branches: ['Cartão de crédito ou PIX normal, via adquirente \"Adin\" → planilha financeira manual, o financeiro processa', 'PIX parcelado ou outras adquirentes (PagBrasil, PagAleve) → nada automático, processo ainda mais manual'], meeting: true },\n    { kind: 'warning', text: 'Estorno parcial em cartão-presente é praticamente impossível de resolver rápido. Caso relatado: cliente com cartão-presente de R$80 precisava de estorno parcial de R$18 (frete) — levou 3 semanas até descobrirem quem no time sabia fazer esse tipo de estorno (nem esse time sabia que tinha essa capacidade).', meeting: true },\n    { kind: 'warning', text: 'Ausência de processo efetivo pra conciliar notas de devolução na conta \"Clientes Nacionais\".', gapRef: '18' },\n    { kind: 'warning', text: 'A forma de pagamento usada pra registrar uma \"troca\" varia por processo — CD usa \"troca\" a partir do pedido original; Digital usa \"bonificação\" em pedido novo; SAC-Care usa Wepipe→Shopify \"bonificação\"; SAC-Granado/Phebo só emite Magento em reposição por extravio. Essa inconsistência de 4 caminhos infla artificialmente a conta contábil \"Clientes Nacionais\".', gapRef: '48' }\n  ]},\n  { id: 'loyalty', title: 'Programa de Fidelidade (Loyalty) — site separado', lanes: ['digital'], nodes: [\n    { kind: 'step', text: 'O Loyalty é um site totalmente separado do e-commerce principal — decisão histórica de manter os dois apartados.' },\n    { kind: 'decision', text: 'O que o cliente pode fazer com pontos?', branches: ['Resgatar item com pontos e retirar em loja — funciona', 'Resgatar item com pontos e pedir entrega expressa — NÃO EXISTE essa opção'] },\n    { kind: 'step', text: 'Cadastro no programa pode acontecer no site do Loyalty, durante a compra no e-commerce (checkbox), ou na loja física.' },\n    { kind: 'warning', text: 'Só pontua comprando nos canais próprios (loja física ou e-commerce principal) — compra via marketplace (TikTok, etc.) não pontua.' },\n    { kind: 'step', text: 'Se o cliente devolver um item resgatado com pontos, os pontos voltam pra conta dele — não há reembolso em dinheiro, já que não houve pagamento.' },\n    { kind: 'warning', text: 'Dor oficial #27: quando a loja recusa um pedido pago com \"fidelidade\", a devolução dos pontos não é automática — o CRM não é notificado do cancelamento pra devolver os pontos manualmente ao cliente.', gapRef: '27' }\n  ]},\n  { id: 'dark-store-nao-retirado', title: 'Dark Store — pedidos \"retire em loja\" não retirados', lanes: ['dark'], nodes: [\n    { kind: 'step', text: 'Rotina diária: verifica no BI pedidos do tipo \"retirada em loja\" parados há mais de 30 dias sem o cliente buscar.' },\n    { kind: 'step', text: 'Confirma com a loja se realmente não foi entregue.' },\n    { kind: 'decision', text: 'Resultado da confirmação', branches: ['Foi entregue mas não atualizado → pede pro TI corrigir o status do pedido via Teams', 'Não foi entregue de fato → entra no fluxo de Logística/CD (reintegração de estoque)'] }\n  ]},\n  { id: 'ativacao-canal', title: 'Como o canal SFS/Retire em Loja é ativado (setup interno)', lanes: ['digital', 'logistica', 'loja'], nodes: [\n    { kind: 'step', text: 'Alinhamento Digital x Varejo: o Digital define com o Varejo quais lojas serão ativadas pra SFS (Ship from Store) e Retire em Loja.' },\n    { kind: 'step', text: 'Cadastro das lojas: o Digital cadastra a loja no Magento (estoque = Sources, opção de retirada = Content); a Logística cadastra a tabela de frete (table rate). Retirar em loja sozinho só precisa desse cadastro no Magento — não passa pela solicitação formal da Logística (mas ela é avisada, por conhecimento).' },\n    { kind: 'warning', text: 'Pra marca Care, tem uma dependência sequencial: a Logística precisa cadastrar a filial no sistema \"Boxlink\" ANTES do time de TI conseguir cadastrar o resto no Shopify — se esse cadastro atrasa, trava toda a ativação de Care naquela loja.' },\n    { kind: 'step', text: 'Solicitação à Logística: o Digital pede a ativação — não precisa esperar o cadastro terminar, avisar a Logística antes já agiliza o processo. A Logística é quem executa (hoje faz SFS e Retire; antes só fazia SFS).' },\n    { kind: 'warning', text: 'Passo mais demorado: a Logística confirma (ou solicita) a tabela de frete e o raio de cobertura com a transportadora pra aquela filial — a transportadora precisa cadastrar a filial, traçar o raio de entrega e definir os CEPs atendidos. É exatamente daqui que nasce o bug de \"CEP fora de abrangência\" documentado na seção Entrega Expressa.' },\n    { kind: 'warning', text: 'Só pra Care: depois da tabela de frete, o TI precisa cadastrar o local na VPN, enviar o código + ID da filial pro parceiro \"FullComm\" (que devolve um token) e configurar um metafield \"Boxlink\" no Shopify (só pras filiais com SFS). Esses passos PRECISAM terminar antes da ativação final — senão a Shopify libera a modalidade mas o pedido não integra corretamente com o OMS. Essa é a explicação mais provável do bug \"checkout oferece frete mas não valida estoque específico\" confirmado ao vivo.' },\n    { kind: 'step', text: 'Ativação final: a Logística ativa oficialmente o SFS e o Retire.' },\n    { kind: 'step', text: 'Regras por marca: Granado/Phebo ativam em todas as lojas, exceto a do aeroporto — quiosque e carrinho ficam de fora por capacidade operacional. Care ativa em loja, quiosque e carrinho, sem restrição (fluxo de operação menor).' },\n    { kind: 'warning', text: 'Governança: a configuração de SFS/Retire deveria ficar só com a Logística — já aconteceu do Digital mexer direto e gerar configuração divergente do combinado.' },\n    { kind: 'decision', text: 'Gatilhos de inativação (sempre manuais — o Digital avisa a Logística e, se for Care, também o TI)', branches: ['Por loja: inativar uma loja específica ou todas', 'Por produto: item de vencimento curto com estoque só no CD (não disponível nas lojas) precisa ser inativado manualmente pra SFS/Retire, avisando o Varejo'] },\n    { kind: 'warning', text: 'Esse gatilho de inativação por produto é manual, não uma checagem automática — se ninguém lembrar de inativar um item de vencimento curto sem estoque de loja, ele continua \"disponível\" pro sistema mesmo sem estoque real. É outra explicação plausível do mesmo bug de checkout que não valida estoque específico.' }\n  ]}\n].map((s, i) => { s.order = i + 1; return s })\n\nconst GAPS = [\n  { num: '1', text: 'Devoluções da marca Care são acompanhadas por e-mail, de forma diferente do fluxo Granado/Phebo via Troquecommerce.', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '5', text: 'Loja física aceitando devolução direto gera impacto negativo pros Canais Digitais.', sectionId: 'loja-excecao', origin: 'bpmn' },\n  { num: '7', text: 'Planilha manual de estornos pendentes (Canais Digitais → Contas a Receber) sujeita a erro de preenchimento humano.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { num: '8', text: 'O estorno de pedidos recusados pela loja só ocorre de forma automática quando é via Adyen — a área não recebe notificação dos estornos que precisam de atuação manual (ex.: PIX parcelado e cartão-presente).', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '14', text: 'Dependência excessiva de ajuste fiscal manual por falhas na execução do processo de devolução entre unidades (Jesmond/Ponteland).', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '3', text: 'Limitação de 10 linhas no pedido de devolução da Jesmond pra Ponteland, por solicitação da área Fiscal, pois os impostos não integram ao JDE de forma automática — gera múltiplos pedidos pra uma mesma devolução (ex.: 3 pedidos de 10 itens em vez de 1 pedido de 30). Já em andamento: enviado pra equipe JDE, prazo 31/08/2026 (mesmo requisito da dor #15).', sectionId: 'nf-faturada', origin: 'bpmn' },\n  { num: '15', text: 'Necessidade de inclusão manual dos impostos dos itens na nota fiscal gerada no SAP S4 (falta integração com o JDE).', sectionId: 'nf-faturada', origin: 'bpmn' },\n  { num: '16', text: 'Limitação do SAP S4 na referência usada, impedindo referenciar corretamente todas as notas de origem dos itens devolvidos.', sectionId: 'nf-faturada', origin: 'bpmn' },\n  { num: '18', text: 'Ausência de processo efetivo pra conciliar notas de devolução na conta \"Clientes Nacionais\".', sectionId: 'contas-receber', origin: 'bpmn' },\n  { num: '19', text: 'Execução manual dos estornos, com alto volume operacional de solicitações.', sectionId: 'contas-receber', origin: 'bpmn' },\n  { num: '20', text: 'Inconsistências recorrentes identificadas pelo time Contábil entre contas de clientes e adquirentes.', sectionId: 'contas-receber', origin: 'bpmn' },\n  { num: '21', text: 'Falta de integração entre os sistemas de tratativa do SAC e a ferramenta de coleta dos Correios.', sectionId: 'tipo3', origin: 'bpmn' },\n  { num: '22', text: 'A loja NÃO aceita devolução sem comunicação prévia do SAC + OK do Digital, mesmo com o cliente já presente na loja.', sectionId: 'tipo3', origin: 'bpmn' },\n  { num: '23', text: 'Demora no SLA de retorno da área de Canais Digitais, exigindo cobranças repetidas do SAC.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { key: 'coleta-loja-retirada-impacto', text: 'Direcionar a coleta pra loja de retirada gera impacto adicional nos Canais Digitais (nota original dos diagramas BPMN — número oficial não confirmado no relatório de auditoria; não deve ser confundido com a dor oficial #24, sobre itens abertos acumulados na matriz).', sectionId: 'tipo3', origin: 'meeting' },\n  { num: '24', text: 'Itens abertos devolvidos na matriz/escritório ficam sem destino definido e sem rastreio sistêmico, existindo só fisicamente. Fix oficial proposto: alinhar que esses itens voltem pro estoque da LOJA de origem e sigam com os demais itens pro CD, visando devolução pra Ponteland — ainda não seguido na prática. Essa é a confirmação oficial do achado \"item aberto acumulado pra sempre\" (ver causa raiz #3 no Diagnóstico).', sectionId: 'tipo3-pos-retirada', origin: 'bpmn' },\n  { num: '42', text: 'A maioria das devoluções fica parada na área Fiscal por recorrência de rejeição das notas geradas (MIRO/SEFAZ).', sectionId: 'nf-faturada', origin: 'bpmn' },\n  { num: '43', text: 'Demora no SLA da área Fiscal pra resolver devoluções.', sectionId: 'nf-faturada', origin: 'bpmn' },\n  { num: '44', text: 'Dificuldade de reintegrar produtos ao estoque quando a compra original e a devolução caem em unidades/caixas diferentes.', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '45', text: 'Não há processo definido pra orientar a loja em caso de estorno de venda feita em dinheiro.', sectionId: 'loja-venda-propria', origin: 'bpmn' },\n  { num: '46', text: 'Depois de ajustar uma ordem de devolução no SAP, é preciso salvar a remessa manualmente pra integrar ao WMS — na emissão INICIAL isso já é automático; só a remessa AJUSTADA exige esse passo manual.', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '48', text: 'A forma de pagamento usada pra registrar uma \"troca\" varia por processo: o CD usa \"troca\" a partir do pedido original; o Digital usa \"bonificação\" em pedido novo (só pra item aberto — lacrado vai direto pra loja); o SAC-Care usa Wepipe→Shopify \"bonificação\"; o SAC-Granado/Phebo só emite no Magento em caso de reposição por extravio. Essa inconsistência de 4 caminhos diferentes está inflando artificialmente a conta contábil \"Clientes Nacionais\".', sectionId: 'contas-receber', origin: 'bpmn' },\n  { num: '49', text: 'Devoluções de Care, Fidelidade e tipo 3 não podem ser tratadas pelo Troquecommerce (mesma regra da seção 1).', sectionId: 'canais-abertura', origin: 'bpmn' },\n  { num: '50', text: 'Ausência de regra clara pra devoluções feitas pela loja física que não envolvam qualidade (ex: estorno em dinheiro).', sectionId: 'loja-venda-propria', origin: 'bpmn' },\n  { num: '10', text: 'Todo cancelamento/devolução é registrado com motivo genérico \"desistência do cliente\", independente do motivo real — perde rastreabilidade do motivo verdadeiro e impede relatório preventivo.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { num: '11', text: 'Processo de lançar devolução no SAP S4 é moroso e não há certeza se existe manual documentando os passos — alta dependência de TI.', sectionId: 'logistica-cd', origin: 'bpmn' },\n  { num: '12', text: 'Não existe controle unificado das devoluções feitas pelo time de Canais Digitais — risco de duplicidade, baixa rastreabilidade.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { num: '25', text: 'Quando o pedido é extraviado, não é possível cancelar no SAP S4 — só dá pra estornar, ficando a venda em aberto sem contrapartida da adquirente.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { num: '27', text: 'Devolução de pontos de fidelidade não é automática quando a loja recusa um pedido pago com \"fidelidade\" — o CRM não é notificado do cancelamento pra devolver os pontos.', sectionId: 'loyalty', origin: 'bpmn' },\n  { num: '47', text: 'Extravio em entrega expressa: o SAT precisa abrir investigação, mas o SLA até Canais Digitais receber a solicitação é alto, perdendo a janela de investigação.', sectionId: 'canais-digitais', origin: 'bpmn' },\n  { num: '28', key: 'estoque-fantasma', text: 'Quando o item devolvido está ABERTO, a remessa fiscal no SAP devolve o produto digitalmente pro estoque da loja de origem, mesmo com o item físico parado no escritório do SAC — gera estoque fantasma, só percebido na próxima contagem de inventário. Confirmado oficialmente (dor #28): esse tipo de devolução (retire em loja/entrega expressa), processado pelo Canais Digitais no SAP S4, afeta cerca de 290 vendas por mês nas lojas e Darkstores.', sectionId: 'tipo3-pos-retirada', origin: 'bpmn' },\n  { key: 'estorno-parcial-cartao-presente', text: 'Estorno parcial em cartão-presente é praticamente impossível de resolver rápido — em um caso real, levou 3 semanas até descobrirem quem sabia processar esse tipo de estorno.', sectionId: 'contas-receber', origin: 'meeting' },\n  { key: 'cep-entrega-expressa', text: 'Falha de cobertura de CEP na Entrega Expressa (parceiro terceirizado): pedidos ficam fora de abrangência e ninguém é notificado automaticamente — só é percebido quando a loja ou o cliente reclamam. Raiz provável: a tabela de frete/raio criada na ativação do canal SFS/Retire (por loja) não foi feita/atualizada direito, ou a cobertura real da transportadora mudou depois.', sectionId: 'entrega-expressa-cep', origin: 'meeting' },\n  { key: 'visibilidade-agregada', text: 'Não existe um número consolidado de quantos casos de troca/devolução estão abertos no sistema todo — cada time só vê a própria fila (ex: o SAC de retirada/entrega expressa só vê o filtro \"tipo de entrega 3\"), o que dificulta até pedir mais recursos pra liderança. Citação direta de quem operava o processo: \"não monitoro nenhum indicador, não sei nem quantos troco eu faço\".', sectionId: null, origin: 'meeting' },\n  { key: 'email-sem-chamado', text: 'Funcionários de loja às vezes pedem cancelamento direto por e-mail ao SAC, sem abrir chamado formal, e às vezes desfazem o pedido pouco depois sem nenhum registro. Uma iniciativa do time de TI está em andamento pra que aberturas feitas na loja gerem automaticamente um registro do lado do SAC.', sectionId: 'canais-abertura', origin: 'meeting' },\n    { key: 'checkout-sem-validacao-estoque', text: 'Na tela de escolha de frete, o sistema valida o CEP contra a cobertura de entrega, mas NÃO valida se o produto específico tem estoque suficiente — testado ao vivo pedindo 80 unidades de um item sem estoque, e mesmo assim o checkout mostrou normalmente as opções de retirada em loja e frete expresso. Duas explicações prováveis: ativação de Care com o TI concluindo depois da liberação no Shopify, ou o gatilho manual de inativação por produto de vencimento curto que ninguém acionou.', sectionId: 'checkout-validacao', origin: 'live' },\n  { key: 'faturamento-parado-sap', text: 'O pedido pode ficar dias parado no SAP sem faturar mesmo com pagamento já aprovado, por motivo que nem o time interno sabe identificar caso a caso — e esse atraso trava a entrega expressa desde o início, porque a separação só é liberada depois do faturamento. Já viram 150 pedidos parados por pendência fiscal em um único dia.', sectionId: 'faturamento-assimetria', origin: 'meeting' },\n  { key: 'risco-fraude-retirada-cancelada', text: 'Pedido cancelado digitalmente por falta de resposta do cliente, mas a loja processa a retirada e recebe pagamento informal do cliente quase ao mesmo tempo, por falta de comunicação a tempo entre loja e time digital. Resultado: sem baixa de estoque, sem nota fiscal — o cliente poderia teoricamente contestar a cobrança no cartão alegando arrependimento, mesmo já tendo o produto. Outro gatilho pro mesmo risco: pedido travado no fiscal sem faturar — a loja sabe mas não pode liberar oficialmente, e entrega informalmente sem nota fiscal se o cliente já pagou e aparece.', sectionId: 'risco-fraude-cancelamento', origin: 'meeting' }\n].map(g => { g.sectionTitle = g.sectionId ? ((OPS_SECTIONS.find(s => s.id === g.sectionId) || {}).title || g.sectionId) : null; return g })\n\nconst IDEAS = [\n  { title: 'Página única de entrada com o código do pedido', text: 'Uma página anterior ao atendimento, onde o cliente só informa o código do pedido — o sistema detecta a origem (CD/Darkstore ou loja) e direciona automaticamente pro caminho certo, em vez de deixar o cliente adivinhar ou ficar procurando telefone/e-mail do SAC.' },\n  { title: 'Unificar tudo na plataforma Troca e Comércio', text: 'Incluir também os casos hoje tratados só via SAC tradicional (retirada em loja, entrega expressa), não apenas os que já passam pelo portal — isso eliminaria as planilhas manuais de estorno do SAC e dos Canais Digitais.' },\n  { title: 'Resolver 100% na própria loja quando o item está lacrado e o cliente está presente', text: 'Tratar como \"carteira do cliente\" — pagando a diferença ali mesmo, sem precisar abrir o processo de coleta. A mesma lógica poderia valer quando o item lacrado já retornou pra loja: deixar a loja processar direto, sem depender do SAC.' },\n  { title: 'WhatsApp conversacional pra pós-venda (visão de longo prazo)', text: 'Hoje o WhatsApp só é usado de forma ativa pro lado de vendas (recém-ativado). Os próprios times reconhecem que o processo interno precisa \"estar redondo\" antes de automatizar essa camada.' },\n  { title: 'Antifraude por CPF/score de risco', text: 'Bater o CPF do cliente contra um score de risco: se for baixo risco, libera o reembolso IMEDIATAMENTE, sem esperar o produto físico voltar. Em paralelo, decide (com uma matriz feita junto da Qualidade) se aquele item precisa voltar fisicamente ou só recebe uma recomendação de descarte responsável — proposta baseada em experiência de outra empresa.' }\n]\n\nconst IN_PROGRESS = [\n  { title: 'E-mail único \"Operação Omni\"', text: 'Substituiu os e-mails fragmentados que iam direto pro atendente do SAC e pro time digital. Já implementado e funcionando, mas volume ainda alto.' },\n  { title: 'Registro automático loja → SAC', text: 'Time de TI construindo um sistema pra que toda abertura de chamado na loja gere também um registro do lado do SAC.' },\n  { title: 'Aviso de prazo pra retirar em loja (mitigação fraca)', text: 'Implementado há cerca de 1 mês: o cliente recebe um aviso de que tem 7 dias corridos pra retirar o pedido — mas isso é só uma notificação, não existe cancelamento automático nenhum. Na prática, o pedido continua parado até cerca de 30 dias depois, quando a gerente da Darkstore avisa informalmente o time Digital (um favor dela, fora do processo oficial) — só aí alguém processa tudo manualmente (SAP + planilha do financeiro).' },\n  { title: 'Limite de 10 linhas no pedido de devolução Jesmond → Ponteland', text: 'Causado pela falta de integração automática de impostos entre SAP S4 e JDE (dores oficiais #3 e #15 — o mesmo requisito resolve as duas). Já formalmente enviado pra equipe JDE resolver, com prazo definido: 31 de agosto de 2026.' }\n]\n\nconst ROOT_CAUSES = [\n  { num: 1, title: 'O fulfillment é invisível pro cliente', text: 'O pedido pode sair do CD, de uma Darkstore ou de uma loja física, e o cliente nunca sabe qual. Só descobre depois de abrir o SAC, quando já é tarde pra evitar a burocracia.' },\n  { num: 2, title: 'O pós-devolução é 100% manual', text: 'Coleta, identificação do item, estorno, atualização de estoque não conversam automaticamente entre si. SAC e Canais Digitais preenchem duas planilhas de estorno em paralelo, sem integração.' },\n  { num: 3, title: 'O sistema não sabe onde o produto está de verdade', text: 'A remessa de devolução no SAP (que também cancela a nota fiscal original) manda o estoque digital de volta pra loja de origem, mesmo quando o item físico está aberto e parado no escritório do SAC. Físico e digital divergem e ninguém corrige isso automaticamente. Confirmado oficialmente pelo relatório de auditoria (dor #24): esses itens ficam sem destino definido e sem rastreio sistêmico, existindo só fisicamente.' }\n]\n\nconst CLIENT_PAINS = [\n  { sev: 'critico', text: 'O estorno é apontado pela própria pessoa que opera esse processo hoje como \"a grande, grande dor\" do cliente — mais do que a confusão de rota entre CD/Darkstore/loja. O pagamento é aprovado em segundos, mas o dinheiro pode levar até 1 mês pra voltar.', source: 'reunião 2 — citação direta' },\n  { sev: 'critico', text: 'Retirou na loja, não gostou, não conseguiu trocar ali (está com o produto na mão, dentro da própria loja, e ainda precisa ir pra casa, abrir chamado, esperar coleta).', source: 'reunião' },\n  { sev: 'critico', text: 'Não sabe qual caminho seguir — de onde o pedido saiu (CD/Darkstore/loja) muda tudo, mas só se revela depois de abrir o chamado.', source: 'reunião + BPMN #49' },\n  { sev: 'alto', text: 'Até 1 mês pra resolver um pedido \"urgente\" (ele escolheu retirada/entrega expressa justamente por pressa).', source: 'reunião' },\n  { sev: 'alto', text: 'Estorno parcial pode travar por semanas — caso real: pedido de entrega expressa caiu fora de área de cobertura, parte do valor era cartão-presente, ninguém sabia quem processava estorno parcial de frete, levou 3 semanas.', source: 'reunião' },\n  { sev: 'medio', text: 'Pedido pode \"desaparecer\" sem aviso — na entrega expressa o cliente não escolhe a loja (é por CEP); se a cobertura falhar, não há alerta, só descobre reclamando. (Em \"retiro em loja\" ele escolhe a loja, esse risco específico não existe.)', source: 'reunião' },\n  { sev: 'medio', text: 'Precisa ficar cobrando o próprio status — sem visibilidade de progresso, manda mensagem todo dia, e cada mensagem gera mais trabalho manual do outro lado.', source: 'reunião' },\n  { sev: 'medio', text: 'Não existe canal único — precisa achar telefone/e-mail/WhatsApp certo e entender uma política que varia por caso.', source: 'reunião' },\n  { sev: 'baixo', text: 'Quem não tem o código do pedido (achou/ganhou o produto) não tem caminho formal, é sempre exceção caso a caso.', source: 'reunião' }\n]\n\nconst OPS_PAINS = [\n  { sev: 'critico', text: 'Estoque fantasma (ver causa raiz #3) — enquanto isso, a loja às vezes vende outra unidade pelo PDV no negativo e \"dá baixa\" depois, um contorno manual pra um problema que não devia existir.', source: 'reunião' },\n  { sev: 'critico', text: 'Fluxo quase 100% manual — coleta, identificação, estorno e atualização de estoque sem integração ponta a ponta.', source: 'reunião + BPMN' },\n  { sev: 'critico', text: 'O checkout não valida estoque do produto específico na tela de frete — testado ao vivo pedindo 80 unidades de um item sem estoque, e as opções de entrega apareceram normalmente.', source: 'confirmado ao vivo' },\n  { sev: 'critico', text: 'Risco de fraude: pedido cancelado digitalmente por falta de resposta do cliente, mas a loja processa a retirada e o pagamento informal quase ao mesmo tempo, por falta de comunicação a tempo — fica sem baixa de estoque e sem nota fiscal.', source: 'reunião' },\n  { sev: 'critico', text: 'A forma de pagamento usada pra registrar uma \"troca\" varia por processo (CD, Digital, SAC-Care e SAC-Granado/Phebo usam 4 caminhos diferentes) — essa inconsistência está inflando artificialmente a conta contábil \"Clientes Nacionais\".', source: 'auditoria oficial — dor #48' },\n  { sev: 'alto', text: 'Ninguém sabe o volume total de casos abertos — cada área só vê o pedaço que toca a ela; dificulta até pedir recurso pra liderança. Citação direta: \"não monitoro nenhum indicador, não sei nem quantos troco eu faço\".', source: 'reunião' },\n  { sev: 'alto', text: 'CEP fora de área falha em silêncio — quando a cobertura de entrega expressa falha, nada alerta ninguém, só é achado quando alguém reclama.', source: 'reunião' },\n  { sev: 'alto', text: 'Sem processo de descarte — na prática, praticamente não acontece: relato de alguém há 10 meses no time que nunca participou de um descarte formal; itens só se acumulam (dois armários lotados, picos em campanhas como a Semana Granada, sacos de correio com dezenas de caixas). Existe um processo de baixa por pré-destruição na fábrica que poderia resolver isso, mas não está plugado — e o item às vezes é usado informalmente pelo time (já houve caso relatado de item devolvido contaminado).', source: 'reunião' },\n  { sev: 'alto', text: 'Faturamento parado no SAP sem motivo claro trava a entrega expressa desde o início (a separação só libera depois de faturado) — já viram 150 pedidos parados por pendência fiscal num único dia.', source: 'reunião' },\n  { sev: 'medio', text: 'Duas planilhas de estorno em paralelo (SAC e Canais Digitais fazem controles manuais parecidos sem integração).', source: 'reunião + BPMN #7' },\n  { sev: 'medio', text: 'Item chega sem identificação clara (falta nome/CPF/nº pedido, ou código de coleta não bate) — investigação manual caso a caso.', source: 'reunião' },\n  { sev: 'medio', text: 'Loja cancela por e-mail informal, sem registro — às vezes a decisão muda 2 minutos depois, sem rastro. Mitigação já em andamento.', source: 'reunião' },\n  { sev: 'estrutural', text: 'Omnichannel sem dono de projeto dedicado — pensaram em como o pedido sai da loja, não em como ele volta.', source: 'reunião' }\n]\n\nconst GAP_THEMES = [\n  { id: 'integracao', title: 'Integração de sistemas', nums: ['1', '21', '46', '48', '10'] },\n  { id: 'fiscal', title: 'Fiscal', nums: ['15', '16', '42', '43'] },\n  { id: 'financeiro', title: 'Financeiro/estorno', nums: ['7', '8', '18', '19', '20', '25', '27'] },\n  { id: 'governanca', title: 'Governança de loja', nums: ['22', '45', '50'] },\n  { id: 'sla', title: 'SLA/comunicação', nums: ['5', '23', '47'] },\n  { id: 'logistica-tema', title: 'Logística/estoque', nums: ['14', '44', '24'] },\n  { id: 'visibilidade', title: 'Visibilidade', nums: ['12'] },\n  { id: 'governanca-doc', title: 'Governança/documentação', nums: ['11'] }\n]\n\nconst TOOLS = ['Troquecommerce', 'Service Cloud (SAC)', 'OMS (Order Management System)', 'SAP S4 (VA01, VL01, VF01, VL02N, VL10B, MIRO, J1BNFE, ZTO2C004, ZTP2P005, ME2M, ME21N)', 'SAP B1', 'SAP Fiori', 'WMS', 'PDV Linx', 'Microvix', 'Magento', 'Shopify', 'Wepipe (marca Care)', 'Pipefy (estornos em dinheiro/loja)', 'CRM / Clube de Fidelidade', 'Site da transportadora (Correios)', 'SAT', 'ServiceNow / TI Backoffice', 'JDE (B2B)', 'Adquirente Cielo', 'Microsoft Teams (TI ↔ Dark Store)', 'BI']\n\nconst ICON_PATHS = {\n  store: '<path d=\"M4 9l1-5h14l1 5\"/><path d=\"M4 9h16v10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9z\"/><path d=\"M9 20v-6h6v6\"/>',\n  gift: '<rect x=\"3\" y=\"8\" width=\"18\" height=\"12\" rx=\"1\"/><path d=\"M3 12h18\"/><path d=\"M12 8v12\"/><path d=\"M8 8c0-2 1.5-3.5 4-3.5s4 1.5 4 3.5\"/>',\n  monitor: '<rect x=\"3\" y=\"4\" width=\"18\" height=\"13\" rx=\"1\"/><path d=\"M8 21h8\"/><path d=\"M12 17v4\"/>',\n  alert: '<path d=\"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z\"/><line x1=\"12\" y1=\"9\" x2=\"12\" y2=\"13\"/><circle cx=\"12\" cy=\"16.6\" r=\"0.6\" fill=\"currentColor\" stroke=\"none\"/>',\n  box: '<path d=\"M21 8l-9-5-9 5 9 5 9-5z\"/><path d=\"M3 8v8l9 5 9-5V8\"/><path d=\"M12 13v8\"/>',\n  bag: '<path d=\"M6 8h12l1 12H5z\"/><path d=\"M9 8a3 3 0 1 1 6 0\"/>',\n  building: '<rect x=\"4\" y=\"3\" width=\"16\" height=\"18\"/><path d=\"M9 21v-4h6v4\"/><path d=\"M8 7h1M8 11h1M8 15h1M15 7h1M15 11h1M15 15h1\"/>',\n  warehouse: '<path d=\"M3 10l9-6 9 6\"/><rect x=\"4\" y=\"10\" width=\"16\" height=\"10\"/><rect x=\"9.5\" y=\"14\" width=\"5\" height=\"6\"/>',\n  ban: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"5.5\" y1=\"18.5\" x2=\"18.5\" y2=\"5.5\"/>',\n  info: '<circle cx=\"12\" cy=\"12\" r=\"9\"/><line x1=\"12\" y1=\"11\" x2=\"12\" y2=\"16\"/><circle cx=\"12\" cy=\"7.6\" r=\"0.75\" fill=\"currentColor\" stroke=\"none\"/>',\n  chevron: '<path d=\"M6 9l6 6 6-6\"/>',\n  check: '<path d=\"M5 13l4 4 10-10\"/>',\n  bulb: '<path d=\"M9 18h6\"/><path d=\"M10 22h4\"/><path d=\"M12 2a7 7 0 0 0-4 12.7c.4.3.6.8.6 1.3v.5a1 1 0 0 0 1 1h4.8a1 1 0 0 0 1-1v-.5c0-.5.2-1 .6-1.3A7 7 0 0 0 12 2z\"/>'\n}\n\nfunction icon(name, cls) {\n  const body = ICON_PATHS[name] || ICON_PATHS.info\n  return `<svg viewBox=\"0 0 24 24\" class=\"icon ${cls || ''}\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\">${body}</svg>`\n}\n\nfunction esc(str) { return String(str == null ? '' : str) }\n\nfunction badgeHtml(b) { return `<span class=\"badge badge-${esc(b.type)}\">${esc(b.text)}</span>` }\n\nfunction noteIconName(type) {\n  if (type === 'exception') return 'ban'\n  if (type === 'warn') return 'alert'\n  return 'info'\n}\n\nfunction noteHtml(n) {\n  return `<div class=\"note note-${esc(n.type)}\"><span class=\"note-icon\">${icon(noteIconName(n.type))}</span><span>${esc(n.text)}</span></div>`\n}\n\nfunction notesBlock(notes) {\n  if (!notes || !notes.length) return ''\n  return `<div class=\"notes\">${notes.map(noteHtml).join('')}</div>`\n}\n\nfunction stepsHtml(steps) {\n  if (!steps || !steps.length) return `<div class=\"empty\">Conteúdo detalhado deste fluxo ainda não foi levantado com o time responsável.</div>`\n  return `<ol class=\"steps\">${steps.map((st, i) => `\n    <li class=\"step\" data-key=\"step-${i}\">\n      <span class=\"step-num\">${i + 1}</span>\n      <span class=\"step-body\"><strong>${esc(st.title)}</strong><span>${esc(st.desc)}</span></span>\n    </li>\n  `).join('')}</ol>`\n}\n\nfunction scenarioHtml(scenario) {\n  return `\n    <div class=\"scenario\" data-key=\"${esc(scenario.id)}\">\n      ${scenario.highlight ? `<div class=\"highlight\"><span class=\"highlight-icon\">${icon('alert')}</span><span>${esc(scenario.highlight)}</span></div>` : ''}\n      <h2>${esc(scenario.title)}</h2>\n      <p class=\"intro\">${esc(scenario.intro)}</p>\n      <div class=\"badges\">${scenario.badges.map(badgeHtml).join('')}</div>\n      ${stepsHtml(scenario.steps)}\n      ${notesBlock(scenario.notes)}\n      ${scenario.contact ? `<div class=\"contact\"><strong>Precisa de ajuda?</strong><span>SAC ${esc(scenario.contact.phone)} (${esc(scenario.contact.hours)}) · ${esc(scenario.contact.email)}</span></div>` : ''}\n    </div>\n  `\n}\n\nfunction buildOnlineSteps(originKey, reasonKey) {\n  const reason = REASONS[reasonKey]\n  const secondStep = originKey === 'cd'\n    ? { title: 'Acesse o portal de trocas', desc: 'Entre em granado.troque.app.br e informe os dados do pedido para abrir a solicitação — sem precisar do SAC.' }\n    : { title: 'Contate o SAC', desc: 'Fale com o SAC por telefone (0800 940 6730, seg. a sex., 9h às 18h) ou e-mail (sac@granado.com.br) informando os dados do pedido — esse caso não tem portal self-service.' }\n  return [\n    { title: 'Confira o prazo', desc: reason.windowText },\n    secondStep,\n    { title: 'Devolva o produto com a nota fiscal', desc: 'Depois da aprovação, você recebe um e-mail com o Código de Postagem Reversa em até 2 dias úteis.' },\n    { title: 'Leve aos Correios', desc: 'Use o código recebido para enviar o produto de volta.' },\n    { title: 'Reembolso', desc: 'No cartão de crédito, o valor volta em até 2 faturas. No PIX ou boleto, é preciso enviar os dados bancários (nome, CPF, banco, agência e conta).' }\n  ]\n}\n\nfunction unknownOrderBlock() {\n  return `\n    <div class=\"scenario nested\" data-key=\"pedido-sem-origem\">\n      <div class=\"highlight\"><span class=\"highlight-icon\">${icon('alert')}</span><span><strong>Caso de exceção — sem código de pedido.</strong> Não existe portal self-service pra isso; é atendimento manual do SAC, caso a caso.</span></div>\n      <h2>Defeito sem saber a origem da compra</h2>\n      <p class=\"intro\">Você tem um produto com problema, mas não sabe de qual pedido ele veio — achou, ganhou de alguém, ou simplesmente não tem o número do pedido.</p>\n      <div class=\"badges\"><span class=\"badge badge-warn\">Caso de exceção</span><span class=\"badge badge-neutral\">Sem portal — só SAC</span></div>\n      <div class=\"notes\"><div class=\"note note-warn\"><span class=\"note-icon\">${icon('alert')}</span><span>Sem o código do pedido não é possível confirmar a origem da compra — inclusive existe o risco de o item ser produto roubado. O SAC avalia cada caso manualmente.</span></div></div>\n      <div class=\"contact\"><strong>Precisa de ajuda?</strong><span>SAC 0800 940 6730 (seg. a sex., 9h às 18h) · sac@granado.com.br</span></div>\n    </div>\n  `\n}\n\nfunction knowsOrderToggle(knowsOrder) {\n  return `\n    <div class=\"origin-label\">Você conhece o pedido?</div>\n    <div class=\"origin-toggle\">\n      <button type=\"button\" class=\"choice-card know-card ${knowsOrder === 'sim' ? 'active' : ''}\" data-key=\"know-sim\" data-knows=\"sim\">\n        <span class=\"origin-icon\">${icon('check')}</span>\n        <span class=\"origin-copy\"><strong>Sim, tenho o código do pedido</strong><span>Comprei online e sei de qual pedido é o produto.</span></span>\n      </button>\n      <button type=\"button\" class=\"choice-card know-card ${knowsOrder === 'nao' ? 'active' : ''}\" data-key=\"know-nao\" data-knows=\"nao\">\n        <span class=\"origin-icon\">${icon('info')}</span>\n        <span class=\"origin-copy\"><strong>Não sei / não tenho o pedido</strong><span>Achei o produto, ganhei de alguém, ou não tenho o número.</span></span>\n      </button>\n    </div>\n  `\n}\n\nfunction onlineOrderHtml(state) {\n  const knowsOrder = state.knowsOrder || 'sim'\n  if (knowsOrder === 'nao') {\n    return `\n      <div class=\"scenario\" data-key=\"pedido-online\">\n        <h2>Fiz um pedido online</h2>\n        ${knowsOrderToggle(knowsOrder)}\n        ${unknownOrderBlock()}\n      </div>\n    `\n  }\n  const originKey = state.origin || 'cd'\n  const reasonKey = state.reason || 'arrependimento'\n  const origin = ORIGINS[originKey]\n  const reason = REASONS[reasonKey]\n  const steps = buildOnlineSteps(originKey, reasonKey)\n  return `\n    <div class=\"scenario\" data-key=\"pedido-online\">\n      <h2>Fiz um pedido online</h2>\n      ${knowsOrderToggle(knowsOrder)}\n      <div class=\"highlight myth\">\n        <span class=\"highlight-icon\">${icon('alert')}</span>\n        <span><strong>Escolheu \"retirar na loja\"? Isso não significa trocar na loja de retirada.</strong> Retirar na loja é só a forma de receber o produto. Quem decide o caminho da troca é de onde o pedido foi separado — selecione abaixo.</span>\n      </div>\n      <p class=\"intro\">Vale tanto para entrega em casa quanto para retirada em loja — o que importa agora é de onde o seu pedido saiu.</p>\n      <div class=\"origin-label\">De onde saiu o seu pedido?</div>\n      <div class=\"origin-toggle\">\n        ${Object.keys(ORIGINS).map(key => `\n          <button type=\"button\" class=\"choice-card origin-card ${key === originKey ? 'active' : ''}\" data-key=\"origin-${key}\" data-origin=\"${key}\">\n            <span class=\"origin-icon\">${icon(ORIGINS[key].icon, 'origin-svg')}</span>\n            <span class=\"origin-copy\"><strong>${esc(ORIGINS[key].label)}</strong><span>${esc(ORIGINS[key].desc)}</span></span>\n          </button>\n        `).join('')}\n      </div>\n      <div class=\"routing ${esc(origin.routingClass)}\">\n        <span class=\"routing-label\">Resolva por</span>\n        <span class=\"routing-value\">${esc(origin.channelLabel)}</span>\n        <span class=\"routing-detail\">${esc(origin.channelDetail)}</span>\n      </div>\n      <div class=\"reason-tabs\">\n        ${Object.keys(REASONS).map(key => `<button type=\"button\" class=\"reason-tab ${key === reasonKey ? 'active' : ''}\" data-key=\"reason-${key}\" data-reason=\"${key}\">${esc(REASONS[key].label)}</button>`).join('')}\n      </div>\n      <div class=\"badges\">${badgeHtml({ text: reason.badge, type: reason.badgeType })}</div>\n      ${stepsHtml(steps)}\n      ${notesBlock(reason.notes)}\n    </div>\n  `\n}\n\nfunction findScenario(id) { return SCENARIOS.find(s => s.id === id) }\n\nfunction tabsHtml(activeId) {\n  return TABS.map(t => `\n    <button type=\"button\" class=\"tab ${t.id === activeId ? 'active' : ''}\" data-key=\"tab-${esc(t.id)}\" data-id=\"${esc(t.id)}\">\n      <span class=\"tab-icon\">${icon(t.icon)}</span>\n      <span class=\"tab-label\">${esc(t.label)}</span>\n    </button>\n  `).join('')\n}\n\nfunction simpleHtml(state) {\n  const active = state.active || TABS[0].id\n  const panel = active === 'pedido-online' ? onlineOrderHtml(state) : scenarioHtml(findScenario(active) || SCENARIOS[0])\n  return `\n    <div class=\"team-note\">${icon('info')}<span>Esse fluxo é reconhecidamente confuso e inconsistente para quem compra hoje — o caminho certo depende de onde o pedido foi separado (fulfillment), não de como foi comprado ou recebido.</span></div>\n    <nav class=\"tabs\">${tabsHtml(active)}</nav>\n    <div class=\"panel\">${panel}</div>\n  `\n}\n\nfunction gapBadge(node) {\n  const chips = []\n  if (node.gapRef) {\n    const refs = Array.isArray(node.gapRef) ? node.gapRef : [node.gapRef]\n    refs.forEach(r => chips.push(`<span class=\"gap-chip\" data-key=\"gapchip-${r}\">gap #${r}</span>`))\n  }\n  if (node.live) chips.push(`<span class=\"live-chip\">confirmado ao vivo</span>`)\n  else if (node.meeting) chips.push(`<span class=\"meeting-chip\">identificado em reunião</span>`)\n  return chips.join('')\n}\n\nfunction opsNodeHtml(node) {\n  if (node.kind === 'warning') return `<div class=\"note note-warn\"><span class=\"note-icon\">${icon('alert')}</span><span>${esc(node.text)}${gapBadge(node)}</span></div>`\n  if (node.kind === 'decision') return `<div class=\"ops-decision\"><div class=\"ops-decision-q\"><span class=\"ops-decision-tag\">SE</span> ${esc(node.text)}</div>${node.branches && node.branches.length ? `<ul class=\"ops-branches\">${node.branches.map(b => `<li>${esc(b)}</li>`).join('')}</ul>` : ''}${gapBadge(node)}</div>`\n  return `<div class=\"ops-step\"><span class=\"ops-step-dot\"></span><span>${esc(node.text)}${gapBadge(node)}</span></div>`\n}\n\nfunction opsSectionHtml(section, openId) {\n  const isOpen = section.id === openId\n  return `\n    <div class=\"ops-card\" data-section-anchor=\"${section.id}\" data-key=\"ops-${section.id}\">\n      <button type=\"button\" class=\"ops-header\" data-key=\"ops-toggle-${section.id}\" data-section=\"${section.id}\">\n        <span class=\"ops-num\">${section.order}</span>\n        <span class=\"ops-title-wrap\"><strong>${esc(section.title)}</strong><span class=\"ops-lanes\">${section.lanes.map(l => `<span class=\"lane lane-${l}\">${esc((LANES[l] || {}).label || l)}</span>`).join('')}</span></span>\n        <span class=\"ops-chevron ${isOpen ? 'open' : ''}\">${icon('chevron')}</span>\n      </button>\n      ${isOpen ? `<div class=\"ops-body\">${section.nodes.map(opsNodeHtml).join('')}</div>` : ''}\n    </div>\n  `\n}\n\nfunction operationalHtml(state) {\n  const openId = state.opsOpen || OPS_SECTIONS[0].id\n  return `\n    <div class=\"ops-wrap\">\n      <div class=\"ops-intro\"><p>Fluxo real, com nomes de sistemas e times internos. Baseado no relatório oficial de auditoria interna, nos diagramas BPMN e em duas reuniões internas de acompanhamento (Omnichannel + Marketplace/Digital). Cada seção pode ser expandida; badges indicam a área responsável (raia).</p></div>\n      <div class=\"scope-note\">${icon('info')}<span>Fora de escopo: Marketplace (iFood, Rappi, TikTok Shopping, Mercado Livre) não faz parte deste guia — esses canais se responsabilizam 100% pelo próprio estorno.</span></div>\n      <div class=\"scope-note\">${icon('info')}<span>Meta-ressalva: toda essa base de conhecimento vem da perspectiva do time Digital/E-commerce. Qualquer afirmação sobre o funcionamento puro da loja física em si (ex.: \"troca no balcão funciona bem\") ainda não foi validada diretamente com o time de loja física — é inferência, não confirmação.</span></div>\n      <div class=\"tools-chip-row\">${TOOLS.map(t => `<span class=\"tool-chip\">${esc(t)}</span>`).join('')}</div>\n      <div class=\"ops-list\">${OPS_SECTIONS.map(s => opsSectionHtml(s, openId)).join('')}</div>\n      <div class=\"context-aside\">Nota de contexto: o projeto Omnichannel foi conduzido inicialmente por alguém do time de SAC, sem perfil de dono de projeto — um padrão comum internamente é \"colocar o negócio pra rodar\" sem planejar o que acontece depois (pensou-se em como o pedido sai da loja, não em como ele volta).</div>\n      <div class=\"context-aside\">Nota de contexto (arquitetura): Granado e Phebo rodam em Magento (hoje Adobe Commerce); Care roda em Shopify, porque foi uma operação adquirida que já vinha com Shopify. Shopify é bem mais simples de manter (uma fração do time técnico); Magento exige um time bem maior — essa diferença de plataforma é a raiz de boa parte das exceções manuais que aparecem pra marca Care nos outros gaps. Já se discute migrar Care do Shopify pro Magento, já que o controle de estoque depende do Magento.</div>\n      <div class=\"context-aside\">Nota de contexto (setup do canal): a seção \"Como o canal SFS/Retire em Loja é ativado\" explica a origem de dois bugs já mapeados aqui — a tabela de frete/CEP e a ativação incompleta de Care.</div>\n    </div>\n  `\n}\n\nfunction problemsHtml() {\n  return `\n    <div class=\"problems-wrap\">\n      <div class=\"ops-intro\"><p>Gaps do relatório oficial de auditoria interna (numerados como no documento original, não sequenciais), problemas relatados em reunião interna (sem número — \"identificado em reunião\") e problemas confirmados ao vivo, testando o site durante a reunião (\"confirmado ao vivo\").</p></div>\n      <div class=\"problems-list\">${GAPS.map(g => `\n        <div class=\"problem-card\" data-key=\"gap-${g.num || g.key}\">\n          <div class=\"problem-head\">\n            <span class=\"problem-num ${g.origin !== 'bpmn' ? 'problem-num-' + g.origin : ''}\">${g.origin === 'live' ? 'Confirmado ao vivo' : g.origin === 'meeting' ? 'Reunião' : '#' + g.num}</span>\n            ${g.sectionId ? `<button type=\"button\" class=\"gap-link\" data-key=\"gaplink-${g.num || g.key}\" data-section=\"${g.sectionId}\">Ver na seção: ${esc(g.sectionTitle)}</button>` : ''}\n          </div>\n          <p>${esc(g.text)}</p>\n        </div>\n      `).join('')}</div>\n    </div>\n  `\n}\n\nfunction ideasHtml() {\n  return `\n    <div class=\"ideas-wrap\">\n      <div class=\"ops-intro\"><p>Propostas discutidas internamente pra melhorar o processo — ainda não implementadas.</p></div>\n      <div class=\"problems-list\">${IDEAS.map((idea, i) => `\n        <div class=\"idea-card\" data-key=\"idea-${i}\">\n          <div class=\"idea-head\"><span class=\"idea-icon\">${icon('bulb')}</span><strong>${esc(idea.title)}</strong></div>\n          <p>${esc(idea.text)}</p>\n        </div>\n      `).join('')}</div>\n    </div>\n  `\n}\n\nfunction severityBadge(sev) {\n  const labels = { critico: 'CRÍTICO', alto: 'ALTO', medio: 'MÉDIO', baixo: 'BAIXO', estrutural: 'ESTRUTURAL' }\n  return `<span class=\"sev-badge sev-${sev}\">${labels[sev] || sev.toUpperCase()}</span>`\n}\n\nfunction rootCauseHtml(rc) {\n  return `<div class=\"cause-card\" data-key=\"cause-${rc.num}\"><span class=\"cause-num\">${rc.num}</span><div class=\"cause-body\"><strong>${esc(rc.title)}</strong><p>${esc(rc.text)}</p></div></div>`\n}\n\nfunction painCardHtml(p, i) {\n  return `<div class=\"pain-card\" data-key=\"pain-${i}\"><div class=\"pain-head\">${severityBadge(p.sev)}<span class=\"pain-source\">${esc(p.source)}</span></div><p>${esc(p.text)}</p></div>`\n}\n\nfunction progressCardHtml(item, i) {\n  return `<div class=\"progress-card\" data-key=\"prog-${i}\"><div class=\"progress-head\">${icon('check')}<strong>${esc(item.title)}</strong></div><p>${esc(item.text)}</p></div>`\n}\n\nfunction gapThemeHtml(theme, openId) {\n  const isOpen = theme.id === openId\n  const items = theme.nums.map(n => GAPS.find(g => g.num === n)).filter(Boolean)\n  return `\n    <div class=\"ops-card\" data-key=\"diag-${theme.id}\">\n      <button type=\"button\" class=\"diag-header\" data-key=\"diag-toggle-${theme.id}\" data-theme=\"${theme.id}\">\n        <span class=\"ops-num\">${items.length}</span>\n        <span class=\"ops-title-wrap\"><strong>${esc(theme.title)}</strong></span>\n        <span class=\"ops-chevron ${isOpen ? 'open' : ''}\">${icon('chevron')}</span>\n      </button>\n      ${isOpen ? `<div class=\"ops-body\">${items.map(g => `<div class=\"ops-step\"><span class=\"ops-step-dot\"></span><span><span class=\"gap-chip\" style=\"margin-left:0;margin-right:6px;\">#${esc(g.num)}</span>${esc(g.text)}</span></div>`).join('')}</div>` : ''}\n    </div>\n  `\n}\n\nfunction diagnosticoHtml(state) {\n  const openTheme = state.diagOpen || null\n  return `\n    <div class=\"diag-wrap\">\n      <div class=\"ops-intro\"><p>Leitura executiva e priorizada de todas as dores levantadas (relatório oficial de auditoria + duas reuniões internas + diagramas BPMN), separando dor do cliente de dor da operação por gravidade.</p></div>\n      <div class=\"scope-note\">${icon('info')}<span>Fora de escopo: Marketplace (iFood, Rappi, TikTok Shopping, Mercado Livre) não entra nesse diagnóstico — esses canais se responsabilizam 100% pelo próprio estorno.</span></div>\n      <div class=\"scope-note\">${icon('info')}<span>Meta-ressalva: toda essa base de conhecimento vem da perspectiva do time Digital/E-commerce. Afirmações sobre o funcionamento puro da loja física ainda não foram validadas diretamente com o time de loja — é inferência, não confirmação.</span></div>\n\n      <h3 class=\"diag-h\">Três causas raiz</h3>\n      <div class=\"cause-list\">${ROOT_CAUSES.map(rootCauseHtml).join('')}</div>\n\n      <div class=\"quote-callout\">${icon('alert')}<span><strong>Prioridade #1 segundo quem opera o processo hoje:</strong> \"o estorno é a grande, grande dor\" do cliente — mais do que a confusão de rota entre CD/Darkstore/loja.</span></div>\n\n      <h3 class=\"diag-h\">Dores do cliente vs. dores da operação</h3>\n      <div class=\"diag-grid\">\n        <div class=\"diag-col\">\n          <div class=\"diag-col-title\">Pra quem compra</div>\n          ${CLIENT_PAINS.map(painCardHtml).join('')}\n        </div>\n        <div class=\"diag-col\">\n          <div class=\"diag-col-title\">Pra quem opera</div>\n          ${OPS_PAINS.map(painCardHtml).join('')}\n        </div>\n      </div>\n\n      <div class=\"context-aside\">Hipótese de impacto no negócio (não é gap operacional, é leitura estratégica): a própria operação relaciona esse processo quebrado com a taxa de recompra baixa — quem passa por uma troca confusa tende a não comprar de novo no site.</div>\n\n      <h3 class=\"diag-h\">Em andamento vs. em discussão</h3>\n      <div class=\"diag-grid\">\n        <div class=\"diag-col\">\n          <div class=\"diag-col-title\">Já em andamento</div>\n          ${IN_PROGRESS.map(progressCardHtml).join('')}\n        </div>\n        <div class=\"diag-col\">\n          <div class=\"diag-col-title\">Em discussão</div>\n          ${IDEAS.map((idea, i) => `<div class=\"idea-card\" data-key=\"diag-idea-${i}\"><div class=\"idea-head\"><span class=\"idea-icon\">${icon('bulb')}</span><strong>${esc(idea.title)}</strong></div><p>${esc(idea.text)}</p></div>`).join('')}\n        </div>\n      </div>\n\n      <h3 class=\"diag-h\">Os gaps por tema</h3>\n      <div class=\"ops-list\">${GAP_THEMES.map(t => gapThemeHtml(t, openTheme)).join('')}</div>\n    </div>\n  `\n}\n\nlet pendingScrollTo = null\n\nexport default {\n  css: `\n    * { box-sizing: border-box; }\n    .icon { width: 16px; height: 16px; display: inline-block; vertical-align: -3px; flex: none; }\n    .icon.origin-svg { width: 22px; height: 22px; }\n    .wrap { height: 100%; overflow-y: auto; background: #f6f1e7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; color: #24312a; padding: 28px 24px 40px; }\n    .head { max-width: 880px; margin: 0 auto 16px; text-align: left; }\n    .head-badge { display: inline-block; font-size: 11px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700; color: #8a6d1f; background: #f1e3bd; border-radius: 999px; padding: 4px 10px; margin-bottom: 10px; }\n    .head h1 { font-family: Georgia, 'Times New Roman', serif; font-size: 28px; margin: 0 0 8px; color: #163b2c; }\n    .head .sub { margin: 0 0 14px; font-size: 14px; line-height: 1.5; color: #5b6b60; max-width: 640px; }\n    .team-note { display: flex; gap: 8px; align-items: flex-start; max-width: 880px; margin: 0 auto 20px; font-size: 12.5px; line-height: 1.5; color: #6b6455; background: #efe9da; border-radius: 10px; padding: 10px 12px; }\n    .team-note .icon { margin-top: 1px; }\n    .scope-note { display: flex; gap: 8px; align-items: flex-start; max-width: 880px; margin: 0 auto 16px; font-size: 12.5px; line-height: 1.5; color: #5b5646; background: #ece8de; border-radius: 10px; padding: 10px 12px; }\n    .scope-note .icon { margin-top: 1px; }\n    .view-switch { display: flex; gap: 8px; max-width: 880px; margin: 0 auto 20px; flex-wrap: wrap; }\n    .view-btn { flex: 1 1 160px; padding: 10px 14px; border-radius: 10px; border: 1px solid #ddd3ba; background: #fff; font-size: 13px; font-weight: 700; color: #45544c; cursor: pointer; text-align: center; }\n    .view-btn.active { background: #163b2c; border-color: #163b2c; color: #fff; }\n    .sim-link { display: inline-flex; align-items: center; justify-content: center; text-decoration: none; background: #0b0f0d; color: #8fd6a8; border-color: #163b2c; }\n    .sim-link:hover { color: #fff; }\n    .tabs { max-width: 880px; margin: 0 auto 20px; display: flex; flex-wrap: wrap; gap: 8px; }\n    .tab { display: flex; align-items: center; gap: 7px; padding: 9px 14px; border-radius: 999px; border: 1px solid #ddd3ba; background: #ffffff; color: #45544c; font-size: 13px; font-weight: 600; cursor: pointer; transition: background .15s ease, color .15s ease, border-color .15s ease; }\n    .tab:hover { border-color: #163b2c; }\n    .tab.active { background: #163b2c; border-color: #163b2c; color: #ffffff; }\n    .panel, .ops-wrap, .problems-wrap, .ideas-wrap, .diag-wrap { max-width: 880px; margin: 0 auto; }\n    .scenario { background: #ffffff; border: 1px solid #e7ddc8; border-radius: 16px; padding: 24px 24px 20px; box-shadow: 0 1px 3px rgba(22,59,44,0.06); }\n    .scenario.nested { margin-top: 16px; box-shadow: none; }\n    .highlight { display: flex; gap: 8px; align-items: flex-start; background: #fdf0dd; border: 1px solid #eecb8f; color: #7a4c0f; border-radius: 10px; padding: 10px 12px; font-size: 13px; line-height: 1.5; margin-bottom: 16px; }\n    .highlight .icon { margin-top: 2px; }\n    .highlight.myth { border-left: 4px solid #b23c17; background: #fdece3; border-color: #f0b79a; color: #7a2f13; }\n    .highlight.myth strong { color: #7a2f13; }\n    .scenario h2 { font-family: Georgia, 'Times New Roman', serif; font-size: 20px; margin: 0 0 6px; color: #163b2c; }\n    .intro { margin: 0 0 14px; font-size: 14px; color: #56635c; line-height: 1.5; }\n    .badges { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 20px; }\n    .badge { font-size: 12px; font-weight: 700; padding: 5px 11px; border-radius: 999px; letter-spacing: .01em; }\n    .badge-ok { background: #e2f2e5; color: #1f7a3d; }\n    .badge-no { background: #fbe4e2; color: #a3271b; }\n    .badge-warn { background: #fdecd2; color: #92590b; }\n    .badge-info { background: #e2ecf7; color: #24548f; }\n    .badge-neutral { background: #ece8de; color: #5b5646; }\n    .origin-label { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: #6b6455; margin: 4px 0 8px; }\n    .origin-toggle { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }\n    .choice-card { flex: 1 1 260px; display: flex; gap: 10px; align-items: flex-start; text-align: left; padding: 12px 14px; border-radius: 12px; border: 1px solid #ddd3ba; background: #fff; cursor: pointer; transition: border-color .15s ease, background .15s ease; }\n    .choice-card:hover { border-color: #163b2c; }\n    .choice-card.active { border-color: #163b2c; background: #eef4ef; }\n    .origin-icon { flex: none; color: #163b2c; }\n    .origin-copy { display: flex; flex-direction: column; gap: 3px; }\n    .origin-copy strong { font-size: 13.5px; color: #24312a; }\n    .origin-copy span { font-size: 12.5px; color: #5b6b60; line-height: 1.4; }\n    .routing { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; border-radius: 12px; padding: 12px 14px; margin-bottom: 16px; font-size: 13px; }\n    .routing-portal { background: #e2f2e5; color: #1f5c39; }\n    .routing-sac { background: #fdecd2; color: #7a4c0f; }\n    .routing-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; opacity: .75; }\n    .routing-value { font-size: 15px; font-weight: 800; }\n    .routing-detail { font-size: 13px; font-weight: 600; }\n    .reason-tabs { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }\n    .reason-tab { padding: 7px 12px; border-radius: 999px; border: 1px solid #ddd3ba; background: #fff; font-size: 12.5px; font-weight: 600; color: #45544c; cursor: pointer; }\n    .reason-tab.active { background: #24312a; border-color: #24312a; color: #fff; }\n    .steps { list-style: none; margin: 0 0 18px; padding: 0; }\n    .step { position: relative; display: flex; gap: 14px; padding-bottom: 22px; }\n    .step:last-child { padding-bottom: 0; }\n    .step:not(:last-child)::before { content: ''; position: absolute; left: 15px; top: 32px; bottom: 0; width: 2px; background: #dfd6bd; }\n    .step-num { position: relative; z-index: 1; flex: 0 0 32px; width: 32px; height: 32px; border-radius: 50%; background: #163b2c; color: #ffffff; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; }\n    .step-body { display: flex; flex-direction: column; gap: 3px; padding-top: 4px; }\n    .step-body strong { font-size: 14px; color: #24312a; }\n    .step-body span { font-size: 13px; color: #5b6b60; line-height: 1.5; }\n    .empty { font-size: 13px; color: #8a8272; background: #f6f1e7; border: 1px dashed #ddd3ba; border-radius: 10px; padding: 16px; margin-bottom: 18px; }\n    .notes { display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px; }\n    .note { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; line-height: 1.5; border-radius: 10px; padding: 10px 12px; }\n    .note .icon { margin-top: 2px; }\n    .note-exception { background: #fbe4e2; color: #8f2418; }\n    .note-legal { background: #eef1f6; color: #3c4a5c; }\n    .note-warn { background: #fdecd2; color: #7a4c0f; }\n    .note-info { background: #eaf1ec; color: #2c4f3c; }\n    .contact { display: flex; flex-direction: column; gap: 3px; font-size: 13px; color: #45544c; border-top: 1px solid #ede7d6; padding-top: 14px; margin-top: 6px; }\n    .contact strong { color: #163b2c; font-size: 13px; }\n    .foot { max-width: 880px; margin: 22px auto 0; font-size: 11px; color: #9a927e; text-align: center; }\n    .ops-intro { font-size: 13px; color: #56635c; margin-bottom: 14px; line-height: 1.5; }\n    .ops-intro p { margin: 0; }\n    .tools-chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 20px; }\n    .tool-chip { font-size: 11.5px; font-weight: 600; background: #ece8de; color: #5b5646; padding: 4px 9px; border-radius: 999px; }\n    .ops-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }\n    .ops-card { background: #fff; border: 1px solid #e7ddc8; border-radius: 14px; overflow: hidden; }\n    .ops-header, .diag-header { width: 100%; display: flex; align-items: center; gap: 12px; padding: 14px 16px; background: none; border: none; cursor: pointer; text-align: left; }\n    .ops-num { flex: none; width: 26px; height: 26px; border-radius: 50%; background: #163b2c; color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; }\n    .ops-title-wrap { flex: 1; display: flex; flex-direction: column; gap: 4px; }\n    .ops-title-wrap strong { font-size: 14px; color: #24312a; }\n    .ops-lanes { display: flex; flex-wrap: wrap; gap: 6px; }\n    .lane { font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: 999px; }\n    .lane-sac { background: #e2ecf7; color: #24548f; }\n    .lane-loja { background: #e2f2e5; color: #1f7a3d; }\n    .lane-dark { background: #ece3f7; color: #5b3a91; }\n    .lane-logistica { background: #fdecd2; color: #92590b; }\n    .lane-digital { background: #dcf2f0; color: #0d6b62; }\n    .lane-financeiro { background: #ece8de; color: #5b5646; }\n    .lane-fiscal { background: #fbe4e2; color: #a3271b; }\n    .ops-chevron { flex: none; width: 16px; height: 16px; transition: transform .15s ease; color: #8a8272; }\n    .ops-chevron.open { transform: rotate(180deg); }\n    .ops-body { padding: 0 16px 16px 16px; display: flex; flex-direction: column; gap: 10px; border-top: 1px solid #f1ece0; padding-top: 12px; }\n    .ops-step { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; color: #3a453f; line-height: 1.5; }\n    .ops-step-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: #163b2c; margin-top: 6px; }\n    .ops-decision { border: 1px dashed #ddd3ba; border-radius: 10px; padding: 10px 12px; background: #faf7ef; }\n    .ops-decision-q { font-size: 13px; font-weight: 600; color: #24312a; margin-bottom: 6px; }\n    .ops-decision-tag { display: inline-block; font-size: 10.5px; font-weight: 800; background: #163b2c; color: #fff; padding: 2px 6px; border-radius: 6px; margin-right: 6px; }\n    .ops-branches { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }\n    .ops-branches li { font-size: 12.5px; color: #56635c; line-height: 1.45; }\n    .gap-chip { display: inline-block; font-size: 10.5px; font-weight: 700; background: #fbe4e2; color: #8f2418; padding: 2px 7px; border-radius: 999px; margin-left: 6px; }\n    .meeting-chip { display: inline-block; font-size: 10.5px; font-weight: 700; background: #e2ecf7; color: #24548f; padding: 2px 7px; border-radius: 999px; margin-left: 6px; }\n    .live-chip { display: inline-block; font-size: 10.5px; font-weight: 700; background: #fbe4e2; color: #a3271b; padding: 2px 7px; border-radius: 999px; margin-left: 6px; }\n    .context-aside { font-size: 12px; color: #8a8272; font-style: italic; line-height: 1.5; padding: 10px 2px 0; max-width: 880px; margin: 0 auto; }\n    .quote-callout { display: flex; gap: 10px; align-items: flex-start; max-width: 880px; margin: 4px auto 20px; background: #fdece3; border: 1px solid #f0b79a; border-left: 4px solid #b23c17; border-radius: 10px; padding: 12px 14px; font-size: 13px; line-height: 1.5; color: #7a2f13; }\n    .quote-callout strong { color: #7a2f13; }\n    .problems-list { display: flex; flex-direction: column; gap: 10px; }\n    .problem-card { background: #fff; border: 1px solid #e7ddc8; border-radius: 12px; padding: 14px 16px; }\n    .problem-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }\n    .problem-num { font-size: 12px; font-weight: 800; color: #163b2c; background: #eef4ef; padding: 3px 9px; border-radius: 999px; }\n    .problem-num-meeting { color: #24548f; background: #e2ecf7; }\n    .problem-num-live { color: #a3271b; background: #fbe4e2; }\n    .gap-link { font-size: 11.5px; font-weight: 700; color: #163b2c; background: #f1e3bd; border: none; border-radius: 999px; padding: 5px 10px; cursor: pointer; }\n    .problem-card p { margin: 0; font-size: 13px; color: #45544c; line-height: 1.5; }\n    .idea-card { background: #eef4fb; border: 1px solid #cfe0f1; border-left: 4px solid #2f6fb0; border-radius: 12px; padding: 14px 16px; }\n    .idea-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }\n    .idea-head strong { font-size: 14px; color: #1d3b5c; }\n    .idea-icon { color: #2f6fb0; }\n    .idea-card p { margin: 0; font-size: 13px; color: #345875; line-height: 1.5; }\n    .diag-h { font-family: Georgia, 'Times New Roman', serif; font-size: 16px; color: #163b2c; margin: 24px 0 12px; }\n    .diag-h:first-of-type { margin-top: 4px; }\n    .cause-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 8px; }\n    .cause-card { display: flex; gap: 12px; background: #fff; border: 1px solid #e7ddc8; border-radius: 14px; padding: 16px; box-shadow: 0 1px 3px rgba(22,59,44,0.06); }\n    .cause-num { flex: none; width: 30px; height: 30px; border-radius: 50%; background: #163b2c; color: #fff; font-weight: 800; font-size: 14px; display: flex; align-items: center; justify-content: center; }\n    .cause-body strong { font-size: 14px; color: #163b2c; display: block; margin-bottom: 4px; }\n    .cause-body p { margin: 0; font-size: 13px; color: #45544c; line-height: 1.5; }\n    .diag-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 8px; }\n    .diag-col { display: flex; flex-direction: column; gap: 10px; }\n    .diag-col-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: #6b6455; margin-bottom: 2px; }\n    .pain-card { background: #fff; border: 1px solid #e7ddc8; border-radius: 12px; padding: 12px 14px; }\n    .pain-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }\n    .pain-source { font-size: 10.5px; color: #8a8272; font-style: italic; }\n    .pain-card p { margin: 0; font-size: 13px; color: #45544c; line-height: 1.5; }\n    .sev-badge { font-size: 11px; font-weight: 800; padding: 3px 9px; border-radius: 999px; letter-spacing: .03em; }\n    .sev-critico { background: #fbe4e2; color: #a3271b; }\n    .sev-alto { background: #fbe0cf; color: #a1490f; }\n    .sev-medio { background: #f7ecc9; color: #8a6d1f; }\n    .sev-baixo { background: #ece8de; color: #5b5646; }\n    .sev-estrutural { background: #e6e9ec; color: #42505c; }\n    .progress-card { background: #eaf5ee; border: 1px solid #cfe6d7; border-left: 4px solid #1f7a3d; border-radius: 12px; padding: 12px 14px; }\n    .progress-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; color: #1f5c39; }\n    .progress-head strong { font-size: 14px; color: #1f5c39; }\n    .progress-head .icon { color: #1f7a3d; }\n    .progress-card p { margin: 0; font-size: 13px; color: #33513f; line-height: 1.5; }\n    @media (max-width: 720px) {\n      .diag-grid { grid-template-columns: 1fr; }\n    }\n    @media (max-width: 600px) {\n      .wrap { padding: 20px 16px 32px; }\n      .head h1 { font-size: 22px; }\n      .scenario { padding: 18px 16px 16px; border-radius: 12px; }\n      .tab { padding: 8px 12px; font-size: 12px; }\n      .choice-card { flex: 1 1 100%; }\n      .view-btn { flex: 1 1 100%; }\n    }\n  `,\n\n  render(d2) {\n    const view = d2.state.view || 'simples'\n    let body\n    if (view === 'operacional') body = operationalHtml(d2.state)\n    else if (view === 'problemas') body = problemsHtml()\n    else if (view === 'ideias') body = ideasHtml()\n    else if (view === 'diagnostico') body = diagnosticoHtml(d2.state)\n    else body = simpleHtml(d2.state)\n    return `\n      <div class=\"wrap\">\n        <div class=\"head\">\n          <span class=\"head-badge\">Estudo interno · não é peça para o cliente</span>\n          <h1>Guia de trocas — Granado</h1>\n          <p class=\"sub\">Visão simples pra explicar rápido, visão operacional fiel ao processo real, problemas conhecidos, ideias em discussão e um diagnóstico executivo priorizado.</p>\n        </div>\n        <div class=\"view-switch\">\n          <button type=\"button\" class=\"view-btn ${view === 'simples' ? 'active' : ''}\" data-key=\"view-simples\" data-view=\"simples\">Visão simples</button>\n          <button type=\"button\" class=\"view-btn ${view === 'operacional' ? 'active' : ''}\" data-key=\"view-operacional\" data-view=\"operacional\">Visão operacional</button>\n          <button type=\"button\" class=\"view-btn ${view === 'problemas' ? 'active' : ''}\" data-key=\"view-problemas\" data-view=\"problemas\">Problemas conhecidos</button>\n          <button type=\"button\" class=\"view-btn ${view === 'ideias' ? 'active' : ''}\" data-key=\"view-ideias\" data-view=\"ideias\">Ideias em discussão</button>\n          <button type=\"button\" class=\"view-btn ${view === 'diagnostico' ? 'active' : ''}\" data-key=\"view-diagnostico\" data-view=\"diagnostico\">Diagnóstico</button>\n          <a class=\"view-btn sim-link\" href=\"https://sala-de-controle.granado-app.next.data2.app/\" target=\"_blank\" rel=\"noopener\" data-key=\"view-simulacao\">Sala de Controle (simulação) ↗</a>\n        </div>\n        ${body}\n        <div class=\"foot\">Conteúdo de apoio interno — sujeito a atualização pelo time de atendimento ao cliente.</div>\n      </div>\n    `\n  },\n\n  mounted(d2) {\n    d2.on('.view-btn', 'click', (e, el) => { d2.state.view = el.dataset.view })\n    d2.on('.tab', 'click', (e, el) => { d2.state.active = el.dataset.id })\n    d2.on('.know-card', 'click', (e, el) => { d2.state.knowsOrder = el.dataset.knows })\n    d2.on('.origin-card', 'click', (e, el) => { d2.state.origin = el.dataset.origin })\n    d2.on('.reason-tab', 'click', (e, el) => { d2.state.reason = el.dataset.reason })\n    d2.on('.ops-header', 'click', (e, el) => { const id = el.dataset.section; d2.state.opsOpen = (d2.state.opsOpen === id ? null : id) })\n    d2.on('.gap-link', 'click', (e, el) => { const id = el.dataset.section; d2.state.view = 'operacional'; d2.state.opsOpen = id; pendingScrollTo = id })\n    d2.on('.diag-header', 'click', (e, el) => { const id = el.dataset.theme; d2.state.diagOpen = (d2.state.diagOpen === id ? null : id) })\n  },\n\n  updated(d2, root) {\n    if (pendingScrollTo) {\n      const el = root.querySelector(`[data-section-anchor=\"${pendingScrollTo}\"]`)\n      if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }) }\n      pendingScrollTo = null\n    }\n  }\n}\n"}}}}}}}},{"_id":"6a85cbe8ae4b297418db54db","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-sim-trocas","package":"granado-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":"function uniform(min, max) { return min + Math.random() * (max - min) }\nfunction chance(p) { return Math.random() < p }\nfunction lerp(a, b, t) { return { x: a.x + (b.x - a.x) * t, y: a.y + (b.y - a.y) * t } }\nfunction mixRgb(a, b, t) { return [Math.round(a[0] + (b[0] - a[0]) * t), Math.round(a[1] + (b[1] - a[1]) * t), Math.round(a[2] + (b[2] - a[2]) * t)] }\nfunction satRgb(s) {\n  s = Math.max(0, Math.min(100, s))\n  if (s > 50) { const t = (100 - s) / 50; return mixRgb([62, 207, 110], [232, 197, 71], t) }\n  const t = (50 - s) / 50; return mixRgb([232, 197, 71], [224, 82, 79], t)\n}\nfunction esc(str) { return String(str == null ? '' : str) }\n\n// ---- Graph model (fiel ao grafo do Gui) ----\nconst NODE_META = {\n  origin_cd: { label: 'Saiu do CD', lane: 'logistica', x: 90, y: 80, decay: 'normal' },\n  origin_dark: { label: 'Saiu da Darkstore', lane: 'dark', x: 90, y: 170, decay: 'normal' },\n  origin_loja_retirada: { label: 'Loja → retirada', lane: 'loja', x: 90, y: 260, decay: 'normal' },\n  origin_loja_expressa: { label: 'Loja/Dark → expressa', lane: 'loja', x: 90, y: 350, decay: 'normal' },\n  origin_loja_presencial: { label: 'Compra presencial', lane: 'loja', x: 90, y: 440, decay: 'normal' },\n  origin_loyalty: { label: 'Fidelidade (resgate)', lane: 'digital', x: 90, y: 530, decay: 'normal' },\n  origin_marketplace: { label: 'Marketplace', lane: 'digital', x: 90, y: 620, decay: 'normal' },\n  origin_desconhecido: { label: 'Sem origem conhecida', lane: 'sac', x: 90, y: 710, decay: 'normal' },\n  node_fiscal_travado: { label: 'Fiscal travado (SAP)', lane: 'fiscal', x: 330, y: 305, decay: 'limbo' },\n  node_bug_multimarca: { label: 'Bug: carrinho multimarca', lane: 'digital', x: 330, y: 200, decay: 'normal' },\n  node_pdv_loja: { label: 'PDV da loja', lane: 'loja', x: 330, y: 440, decay: 'slow' },\n  node_troque_portal: { label: 'Portal Troca e Comércio', lane: 'digital', x: 560, y: 110, decay: 'slow' },\n  node_sac_contact: { label: 'Contato com o SAC', lane: 'sac', x: 700, y: 260, decay: 'normal' },\n  node_cep_check: { label: 'CEP cobre a entrega?', lane: 'logistica', x: 560, y: 400, decay: 'normal' },\n  node_cep_fora: { label: 'CEP fora de área', lane: 'logistica', x: 560, y: 480, decay: 'limbo' },\n  node_lacrado_check: { label: 'Item lacrado?', lane: 'sac', x: 870, y: 260, decay: 'normal' },\n  node_volta_loja: { label: 'Aguarda loja confirmar', lane: 'loja', x: 1060, y: 170, decay: 'limbo' },\n  node_estoque_negativo_pdv: { label: 'Estoque negativo (PDV)', lane: 'loja', x: 1060, y: 100, decay: 'limbo' },\n  node_escritorio: { label: 'Escritório do SAC', lane: 'sac', x: 1060, y: 370, decay: 'limbo' },\n  node_fraude_risco: { label: 'Risco de fraude', lane: 'loja', x: 330, y: 620, decay: 'normal' },\n  node_estorno_router: { label: 'Roteador de estorno', lane: 'financeiro', x: 1270, y: 220, decay: 'normal' },\n  node_estorno_auto: { label: 'Estorno automático', lane: 'financeiro', x: 1430, y: 80, decay: 'normal' },\n  node_estorno_manual_padrao: { label: 'Estorno manual', lane: 'financeiro', x: 1430, y: 160, decay: 'normal' },\n  node_estorno_manual_care: { label: 'Estorno manual (Care)', lane: 'financeiro', x: 1430, y: 240, decay: 'normal' },\n  node_estorno_manual_pagbrasil: { label: 'Estorno manual (PagBrasil)', lane: 'financeiro', x: 1430, y: 320, decay: 'normal' },\n  node_estorno_parcial: { label: 'Estorno parcial', lane: 'financeiro', x: 1430, y: 400, decay: 'normal' },\n  term_resolvido: { label: 'Resolvido', lane: 'financeiro', x: 1560, y: 220, decay: 'normal' },\n  term_cancelado_nao_retirado: { label: 'Cancelado (não retirado)', lane: 'loja', x: 330, y: 530, decay: 'normal' },\n  term_limbo_escritorio: { label: 'Acumulado (escritório)', lane: 'sac', x: 1150, y: 420, decay: 'limbo' },\n  term_irregular: { label: 'Irregular / fraude', lane: 'loja', x: 500, y: 620, decay: 'normal' },\n  term_marketplace: { label: 'Marketplace (fora escopo)', lane: 'digital', x: 330, y: 660, decay: 'slow' },\n  term_excecao_manual: { label: 'Exceção manual', lane: 'sac', x: 330, y: 710, decay: 'normal' },\n  term_descarte_fabrica: { label: 'Descarte (fábrica)', lane: 'logistica', x: 1060, y: 480, decay: 'normal' }\n}\n\nconst EDGES = [\n  ['origin_cd', 'node_troque_portal'], ['origin_dark', 'node_troque_portal'],\n  ['origin_loja_retirada', 'node_fiscal_travado'], ['origin_loja_retirada', 'node_bug_multimarca'], ['origin_loja_retirada', 'node_sac_contact'], ['origin_loja_retirada', 'term_cancelado_nao_retirado'], ['origin_loja_retirada', 'node_fraude_risco'],\n  ['node_fiscal_travado', 'node_sac_contact'], ['node_fiscal_travado', 'node_cep_check'],\n  ['node_bug_multimarca', 'node_sac_contact'],\n  ['node_fraude_risco', 'term_irregular'],\n  ['origin_loja_expressa', 'node_cep_check'],\n  ['node_cep_check', 'node_sac_contact'], ['node_cep_check', 'node_cep_fora'],\n  ['node_cep_fora', 'node_sac_contact'],\n  ['origin_loyalty', 'node_sac_contact'],\n  ['node_sac_contact', 'node_lacrado_check'],\n  ['node_lacrado_check', 'node_volta_loja'], ['node_lacrado_check', 'node_escritorio'],\n  ['node_volta_loja', 'node_estoque_negativo_pdv'], ['node_volta_loja', 'node_estorno_router'],\n  ['node_escritorio', 'term_descarte_fabrica'], ['node_escritorio', 'term_limbo_escritorio'], ['node_escritorio', 'node_estorno_router'],\n  ['node_troque_portal', 'node_estorno_router'],\n  ['node_estorno_router', 'node_estorno_auto'], ['node_estorno_router', 'node_estorno_manual_padrao'], ['node_estorno_router', 'node_estorno_manual_care'], ['node_estorno_router', 'node_estorno_manual_pagbrasil'], ['node_estorno_router', 'node_estorno_parcial'],\n  ['node_estorno_auto', 'term_resolvido'], ['node_estorno_manual_padrao', 'term_resolvido'], ['node_estorno_manual_care', 'term_resolvido'], ['node_estorno_manual_pagbrasil', 'term_resolvido'], ['node_estorno_parcial', 'term_resolvido'],\n  ['origin_loja_presencial', 'node_pdv_loja'], ['node_pdv_loja', 'term_resolvido'],\n  ['origin_marketplace', 'term_marketplace'],\n  ['origin_desconhecido', 'term_excecao_manual']\n]\n\nfunction retiradaPickupPlan() {\n  if (chance(0.85)) return { duration: uniform(0.5, 7), next: () => chance(0.08) ? 'node_bug_multimarca' : 'node_sac_contact' }\n  return { duration: 7, next: () => chance(1 / 3) ? 'node_fraude_risco' : 'term_cancelado_nao_retirado' }\n}\n\nconst PLAN = {\n  origin_cd: () => ({ duration: 0.05, next: 'node_troque_portal' }),\n  origin_dark: () => ({ duration: 0.05, next: 'node_troque_portal' }),\n  origin_marketplace: () => ({ duration: 0.05, next: 'term_marketplace' }),\n  origin_desconhecido: () => ({ duration: uniform(2, 30), next: 'term_excecao_manual' }),\n  origin_loja_presencial: () => ({ duration: 0.05, next: 'node_pdv_loja' }),\n  origin_loyalty: () => ({ duration: 0.05, next: 'node_sac_contact' }),\n  origin_loja_retirada: (p) => {\n    if (!p._fiscalChecked) { p._fiscalChecked = true; if (chance(0.12)) return { duration: 0.05, next: 'node_fiscal_travado' } }\n    return retiradaPickupPlan()\n  },\n  origin_loja_expressa: (p) => {\n    if (!p._fiscalChecked) { p._fiscalChecked = true; if (chance(0.12)) return { duration: 0.05, next: 'node_fiscal_travado' } }\n    return { duration: uniform(0.1, 0.3), next: 'node_cep_check' }\n  },\n  node_fiscal_travado: (p) => ({ duration: uniform(3, 7), next: p.originId === 'origin_loja_retirada' ? 'origin_loja_retirada' : 'node_cep_check' }),\n  node_bug_multimarca: () => ({ duration: uniform(0.5, 1), next: 'node_sac_contact' }),\n  node_sac_contact: () => ({ duration: 1, next: 'node_lacrado_check' }),\n  node_cep_check: () => chance(0.85) ? { duration: 0.05, next: 'node_sac_contact' } : { duration: 0.05, next: 'node_cep_fora' },\n  node_cep_fora: () => ({ duration: uniform(3, 10), next: 'node_sac_contact' }),\n  node_lacrado_check: () => chance(0.6) ? { duration: uniform(0.1, 0.3), next: 'node_volta_loja' } : { duration: uniform(0.1, 0.3), next: 'node_escritorio' },\n  node_volta_loja: () => ({ duration: 3 + 22 * Math.pow(Math.random(), 1.8), next: 'node_estorno_router', detour: chance(0.2) ? 'node_estoque_negativo_pdv' : null }),\n  node_escritorio: () => {\n    const idDelay = chance(0.3) ? uniform(2, 5) : 0\n    const baseDur = uniform(1, 3) + idDelay\n    if (chance(0.05)) return { duration: baseDur + uniform(5, 10), next: 'term_descarte_fabrica' }\n    if (chance(0.9)) return { duration: baseDur, next: 'term_limbo_escritorio' }\n    return { duration: baseDur + uniform(20, 45), next: 'node_estorno_router' }\n  },\n  node_fraude_risco: () => ({ duration: uniform(0.5, 2), next: 'term_irregular' }),\n  node_troque_portal: () => ({ duration: uniform(1, 3), next: 'node_estorno_router' }),\n  node_pdv_loja: () => ({ duration: uniform(0.2, 1), next: 'term_resolvido' }),\n  node_estorno_router: () => {\n    const r = Math.random(); let next\n    if (r < 0.40) next = 'node_estorno_auto'\n    else if (r < 0.75) next = 'node_estorno_manual_padrao'\n    else if (r < 0.85) next = 'node_estorno_manual_care'\n    else if (r < 0.95) next = 'node_estorno_manual_pagbrasil'\n    else next = 'node_estorno_parcial'\n    return { duration: 0.05, next }\n  },\n  node_estorno_auto: () => ({ duration: uniform(0, 2), next: 'term_resolvido' }),\n  node_estorno_manual_padrao: () => ({ duration: uniform(5, 15), next: 'term_resolvido' }),\n  node_estorno_manual_care: () => ({ duration: uniform(8, 20), next: 'term_resolvido' }),\n  node_estorno_manual_pagbrasil: () => ({ duration: uniform(10, 25), next: 'term_resolvido' }),\n  node_estorno_parcial: () => ({ duration: uniform(15, 25), next: 'term_resolvido' })\n}\n\nconst ORIGIN_WEIGHTS = [\n  ['origin_cd', 0.30], ['origin_dark', 0.15], ['origin_loja_retirada', 0.25],\n  ['origin_loja_expressa', 0.10], ['origin_loja_presencial', 0.12],\n  ['origin_loyalty', 0.03], ['origin_marketplace', 0.04], ['origin_desconhecido', 0.01]\n]\nfunction pickOrigin() {\n  let r = Math.random(), acc = 0\n  for (const pair of ORIGIN_WEIGHTS) { acc += pair[1]; if (r <= acc) return pair[0] }\n  return ORIGIN_WEIGHTS[ORIGIN_WEIGHTS.length - 1][0]\n}\n\nconst ORIGIN_SHORT = {\n  origin_cd: 'saiu do CD', origin_dark: 'saiu da Darkstore', origin_loja_retirada: 'saiu da loja pra retirada',\n  origin_loja_expressa: 'saiu pra entrega expressa', origin_loja_presencial: 'foi comprado presencialmente',\n  origin_loyalty: 'foi resgatado com pontos de fidelidade', origin_marketplace: 'foi comprado via marketplace',\n  origin_desconhecido: 'não tem origem conhecida'\n}\n\nconst LANE_COLORS = { sac: '#4a90d9', loja: '#4caf7a', dark: '#8a6bd9', logistica: '#e0a458', digital: '#3fc7c2', financeiro: '#9c9184', fiscal: '#d9636b' }\n\nlet simDay = 0\nlet totalResolvedEver = 0, totalLimboEver = 0, totalIrregularEver = 0\nconst DAYS_PER_SECOND = 0.4\nconst SPAWN_INTERVAL_DAYS = 0.15\nconst MAX_ACTIVE = 220\nconst TERMINAL_REST_DAYS = 4\nconst LIMBO_RENDER_CAP = 260\n\nfunction resolveNext(next) { return typeof next === 'function' ? next() : next }\n\nfunction startNode(p, nodeId) {\n  const meta = NODE_META[nodeId]\n  p.fromPos = p.toPos ? { x: p.toPos.x, y: p.toPos.y } : { x: meta.x - 40, y: meta.y }\n  p.toPos = { x: meta.x, y: meta.y }\n  p.viaPos = null\n  p.node = nodeId\n  p.enterDay = simDay\n  p.history.push({ node: nodeId, day: Math.round(simDay * 10) / 10 })\n  p.decayTier = meta.decay || 'normal'\n  const planFn = PLAN[nodeId]\n  if (!planFn) {\n    p.terminal = true\n    p.duration = Infinity\n    if (!p.frozen) { p.frozen = true; p.frozenSatisfaction = p.satisfaction; p.frozenDay = simDay }\n    if (nodeId === 'term_limbo_escritorio') totalLimboEver++\n    if (nodeId === 'term_resolvido') totalResolvedEver++\n    if (nodeId === 'term_irregular') totalIrregularEver++\n    return\n  }\n  const plan = planFn(p)\n  p.duration = plan.duration\n  p._next = plan.next\n  if (plan.detour) p.viaPos = { x: NODE_META[plan.detour].x, y: NODE_META[plan.detour].y }\n}\n\nfunction spawnParticle() {\n  const originId = pickOrigin()\n  const p = { satisfaction: 100, frozen: false, terminal: false, history: [], toPos: null, originId: originId }\n  startNode(p, originId)\n  return p\n}\n\nfunction particlePos(p) {\n  const dur = p.terminal ? 1 : Math.max(0.0001, p.duration)\n  const t = p.terminal ? 1 : Math.min(1, (simDay - p.enterDay) / dur)\n  if (p.viaPos) {\n    if (t < 0.5) return lerp(p.fromPos, p.viaPos, t / 0.5)\n    return lerp(p.viaPos, p.toPos, (t - 0.5) / 0.5)\n  }\n  return lerp(p.fromPos, p.toPos, t)\n}\n\nconst DECAY = { slow: 0.3, normal: 1.2, limbo: 4.5 }\n\nfunction buildSummary(p) {\n  const daysHere = Math.round(simDay - p.enterDay)\n  const o = ORIGIN_SHORT[p.originId] || ''\n  const templates = {\n    node_escritorio: `Pedido ${o}. Item aberto, parado no escritório do SAC há ${daysHere} dia(s), aguardando identificação/processamento.`,\n    term_limbo_escritorio: `Pedido ${o}. Item aberto, ACUMULADO no escritório do SAC há ${Math.round(simDay - p.frozenDay)} dia(s) — sem previsão de saída.`,\n    node_volta_loja: `Pedido ${o}. Item lacrado, aguardando a loja confirmar o recebimento há ${daysHere} dia(s).`,\n    node_fiscal_travado: `Pedido ${o}. Parado no SAP sem faturar há ${daysHere} dia(s) — motivo não identificado internamente.`,\n    node_cep_fora: `Pedido ${o}. CEP fora da área de cobertura, parado sem ninguém notar há ${daysHere} dia(s).`,\n    term_irregular: `Pedido ${o}. Cancelado digitalmente, mas o cliente já retirou e pagou informalmente na loja — situação irregular.`,\n    term_resolvido: `Pedido ${o}. Resolvido após ${Math.max(0, Math.round(p.frozenDay - (p.history[0] ? p.history[0].day : 0)))} dia(s), satisfação final ${Math.round(p.frozenSatisfaction)}/100.`,\n    term_marketplace: `Pedido ${o}. Resolvido pelo próprio canal — fora do escopo deste guia.`,\n    term_cancelado_nao_retirado: `Pedido ${o}. Cancelado automaticamente por não ter sido retirado em 7 dias.`,\n    term_descarte_fabrica: `Pedido ${o}. Encaminhado pra baixa por pré-destruição na fábrica.`,\n    term_excecao_manual: `Pedido ${o}. Tratado manualmente pelo SAC como exceção, caso a caso.`\n  }\n  return templates[p.node] || `Pedido ${o}, atualmente em \"${(NODE_META[p.node] || {}).label || p.node}\" há ${daysHere} dia(s).`\n}\n\nfunction buildDetailHtml(p) {\n  const pathLines = p.history.map(h => `${(NODE_META[h.node] || {}).label || h.node} <small>(dia ${h.day})</small>`).join(' → ')\n  return `<div class=\"detail-row\"><strong>Origem:</strong> ${esc((NODE_META[p.originId] || {}).label || p.originId)}</div>\n    <div class=\"detail-row\"><strong>Caminho:</strong> ${pathLines}</div>\n    <div class=\"detail-row\"><strong>Satisfação${p.frozen ? ' (congelada)' : ''}:</strong> ${Math.round(p.frozen ? p.frozenSatisfaction : p.satisfaction)}/100</div>\n    <div class=\"detail-summary\">${esc(buildSummary(p))}</div>`\n}\n\nfunction roundRect(ctx, x, y, w, h, r) {\n  ctx.beginPath()\n  ctx.moveTo(x + r, y)\n  ctx.arcTo(x + w, y, x + w, y + h, r)\n  ctx.arcTo(x + w, y + h, x, y + h, r)\n  ctx.arcTo(x, y + h, x, y, r)\n  ctx.arcTo(x, y, x + w, y, r)\n  ctx.closePath()\n}\n\nexport default {\n  css: `\n    .sim-root { position: relative; width: 100%; height: 100%; background: #0b0f0d; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif; }\n    .sim-canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }\n    .sim-disclaimer { position: absolute; top: 0; left: 0; right: 0; z-index: 5; background: #7a4c0f; color: #fff2dc; font-size: 12px; font-weight: 700; text-align: center; padding: 7px 12px; }\n    .sim-hud { position: absolute; top: 40px; left: 12px; z-index: 5; background: rgba(10,14,12,0.85); border: 1px solid #2c3630; border-radius: 10px; padding: 10px 14px; display: flex; flex-wrap: wrap; gap: 16px; font-family: 'SF Mono', 'Consolas', monospace; }\n    .hud-item { display: flex; flex-direction: column; gap: 2px; min-width: 90px; }\n    .hud-label { font-size: 9px; letter-spacing: .04em; color: #7b8a80; text-transform: uppercase; }\n    .hud-value { font-size: 18px; font-weight: 700; color: #d8ecdf; }\n    .hud-value.hud-danger { color: #e0524f; }\n    .sim-speed { position: absolute; top: 40px; right: 12px; z-index: 5; display: flex; gap: 6px; }\n    .speed-btn { background: rgba(20,26,23,0.85); border: 1px solid #2c3630; color: #a9b6ae; font-size: 11px; font-weight: 700; padding: 5px 9px; border-radius: 6px; cursor: pointer; font-family: monospace; }\n    .speed-btn.active { background: #163b2c; color: #fff; border-color: #163b2c; }\n    .sim-legend { position: absolute; bottom: 12px; left: 12px; z-index: 5; background: rgba(10,14,12,0.85); border: 1px solid #2c3630; border-radius: 10px; padding: 8px 12px; font-size: 10.5px; color: #a9b6ae; display: flex; flex-direction: column; gap: 4px; max-width: 260px; }\n    .legend-bar { height: 6px; border-radius: 3px; background: linear-gradient(90deg, #3ecf6e, #e8c547, #e0524f); }\n    .sim-detail { position: absolute; top: 90px; right: 12px; z-index: 6; width: 300px; max-height: 60%; overflow-y: auto; background: rgba(14,18,16,0.96); border: 1px solid #33443a; border-radius: 12px; padding: 14px; color: #d8ecdf; font-size: 12.5px; line-height: 1.5; display: none; }\n    .sim-detail[data-open=\"true\"] { display: block; }\n    .detail-close { position: absolute; top: 8px; right: 10px; background: none; border: none; color: #a9b6ae; font-size: 16px; cursor: pointer; }\n    .detail-row { margin-bottom: 8px; }\n    .detail-row strong { color: #8fd6a8; }\n    .detail-summary { margin-top: 8px; padding-top: 8px; border-top: 1px solid #2c3630; font-style: italic; color: #cfd9d3; }\n  `,\n  render(d2) {\n    return `\n      <div class=\"sim-root\" data-d2-static>\n        <div class=\"sim-disclaimer\">⚠ Volumes estimados — hoje a Granado não tem esse número real. É exatamente um dos problemas mapeados.</div>\n        <canvas class=\"sim-canvas\"></canvas>\n        <div class=\"sim-hud\">\n          <div class=\"hud-item\"><span class=\"hud-label\">Ativos</span><span class=\"hud-value\" data-hud=\"active\">0</span></div>\n          <div class=\"hud-item\"><span class=\"hud-label\">No escritório (limbo)</span><span class=\"hud-value hud-danger\" data-hud=\"limbo\">0</span></div>\n          <div class=\"hud-item\"><span class=\"hud-label\">Satisfação média</span><span class=\"hud-value\" data-hud=\"satisfaction\">100</span></div>\n          <div class=\"hud-item\"><span class=\"hud-label\">Risco fraude/irregular</span><span class=\"hud-value hud-danger\" data-hud=\"fraud\">0</span></div>\n          <div class=\"hud-item\"><span class=\"hud-label\">Resolvidos</span><span class=\"hud-value\" data-hud=\"resolved\">0</span></div>\n          <div class=\"hud-item\"><span class=\"hud-label\">Tempo simulado</span><span class=\"hud-value\" data-hud=\"simday\">dia 0</span></div>\n        </div>\n        <div class=\"sim-speed\">\n          <button type=\"button\" class=\"speed-btn\" data-speed=\"0.25\">0.25x</button>\n          <button type=\"button\" class=\"speed-btn active\" data-speed=\"1\">1x</button>\n          <button type=\"button\" class=\"speed-btn\" data-speed=\"3\">3x</button>\n          <button type=\"button\" class=\"speed-btn\" data-speed=\"8\">8x</button>\n        </div>\n        <div class=\"sim-legend\">\n          <strong style=\"color:#d8ecdf\">Satisfação do cliente</strong>\n          <div class=\"legend-bar\"></div>\n          <span>Verde = satisfeito · Vermelho = insatisfeito · Cinza/empoeirado = acumulado pra sempre no escritório</span>\n          <span>Clique numa bolinha pra ver o caso individual</span>\n        </div>\n        <div class=\"sim-detail\" data-open=\"false\">\n          <button type=\"button\" class=\"detail-close\">×</button>\n          <div class=\"detail-body\"></div>\n        </div>\n      </div>\n    `\n  },\n  mounted(d2, root) {\n    const canvasEl = root.querySelector('.sim-canvas')\n    const ctx = canvasEl && canvasEl.getContext ? canvasEl.getContext('2d') : null\n    const hud = {\n      active: root.querySelector('[data-hud=\"active\"]'),\n      limbo: root.querySelector('[data-hud=\"limbo\"]'),\n      satisfaction: root.querySelector('[data-hud=\"satisfaction\"]'),\n      fraud: root.querySelector('[data-hud=\"fraud\"]'),\n      resolved: root.querySelector('[data-hud=\"resolved\"]'),\n      simday: root.querySelector('[data-hud=\"simday\"]')\n    }\n    const detailPanel = root.querySelector('.sim-detail')\n    const detailBody = root.querySelector('.detail-body')\n\n    let cssW = 800, cssH = 600\n    function resizeCanvas() {\n      if (!canvasEl) return\n      const rect = canvasEl.getBoundingClientRect()\n      const dpr = (typeof window !== 'undefined' && window.devicePixelRatio) || 1\n      cssW = rect.width || 800\n      cssH = rect.height || 600\n      canvasEl.width = Math.max(1, Math.round(cssW * dpr))\n      canvasEl.height = Math.max(1, Math.round(cssH * dpr))\n      if (ctx) ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n    }\n    resizeCanvas()\n    if (typeof window !== 'undefined') d2.on(window, 'resize', resizeCanvas)\n\n    function toScreen(x, y) {\n      const scale = Math.min(cssW / 1600, cssH / 900)\n      const offX = (cssW - 1600 * scale) / 2\n      const offY = (cssH - 900 * scale) / 2\n      return { x: offX + x * scale, y: offY + y * scale, scale: scale }\n    }\n\n    let particles = []\n    let spawnTimer = 0\n    let lastT = null\n    let timeScale = 1\n    let lastFrameParticles = []\n\n    d2.on('.speed-btn', 'click', (e, el) => {\n      timeScale = parseFloat(el.dataset.speed)\n      const all = root.querySelectorAll('.speed-btn')\n      for (let i = 0; i < all.length; i++) all[i].classList.remove('active')\n      el.classList.add('active')\n    })\n    if (detailPanel) {\n      const closeBtn = root.querySelector('.detail-close')\n      if (closeBtn) d2.on(closeBtn, 'click', () => { detailPanel.setAttribute('data-open', 'false') })\n    }\n    if (canvasEl) {\n      d2.on(canvasEl, 'click', (e) => {\n        const rect = canvasEl.getBoundingClientRect()\n        const cx = e.clientX - rect.left\n        const cy = e.clientY - rect.top\n        let best = null, bestDist = 22\n        for (let i = 0; i < lastFrameParticles.length; i++) {\n          const it = lastFrameParticles[i]\n          const dx = it.sx - cx, dy = it.sy - cy\n          const d = Math.sqrt(dx * dx + dy * dy)\n          if (d < bestDist) { bestDist = d; best = it.p }\n        }\n        if (best && detailPanel && detailBody) {\n          detailBody.innerHTML = buildDetailHtml(best)\n          detailPanel.setAttribute('data-open', 'true')\n        }\n      })\n    }\n\n    function drawEdges() {\n      if (!ctx) return\n      ctx.save()\n      ctx.strokeStyle = 'rgba(120,140,150,0.16)'\n      ctx.lineWidth = 1\n      for (let i = 0; i < EDGES.length; i++) {\n        const a = NODE_META[EDGES[i][0]], b = NODE_META[EDGES[i][1]]\n        const pa = toScreen(a.x, a.y), pb = toScreen(b.x, b.y)\n        ctx.beginPath(); ctx.moveTo(pa.x, pa.y); ctx.lineTo(pb.x, pb.y); ctx.stroke()\n      }\n      ctx.restore()\n    }\n\n    function drawNode(id, occCount) {\n      if (!ctx) return\n      const meta = NODE_META[id]\n      const s = toScreen(meta.x, meta.y)\n      const w = 148 * s.scale, h = 38 * s.scale\n      const laneColor = LANE_COLORS[meta.lane] || '#6b7a8a'\n      const intensity = Math.min(1, occCount / 10)\n      ctx.save()\n      ctx.shadowColor = laneColor\n      ctx.shadowBlur = 3 + intensity * 24\n      ctx.fillStyle = 'rgba(16,20,18,0.92)'\n      roundRect(ctx, s.x - w / 2, s.y - h / 2, w, h, 7 * s.scale)\n      ctx.fill()\n      ctx.lineWidth = 1.2 * s.scale + intensity * 2.2\n      ctx.strokeStyle = laneColor\n      ctx.stroke()\n      ctx.restore()\n      ctx.fillStyle = '#dfe6e1'\n      ctx.font = Math.max(9, 9.5 * s.scale) + 'px -apple-system, sans-serif'\n      ctx.textAlign = 'center'\n      ctx.textBaseline = 'middle'\n      ctx.fillText(meta.label, s.x, s.y - 2 * s.scale, w - 8)\n      if (occCount > 0) {\n        ctx.fillStyle = laneColor\n        ctx.font = 'bold ' + Math.max(9, 10.5 * s.scale) + 'px monospace'\n        ctx.fillText(String(occCount), s.x, s.y + h / 2 + 9 * s.scale)\n      }\n    }\n\n    function drawParticles() {\n      if (!ctx) return\n      lastFrameParticles = []\n      for (let i = 0; i < particles.length; i++) {\n        const p = particles[i]\n        const pos = particlePos(p)\n        const s = toScreen(pos.x, pos.y)\n        let rgb = satRgb(p.frozen ? p.frozenSatisfaction : p.satisfaction)\n        if (p.node === 'term_limbo_escritorio') {\n          const dust = Math.min(1, (simDay - p.frozenDay) / 60)\n          rgb = mixRgb(rgb, [112, 112, 114], dust)\n        }\n        const r = 4.5 * s.scale\n        ctx.save()\n        ctx.shadowColor = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'\n        ctx.shadowBlur = 9 * s.scale\n        ctx.fillStyle = 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'\n        ctx.beginPath(); ctx.arc(s.x, s.y, r, 0, Math.PI * 2); ctx.fill()\n        ctx.restore()\n        lastFrameParticles.push({ p: p, sx: s.x, sy: s.y })\n      }\n    }\n\n    function draw() {\n      if (!ctx) return\n      ctx.clearRect(0, 0, cssW, cssH)\n      ctx.fillStyle = '#0b0f0d'\n      ctx.fillRect(0, 0, cssW, cssH)\n      drawEdges()\n      const occ = {}\n      for (let i = 0; i < particles.length; i++) occ[particles[i].node] = (occ[particles[i].node] || 0) + 1\n      const ids = Object.keys(NODE_META)\n      for (let i = 0; i < ids.length; i++) drawNode(ids[i], occ[ids[i]] || 0)\n      drawParticles()\n    }\n\n    function updateHud() {\n      let active = 0, fraud = 0, sumSat = 0, sumCount = 0\n      for (let i = 0; i < particles.length; i++) {\n        const p = particles[i]\n        if (!p.terminal) { active++; sumSat += p.satisfaction; sumCount++ }\n      }\n      if (hud.active) hud.active.textContent = String(active)\n      if (hud.limbo) hud.limbo.textContent = String(totalLimboEver)\n      if (hud.satisfaction) hud.satisfaction.textContent = String(sumCount ? Math.round(sumSat / sumCount) : 100)\n      if (hud.fraud) hud.fraud.textContent = String(totalIrregularEver)\n      if (hud.resolved) hud.resolved.textContent = String(totalResolvedEver)\n      if (hud.simday) hud.simday.textContent = 'dia ' + Math.floor(simDay)\n    }\n\n    function frame(t) {\n      if (lastT == null) lastT = t\n      const dtMs = Math.min(120, t - lastT)\n      lastT = t\n      const dtDays = (dtMs / 1000) * DAYS_PER_SECOND * timeScale\n      simDay += dtDays\n      spawnTimer -= dtDays\n      if (spawnTimer <= 0 && particles.length < MAX_ACTIVE) {\n        spawnTimer = SPAWN_INTERVAL_DAYS * (0.6 + Math.random() * 0.8)\n        particles.push(spawnParticle())\n      }\n      for (let i = 0; i < particles.length; i++) {\n        const p = particles[i]\n        if (!p.frozen) p.satisfaction = Math.max(0, p.satisfaction - (DECAY[p.decayTier] || DECAY.normal) * dtDays)\n        if (!p.terminal && (simDay - p.enterDay) >= p.duration) startNode(p, resolveNext(p._next))\n      }\n      let limboSeen = 0\n      particles = particles.filter((p) => {\n        if (p.node === 'term_limbo_escritorio') {\n          limboSeen++\n          if (limboSeen > LIMBO_RENDER_CAP) return false\n          return true\n        }\n        if (p.terminal) return (simDay - p.frozenDay) < TERMINAL_REST_DAYS\n        return true\n      })\n      draw()\n      updateHud()\n      return true\n    }\n    if (d2 && d2.raf) d2.raf(frame)\n  }\n}\n"}}}}}}}},{"_id":"6a85d0b5b66ce8dd86300ac0","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-troca-sim","package":"granado-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;position:relative;width:100%;height:100%}\n*{box-sizing:border-box;margin:0;padding:0}\n.sim{position:absolute;inset:0;display:grid;grid-template-rows:auto auto 1fr;background:#0a0e0c;color:#d7e6dc;font-family:'Segoe UI',system-ui,-apple-system,sans-serif;overflow:hidden}\nheader{display:flex;align-items:center;gap:18px;padding:9px 16px;background:#0d1411;border-bottom:1px solid #1c2a22}\nheader .ttl{font-size:14px;font-weight:800;letter-spacing:.4px;color:#eaf5ee;white-space:nowrap;line-height:1.2}\nheader .ttl small{display:block;font-size:9px;color:#6f8a7c;font-weight:500;letter-spacing:1px;text-transform:uppercase}\n.stats{display:flex;gap:16px;flex:1;justify-content:center;flex-wrap:wrap}\n.stat{text-align:center;min-width:64px}\n.stat b{display:block;font-size:16px;color:#eaf5ee;font-family:ui-monospace,Menlo,Consolas,monospace;line-height:1.1}\n.stat span{font-size:8.5px;letter-spacing:.6px;text-transform:uppercase;color:#6f8a7c}\n.stat.bad b{color:#ff6b5e}.stat.warn b{color:#ffb454}.stat.good b{color:#4ade80}\n.ctrl{display:flex;gap:7px}\n.ctrl button{background:#152019;border:1px solid #27392e;color:#cfe8d8;border-radius:8px;padding:6px 11px;font-size:12px;cursor:pointer;font-family:inherit;font-weight:600}\n.ctrl button:hover{background:#1e2c22}\n.ctrl button.hot{background:#274332;border-color:#3d6a4d;color:#aef2c9}\n.warn{background:#241b09;border-bottom:1px solid #3d2f12;color:#e8b45a;font-size:10.5px;padding:5px 16px;letter-spacing:.2px}\n.body{display:grid;grid-template-columns:1fr 370px;min-height:0}\n.mapwrap{position:relative;min-width:0}\ncanvas.map{position:absolute;inset:0;width:100%;height:100%;display:block}\n.legend{position:absolute;left:12px;bottom:10px;background:rgba(10,16,12,.85);border:1px solid #1e2c24;border-radius:10px;padding:7px 11px;font-size:9.5px;color:#8fa89a;pointer-events:none;line-height:1.5}\n.legend .grad{width:150px;height:6px;border-radius:3px;background:linear-gradient(90deg,#ff5d52,#ffb454,#ffe084,#7ade8f,#3ddc84);margin:3px 0 1px}\n.legend .lr{display:flex;justify-content:space-between;font-size:8.5px}\n.side{display:grid;grid-template-rows:minmax(0,1fr) minmax(0,1.15fr);border-left:1px solid #1c2a22;background:#0c1210;min-height:0}\n.side h3{font-size:9.5px;letter-spacing:1.2px;text-transform:uppercase;color:#6f8a7c;padding:9px 14px 5px}\n.cases{display:grid;grid-template-rows:auto 1fr;min-height:0;border-bottom:1px solid #1c2a22}\n.list{overflow-y:auto;padding:0 8px 8px}\n.crow{display:flex;align-items:center;gap:9px;padding:6px 8px;border-radius:9px;cursor:pointer;border:1px solid transparent}\n.crow:hover{background:#121a15}\n.crow.sel{background:#14231b;border-color:#2c4636}\n.crow .av{width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:800;color:#07130c;flex:none;background:#3ddc84}\n.crow .meta{flex:1;min-width:0}\n.crow .nm{font-size:11.5px;font-weight:700;color:#e4f1e8;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.crow .pr{font-size:9.5px;color:#7d968a;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.crow .st{font-size:9px;color:#9fb7a9;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.crow .right{text-align:right;flex:none}\n.crow .day{font-size:9.5px;color:#8fa89a;font-family:ui-monospace,Menlo,monospace}\n.crow .bar{width:42px;height:4px;background:#1a251e;border-radius:2px;margin-top:4px;overflow:hidden}\n.crow .bar i{display:block;height:100%;border-radius:2px;background:#3ddc84;width:100%}\n.tabs{display:flex;gap:6px;padding:8px 14px 4px}\n.tab-btn{flex:1;background:#121a15;border:1px solid #223227;color:#8fa89a;border-radius:8px;padding:6px 4px;font-size:10.5px;font-weight:700;cursor:pointer;font-family:inherit}\n.tab-btn.hot{background:#1d5c3c;border-color:#2e8f5b;color:#eafff2}\n.frow{display:flex;align-items:center;gap:9px;padding:8px 8px;border-radius:9px;cursor:pointer;border:1px solid transparent;margin:0 8px}\n.frow:hover{background:#121a15}\n.frow.sel{background:#14231b;border-color:#2c4636}\n.fem{width:30px;height:30px;border-radius:8px;background:#182018;display:flex;align-items:center;justify-content:center;font-size:15px;flex:none}\n.fmeta{flex:1;min-width:0}\n.fnm{font-size:11px;font-weight:700;color:#e4f1e8;line-height:1.35}\n.foc{font-size:9.5px;color:#7d968a}\n.chip{display:inline-block;font-size:8px;letter-spacing:.4px;padding:1.5px 6px;border-radius:99px;background:#1a2a20;color:#9fc4ad;margin:0 4px 3px 0;text-transform:uppercase;font-weight:700}\n.detail{display:grid;grid-template-rows:auto 1fr;min-height:0}\n.story{overflow-y:auto;padding:2px 14px 14px;font-size:12px}\n.story .empty{color:#5d7568;font-size:11px;padding-top:6px;line-height:1.6}\n.shead{display:flex;align-items:center;gap:10px;margin-bottom:6px}\n.shead .av{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:10.5px;color:#07130c;flex:none}\n.shead .em{width:32px;height:32px;border-radius:9px;background:#152019;border:1px solid #27392e;display:flex;align-items:center;justify-content:center;font-size:16px;flex:none}\n.shead .nm{font-weight:800;font-size:13.5px;color:#eef7f0;line-height:1.2}\n.shead .oc{font-size:10px;color:#9fb7a9}\n.now{background:#122019;border:1px solid #24382c;border-radius:9px;padding:7px 10px;font-size:11px;color:#cfe8d8;margin:6px 0 10px;line-height:1.5}\n.now b{color:#fff}\n.walkbar{margin:4px 0 8px}\n.walkbar button{background:#1d5c3c;border:1px solid #2e8f5b;color:#eafff2;border-radius:8px;padding:5px 10px;font-size:10.5px;font-weight:700;cursor:pointer;font-family:inherit}\n.walkbar button:hover{background:#237049}\n.steps{display:flex;gap:5px;margin:8px 0}\n.sdot{width:9px;height:9px;border-radius:50%;background:#1c2a22;border:1px solid #2c4636;display:inline-block}\n.sdot.done{background:#3ddc84;border-color:#3ddc84}\n.sdot.cur{background:#ffd166;border-color:#ffd166;box-shadow:0 0 0 3px rgba(255,209,102,0.25)}\n.stepcard{display:flex;align-items:center;gap:10px;background:#121a15;border:1px solid #223227;border-radius:10px;padding:9px 11px;margin:8px 0}\n.stepcard .em{font-size:22px;width:38px;height:38px;border-radius:9px;background:#182018;display:flex;align-items:center;justify-content:center;flex:none}\n.stitle{font-size:12.5px;font-weight:800;color:#eef7f0}\n.ssub{font-size:9.5px;color:#8fa89a;font-family:ui-monospace,Menlo,monospace}\n.sdecision{background:#122019;border:1px solid #24382c;border-left:3px solid #ffd166;border-radius:8px;padding:8px 11px;margin-bottom:10px}\n.sdecision b{font-size:12px;color:#ffe9b8;display:block;margin-bottom:3px}\n.sdecision p{font-size:11.5px;color:#cfe8d8;line-height:1.55;margin:0}\n.walknav{display:flex;gap:7px;margin-top:4px}\n.walknav button{flex:1;background:#152019;border:1px solid #27392e;color:#cfe8d8;border-radius:8px;padding:8px 6px;font-size:11px;font-weight:700;cursor:pointer;font-family:inherit}\n.walknav button:hover:not(:disabled){background:#1e2c22}\n.walknav button:disabled{opacity:.35;cursor:default}\n.notewrap{margin-top:12px}\n.notelbl{display:block;font-size:9px;letter-spacing:.6px;text-transform:uppercase;color:#6f8a7c;margin-bottom:5px}\n.noteta{width:100%;min-height:70px;background:#121a15;border:1px solid #223227;border-radius:8px;color:#e4f1e8;font-family:inherit;font-size:11.5px;padding:8px 10px;resize:vertical;line-height:1.5}\n.noteta:focus{outline:none;border-color:#3d6a4d}\n.notesov{position:absolute;inset:0;background:rgba(6,10,8,.9);display:flex;align-items:center;justify-content:center;z-index:6}\n.notesov.hidden{display:none}\n.notescard{background:#101a14;border:1px solid #2a4234;border-radius:16px;padding:22px 26px;width:640px;max-width:90%}\n.notescard h2{color:#eef7f0;font-size:16px;margin-bottom:10px}\n.notes-out{width:100%;height:52vh;background:#0e130f;color:#d7e6dc;border:1px solid #24382c;border-radius:10px;padding:12px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:1.6;white-space:pre-wrap;resize:none}\n.notesbtns{display:flex;gap:8px;margin-top:12px}\n.notesbtns button{background:#1d5c3c;border:none;color:#eafff2;border-radius:9px;padding:9px 18px;font-size:12.5px;font-weight:800;cursor:pointer}\n.notesbtns button:hover{background:#237049}\n.notesbtns button.b-notes-close{background:#152019;border:1px solid #27392e}\n.tl{border-left:2px solid #22352a;margin-left:6px;padding-left:14px;display:flex;flex-direction:column;gap:8px;padding-bottom:6px}\n.ev{position:relative;font-size:11px;color:#b9cec1;line-height:1.45}\n.ev:before{content:'';position:absolute;left:-19.5px;top:3px;width:8px;height:8px;border-radius:50%;background:#2f4b3a;border:2px solid #0c1210}\n.ev.last:before{background:#4ade80}\n.ev .d{font-family:ui-monospace,Menlo,monospace;font-size:9px;color:#729183;display:block}\n.resid{margin-top:10px;background:#2a1610;border:1px solid #4a281c;border-radius:8px;padding:7px 10px;font-size:10.5px;color:#ffa694;line-height:1.5}\n.kv{display:flex;gap:8px;margin:8px 0 10px}\n.kv .k{flex:1;background:#121a15;border:1px solid #223227;border-radius:8px;padding:7px 4px;text-align:center}\n.kv .k b{display:block;font-size:15px;color:#eaf5ee;font-family:ui-monospace,Menlo,monospace}\n.kv .k.bad b{color:#ff6b5e}.kv .k.warn b{color:#ffb454}\n.kv .k span{font-size:7.5px;color:#6f8a7c;text-transform:uppercase;letter-spacing:.5px}\n.sec{font-size:9px;letter-spacing:1px;text-transform:uppercase;color:#6f8a7c;margin:10px 0 5px;font-weight:700}\n.plist{display:flex;flex-direction:column;gap:6px}\n.pit{background:#1b1210;border:1px solid #3a221c;border-left:3px solid #c93a2e;border-radius:7px;padding:6px 9px;font-size:10.5px;color:#e8beb5;line-height:1.5}\n.pit .ref{font-family:ui-monospace,Menlo,monospace;font-size:8.5px;color:#b07a6e;margin-left:4px}\n.sit{background:#101b13;border:1px solid #1e3a28;border-left:3px solid #2e8f5b;border-radius:7px;padding:6px 9px;font-size:10.5px;color:#bfe3cc;line-height:1.5}\n.endov{position:absolute;inset:0;background:rgba(6,10,8,.9);display:flex;align-items:center;justify-content:center;z-index:5}\n.endov.hidden{display:none}\n.endov .card{background:#101a14;border:1px solid #2a4234;border-radius:16px;padding:26px 32px;max-width:540px;text-align:center}\n.endov h2{color:#eef7f0;font-size:18px;margin-bottom:10px}\n.endov p{color:#a9c2b2;font-size:12.5px;line-height:1.65;margin-bottom:6px}\n.endov .big{font-size:14px;color:#ffb454;font-weight:700}\n.endov button{margin-top:14px;background:#1d5c3c;border:none;color:#eafff2;border-radius:10px;padding:10px 22px;font-size:13px;font-weight:800;cursor:pointer}\n.endov button:hover{background:#237049}\n`,\n  render(d2){\n    return `<div class=\"sim\">\n      <header>\n        <div class=\"ttl\">SALA DE OPERAÇÕES · TROCAS &amp; DEVOLUÇÕES<small>simulação ao vivo sobre o processo real mapeado</small></div>\n        <div class=\"stats\">\n          <div class=\"stat\"><b class=\"h-dia\">0</b><span>dia</span></div>\n          <div class=\"stat\"><b class=\"h-atv\">0</b><span>casos ativos</span></div>\n          <div class=\"stat good\"><b class=\"h-res\">0</b><span>resolvidos</span></div>\n          <div class=\"stat bad\"><b class=\"h-arm\">0</b><span>no armário</span></div>\n          <div class=\"stat warn\"><b class=\"h-sat\">100</b><span>satisfação média</span></div>\n          <div class=\"stat warn\"><b class=\"h-din\">R$ 0</b><span>esperando estorno</span></div>\n        </div>\n        <div class=\"ctrl\">\n          <button class=\"b-mult\" title=\"volume de pedidos — multiplica os casos pra escancarar os gargalos\">×1</button>\n          <button class=\"b-pause\" title=\"pausar/continuar\">⏸</button>\n          <button class=\"b-speed\" title=\"velocidade\">1×</button>\n          <button class=\"b-restart\" title=\"reiniciar rodada\">↻</button>\n          <button class=\"b-notes\" title=\"ver e copiar anotações salvas\">📋 Notas</button>\n        </div>\n      </header>\n      <div class=\"warn\">⚠️ Casos fictícios e volumes ilustrativos — hoje a operação <b>não tem</b> esse número consolidado de verdade (essa é uma das dores mapeadas). Os caminhos, esperas e regras são os reais, levantados nas reuniões e no relatório de auditoria — mas vêm do time Digital/E-commerce; a perspectiva pura da loja física em si ainda não foi validada com o time de loja.</div>\n      <div class=\"body\">\n        <div class=\"mapwrap\" data-d2-static>\n          <canvas class=\"map\"></canvas>\n          <div class=\"legend\">\n            <div>satisfação do cliente</div>\n            <div class=\"grad\"></div>\n            <div class=\"lr\"><span>no limite</span><span>tranquilo</span></div>\n            <div style=\"margin-top:5px\">🔴 fila grande = gargalo · cinza = esquecido no armário</div>\n            <div><b>clique numa estação</b> → dores e sugestões daquele processo</div>\n            <div>clique numa bolinha ou num caso → a história da pessoa</div>\n            <div>use <b>×1/×5/×20/×50</b> pra multiplicar o volume</div>\n          </div>\n        </div>\n        <aside class=\"side\">\n          <div class=\"cases\">\n            <div class=\"tabs\"><button class=\"tab-btn tab-cli hot\" data-tab=\"cli\">👤 Clientes</button><button class=\"tab-btn tab-flu\" data-tab=\"flu\">🧭 Fluxos</button></div>\n            <div class=\"list\" data-d2-static></div>\n          </div>\n          <div class=\"detail\">\n            <h3>Detalhe — caso ou processo</h3>\n            <div class=\"story\" data-d2-static><div class=\"empty\">Clique em um <b>caso</b> (lista ou bolinha) para acompanhar a história da pessoa, ou em uma <b>estação do mapa</b> para ver o processo por dentro: quantos estão parados ali, as dores conhecidas e as sugestões de melhoria.</div></div>\n          </div>\n        </aside>\n      </div>\n      <div class=\"endov hidden\" data-d2-static>\n        <div class=\"card\">\n          <h2>Fim da rodada</h2>\n          <div class=\"sumry\"></div>\n          <button class=\"b-again\">▶ Rodar de novo</button>\n        </div>\n      </div>\n      <div class=\"notesov hidden\" data-d2-static>\n        <div class=\"notescard\">\n          <h2>📋 Anotações salvas</h2>\n          <textarea class=\"notes-out\" readonly></textarea>\n          <div class=\"notesbtns\"><button class=\"b-notes-copy\">Copiar</button><button class=\"b-notes-close\">Fechar</button></div>\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2){\n    const wrap=d2.$('.mapwrap'), cv=d2.$('canvas.map')\n    if(!wrap||!cv||!cv.getContext)return\n    const ctx=cv.getContext('2d')\n    const T=0.6\n    const ST={\n      casa:{x:.085,y:.42,e:'🏠',n:'Casa do cliente'},\n      site:{x:.085,y:.15,e:'🛒',n:'Site / Checkout'},\n      troque:{x:.30,y:.13,e:'🖥️',n:'Portal Troque'},\n      sac:{x:.30,y:.40,e:'☎️',n:'SAC (chamado)'},\n      correios:{x:.30,y:.67,e:'📦',n:'Transportadora',n2:'/ Correios'},\n      cd:{x:.525,y:.12,e:'🏭',n:'CD Seropédica'},\n      dark:{x:.525,y:.35,e:'🌙',n:'Darkstore RJ·SP'},\n      loja:{x:.525,y:.60,e:'🏬',n:'Loja física'},\n      fabrica:{x:.50,y:.87,e:'🧪',n:'Fábrica /',n2:'Qualidade'},\n      sap:{x:.685,y:.87,e:'🗼',n:'SAP / Fiscal'},\n      fin:{x:.865,y:.87,e:'🧾',n:'Financeiro /',n2:'planilhas manuais'},\n      escritorio:{x:.845,y:.53,e:'🗄️',n:'Escritório do SAC',n2:'(matriz)'},\n      resolvido:{x:.845,y:.13,e:'✅',n:'Resolvidos'}\n    }\n    const STINFO={\n      site:{sobre:'Onde a jornada começa. O checkout decide fulfillment, frete e loja de retirada — e é aqui que vários problemas nascem antes mesmo da troca. Um comportamento que PARECE bug mas não é: carrinho misturando marcas (Granado+Phebo) não retorna loja disponível pra retirada — é esperado, porque nenhuma loja teria estoque das duas marcas prontas pro mesmo picking.',\n        dores:[\n          {t:'A escolha de frete valida o CEP, mas NÃO o estoque do produto específico — 80 unidades sem estoque ainda mostraram frete disponível.',ref:'confirmado ao vivo'},\n          {t:'O cliente nunca sabe de onde o pedido vai sair (CD, Darkstore ou loja) — e é isso que decide como ele vai poder trocar.',ref:'reunião'}],\n        sug:['Página única de entrada: cliente digita o nº do pedido e o sistema roteia sozinho pro caminho certo.','Validar estoque do item + cobertura de entrega juntos, no checkout.']},\n      troque:{sobre:'O portal self-service de trocas. Funciona bem — mas só recebe pedidos que saíram do CD ou da Darkstore.',\n        dores:[\n          {t:'Devoluções de Care, Fidelidade e tipo 3 (retirada/expressa) NÃO podem ser tratadas aqui — caem no SAC manual.',ref:'dor oficial #49'},\n          {t:'Sem integração com a ferramenta de coleta dos Correios — ninguém enxerga a logística reversa em tempo real.',ref:'dor oficial #21'}],\n        sug:['Proposta do próprio time: unificar TODAS as trocas no portal, independente da origem.','Status da reversa em tempo real integrado ao atendimento.']},\n      sac:{sobre:'A porta de entrada manual: e-mail, WhatsApp e telefone. Tudo que não é CD/Darkstore passa por aqui.',\n        dores:[\n          {t:'Solicitações chegam por canais dispersos e manuais — baixa rastreabilidade, risco de duplicidade e de perda.',ref:'dor oficial #5'},\n          {t:'SLA lento de retorno dos Canais Digitais — o SAC precisa cobrar várias vezes.',ref:'dor oficial #23'},\n          {t:'Todo cancelamento/devolução é registrado como \"desistência do cliente\", seja qual for o motivo real.',ref:'dor oficial #10'},\n          {t:'Cliente cobra status todo dia — e cada mensagem gera repasse manual de cobrança.',ref:'reunião'}],\n        sug:['Canal único com status visível pro cliente (ele para de cobrar quando consegue ver).','Registro automático loja→SAC — já em construção pelo time de TI.','Lista real de motivos de devolução no lugar do motivo genérico.']},\n      correios:{sobre:'Coleta e transporte da logística reversa — e o ponto cego da entrega expressa.',\n        dores:[\n          {t:'CEP fora de abrangência falha em silêncio: o pedido fica parado e NINGUÉM é alertado — só quando alguém reclama.',ref:'reunião'},\n          {t:'Extravio na expressa: o prazo de investigação se perde porque a solicitação demora a chegar em quem investiga.',ref:'dor oficial #47'},\n          {t:'A cobertura de CEPs da expressa é uma planilha manual subida pela logística — automatizar já foi tentado e falhou.',ref:'reunião'}],\n        sug:['Alerta automático de pedido sem rota/parado — hoje o silêncio custa 8+ dias.','Comunicar extravio em paralelo pro Digital e pro SAT (fix oficial da dor #47).']},\n      cd:{sobre:'O centro de distribuição. Recebe devoluções do portal Troque, confere, reintegra estoque e dispara estorno.',\n        dores:[\n          {t:'Estornos pendentes vão pro Contas a Receber via planilha manual preenchida pelo CD.',ref:'dor oficial #2'},\n          {t:'Pedido de devolução limitado a 10 linhas (impostos não integram ao JDE) — 30 itens viram 3 pedidos.',ref:'dores #3+#15'},\n          {t:'Notas de devolução rejeitadas com recorrência pela SEFAZ — processo para e vira ajuste manual do Fiscal.',ref:'dores #4/#42'},\n          {t:'Remessa ajustada não integra sozinha ao WMS — alguém entra na transação só pra clicar salvar.',ref:'dor oficial #46'},\n          {t:'A conferência manual dos itens que chegam ao CD pode acumular por MESES antes de ser processada — e quem confere ainda preenche outra planilha própria pra avisar o financeiro.',ref:'gui'}],\n        sug:['Relatório sistêmico de estornos pro Contas a Receber, matando a planilha (fix oficial #2).','Integração SAP↔JDE em andamento — prazo 31/08/2026 (dores #3+#15).']},\n      dark:{sobre:'As darkstores do Rio e de São Paulo — atendem as duas marcas e alimentam a entrega expressa.',\n        dores:[\n          {t:'A entrega expressa sai de Darkstores E de lojas físicas — mas a cobertura depende de planilha manual de CEPs por unidade, e a opção aparece no site mesmo fora do raio real.',ref:'reunião'},\n          {t:'Buraco 1 — retirada nunca retirada: o cliente compra pra buscar na loja e some. Uma rotina diária de BI caça, na mão e loja a loja, pedidos parados há 30+ dias (às vezes é só status errado, corrigido via chamado pro TI).',ref:'reunião'},\n          {t:'Buraco 2 — expressa órfã: pedido feito numa região onde se acreditava ter cobertura, mas por problema de base/CEP nenhum entregador pega. Fica parado sem dono e sem alerta.',ref:'reunião'}],\n        sug:['Alerta automático pros DOIS buracos de pedido parado (não retirado e expressa órfã) — hoje ambos dependem de alguém notar.','Visão consolidada de pedidos parados — hoje ninguém tem o número total.']},\n      loja:{sobre:'A loja física: vende, entrega retiradas, recebe devoluções de volta — mas com as mãos amarradas.',\n        dores:[\n          {t:'A loja NÃO pode aceitar devolução sem autorização do SAC + OK do Digital — mesmo com o cliente parado no balcão.',ref:'dor oficial #22'},\n          {t:'Não existe regra pra estorno de venda em dinheiro — cada loja resolve de um jeito.',ref:'dores #45/#50'},\n          {t:'Estoque físico divergente do sistema faz a loja recusar pedidos de retirada.',ref:'reunião'},\n          {t:'Enquanto espera o item devolvido ser confirmado, a loja vende outra unidade no negativo e \"dá baixa\" depois.',ref:'reunião'},\n          {t:'Cancelamentos pedidos por e-mail informal, sem registro — às vezes desfeitos 2 minutos depois.',ref:'reunião'},\n          {t:'A conexão OMS↔Shopify não mostra pro cliente quando a loja recusou um pedido Care — ele fica sem saber. O cancelamento via OMS+Magento (Granado/Phebo) já é automático e funciona bem.',ref:'gui'}],\n        sug:['Proposta do time: item lacrado + cliente presente = resolver 100% na loja, como \"carteira do cliente\", pagando diferença ali.','Regra padronizada pra devolução em dinheiro (recomendação da auditoria).','Registro automático de toda solicitação da loja (em construção).']},\n      fabrica:{sobre:'Qualidade e análise laboratorial. O único destino que DEVE receber produto de volta: casos de segurança/ANVISA.',\n        dores:[\n          {t:'A baixa por pré-destruição (descarte ecológico) existe no SAP — mas NÃO está plugada ao fluxo de devoluções abertas.',ref:'reunião'}],\n        sug:['Plugar os itens abertos do escritório na baixa por pré-destruição — o processo já existe, falta conectar.']},\n      sap:{sobre:'O coração fiscal. Faturamento, notas de devolução, remessas — e o lugar onde pedidos travam sem explicação.',\n        dores:[\n          {t:'Pedidos travam sem faturar, motivo desconhecido — já houve dia com 150 parados. Destrava-se por e-mail diário + ação manual.',ref:'reunião'},\n          {t:'Na retirada, o travamento esconde o pedido da loja; na expressa, trava a coleta inteira.',ref:'dor oficial #6'},\n          {t:'Mensagens de erro incompreensíveis — ninguém sabe qual área resolve o quê.',ref:'dor oficial #33'},\n          {t:'O sistema deixa concluir com erro — e a loja refaz TUDO na hora de emitir a nota.',ref:'dor oficial #41'},\n          {t:'Referência de notas limitada: usuários usam notas aleatórias com impostos desatualizados.',ref:'dor oficial #16'}],\n        sug:['Alerta automático de falha de faturamento no OMS pras lojas (fix oficial #6).','Trava sistêmica que impeça concluir com inconsistência (fix oficial #41).','Erros com mensagem clara + área responsável nomeada (fix oficial #33).']},\n      fin:{sobre:'Contas a Receber e as planilhas de estorno. É aqui que o dinheiro do cliente espera.',\n        dores:[\n          {t:'DUAS planilhas manuais de estorno em paralelo (SAC + Canais Digitais), sem integração — alto volume diário.',ref:'dores #7/#19'},\n          {t:'Estorno automático SÓ pra Adyen + Granado/Phebo. Care: manual sempre. PagBrasil/PIX parcelado: manual pra todo mundo.',ref:'dor oficial #8'},\n          {t:'Estorno parcial de cartão-presente: 3 semanas pra achar quem sabia fazer.',ref:'caso real'},\n          {t:'Sem conciliação automática — devoluções em aberto inflam a conta Clientes Nacionais.',ref:'dores #18/#20/#48'}],\n        sug:['Estorno automático via integração com as adquirentes, matando as planilhas (fix oficial #7).','Fluxo automatizado independente do meio de pagamento (fix oficial #8).','Antifraude por CPF/score: cliente de baixo risco recebe o reembolso NA HORA, produto volta em paralelo.']},\n      escritorio:{sobre:'O fim errado da linha. Itens abertos devolvidos chegam aqui — e não têm pra onde ir.',\n        dores:[\n          {t:'Itens abertos ficam sem destino definido e sem rastreio — existem \"somente fisicamente\".',ref:'dor oficial #24'},\n          {t:'Pacotes chegam sem identificação (sem nome, pedido ou CPF que bata) — cada um vira uma investigação.',ref:'reunião'},\n          {t:'Não há descarte: 10 meses de operação sem UM descarte formal. Dois armários lotados; picos na Semana Granado.',ref:'reunião'},\n          {t:'Estoque fantasma: a remessa de devolução manda o estoque digital pra loja de origem, com o item aberto parado aqui.',ref:'reunião + #28'}],\n        sug:['Fix oficial #24: item volta pro estoque da loja de origem e segue com os demais pro CD.','Plugar a baixa por pré-destruição da fábrica (o processo já existe).','Etiqueta padronizada de reversa com nº do pedido obrigatório.']},\n      casa:{sobre:'Onde o cliente espera — pelo produto, pela coleta, pelo estorno, por alguma resposta.',\n        dores:[\n          {t:'Pedido \"urgente\" (retirada/expressa) pode levar 1 mês até o estorno cair.',ref:'reunião'},\n          {t:'O dinheiro sai na hora (20 segundos) e volta em até 2 faturas.',ref:'reunião'},\n          {t:'Sem visibilidade de status, o cliente vira o follow-up do processo.',ref:'reunião'}],\n        sug:['Estorno imediato pra cliente de baixo risco (antifraude por score).','Status self-service — o cliente para de cobrar quando consegue ver.']},\n      resolvido:{sobre:'A chegada. Repare na COR de cada bolinha que estaciona aqui: é a satisfação com que o cliente terminou.',\n        dores:[\n          {t:'Muitos casos chegam \"resolvidos\" com o cliente já no limite — e experiência ruim de troca mata a segunda compra (hipótese do time pra recompra baixa).',ref:'reunião'}],\n        sug:['Medir satisfação e tempo de ciclo por rota — hoje nenhum indicador de troca é monitorado.']}\n    }\n    const CASES=[\n      {id:'c1',flowName:'CD/Darkstore → Portal Troque (caminho feliz)',n:'Marina Duarte',ini:'MD',prod:'Polvilho Antisséptico 100g',canal:'Site → entrega em casa (CD)',oc:'Desistência (7 dias)',pay:'Cartão · Adyen',marca:'Granado',val:38,spawn:0,w:5,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou no site, entrega em casa. Pagamento aprovado em 20 segundos.'},\n        {to:'casa',a:1,r:1,s:'produto em casa',note:'Recebeu o pedido. Mudou de ideia — quer devolver (direito de 7 dias).'},\n        {to:'troque',a:3,r:1,s:'no portal Troque',note:'Pedido saiu do CD → tem acesso ao portal de trocas. Subiu fotos, recebeu instruções por e-mail. Autonomia total.'},\n        {to:'correios',a:4.5,r:1,s:'postando a devolução',note:'Postou o produto com o código de postagem reversa.'},\n        {to:'cd',a:6,r:1,s:'em conferência no CD',note:'Chegou ao CD — a conferência é manual, feita por uma pessoa só, com fila grande; às vezes leva meses. Quem confere ainda preenche outra planilha própria pra avisar o financeiro.'},\n        {to:'resolvido',a:7.5,end:'resolved',s:'resolvido, quando dá certo',note:'No financeiro, alguém insere manualmente o estorno na Adyen (cartão/PIX) pra liberar. Quando a fila do CD não trava, é o caminho mais rápido que existe — mas não é bem \"automático\".'}]},\n      {id:'c2',flowName:'Retirada em loja → item lacrado → SAC → loja → SAP → financeiro',n:'Carlos Menezes',ini:'CM',prod:'Água de Colônia Phebo 200ml',canal:'Site → retirada em loja',oc:'Troca (item lacrado)',pay:'Cartão · Adyen',marca:'Phebo',val:129,spawn:0.5,w:3,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou no site pra retirar na loja — queria rápido.'},\n        {to:'loja',a:1.5,r:1,s:'retirando na loja',note:'Retirou o pedido na loja.'},\n        {to:'casa',a:2,r:2,s:'em casa, arrependido',note:'Em casa, percebeu que queria a outra fragrância. O item está lacrado.'},\n        {to:'sac',a:3,r:3,s:'no SAC, descobrindo a burocracia',note:'Foi à loja trocar → a loja NÃO pode aceitar sem autorização do SAC + OK do Digital. Existe até uma forma de fazer a troca dentro do sistema da própria loja, mas esse caminho não é usado — a loja manda o cliente voltar pra casa e abrir chamado.'},\n        {to:'correios',a:5,r:2,s:'postando o item',note:'O SAC mandou uma coleta pra casa dele. Postou o item lacrado.'},\n        {to:'loja',a:7,r:4,s:'loja precisa avisar que chegou…',note:'O item voltou pra LOJA. Agora é o time de Digital/E-commerce que depende de a loja avisar por e-mail que chegou — sem prazo definido, com cobrança manual — pra então dar entrada no estoque e pedir o reembolso pela planilha.'},\n        {to:'sap',a:19,r:3,s:'devolução manual no SAP',note:'12 dias depois a loja confirmou. Devolução lançada manualmente no SAP + nota fiscal de devolução.'},\n        {to:'fin',a:21,r:3,s:'na planilha do financeiro',note:'Estorno pedido via planilha manual pro financeiro.'},\n        {to:'resolvido',a:24,end:'resolved',s:'resolvido em 24 dias',note:'Estorno concluído. Uma troca simples de fragrância levou 24 dias.'}]},\n      {id:'c3',flowName:'Retirada em loja → item aberto → escritório (armário)',n:'Fernanda Lima',ini:'FL',prod:'Kit Sabonetes Glicerina',canal:'Site → retirada em loja',oc:'Devolução (item aberto)',pay:'PIX · Adyen',marca:'Granado',val:54,spawn:1,w:2,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou o kit pra retirar na loja.'},\n        {to:'loja',a:1,r:1,s:'retirando na loja',note:'Retirou o pedido.'},\n        {to:'casa',a:1.6,r:2,s:'em casa, não gostou',note:'Abriu em casa e não gostou do cheiro. Item aberto = não pode voltar pra loja.'},\n        {to:'sac',a:2.5,r:3,s:'no SAC',note:'Abriu chamado. Item aberto vai pro ESCRITÓRIO do SAC, não pra loja.'},\n        {to:'correios',a:4.5,r:2,s:'postando o item',note:'Postou o kit pela coleta.'},\n        {to:'escritorio',a:7,end:'limbo',r:5,s:'no escritório, sem identificação',note:'O pacote chegou no escritório SEM identificação clara — 4 dias só pra descobrir de qual pedido era.'}],\n        ev:[{d:11,note:'Item identificado. Estorno solicitado ao financeiro (planilha manual).'},{d:26,note:'Estorno finalmente cai pra cliente — 25 dias depois da compra.'}],\n        refund:26,resid:'O kit continua no armário do escritório — e é DE PROPÓSITO: se alguém lançasse a devolução no SAP, o sistema creditaria o estoque da loja mesmo o produto não estando lá (estoque fantasma). Pra evitar isso, o time simplesmente não processa — então o item nunca sai do armário (dor oficial #24).'},\n      {id:'c4',flowName:'Entrega expressa → CEP fora de abrangência',n:'Roberto Salles',ini:'RS',prod:'Óleo de Barba Granado',canal:'Site → entrega expressa (RJ)',oc:'CEP fora de abrangência',pay:'Cartão-presente',marca:'Granado',val:94,spawn:1.5,w:1,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Pagou R$ 18 de entrega expressa pra receber em 1 dia. Pagamento: cartão-presente de R$ 94.'},\n        {to:'dark',a:0.8,r:1,s:'separado na Darkstore',note:'Pedido separado e despachado pela Darkstore.'},\n        {to:'correios',a:1.4,r:6,s:'PARADO — fora de abrangência',note:'O CEP dele estava fora do raio real de entrega (transportadora EuEntrego) — mas o site deixou comprar. Podia ser isso, ou faturamento pendente no SAP por tributos — causas diferentes levam ao mesmo sintoma: pedido parado, SEM ALERTA pra ninguém.'},\n        {to:'sac',a:9.5,r:4,s:'no SAC, depois de reclamar',note:'8 dias depois, ELE teve que reclamar pra alguém descobrir o problema — esse padrão de \"só descobre quem reclama\" também acontece com retirada em loja, não só expressa. Opções dadas: retirar na loja ou cancelar.'},\n        {to:'fin',a:13,r:5,s:'ninguém sabe estornar parcial…',note:'Pediu o estorno do frete (R$ 18). Ninguém sabia quem processava estorno PARCIAL de cartão-presente.'},\n        {to:'resolvido',a:21,end:'resolved',s:'resolvido em 21 dias',note:'3 semanas pra devolver R$ 18. Caso real relatado pela operação.'}]},\n      {id:'c5',flowName:'Retirada em loja → loja recusa (marca Care) → estorno manual',n:'Ana Beatriz Rocha',ini:'AB',prod:'Hidratante Care 250ml',canal:'Site → retirada em loja',oc:'Loja recusou (sem estoque)',pay:'Cartão · Adyen',marca:'Care',val:89,spawn:2,w:2,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou um produto Care pra retirar na loja.'},\n        {to:'loja',a:1.2,r:2,s:'pedido recusado pela loja',note:'A loja recusou: o estoque físico não batia com o sistema.'},\n        {to:'sac',a:2.5,r:4,s:'esperando estorno manual (Care)',note:'A conexão OMS↔Shopify nem mostra pro cliente que a loja recusou — ele fica sem saber. E o estorno é 100% manual: alguém do time Digital precisa atualizar o Shopify, lançar a devolução no SAP e preencher a planilha do financeiro. Pra Granado/Phebo via Magento, esse cancelamento já é automático e funciona bem.'},\n        {to:'fin',a:6,r:4,s:'na fila do financeiro',note:'Alguém precisa ir manualmente devolver o valor.'},\n        {to:'resolvido',a:15,end:'resolved',s:'resolvido em 15 dias',note:'Estorno concluído — 15 dias pra um pedido que a própria loja recusou.'}]},\n      {id:'c6',flowName:'Carrinho multimarca → retirada indisponível (comportamento correto, não é bug)',n:'Juliana Castro',ini:'JC',prod:'Sabonete Granado + Colônia Phebo',canal:'Site → carrinho misto',oc:'Retirada indisponível (esperado, não é bug)',pay:'Cartão · Adyen',marca:'Granado+Phebo',val:97,spawn:2.5,w:1,legs:[\n        {to:'site',a:0,r:3,s:'tentando retirar em loja…',note:'Misturou Granado e Phebo no carrinho → nenhuma loja aparece pra retirada. Isso é CORRETO, não é bug: nenhuma loja teria estoque das duas marcas prontas pro mesmo picking. Pagou entrega comum em vez disso.'},\n        {to:'cd',a:1.8,r:1,s:'saindo do CD',note:'Pedido saiu do CD como entrega comum.'},\n        {to:'casa',a:3.5,r:2,s:'recebeu, mas desistiu',note:'Recebeu dias depois do que queria. Desistiu da compra.'},\n        {to:'troque',a:5,r:1,s:'no portal Troque',note:'Como saiu do CD, pôde usar o portal.'},\n        {to:'correios',a:6.5,r:1,s:'postando devolução',note:'Postou os produtos.'},\n        {to:'cd',a:8,r:1,s:'em conferência no CD',note:'Chegou e foi conferido.'},\n        {to:'resolvido',a:9.5,end:'resolved',s:'resolvido em 9 dias',note:'Estorno automático. Mas a venda foi perdida por um bug de carrinho.'}]},\n      {id:'c7',flowName:'Compra presencial → troca no balcão',n:'Pedro Álvares',ini:'PA',prod:'Sabonete Bebê Granado',canal:'Compra presencial na loja',oc:'Troca no balcão',pay:'Dinheiro',marca:'Granado',val:22,spawn:3,w:4,legs:[\n        {to:'loja',a:0,r:0.5,s:'comprando na loja',note:'Comprou direto na loja física.'},\n        {to:'casa',a:0.4,r:0.5,s:'em casa',note:'Em casa, viu que era a versão errada.'},\n        {to:'loja',a:1,r:0.5,s:'trocando no balcão',note:'Voltou com o cupom fiscal, item lacrado, dentro dos 30 dias.'},\n        {to:'resolvido',a:1.5,end:'resolved',s:'trocado na hora',note:'Troca no balcão em 30 minutos. Presencial puro funciona.'}]},\n      {id:'c8',flowName:'Retirada em loja → nunca retirada → cancelamento em 7 dias',n:'Tereza Nunes',ini:'TN',prod:'Polvilho Antisséptico 100g',canal:'Site → retirada em loja',oc:'Nunca retirou',pay:'PIX · Adyen',marca:'Granado',val:38,spawn:3.5,w:2,legs:[\n        {to:'site',a:0,r:0.5,s:'comprando no site',note:'Comprou pra retirar em loja.'},\n        {to:'loja',a:1,r:0.5,s:'aguardando retirada (7 dias?)',note:'Pedido pronto na loja. Ela nunca apareceu. O aviso de 7 dias é só isso — um AVISO; não existe cancelamento automático.'},\n        {to:'resolvido',a:30,end:'gone',s:'cancelado ~30 dias depois',note:'Não tem nada de automático: a gerente da Darkstore, informalmente, avisa o time de Digital depois de ~30 dias — daí em diante é tudo manual (SAP + planilha do financeiro).'}]},\n      {id:'c9',flowName:'Retirada em loja → pedido travado no fiscal (SAP sem faturar)',n:'Marcos Vinícius',ini:'MV',prod:'Perfume Encanto Phebo',canal:'Site → retirada em loja',oc:'Pedido travado no fiscal',pay:'PIX · Adyen',marca:'Phebo',val:159,spawn:4,w:2,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Pagamento aprovado em 20 segundos.'},\n        {to:'sap',a:0.5,r:4,s:'TRAVADO no SAP sem faturar',note:'O pedido travou no SAP sem faturar — motivo desconhecido até pra operação. A LOJA SABE, mas não pode liberar oficialmente sem a fatura. Às vezes o cliente já pagou e passa pra buscar mesmo assim — a loja entrega informalmente, sem nota fiscal (mesmo risco de contestação/chargeback já visto em outro cenário). Já houve dia com 150 pedidos travados assim.'},\n        {to:'loja',a:6.5,r:2,s:'finalmente preparado',note:'Alguém do Fiscal forçou o faturamento na mão (rotina diária de e-mail). Só então a loja preparou o pedido.'},\n        {to:'resolvido',a:8,end:'resolved',s:'retirou por volta do 8º dia',note:'Retirou por volta do 8º dia um pedido que era pra ser rápido. Ele foi à loja no dia 1 e não tinha nada lá.'}]},\n      {id:'c10',flowName:'Presente → cupom de troca → troca na loja',n:'Luiza Prado',ini:'LP',prod:'Kit Presente Phebo',canal:'Ganhou de presente',oc:'Troca com Cupom de Troca',pay:'—',marca:'Phebo',val:115,spawn:4.5,w:2,legs:[\n        {to:'casa',a:0,r:0.5,s:'ganhou de presente 🎁',note:'Ganhou o kit de presente — quem comprou pediu o Cupom de Troca na loja.'},\n        {to:'loja',a:1.2,r:0.5,s:'trocando na loja',note:'Foi à loja com o Cupom de Troca, dentro dos 30 dias, item lacrado.'},\n        {to:'resolvido',a:2,end:'resolved',s:'trocado sem atrito',note:'Trocou por outro produto na hora. O cupom de troca do presente funciona.'}]},\n      {id:'c11',flowName:'CD → Portal Troque → extravio na devolução',n:'Ricardo Faria',ini:'RF',prod:'Colônia Vetiver Granado',canal:'Site → entrega em casa (CD)',oc:'Extravio na devolução',pay:'Cartão · Adyen',marca:'Granado',val:142,spawn:5,w:1,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou no site, entrega em casa.'},\n        {to:'casa',a:1.5,r:1,s:'produto em casa',note:'Recebeu, quis devolver.'},\n        {to:'troque',a:3,r:1,s:'no portal Troque',note:'Abriu a devolução pelo portal, tudo certo.'},\n        {to:'correios',a:4.5,r:4,s:'pacote EXTRAVIADO',note:'Postou… e o pacote extraviou no caminho de volta.'},\n        {to:'sac',a:9,r:2,s:'extravio confirmado',note:'Extravio confirmado. O estorno foi feito — mas o pedido NÃO pode ser cancelado no SAP: fica uma venda aberta pra sempre, sem contrapartida da adquirente.'},\n        {to:'resolvido',a:12,end:'resolved',s:'resolvido (com resíduo)',note:'Cliente estornado em 12 dias.'}],\n        resid:'Resíduo interno: a venda segue ABERTA no SAP pra sempre — infla a conta Clientes Nacionais (dor oficial #25).'},\n      {id:'c12',flowName:'Fidelidade (Loyalty) → resgate recusado → pontos não devolvidos',n:'Sofia Almeida',ini:'SA',prod:'Resgate: Colônia (1.400 pts)',canal:'Clube Fidelidade (site separado)',oc:'Pontos não devolvidos',pay:'Pontos fidelidade',marca:'Granado',val:0,spawn:5.5,w:1,legs:[\n        {to:'site',a:0,r:1,s:'resgatando pontos',note:'Resgatou 1.400 pontos no site do Clube (que é separado do e-commerce).'},\n        {to:'loja',a:1.2,r:3,s:'resgate recusado pela loja',note:'A loja recusou — sem estoque do item.'},\n        {to:'casa',a:2,r:5,s:'esperando os pontos voltarem…',note:'Os pontos NÃO voltam automaticamente. O CRM nem fica sabendo do cancelamento (dor oficial #27). Ela espera. Nada acontece.'},\n        {to:'sac',a:10.5,r:3,s:'abriu chamado ela mesma',note:'8 dias depois, ELA abriu chamado no SAC perguntando pelos pontos.'},\n        {to:'fin',a:13,r:3,s:'devolução manual dos pontos',note:'O CRM devolve os pontos manualmente.'},\n        {to:'resolvido',a:18,end:'resolved',s:'pontos de volta em 18 dias',note:'18 dias pra devolver pontos de um resgate que a própria loja recusou.'}]},\n      {id:'c13',flowName:'Entrega expressa → defeito de qualidade → análise laboratorial',n:'Henrique Dias',ini:'HD',prod:'Sabonete Lavanda (defeito)',canal:'Site → entrega expressa (SP)',oc:'Defeito de qualidade',pay:'Cartão · Adyen',marca:'Granado',val:18,spawn:6,w:1,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou com entrega expressa em SP.'},\n        {to:'dark',a:0.8,r:1,s:'saindo da Darkstore',note:'Despachado pela Darkstore de SP.'},\n        {to:'casa',a:1.5,r:2,s:'recebeu com defeito',note:'Recebeu no prazo — mas o produto veio com corpo estranho. Caso de qualidade/segurança.'},\n        {to:'sac',a:2.5,r:2,s:'no SAC (caso de qualidade)',note:'Abriu chamado. Caso de qualidade é o ÚNICO tipo que DEVE voltar fisicamente — vai pra análise.'},\n        {to:'correios',a:4,r:1,s:'postando pra análise',note:'Postou o item.'},\n        {to:'fabrica',a:6,r:2,s:'em análise na fábrica',note:'Análise laboratorial (protocolo ANVISA).'},\n        {to:'resolvido',a:9,end:'resolved',s:'reposição enviada',note:'Reposição enviada em 9 dias. O fluxo de qualidade existe e roda.'}]},\n      {id:'c14',flowName:'Retirada em loja → item aberto → escritório (2ª variação, pico de campanha)',n:'Camila Torres',ini:'CT',prod:'Colônia Encanto (aberta)',canal:'Site → retirada em loja',oc:'Devolução (item aberto)',pay:'Cartão · Adyen',marca:'Phebo',val:139,spawn:7,w:2,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou pra retirar na loja.'},\n        {to:'loja',a:1.2,r:1,s:'retirando na loja',note:'Retirou o pedido.'},\n        {to:'casa',a:2,r:2,s:'abriu e enjoou do perfume',note:'Usou uma vez e enjoou. Item aberto.'},\n        {to:'sac',a:3,r:3,s:'no SAC',note:'Chamado aberto. Item aberto → escritório.'},\n        {to:'correios',a:5,r:2,s:'postando o item',note:'Postou pela coleta.'},\n        {to:'escritorio',a:9.5,end:'limbo',r:5,s:'no escritório, na pilha',note:'Chegou junto com a pilha de devoluções da Semana Granado — sacos de correio se acumulando.'}],\n        ev:[{d:20,note:'Estorno sai depois de cobrança. O frasco fica no armário, de propósito: lançar a devolução no SAP creditaria o estoque errado da loja, então ninguém processa.'}],\n        refund:20,resid:'Mais um frasco no armário. Relato real: 10 meses de trabalho no time e nenhum descarte formal aconteceu.'},\n      {id:'c15',flowName:'Entrega expressa → EuEntrego marca como entregue mas extraviou → SAC',n:'Bianca Freitas',ini:'BF',prod:'Sabonete em Barra Granado (kit 3un)',canal:'Site → entrega expressa (SP)',oc:'Extravio disfarçado de entrega',pay:'Cartão · Adyen',marca:'Granado',val:47,spawn:7.5,w:2,legs:[\n        {to:'site',a:0,r:1,s:'comprando no site',note:'Comprou com entrega expressa em SP, esperando receber rápido.'},\n        {to:'dark',a:0.8,r:1,s:'saindo da Darkstore',note:'Pedido separado e despachado pela Darkstore.'},\n        {to:'correios',a:1.4,r:2,s:'a caminho pela EuEntrego',note:'Saiu pra entrega com a transportadora terceirizada EuEntrego.'},\n        {to:'sac',a:3,r:3,s:'marcado como entregue, mas nunca chegou',note:'A EuEntrego deu baixa como \"entregue\" no sistema — mas o produto nunca chegou na casa do cliente. Ele abre um chamado no SAC contestando a entrega.'},\n        {to:'resolvido',a:10,end:'resolved',s:'resolvido, com resíduo',note:'Extravio confirmado depois de investigação. Cliente estornado.'}],\n        resid:'Mesma dor oficial #25: pedido extraviado não pode ser cancelado no SAP — só estornado, ficando uma venda aberta pra sempre, sem contrapartida da adquirente.'}\n    ]\n    const FLOWS=CASES.map(c=>({id:c.id,name:c.flowName,oc:c.oc,legs:c.legs,resid:c.resid}))\n    const S={t:0,mult:1,speed:1,paused:false,tab:'cli',selCase:null,selSt:null,walk:null,cw:0,ch:0,tray:0,armTotal:0,totRes:0,totGone:0,hitList:[],stRects:{},peak:{},lastDw:{}}\n    d2._sim=S\n    S.resize=function(){\n      const w=wrap.clientWidth,h=wrap.clientHeight,dpr=window.devicePixelRatio||1\n      if(!w||!h)return\n      S.cw=w;S.ch=h\n      cv.width=Math.round(w*dpr);cv.height=Math.round(h*dpr)\n      ctx.setTransform(dpr,0,0,dpr,0,0)\n    }\n    function mkRT(c,spawn,scale){\n      const L=c.legs.map(l=>({...l,A:spawn+l.a*scale}))\n      const segs=[]\n      for(let i=0;i<L.length;i++){\n        if(i>0)segs.push({t0:L[i].A-T,t1:L[i].A,rate:1})\n        const dep=i<L.length-1?L[i+1].A-T:L[i].A\n        if(dep>L[i].A)segs.push({t0:L[i].A,t1:dep,rate:L[i].r||1})\n      }\n      const last=L[L.length-1]\n      return{c,L,segs,termT:last.A,termType:last.end||'resolved',refundT:c.refund!=null?spawn+c.refund*scale:last.A,parkIdx:null,armSlot:null,counted:false,lingerT:null,val:c.val}\n    }\n    const RT={}\n    let GH=[]\n    function buildRT(){for(const c of CASES)RT[c.id]=mkRT(c,c.spawn,1)}\n    function buildGhosts(){\n      GH=[]\n      if(S.mult<=1)return\n      const Wsum=CASES.reduce((a,c)=>a+c.w,0)\n      const total=Math.min(500,(S.mult-1)*CASES.length)\n      for(const c of CASES){\n        const n=Math.round(total*c.w/Wsum)\n        for(let i=0;i<n;i++){\n          GH.push(mkRT(c,S.t+Math.random()*9,0.8+Math.random()*0.5))\n        }\n      }\n    }\n    buildRT();buildGhosts()\n    function allUnits(){const u=[];for(const c of CASES)u.push(RT[c.id]);for(const g of GH)u.push(g);return u}\n    function resetRT(){\n      S.tray=0;S.armTotal=0;S.totRes=0;S.totGone=0;S.peak={}\n      buildRT();buildGhosts()\n    }\n    function respawnInPlace(rt,base){\n      const nr=mkRT(rt.c,base,0.85+Math.random()*0.35)\n      rt.L=nr.L;rt.segs=nr.segs;rt.termT=nr.termT;rt.termType=nr.termType;rt.refundT=nr.refundT\n      rt.parkIdx=null;rt.armSlot=null;rt.counted=false;rt.lingerT=null\n    }\n    function book(t){\n      for(const rt of allUnits()){\n        if(!rt.counted&&t>=rt.termT){\n          rt.counted=true\n          if(rt.termType==='resolved'){S.totRes++;if(rt.parkIdx==null)rt.parkIdx=S.tray++}\n          else if(rt.termType==='gone')S.totGone++\n          else if(rt.termType==='limbo'&&rt.armSlot==null){rt.armSlot=S.armTotal++}\n          rt.lingerT=rt.termT+(rt.termType==='limbo'?10+Math.random()*8:5+Math.random()*6)\n        }\n        if(rt.counted&&rt.lingerT!=null&&t>=rt.lingerT){\n          if(rt.c&&RT[rt.c.id]===rt&&S.selCase===rt.c.id)continue\n          respawnInPlace(rt,t+0.5+Math.random()*4)\n        }\n      }\n    }\n    function satAt(rt,t){\n      if(t<rt.L[0].A)return 100\n      let cap=t\n      if(t>rt.termT&&rt.termType!=='limbo')cap=rt.termT\n      let s=100\n      for(const g of rt.segs){if(cap<=g.t0)break;s-=g.rate*(Math.min(cap,g.t1)-g.t0)}\n      if(rt.termType==='limbo'&&t>rt.termT)s-=3.5*(t-rt.termT)\n      return Math.max(6,Math.min(100,s))\n    }\n    function satColor(s){\n      const h=4+(s/100)*141\n      return 'hsl('+h.toFixed(0)+',72%,'+(46+s*0.10).toFixed(0)+'%)'\n    }\n    function whereAt(rt,t){\n      const L=rt.L\n      if(t<L[0].A)return null\n      if(t>=rt.termT)return{mode:rt.termType}\n      for(let i=0;i<L.length;i++){\n        const dep=i<L.length-1?L[i+1].A-T:Infinity\n        if(t>=L[i].A&&t<dep)return{mode:'dwell',st:L[i].to,leg:i}\n        if(i<L.length-1&&t>=dep&&t<L[i+1].A)return{mode:'travel',from:L[i].to,to:L[i+1].to,u:(t-dep)/T,leg:i+1}\n      }\n      return{mode:rt.termType}\n    }\n    function P(st){return{x:ST[st].x*S.cw,y:ST[st].y*S.ch}}\n    function curvePt(a,b,u){\n      const A=P(a),B=P(b)\n      const mx=(A.x+B.x)/2,my=(A.y+B.y)/2\n      const dx=B.x-A.x,dy=B.y-A.y,len=Math.hypot(dx,dy)||1\n      const sgn=(a<b?1:-1)\n      const cx=mx+(-dy/len)*26*sgn,cy=my+(dx/len)*26*sgn\n      const v=1-u\n      return{x:v*v*A.x+2*v*u*cx+u*u*B.x,y:v*v*A.y+2*v*u*cy+u*u*B.y}\n    }\n    function ease(u){return u<0.5?2*u*u:1-Math.pow(-2*u+2,2)/2}\n    function rr(x,y,w,h,r){ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath()}\n    const roads=[]\n    ;(function(){const seen={};for(const c of CASES){const L=c.legs;for(let i=1;i<L.length;i++){const k=L[i-1].to+'>'+L[i].to;if(!seen[k]&&L[i-1].to!==L[i].to){seen[k]=1;roads.push([L[i-1].to,L[i].to])}}}})()\n    const list=d2.$('.list')\n    const rows={}\n    function renderList(){\n      if(S.tab==='flu'){\n        list.innerHTML=FLOWS.map(f=>'<div class=\"frow\" data-fid=\"'+f.id+'\">'\n          +'<div class=\"fem\">'+ST[f.legs[0].to].e+'</div>'\n          +'<div class=\"fmeta\"><div class=\"fnm\">'+f.name+'</div><div class=\"foc\">'+f.oc+'</div></div>'\n          +'</div>').join('')\n      }else{\n        list.innerHTML=CASES.map(c=>'<div class=\"crow\" data-cid=\"'+c.id+'\">'\n          +'<div class=\"av\">'+c.ini+'</div>'\n          +'<div class=\"meta\"><div class=\"nm\">'+c.n+'</div><div class=\"pr\">'+c.prod+'</div><div class=\"st\">—</div></div>'\n          +'<div class=\"right\"><div class=\"day\">—</div><div class=\"bar\"><i></i></div></div>'\n          +'</div>').join('')\n        d2.$$('.crow').forEach(r=>{rows[r.getAttribute('data-cid')]=r})\n      }\n    }\n    renderList()\n    function draw(t){\n      const w=S.cw,h=S.ch\n      if(!w||!h)return\n      ctx.clearRect(0,0,w,h)\n      ctx.fillStyle='#0a0e0c';ctx.fillRect(0,0,w,h)\n      ctx.fillStyle='rgba(255,255,255,0.028)'\n      for(let gx=14;gx<w;gx+=30)for(let gy=14;gy<h;gy+=30){ctx.fillRect(gx,gy,1.5,1.5)}\n      ctx.strokeStyle='rgba(126,220,170,0.07)';ctx.lineWidth=1\n      ;[.195,.41,.70].forEach(fx=>{ctx.beginPath();ctx.moveTo(fx*w,26);ctx.lineTo(fx*w,h*.74);ctx.stroke()})\n      ctx.beginPath();ctx.moveTo(20,h*.765);ctx.lineTo(w-20,h*.765);ctx.stroke()\n      ctx.font='700 9px ui-monospace,Menlo,monospace';ctx.textAlign='center'\n      ctx.fillStyle='rgba(143,168,154,0.5)'\n      ctx.fillText('CLIENTE',.085*w,20);ctx.fillText('ATENDIMENTO',.30*w,20)\n      ctx.fillText('LOGÍSTICA & LOJAS',.525*w,20);ctx.fillText('DESFECHO',.845*w,20)\n      ctx.fillText('BASTIDORES — FISCAL · FINANCEIRO · FÁBRICA',.5*w,h*.765+14)\n      ctx.strokeStyle='rgba(126,220,170,0.075)';ctx.lineWidth=1.5\n      for(const rd of roads){ctx.beginPath();const A=P(rd[0]);ctx.moveTo(A.x,A.y);for(let u=0.05;u<=1.001;u+=0.05){const q=curvePt(rd[0],rd[1],u);ctx.lineTo(q.x,q.y)}ctx.stroke()}\n      if(S.selCase&&RT[S.selCase]){\n        const rtSel=RT[S.selCase],cSel=rtSel.c\n        const walking=S.walk&&S.walk.id===S.selCase\n        const L2=walking?cSel.legs:rtSel.L\n        ctx.save()\n        for(let i=1;i<L2.length;i++){\n          const pa=L2[i-1].to,pb=L2[i].to\n          if(pa===pb)continue\n          const past=walking?(i<=S.walk.idx):(t>=L2[i].A)\n          ctx.strokeStyle=past?'rgba(255,255,255,0.55)':'rgba(255,255,255,0.20)'\n          ctx.lineWidth=past?2.2:1.3\n          ctx.setLineDash(past?[]:[5,5])\n          ctx.beginPath();const A2=P(pa);ctx.moveTo(A2.x,A2.y)\n          for(let u=0.05;u<=1.001;u+=0.05){const q=curvePt(pa,pb,u);ctx.lineTo(q.x,q.y)}\n          ctx.stroke()\n        }\n        ctx.setLineDash([])\n        const seenSt={}\n        for(let i=0;i<L2.length;i++){\n          const l=L2[i]\n          if(seenSt[l.to])continue;seenSt[l.to]=1\n          const p2=P(l.to),vis=walking?(i<=S.walk.idx):(t>=l.A)\n          ctx.strokeStyle=vis?'rgba(255,255,255,0.5)':'rgba(255,255,255,0.16)'\n          ctx.lineWidth=1.2\n          rr(p2.x-63,p2.y-26,126,52,12);ctx.stroke()\n        }\n        ctx.restore()\n      }\n      const units=allUnits()\n      const dw={}\n      const locs=new Map()\n      for(const rt of units){\n        const loc=whereAt(rt,t)\n        if(!loc)continue\n        locs.set(rt,loc)\n        if(loc.mode==='dwell'){(dw[loc.st]=dw[loc.st]||[]).push(rt)}\n      }\n      S.lastDw=dw\n      S.stRects={}\n      for(const id in ST){\n        const st=ST[id],x=st.x*w,y=st.y*h,cw2=118,ch2=44\n        const q=(dw[id]||[]).length\n        S.peak[id]=Math.max(S.peak[id]||0,q)\n        S.stRects[id]={x:x-cw2/2,y:y-ch2/2,w:cw2,h:ch2}\n        ctx.save()\n        if(q>=8){ctx.shadowColor='rgba(255,80,60,0.85)';ctx.shadowBlur=26}\n        else if(q>=4){ctx.shadowColor='rgba(255,80,60,0.7)';ctx.shadowBlur=18}\n        else if(q>=2){ctx.shadowColor='rgba(255,180,84,0.4)';ctx.shadowBlur=12}\n        ctx.fillStyle='#121a15'\n        rr(x-cw2/2,y-ch2/2,cw2,ch2,10);ctx.fill()\n        ctx.restore()\n        ctx.strokeStyle=S.selSt===id?'#fff':q>=4?'rgba(255,107,94,0.65)':'rgba(126,220,170,0.22)'\n        ctx.lineWidth=S.selSt===id?1.8:1\n        rr(x-cw2/2,y-ch2/2,cw2,ch2,10);ctx.stroke()\n        ctx.font='16px sans-serif';ctx.textAlign='left';ctx.fillStyle='#fff'\n        ctx.fillText(st.e,x-cw2/2+9,y+6)\n        ctx.font='600 9.5px ui-monospace,Menlo,monospace';ctx.fillStyle='#cfe8d8'\n        if(st.n2){ctx.fillText(st.n,x-cw2/2+32,y-1);ctx.fillText(st.n2,x-cw2/2+32,y+10)}\n        else ctx.fillText(st.n,x-cw2/2+32,y+4)\n        ctx.font='600 8px ui-monospace,Menlo,monospace';ctx.fillStyle='rgba(143,168,154,0.55)'\n        ctx.fillText('ⓘ',x+cw2/2-11,y+ch2/2-5)\n        if(q>0){\n          const br=q>99?11:9\n          ctx.beginPath();ctx.arc(x+cw2/2-4,y-ch2/2+4,br,0,7)\n          ctx.fillStyle=q>=8?'#e03123':q>=4?'#c93a2e':q>=2?'#9a6c1f':'#2c4636';ctx.fill()\n          ctx.font='700 '+(q>99?'8':'9')+'px ui-monospace,Menlo,monospace';ctx.textAlign='center';ctx.fillStyle='#fff'\n          ctx.fillText(''+q,x+cw2/2-4,y-ch2/2+7)\n        }\n      }\n      const eo=P('escritorio')\n      const armN=S.armTotal\n      ctx.font='700 8.5px ui-monospace,Menlo,monospace';ctx.textAlign='left'\n      ctx.fillStyle='rgba(255,138,122,0.85)'\n      ctx.fillText('no armário: '+armN+' (acumulado)',eo.x-59,eo.y+36)\n      for(let i=0;i<Math.min(armN,24);i++){\n        const col=i%8,row=Math.floor(i/8)\n        ctx.fillStyle='rgba(105,120,110,0.55)'\n        rr(eo.x-59+col*14,eo.y+42+row*13,11,10,2);ctx.fill()\n      }\n      if(armN>24){ctx.fillStyle='rgba(255,138,122,0.7)';ctx.fillText('+'+(armN-24)+' na pilha…',eo.x-59,eo.y+42+3*13+10)}\n      const rv=P('resolvido')\n      ctx.fillStyle='rgba(95,227,154,0.8)'\n      ctx.fillText('finalizados: '+S.totRes,rv.x-59,rv.y+34)\n      S.hitList.length=0\n      // ghosts first (small, under heroes)\n      for(const rt of GH){\n        const loc=locs.get(rt)\n        if(!loc)continue\n        let px,py,col\n        if(loc.mode==='dwell'){\n          const arr=dw[loc.st],i=arr.indexOf(rt)\n          if(i>44)continue\n          const b=P(loc.st)\n          px=b.x-45+(i%10)*10;py=b.y+32+Math.floor(i/10)*10\n          col=satColor(satAt(rt,t))\n        }else if(loc.mode==='travel'){\n          const q=curvePt(loc.from,loc.to,ease(Math.min(1,loc.u)));px=q.x;py=q.y\n          col=satColor(satAt(rt,t))\n        }else if(loc.mode==='resolved'){\n          if(rt.parkIdx==null){rt.parkIdx=S.tray++}\n          const sl=rt.parkIdx%21\n          px=rv.x-52+(sl%7)*19;py=rv.y+46+Math.floor(sl/7)*19\n          col=satColor(satAt(rt,rt.termT))\n        }else if(loc.mode==='limbo'){\n          if(rt.armSlot==null){rt.armSlot=S.armTotal++}\n          continue\n        }else continue\n        ctx.save()\n        ctx.globalAlpha=S.selCase||S.selSt?0.35:0.75\n        ctx.beginPath();ctx.arc(px,py,3.2,0,7)\n        ctx.fillStyle=col;ctx.fill()\n        ctx.restore()\n      }\n      // heroes\n      for(const c of CASES){\n        const rt=RT[c.id]\n        if(S.walk&&S.walk.id===c.id){\n          const wl=c.legs[S.walk.idx]\n          const b=P(wl.to)\n          const px=b.x,py=b.y-30\n          ctx.save()\n          ctx.shadowColor='#ffd166';ctx.shadowBlur=18\n          ctx.beginPath();ctx.arc(px,py,12,0,7)\n          ctx.fillStyle='#ffd166';ctx.fill()\n          ctx.shadowBlur=0\n          ctx.strokeStyle='#fff';ctx.lineWidth=2.2\n          ctx.beginPath();ctx.arc(px,py,15.5,0,7);ctx.stroke()\n          ctx.font='700 8px ui-monospace,Menlo,monospace';ctx.textAlign='center'\n          ctx.fillStyle='#07130c';ctx.fillText(c.ini,px,py+2.8)\n          ctx.restore()\n          S.hitList.push({id:c.id,x:px,y:py})\n          continue\n        }\n        const loc=locs.get(rt)\n        if(!loc)continue\n        let px,py,alpha=1,col=satColor(satAt(rt,t)),dusty=false\n        if(loc.mode==='dwell'){\n          const arr=dw[loc.st]\n          const heroes=arr.filter(u=>u.c&&RT[u.c.id]===u)\n          const i=heroes.indexOf(rt)\n          const b=P(loc.st)\n          px=b.x-33+(i%4)*22;py=b.y-32-Math.floor(i/4)*20\n        }else if(loc.mode==='travel'){\n          const q=curvePt(loc.from,loc.to,ease(Math.min(1,loc.u)));px=q.x;py=q.y\n        }else if(loc.mode==='resolved'){\n          if(rt.parkIdx==null){rt.parkIdx=S.tray++}\n          const sl=rt.parkIdx%21\n          px=rv.x-52+(sl%7)*19;py=rv.y+46+Math.floor(sl/7)*19\n          col=satColor(satAt(rt,rt.termT))\n        }else if(loc.mode==='limbo'){\n          if(rt.armSlot==null){rt.armSlot=S.armTotal++}\n          const i=rt.armSlot%24\n          px=eo.x-59+(i%8)*14+5.5;py=eo.y+42+Math.floor(i/8)*13+5\n          const age=t-rt.termT\n          dusty=age>5\n          col=dusty?'#8a958d':satColor(satAt(rt,t))\n        }else{\n          const b=P('loja');px=b.x;py=b.y-30\n          alpha=Math.max(0,1-(t-rt.termT)/1.4)\n          if(alpha<=0)continue\n        }\n        const isSel=S.selCase===c.id\n        ctx.save()\n        ctx.globalAlpha=(S.selCase&&!isSel)?alpha*0.35:alpha\n        ctx.shadowColor=col;ctx.shadowBlur=dusty?2:11\n        ctx.beginPath();ctx.arc(px,py,loc.mode==='limbo'?5:9,0,7)\n        ctx.fillStyle=col;ctx.fill()\n        ctx.shadowBlur=0\n        if(loc.mode!=='limbo'){\n          ctx.font='700 7.5px ui-monospace,Menlo,monospace';ctx.textAlign='center'\n          ctx.fillStyle='#07130c';ctx.fillText(c.ini,px,py+2.7)\n        }\n        if(isSel){\n          ctx.strokeStyle='#fff';ctx.lineWidth=1.6\n          ctx.beginPath();ctx.arc(px,py,13,0,7);ctx.stroke()\n          ctx.font='700 10px ui-monospace,Menlo,monospace';ctx.textAlign='center'\n          const tag=c.n\n          const tw=ctx.measureText(tag).width+12\n          ctx.fillStyle='rgba(10,16,12,0.9)'\n          rr(px-tw/2,py-34,tw,17,5);ctx.fill()\n          ctx.strokeStyle='rgba(255,255,255,0.35)';ctx.lineWidth=0.8;rr(px-tw/2,py-34,tw,17,5);ctx.stroke()\n          ctx.fillStyle='#fff';ctx.fillText(tag,px,py-22)\n        }\n        ctx.restore()\n        S.hitList.push({id:c.id,x:px,y:py})\n      }\n    }\n    function statusOf(rt,t){\n      const loc=whereAt(rt,t)\n      if(!loc)return{s:'ainda vai comprar…',d:''}\n      const day=Math.floor(t-rt.L[0].A)\n      if(loc.mode==='resolved')return{s:rt.L[rt.L.length-1].s,d:'✓'}\n      if(loc.mode==='gone')return{s:'cancelado (não retirou)',d:'✕'}\n      if(loc.mode==='limbo')return{s:'🗄️ no armário — dia '+day,d:'dia '+day}\n      if(loc.mode==='travel')return{s:'→ '+ST[loc.to].n,d:'dia '+day}\n      return{s:rt.L[loc.leg].s,d:'dia '+day}\n    }\n    function fmtBR(v){return 'R$ '+Math.round(v).toLocaleString('pt-BR')}\n    function updPanels(t){\n      let atv=0,res=0,ret=0,satSum=0,satN=0\n      for(const rt of allUnits()){\n        const spawned=t>=rt.L[0].A\n        if(!spawned)continue\n        const sat=satAt(rt,t)\n        if(t<rt.termT)atv++\n        if(t<rt.refundT&&rt.termType!=='gone'&&rt.val>0)ret+=rt.val\n        satSum+=sat;satN++\n      }\n      for(const c of CASES){\n        const rt=RT[c.id]\n        const spawned=t>=rt.L[0].A\n        const row=rows[c.id]\n        if(!row)continue\n        if(!spawned){row.style.opacity=0.3;row.querySelector('.st').textContent='ainda vai acontecer…';row.querySelector('.day').textContent='—';continue}\n        row.style.opacity=1\n        const sat=satAt(rt,t),colr=satColor(sat)\n        const st=statusOf(rt,t)\n        row.querySelector('.st').textContent=st.s\n        row.querySelector('.day').textContent=st.d\n        row.querySelector('.av').style.background=colr\n        const bar=row.querySelector('.bar i');bar.style.width=sat+'%';bar.style.background=colr\n        if(t>=rt.termT)row.style.opacity=rt.termType==='limbo'?0.9:0.55\n      }\n      d2.$('.h-dia').textContent=Math.floor(t)\n      d2.$('.h-atv').textContent=atv\n      d2.$('.h-res').textContent=S.totRes\n      d2.$('.h-arm').textContent=S.armTotal\n      d2.$('.h-sat').textContent=satN?Math.round(satSum/satN):100\n      d2.$('.h-din').textContent=fmtBR(ret)\n      if(S.selCase&&S.walk&&S.walk.id===S.selCase){}\n      else if(S.selCase)renderStory(S.selCase,t)\n      else if(S.selSt)renderStation(S.selSt,t)\n    }\n    function renderStory(id,t){\n      const rt=RT[id],c=rt.c\n      const story=d2.$('.story')\n      if(t<rt.L[0].A){story.innerHTML='<div class=\"empty\">Esse caso ainda vai acontecer — aguarde o dia '+Math.ceil(rt.L[0].A)+' da simulação.</div>';return}\n      const sat=satAt(rt,t),colr=satColor(sat)\n      const evs=[]\n      for(const l of rt.L){if(t>=l.A)evs.push({d:l.A-rt.L[0].A,note:l.note})}\n      if(c.ev)for(const e of c.ev){if(t>=rt.L[0].A+e.d)evs.push({d:e.d,note:e.note})}\n      evs.sort((a,b)=>a.d-b.d)\n      const st=statusOf(rt,t)\n      const done=t>=rt.termT\n      let html='<div class=\"shead\"><div class=\"av\" style=\"background:'+colr+'\">'+c.ini+'</div>'\n        +'<div><div class=\"nm\">'+c.n+'</div><div class=\"oc\">'+c.oc+'</div></div></div>'\n        +'<div class=\"chips\"><span class=\"chip\">'+c.canal+'</span><span class=\"chip\">'+c.pay+'</span><span class=\"chip\">'+c.marca+'</span>'+(c.val>0?'<span class=\"chip\">'+fmtBR(c.val)+'</span>':'')+'</div>'\n        +'<div class=\"walkbar\"><button class=\"b-walk-enter\">🎬 Ver passo a passo</button></div>'\n        +'<div class=\"now\"><b>Agora:</b> '+st.s+' · satisfação <b style=\"color:'+colr+'\">'+Math.round(sat)+'/100</b>'+(done&&rt.termType==='limbo'?' · <b style=\"color:#ff8a7a\">produto preso há '+Math.floor(t-rt.termT)+' dias</b>':'')+'</div>'\n        +'<div class=\"tl\">'+evs.map((e,i)=>'<div class=\"ev'+(i===evs.length-1?' last':'')+'\"><span class=\"d\">dia '+Math.round(e.d)+'</span>'+e.note+'</div>').join('')+'</div>'\n      if(done&&c.resid)html+='<div class=\"resid\">⚠ '+c.resid+'</div>'\n      story.innerHTML=html\n    }\n    function renderWalk(){\n      const kind=S.walk.kind||'case', id=S.walk.id, idx=S.walk.idx\n      const src=kind==='flow'?FLOWS.find(x=>x.id===id):CASES.find(x=>x.id===id)\n      const N=src.legs.length,l=src.legs[idx]\n      const story=d2.$('.story')\n      const dots=src.legs.map((_,i)=>'<span class=\"sdot'+(i<idx?' done':i===idx?' cur':'')+'\"></span>').join('')\n      const isLast=idx===N-1\n      const outcome=isLast?(l.end==='limbo'?'🗄️ preso no armário, sem previsão de saída':l.end==='gone'?'✕ cancelado (não retirado)':'✅ resolvido'):null\n      const dayWord=kind==='flow'?'do fluxo':'desde a compra'\n      const exitLabel=kind==='flow'?'✕ Fechar fluxo':'✕ voltar ao tempo real'\n      const headerName=kind==='flow'?src.name:src.n\n      const headerSub=kind==='flow'?('passo a passo · '+src.oc):(src.oc+' · passo a passo')\n      const chipsRow=kind==='flow'?'':('<div class=\"chips\"><span class=\"chip\">'+src.canal+'</span><span class=\"chip\">'+src.pay+'</span><span class=\"chip\">'+src.marca+'</span>'+(src.val>0?'<span class=\"chip\">'+fmtBR(src.val)+'</span>':'')+'</div>')\n      const avEm=kind==='flow'?('<div class=\"fem\" style=\"width:32px;height:32px;font-size:16px;border-radius:9px\">'+ST[src.legs[0].to].e+'</div>'):('<div class=\"av\" style=\"background:#ffd166\">'+src.ini+'</div>')\n      const noteKey='gsimnote:'+kind+':'+id+':'+idx\n      let noteVal=''; try{noteVal=localStorage.getItem(noteKey)||''}catch(e){}\n      let html='<div class=\"shead\">'+avEm\n        +'<div><div class=\"nm\">'+headerName+'</div><div class=\"oc\">'+headerSub+'</div></div></div>'\n        +chipsRow\n        +'<div class=\"walkbar\"><button class=\"b-walk-exit\">'+exitLabel+'</button></div>'\n        +'<div class=\"steps\">'+dots+'</div>'\n        +'<div class=\"stepcard\"><div class=\"em\">'+ST[l.to].e+'</div><div><div class=\"stitle\">'+ST[l.to].n+(ST[l.to].n2?' '+ST[l.to].n2:'')+'</div><div class=\"ssub\">dia '+l.a+' '+dayWord+' · passo '+(idx+1)+' de '+N+'</div></div></div>'\n        +'<div class=\"sdecision\"><b>'+l.s+'</b><p>'+l.note+'</p></div>'\n        +(outcome?'<div class=\"resid\" style=\"border-color:#2e8f5b;background:#101b13;color:#bfe3cc\">'+outcome+(src.resid&&l.end==='limbo'?'<br><span style=\"color:#ffa694\">⚠ '+src.resid+'</span>':'')+'</div>':'')\n        +'<div class=\"walknav\">'\n        +'<button class=\"b-walk-prev\" '+(idx===0?'disabled':'')+'>◀ Anterior</button>'\n        +'<button class=\"b-walk-restart\">↻ Reiniciar</button>'\n        +(isLast?'':'<button class=\"b-walk-next\">Próximo passo ▶</button>')\n        +'</div>'\n        +'<div class=\"notewrap\"><label class=\"notelbl\">💬 seu comentário sobre esse passo</label><textarea class=\"noteta\" data-notekey=\"'+noteKey+'\" placeholder=\"Anotação, dúvida, correção...\">'+noteVal.replace(/</g,'&lt;')+'</textarea></div>'\n      story.innerHTML=html\n    }\n    function renderStation(id,t){\n      const st=ST[id],info=STINFO[id]\n      const story=d2.$('.story')\n      const arr=S.lastDw[id]||[]\n      const q=arr.length\n      let wait=0\n      for(const rt of arr){const loc=whereAt(rt,t);if(loc&&loc.mode==='dwell')wait+=t-rt.L[loc.leg].A}\n      const avgW=q?(wait/q):0\n      const peak=S.peak[id]||0\n      const armN=S.armTotal\n      let html='<div class=\"shead\"><div class=\"em\">'+st.e+'</div>'\n        +'<div><div class=\"nm\">'+st.n+(st.n2?' '+st.n2:'')+'</div><div class=\"oc\">estação do processo</div></div></div>'\n        +'<div class=\"now\">'+(info?info.sobre:'')+'</div>'\n        +'<div class=\"kv\">'\n        +'<div class=\"k'+(q>=4?' bad':q>=2?' warn':'')+'\"><b>'+q+'</b><span>parados agora</span></div>'\n        +'<div class=\"k'+(avgW>5?' bad':avgW>2?' warn':'')+'\"><b>'+avgW.toFixed(1)+'d</b><span>espera média</span></div>'\n        +'<div class=\"k'+(peak>=6?' bad':peak>=3?' warn':'')+'\"><b>'+peak+'</b><span>pico</span></div>'\n        +(id==='escritorio'?'<div class=\"k bad\"><b>'+armN+'</b><span>acumulados</span></div>':'')\n        +'</div>'\n      if(info&&info.dores&&info.dores.length){\n        html+='<div class=\"sec\">🔴 dores conhecidas aqui</div><div class=\"plist\">'\n          +info.dores.map(dd=>'<div class=\"pit\">'+dd.t+(dd.ref?'<span class=\"ref\">['+dd.ref+']</span>':'')+'</div>').join('')+'</div>'\n      }\n      if(info&&info.sug&&info.sug.length){\n        html+='<div class=\"sec\">💡 sugestões de melhoria</div><div class=\"plist\">'\n          +info.sug.map(ss=>'<div class=\"sit\">'+ss+'</div>').join('')+'</div>'\n      }\n      story.innerHTML=html\n    }\n\n    function restart(){\n      resetRT();S.t=0\n      d2.$('.endov').classList.add('hidden')\n    }\n    function compileNotes(){\n      let keys=[]\n      try{ for(let i=0;i<localStorage.length;i++){const k=localStorage.key(i); if(k&&k.indexOf('gsimnote:')===0) keys.push(k)} }catch(e){}\n      const items=[]\n      for(const k of keys){\n        let v=''; try{v=localStorage.getItem(k)}catch(e){}\n        if(!v||!v.trim())continue\n        const parts=k.split(':')\n        const kind=parts[1],id=parts[2],idx=parseInt(parts[3],10)\n        const src=kind==='flow'?FLOWS.find(x=>x.id===id):CASES.find(x=>x.id===id)\n        if(!src)continue\n        const leg=src.legs[idx]\n        const label=kind==='flow'?src.name:(src.n+' — '+src.oc)\n        items.push({kind,idx,label,step:leg?(leg.s+' ('+ST[leg.to].n+')'):('passo '+(idx+1)),text:v})\n      }\n      items.sort((a,b)=> a.label===b.label ? a.idx-b.idx : (a.label<b.label?-1:1))\n      if(!items.length)return 'Nenhuma anotação salva ainda. Adicione comentários nos passos (aba Fluxos ou Clientes, modo passo a passo) e volte aqui.'\n      let out='ANOTAÇÕES — SALA DE OPERAÇÕES (TROCA GRANADO)\\n'+new Array(50).fill('=').join('')+'\\n'\n      let lastLabel=null\n      for(const it of items){\n        if(it.label!==lastLabel){ out+='\\n### '+it.label+'\\n'; lastLabel=it.label }\n        out+='- Passo: '+it.step+'\\n  » '+it.text.trim()+'\\n'\n      }\n      return out\n    }\n    function flashCopied(){\n      const b=d2.$('.b-notes-copy'); if(!b)return\n      const old=b.textContent; b.textContent='✓ copiado!'\n      setTimeout(()=>{ if(b) b.textContent=old },1500)\n    }\n    function fallbackCopy(txt){\n      try{ const ta=d2.$('.notes-out'); ta.focus(); ta.select(); document.execCommand('copy'); flashCopied() }catch(e){}\n    }\n    function copyToClipboard(txt){\n      try{\n        if(navigator.clipboard&&navigator.clipboard.writeText){ navigator.clipboard.writeText(txt).then(flashCopied).catch(()=>fallbackCopy(txt)) }\n        else fallbackCopy(txt)\n      }catch(e){ fallbackCopy(txt) }\n    }\n    function selectCase(id){\n      S.selCase=id;S.selSt=null;S.walk=null\n      d2.$$('.crow').forEach(x=>x.classList.toggle('sel',x.getAttribute('data-cid')===id))\n      d2.$$('.frow').forEach(x=>x.classList.remove('sel'))\n      if(id)renderStory(id,S.t)\n      else d2.$('.story').innerHTML='<div class=\"empty\">Clique em um <b>caso</b> para a história da pessoa, ou em uma <b>estação</b> para ver o processo por dentro.</div>'\n    }\n    d2.on('.tab-cli','click',()=>{ S.tab='cli'; d2.$$('.tab-btn').forEach(b=>b.classList.toggle('hot',b.classList.contains('tab-cli'))); renderList() })\n    d2.on('.tab-flu','click',()=>{ S.tab='flu'; d2.$$('.tab-btn').forEach(b=>b.classList.toggle('hot',b.classList.contains('tab-flu'))); renderList() })\n    d2.on('.frow','click',e=>{\n      const r=e.target.closest('.frow');if(!r)return\n      const fid=r.getAttribute('data-fid')\n      S.selCase=null;S.selSt=null\n      S.walk={kind:'flow',id:fid,idx:0}\n      d2.$$('.frow').forEach(x=>x.classList.toggle('sel',x.getAttribute('data-fid')===fid))\n      renderWalk()\n    })\n    d2.on('.noteta','input',e=>{\n      const k=e.target.getAttribute('data-notekey')\n      try{ if(e.target.value.trim()) localStorage.setItem(k,e.target.value); else localStorage.removeItem(k) }catch(err){}\n    })\n    d2.on('.b-notes','click',()=>{\n      const txt=compileNotes()\n      const ta=d2.$('.notes-out'); ta.value=txt\n      d2.$('.notesov').classList.remove('hidden')\n      try{ta.focus();ta.select()}catch(e){}\n      copyToClipboard(txt)\n    })\n    d2.on('.b-notes-copy','click',()=>{ copyToClipboard(d2.$('.notes-out').value) })\n    d2.on('.b-notes-close','click',()=>{ d2.$('.notesov').classList.add('hidden') })\n    d2.on('.b-walk-enter','click',()=>{ if(S.selCase){S.walk={kind:'case',id:S.selCase,idx:0};renderWalk()} })\n    d2.on('.b-walk-exit','click',()=>{\n      const wasFlow=S.walk&&S.walk.kind==='flow'\n      S.walk=null\n      if(wasFlow){ d2.$$('.frow').forEach(x=>x.classList.remove('sel')); d2.$('.story').innerHTML='<div class=\"empty\">Clique em um <b>fluxo</b> pra estudar passo a passo, ou em um <b>cliente</b> pra acompanhar a jornada dele.</div>' }\n      else if(S.selCase) renderStory(S.selCase,S.t)\n    })\n    d2.on('.b-walk-prev','click',()=>{ if(S.walk){S.walk.idx=Math.max(0,S.walk.idx-1);renderWalk()} })\n    d2.on('.b-walk-next','click',()=>{ if(S.walk){const cc=CASES.find(x=>x.id===S.walk.id);S.walk.idx=Math.min(cc.legs.length-1,S.walk.idx+1);renderWalk()} })\n    d2.on('.b-walk-restart','click',()=>{ if(S.walk){S.walk.idx=0;renderWalk()} })\n    d2.on('.crow','click',e=>{\n      const r=e.target.closest('.crow');if(!r)return\n      const id=r.getAttribute('data-cid')\n      selectCase(S.selCase===id?null:id)\n    })\n    d2.on('.b-pause','click',()=>{S.paused=!S.paused;d2.$('.b-pause').textContent=S.paused?'▶':'⏸'})\n    d2.on('.b-speed','click',()=>{\n      S.speed=S.speed===0.5?1:S.speed===1?2:S.speed===2?4:0.5\n      d2.$('.b-speed').textContent=(S.speed===0.5?'0.5':S.speed)+'×'\n    })\n    d2.on('.b-mult','click',()=>{\n      S.mult=S.mult===1?5:S.mult===5?20:S.mult===20?50:1\n      const b=d2.$('.b-mult')\n      b.textContent='×'+S.mult\n      b.classList.toggle('hot',S.mult>1)\n      buildGhosts()\n    })\n    d2.on('.b-restart','click',restart)\n    d2.on('.b-again','click',restart)\n    d2.on(cv,'click',e=>{\n      const b=cv.getBoundingClientRect(),mx=e.clientX-b.left,my=e.clientY-b.top\n      let best=null,bd=18\n      for(const hc of S.hitList){const dd=Math.hypot(hc.x-mx,hc.y-my);if(dd<bd){bd=dd;best=hc.id}}\n      if(best){selectCase(best)\n        const row=rows[best];if(row&&row.scrollIntoView)row.scrollIntoView({block:'nearest'})\n        return}\n      for(const id in S.stRects){\n        const r=S.stRects[id]\n        if(mx>=r.x&&mx<=r.x+r.w&&my>=r.y&&my<=r.y+r.h){\n          if(S.selSt===id){S.selSt=null;selectCase(null)}\n          else{S.selCase=null;S.selSt=id\n            d2.$$('.crow').forEach(x=>x.classList.remove('sel'))\n            renderStation(id,S.t)}\n          return\n        }\n      }\n    })\n    d2.on(cv,'mousemove',e=>{\n      const b=cv.getBoundingClientRect(),mx=e.clientX-b.left,my=e.clientY-b.top\n      let hit=false\n      for(const hc of S.hitList){if(Math.hypot(hc.x-mx,hc.y-my)<14){hit=true;break}}\n      if(!hit){for(const id in S.stRects){const r=S.stRects[id];if(mx>=r.x&&mx<=r.x+r.w&&my>=r.y&&my<=r.y+r.h){hit=true;break}}}\n      cv.style.cursor=hit?'pointer':'default'\n    })\n    S.resize()\n    let prev=null,acc=0\n    d2.raf(ts=>{\n      if(prev==null)prev=ts\n      let dt=(ts-prev)/1000;prev=ts\n      dt=Math.min(dt,1/20)\n      if(wrap.clientWidth!==S.cw||wrap.clientHeight!==S.ch)S.resize()\n      if(!S.paused){\n        S.t+=dt*0.9*S.speed\n      }\n      book(S.t)\n      draw(S.t)\n      acc+=dt\n      if(acc>0.28){acc=0;updPanels(S.t)}\n    })\n    updPanels(0)\n  },\n  updated(d2){ if(d2._sim&&d2._sim.resize)d2._sim.resize() }\n}"}}}}}}}},{"_id":"6a860b757eb356b31fea0ce1","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-dash-incidentes","package":"granado-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;position:relative;width:100%;height:100%}\n*{box-sizing:border-box;margin:0;padding:0}\n.dash{position:absolute;inset:0;display:grid;grid-template-rows:auto auto 1fr;background:#0a0e0c;color:#d7e6dc;font-family:'Segoe UI',system-ui,-apple-system,sans-serif;overflow:hidden}\nheader{padding:14px 20px 10px;background:#0d1411;border-bottom:1px solid #1c2a22}\nheader h1{font-size:17px;font-weight:800;color:#eaf5ee;display:flex;align-items:center;gap:10px}\nheader .badge{font-size:9.5px;background:#241b09;color:#e8b45a;border:1px solid #3d2f12;border-radius:99px;padding:3px 10px;font-weight:700;letter-spacing:.3px}\nheader .sub{font-size:11px;color:#8fa89a;margin-top:4px;max-width:760px;line-height:1.5}\n.stats{display:flex;gap:22px;padding:10px 20px;background:#0c1210;border-bottom:1px solid #1c2a22}\n.stat{text-align:left}\n.stat b{display:block;font-size:19px;font-family:ui-monospace,Menlo,monospace;color:#eaf5ee;line-height:1.1}\n.stat span{font-size:9px;letter-spacing:.5px;text-transform:uppercase;color:#6f8a7c}\n.stat.bad b{color:#ff6b5e}.stat.warn b{color:#ffb454}.stat.good b{color:#4ade80}\n.body{display:grid;grid-template-columns:330px 1fr;min-height:0}\n.list{overflow-y:auto;border-right:1px solid #1c2a22;background:#0b100e;padding:8px}\n.irow{display:flex;gap:10px;align-items:flex-start;padding:10px 10px;border-radius:10px;cursor:pointer;border:1px solid transparent;margin-bottom:4px}\n.irow:hover{background:#121a15}\n.irow.sel{background:#14231b;border-color:#2c4636}\n.irow .av{width:32px;height:32px;border-radius:50%;background:#182018;display:flex;align-items:center;justify-content:center;font-size:10.5px;font-weight:800;color:#cfe8d8;flex:none}\n.irow .meta{flex:1;min-width:0}\n.irow .nm{font-size:12px;font-weight:700;color:#e4f1e8}\n.irow .pr{font-size:10px;color:#7d968a;margin-top:1px}\n.irow .tip{font-size:8.5px;color:#9fc4ad;background:#152019;border-radius:99px;padding:1.5px 7px;display:inline-block;margin-top:4px}\n.irow .right{text-align:right;flex:none}\n.irow .dot{width:9px;height:9px;border-radius:50%;margin-left:auto}\n.irow .days{font-size:9.5px;color:#8fa89a;font-family:ui-monospace,Menlo,monospace;margin-top:5px}\n.dot.ok{background:#4ade80}.dot.atencao{background:#ffb454}.dot.estourado{background:#ff5d52;box-shadow:0 0 8px rgba(255,93,82,.6)}\n.detail{overflow-y:auto;padding:18px 24px}\n.dhead{display:flex;align-items:center;gap:12px;margin-bottom:4px}\n.dhead .av{width:40px;height:40px;border-radius:50%;background:#182018;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#cfe8d8}\n.dhead .nm{font-size:16px;font-weight:800;color:#eef7f0}\n.dhead .pr{font-size:11.5px;color:#9fb7a9}\n.dchips{display:flex;gap:6px;margin:8px 0 4px;flex-wrap:wrap}\n.dchip{font-size:9.5px;background:#152019;border:1px solid #223227;color:#bfe3cc;border-radius:99px;padding:3px 10px;font-weight:600}\n.dchip.est{background:#33150f;border-color:#4a281c;color:#ff9a8c}\n.dchip.atn{background:#332508;border-color:#4a3711;color:#ffc46e}\n.note{background:#13201a;border:1px solid #22392c;border-left:3px solid #ffd166;border-radius:9px;padding:9px 12px;font-size:11.5px;color:#e8dcb8;line-height:1.55;margin:12px 0}\n.tracks{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:16px}\n@media(max-width:900px){.tracks{grid-template-columns:1fr}}\n.tcol h3{font-size:12px;font-weight:800;color:#eaf5ee;display:flex;align-items:center;gap:7px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #1c2a22}\n.steps2{border-left:2px solid #22352a;margin-left:9px;padding-left:18px;display:flex;flex-direction:column;gap:16px}\n.step2{position:relative}\n.step2:before{content:'';position:absolute;left:-25px;top:2px;width:13px;height:13px;border-radius:50%;background:#1c2a22;border:2px solid #0a0e0c}\n.step2.done:before{background:#3ddc84}\n.step2.atrasado:before{background:#ff5d52;box-shadow:0 0 0 4px rgba(255,93,82,.18)}\n.step2.bloqueado:before{background:#ff5d52;animation:pulse2 1.6s infinite}\n.step2.pendente:before{background:#3a4a3f}\n@keyframes pulse2{0%,100%{box-shadow:0 0 0 0 rgba(255,93,82,.5)}50%{box-shadow:0 0 0 6px rgba(255,93,82,0)}}\n.step2 .lbl{font-size:12.5px;font-weight:700;color:#eaf5ee;margin-bottom:5px}\n.step2 .row{display:flex;gap:6px;flex-wrap:wrap}\n.sc{font-size:9px;border-radius:6px;padding:2.5px 8px;font-weight:600;background:#121a15;color:#9fb7a9;border:1px solid #223227}\n.sc.resp{color:#bfe3cc}\n.sc.sla{color:#8fa89a}\n.sc.dec{color:#8fa89a}\n.sc.dec.late{background:#33150f;color:#ff9a8c;border-color:#4a281c;font-weight:800}\n.na{color:#5d7568;font-size:11.5px;font-style:italic;padding:6px 0}\n`,\n  render(d2){\n    return `<div class=\"dash\">\n      <header>\n        <h1>🚨 Dashboard Incidentes / Trocas / Devoluções <span class=\"badge\">🧪 SIMULADO</span></h1>\n        <div class=\"sub\">Prévia de como ficaria com SAP, EuEntrego, SAC, Portal Troque e Financeiro integrados num só painel — visibilidade completa de todo pedido com alguma issue, com duas linhas do tempo por caso: a jornada física do produto e a reparação/reembolso, cada passo com responsável e SLA.</div>\n      </header>\n      <div class=\"stats\" data-d2-static>\n        <div class=\"stat\"><b class=\"s-tot\">0</b><span>incidentes abertos</span></div>\n        <div class=\"stat bad\"><b class=\"s-est\">0</b><span>SLA estourado</span></div>\n        <div class=\"stat warn\"><b class=\"s-atn\">0</b><span>em atenção</span></div>\n        <div class=\"stat\"><b class=\"s-med\">0</b><span>dias médios em aberto</span></div>\n      </div>\n      <div class=\"body\">\n        <div class=\"list\" data-d2-static></div>\n        <div class=\"detail\" data-d2-static></div>\n      </div>\n    </div>`\n  },\n  mounted(d2){\n    const list=d2.$('.list'), detail=d2.$('.detail')\n    if(!list||!detail)return\n    const INC=[\n      {id:'i1',n:'Marina Duarte',ini:'MD',prod:'Polvilho Antisséptico 100g',tipo:'CD → Portal Troque',dias:8,sla:'atencao',\n        logistica:[\n          {lbl:'Pedido separado e faturado',resp:'CD (Logística)',s:'—',d:'dia 0',st:'done'},\n          {lbl:'Entrega em casa',resp:'Transportadora',s:'até 2 dias',d:'dia 1',st:'done'},\n          {lbl:'Cliente posta a devolução',resp:'Cliente',s:'—',d:'dia 3',st:'done'},\n          {lbl:'Conferência no CD',resp:'CD (Logística)',s:'sem SLA formal — fila pode levar meses',d:'dia 6 (neste caso)',st:'atrasado'}],\n        reparacao:[\n          {lbl:'Abertura self-service',resp:'Portal Troque',s:'imediato',d:'dia 3',st:'done'},\n          {lbl:'Instruções por e-mail',resp:'Sistema',s:'até 1 dia',d:'1 dia',st:'done'},\n          {lbl:'Liberação após conferência',resp:'CD',s:'—',d:'dia 6',st:'done'},\n          {lbl:'Input manual do estorno (Adyen)',resp:'Financeiro',s:'sem SLA definido',d:'dia 7',st:'atrasado'},\n          {lbl:'Estorno concluído',resp:'Financeiro/Adyen',s:'até 2 faturas',d:'dia 7–9',st:'done'}],\n        nota:'O \"caminho feliz\" só é rápido quando a fila de conferência do CD está vazia — não há SLA formal pra essa etapa.'},\n      {id:'i2',n:'Carlos Menezes',ini:'CM',prod:'Água de Colônia Phebo 200ml',tipo:'Retirada → item lacrado',dias:24,sla:'estourado',\n        logistica:[\n          {lbl:'Retirou na loja',resp:'Loja física',s:'—',d:'dia 1.5',st:'done'},\n          {lbl:'Coleta enviada pelo SAC',resp:'Transportadora',s:'até 2 dias',d:'dia 5',st:'done'},\n          {lbl:'Item volta pra loja',resp:'Loja física',s:'~3-5 dias esperado',d:'12 dias',st:'atrasado'}],\n        reparacao:[\n          {lbl:'Chamado aberto no SAC',resp:'SAC',s:'—',d:'dia 3',st:'done'},\n          {lbl:'Loja confirma recebimento',resp:'Loja → Digital',s:'sem prazo definido',d:'12 dias',st:'bloqueado'},\n          {lbl:'Devolução lançada no SAP',resp:'Digital/E-commerce',s:'—',d:'dia 19',st:'done'},\n          {lbl:'Duas planilhas manuais (SAC + Financeiro)',resp:'Financeiro',s:'—',d:'dia 21',st:'done'},\n          {lbl:'Estorno processado',resp:'Financeiro',s:'—',d:'dia 24',st:'done'}],\n        nota:'Uma troca simples de fragrância levou 24 dias — o gargalo real é a loja avisar que o item chegou, sem SLA nenhum.'},\n      {id:'i3',n:'Fernanda Lima',ini:'FL',prod:'Kit Sabonetes Glicerina',tipo:'Retirada → item aberto',dias:26,sla:'estourado',\n        logistica:[\n          {lbl:'Retirou e abriu em casa',resp:'Cliente',s:'—',d:'dia 1.6',st:'done'},\n          {lbl:'Coleta enviada',resp:'Transportadora',s:'até 2 dias',d:'dia 4.5',st:'done'},\n          {lbl:'Chega ao escritório sem identificação',resp:'SAC (escritório)',s:'—',d:'4 dias só p/ identificar',st:'bloqueado'},\n          {lbl:'Fica no armário — SEM PRÓXIMO PASSO',resp:'—',s:'—',d:'permanente',st:'bloqueado'}],\n        reparacao:[\n          {lbl:'Chamado aberto',resp:'SAC',s:'—',d:'dia 2.5',st:'done'},\n          {lbl:'Item identificado',resp:'SAC',s:'—',d:'dia 11',st:'done'},\n          {lbl:'Estorno solicitado (planilha)',resp:'Financeiro',s:'—',d:'dia 11',st:'done'},\n          {lbl:'Estorno concluído',resp:'Financeiro',s:'—',d:'dia 26',st:'done'}],\n        nota:'Cliente é ressarcido, mas o produto físico nunca sai do armário — workaround deliberado pra evitar o bug do estoque fantasma no SAP.'},\n      {id:'i4',n:'Roberto Salles',ini:'RS',prod:'Óleo de Barba Granado',tipo:'Expressa → CEP fora de área',dias:21,sla:'estourado',\n        logistica:[\n          {lbl:'Despachado pela Darkstore',resp:'Darkstore',s:'—',d:'dia 0.8',st:'done'},\n          {lbl:'EuEntrego não entrega — fora de área',resp:'Transportadora',s:'—',d:'8 dias sem alerta',st:'bloqueado'},\n          {lbl:'Cliente contatado (retirar ou cancelar)',resp:'SAC',s:'—',d:'dia 9.5',st:'done'}],\n        reparacao:[\n          {lbl:'Chamado aberto (só após reclamar)',resp:'SAC',s:'—',d:'dia 9.5',st:'atrasado'},\n          {lbl:'Pedido de estorno do frete parcial',resp:'Financeiro',s:'—',d:'dia 13',st:'done'},\n          {lbl:'Ninguém sabe processar estorno parcial (cartão-presente)',resp:'Financeiro',s:'—',d:'3 semanas travado',st:'bloqueado'},\n          {lbl:'Estorno concluído',resp:'Financeiro',s:'—',d:'dia 21',st:'done'}],\n        nota:'3 semanas pra devolver R$18 de frete — caso real relatado pela operação.'},\n      {id:'i5',n:'Ana Beatriz Rocha',ini:'AB',prod:'Hidratante Care 250ml',tipo:'Retirada → loja recusa (Care)',dias:15,sla:'estourado',\n        logistica:[\n          {lbl:'Loja recusa (estoque não bate)',resp:'Loja física',s:'—',d:'dia 1.2',st:'done'}],\n        reparacao:[\n          {lbl:'Cliente NÃO é avisado da recusa (bug Shopify)',resp:'—',s:'—',d:'sem visibilidade',st:'bloqueado'},\n          {lbl:'Estorno manual solicitado',resp:'Digital/E-commerce',s:'—',d:'dia 2.5',st:'done'},\n          {lbl:'Atualiza Shopify + SAP + planilha',resp:'Digital/E-commerce',s:'—',d:'dia 6',st:'done'},\n          {lbl:'Estorno processado',resp:'Financeiro',s:'—',d:'dia 15',st:'done'}],\n        nota:'Pra Granado/Phebo via Magento esse cancelamento já é automático. Pra Care é 100% manual, e o cliente nem fica sabendo da recusa.'},\n      {id:'i6',n:'Tereza Nunes',ini:'TN',prod:'Polvilho Antisséptico 100g',tipo:'Retirada → nunca retirada',dias:30,sla:'estourado',\n        logistica:[\n          {lbl:'Pedido pronto na loja',resp:'Loja física',s:'—',d:'dia 1',st:'done'},\n          {lbl:'Cliente nunca aparece',resp:'Cliente',s:'—',d:'pendente indefinido',st:'pendente'},\n          {lbl:'Gerente avisa informalmente (sem processo oficial)',resp:'Darkstore (gerente)',s:'sem SLA — favor informal',d:'~dia 30',st:'atrasado'}],\n        reparacao:[\n          {lbl:'Aviso de 7 dias enviado (sem efeito prático)',resp:'Sistema',s:'—',d:'dia 1',st:'done'},\n          {lbl:'Ação disparada pelo aviso informal',resp:'Digital/E-commerce',s:'—',d:'dia 30',st:'atrasado'},\n          {lbl:'Cancelamento processado no SAP',resp:'Digital/E-commerce',s:'—',d:'dia 30–31',st:'done'},\n          {lbl:'Estorno via planilha',resp:'Financeiro',s:'—',d:'~dia 32',st:'done'}],\n        nota:'Não existe cancelamento automático — só um aviso ao cliente. A resolução de verdade depende de um favor informal da gerente da Darkstore.'},\n      {id:'i7',n:'Marcos Vinícius',ini:'MV',prod:'Perfume Encanto Phebo',tipo:'Retirada → travado no fiscal',dias:8,sla:'atencao',\n        logistica:[\n          {lbl:'Pago, mas travado no SAP sem faturar',resp:'SAP/Fiscal',s:'motivo desconhecido',d:'dia 0.5',st:'bloqueado'},\n          {lbl:'Cliente aparece; loja entrega SEM NOTA',resp:'Loja física',s:'—',d:'~dia 1',st:'atrasado'},\n          {lbl:'Fiscal força faturamento manual',resp:'SAP/Fiscal',s:'e-mail diário',d:'dia 6.5',st:'done'}],\n        reparacao:[\n          {lbl:'Sem reembolso — resolvido por retirada tardia',resp:'—',s:'—',d:'—',st:'pendente'},\n          {lbl:'Risco de contestação (chargeback) em aberto',resp:'—',s:'—',d:'produto entregue sem nota fiscal',st:'bloqueado'}],\n        nota:'A loja sabia do travamento mas não podia liberar oficialmente — entregou informalmente pro cliente que já tinha pago.'},\n      {id:'i8',n:'Bianca Freitas',ini:'BF',prod:'Sabonete em Barra (kit 3un)',tipo:'Expressa → EuEntrego falso-entregue',dias:10,sla:'estourado',\n        logistica:[\n          {lbl:'Despachado pela Darkstore',resp:'Darkstore',s:'—',d:'dia 0.8',st:'done'},\n          {lbl:'EuEntrego marca como ENTREGUE (dado errado)',resp:'Transportadora',s:'—',d:'dia 3',st:'bloqueado'},\n          {lbl:'Cliente nunca recebe o produto',resp:'Cliente',s:'—',d:'—',st:'atrasado'}],\n        reparacao:[\n          {lbl:'Cliente contesta a entrega no SAC',resp:'SAC',s:'—',d:'dia 3',st:'done'},\n          {lbl:'Investigação de extravio',resp:'SAC/Transportadora',s:'—',d:'dia 9',st:'done'},\n          {lbl:'Estorno ao cliente',resp:'Financeiro',s:'—',d:'dia 10',st:'done'},\n          {lbl:'Venda fica aberta no SAP pra sempre',resp:'—',s:'—',d:'resíduo permanente',st:'bloqueado'}],\n        nota:'Mesma dor oficial #25: pedido extraviado não pode ser cancelado no SAP, só estornado.'}\n    ]\n    const slaLbl={ok:'🟢 dentro do prazo',atencao:'🟡 em atenção',estourado:'🔴 SLA estourado'}\n    let sel=INC[0].id\n    function renderList(){\n      list.innerHTML=INC.map(i=>'<div class=\"irow'+(i.id===sel?' sel':'')+'\" data-iid=\"'+i.id+'\">'\n        +'<div class=\"av\">'+i.ini+'</div>'\n        +'<div class=\"meta\"><div class=\"nm\">'+i.n+'</div><div class=\"pr\">'+i.prod+'</div><span class=\"tip\">'+i.tipo+'</span>'\n        +'<div class=\"days\">'+i.dias+' dias em aberto</div></div>'\n        +'<div class=\"right\"><div class=\"dot '+i.sla+'\"></div></div>'\n        +'</div>').join('')\n    }\n    function track(steps){\n      return '<div class=\"steps2\">'+steps.map(s=>'<div class=\"step2 '+s.st+'\"><div class=\"lbl\">'+s.lbl+'</div><div class=\"row\">'\n        +'<span class=\"sc resp\">👤 '+s.resp+'</span>'\n        +'<span class=\"sc sla\">🎯 SLA: '+s.s+'</span>'\n        +'<span class=\"sc dec'+(s.st==='atrasado'||s.st==='bloqueado'?' late':'')+'\">⏱ '+s.d+'</span>'\n        +'</div></div>').join('')+'</div>'\n    }\n    function renderDetail(id){\n      const i=INC.find(x=>x.id===id); if(!i)return\n      detail.innerHTML='<div class=\"dhead\"><div class=\"av\">'+i.ini+'</div><div><div class=\"nm\">'+i.n+'</div><div class=\"pr\">'+i.prod+'</div></div></div>'\n        +'<div class=\"dchips\"><span class=\"dchip\">'+i.tipo+'</span><span class=\"dchip\">'+i.dias+' dias em aberto</span><span class=\"dchip '+(i.sla==='estourado'?'est':i.sla==='atencao'?'atn':'')+'\">'+slaLbl[i.sla]+'</span></div>'\n        +'<div class=\"note\">💬 '+i.nota+'</div>'\n        +'<div class=\"tracks\">'\n        +'<div class=\"tcol\"><h3>📦 Logística do Pedido</h3>'+(i.logistica.length?track(i.logistica):'<div class=\"na\">Sem etapas logísticas nesse incidente.</div>')+'</div>'\n        +'<div class=\"tcol\"><h3>💳 Reparação &amp; Reembolso</h3>'+(i.reparacao.length?track(i.reparacao):'<div class=\"na\">Sem etapas de reparação nesse incidente.</div>')+'</div>'\n        +'</div>'\n    }\n    function renderStats(){\n      const est=INC.filter(i=>i.sla==='estourado').length\n      const atn=INC.filter(i=>i.sla==='atencao').length\n      const med=Math.round(INC.reduce((a,i)=>a+i.dias,0)/INC.length)\n      const tot=d2.$('.s-tot'), es=d2.$('.s-est'), at=d2.$('.s-atn'), me=d2.$('.s-med')\n      if(tot)tot.textContent=INC.length\n      if(es)es.textContent=est\n      if(at)at.textContent=atn\n      if(me)me.textContent=med\n    }\n    d2.on('.irow','click',e=>{\n      const r=e.target.closest('.irow'); if(!r)return\n      sel=r.getAttribute('data-iid')\n      renderList(); renderDetail(sel)\n    })\n    renderStats(); renderList(); renderDetail(sel)\n  }\n}"}}}}}}}},{"_id":"6a8752db2d977dd049f3321e","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-wa-monitor","package":"granado-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;position:relative;width:100%;height:100%}\n*{box-sizing:border-box;margin:0;padding:0}\n.emap{position:absolute;inset:0;display:grid;grid-template-rows:auto auto auto 1fr;background:#0a0e0c;color:#d7e6dc;font-family:'Segoe UI',system-ui,-apple-system,sans-serif;overflow:hidden}\nheader{padding:14px 20px 10px;background:#0d1411;border-bottom:1px solid #1c2a22}\nheader h1{font-size:16px;font-weight:800;color:#eaf5ee;display:flex;align-items:center;gap:10px}\nheader h1 button{background:#152019;border:1px solid #27392e;color:#cfe8d8;border-radius:8px;padding:4px 10px;font-size:13px;cursor:pointer}\nheader .sub{font-size:11px;color:#8fa89a;margin-top:4px}\n.stats{display:flex;gap:22px;padding:9px 20px;background:#0c1210;border-bottom:1px solid #1c2a22;align-items:center}\n.stat b{display:block;font-size:18px;font-family:ui-monospace,Menlo,monospace;color:#eaf5ee;line-height:1.1}\n.stat span{font-size:9px;letter-spacing:.5px;text-transform:uppercase;color:#6f8a7c}\n.mono{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:#8fa89a}\n.tabs{display:flex;gap:10px;padding:9px 20px;background:#0b100e;border-bottom:1px solid #1c2a22;align-items:center;justify-content:space-between}\n.tabbtns{display:flex;gap:6px;flex-wrap:wrap}\n.search{background:#121a15;border:1px solid #223227;color:#eaf5ee;border-radius:8px;padding:6px 12px;font-size:12px;width:230px}\n.search:focus{outline:none;border-color:#3d6a4d}\n.search::placeholder{color:#5d7568}\n.tab{background:#121a15;border:1px solid #223227;color:#8fa89a;border-radius:99px;padding:6px 16px;font-size:12px;font-weight:700;cursor:pointer}\n.tab.hot{background:#1d5c3c;border-color:#2e8f5b;color:#eafff2}\n.body{display:grid;grid-template-columns:1fr 360px;min-height:0}\n.gridcol{overflow-y:auto;padding:14px}\n.egrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px}\n.ecard{background:#121a15;border:1px solid #1c2a22;border-radius:12px;padding:12px 13px;cursor:pointer}\n.ecard:hover{border-color:#2c4636}\n.ecard.sel{border-color:#3ddc84;background:#132018}\n.ehead{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}\n.etipo{font-size:8.5px;letter-spacing:.5px;text-transform:uppercase;color:#7d968a;font-weight:700}\n.etime{font-size:9px;color:#5d7568;font-family:ui-monospace,Menlo,monospace}\n.ename{font-size:13px;font-weight:800;color:#eaf5ee;margin-bottom:6px;line-height:1.3}\n.ename.mono{font-family:ui-monospace,Menlo,monospace;color:#ffd166}\n.erow{font-size:10.5px;color:#9fb7a9;margin-bottom:6px}\n.erow b{color:#e4f1e8}\n.ebadges{display:flex;flex-wrap:wrap;gap:4px;margin-bottom:5px}\n.epill{font-size:8.5px;border-radius:99px;padding:2px 8px;font-weight:700;background:#1a2a20;color:#9fc4ad}\n.epill.crit{background:#33150f;color:#ff9a8c}\n.epill.warn{background:#332508;color:#ffc46e}\n.epill.info{background:#101b2a;color:#8ec2ff}\n.echips{display:flex;flex-wrap:wrap;gap:4px}\n.echip{font-family:ui-monospace,Menlo,monospace;font-size:9px;background:#182018;color:#ffd166;border-radius:6px;padding:1.5px 6px}\n.detail{overflow-y:auto;padding:16px;border-left:1px solid #1c2a22;background:#0b100e}\n.detail h3{font-size:10px;letter-spacing:.6px;text-transform:uppercase;color:#6f8a7c;margin:14px 0 8px}\n.dhead{font-size:15px;font-weight:800;color:#eef7f0;margin-bottom:3px}\n.dsub{font-size:11px;color:#8fa89a;margin-bottom:10px}\n.evrow{background:#111a14;border:1px solid #1c2a22;border-radius:8px;padding:8px 10px;margin-bottom:6px}\n.evtop{display:flex;justify-content:space-between;font-size:9.5px;color:#7d968a;margin-bottom:3px;font-family:ui-monospace,Menlo,monospace}\n.evtext{font-size:11.5px;color:#cfe8d8;line-height:1.5}\n.na{color:#5d7568;font-size:11px;font-style:italic;padding:10px 0}\n.b-back{background:#152019;border:1px solid #27392e;color:#cfe8d8;border-radius:8px;padding:5px 12px;font-size:11px;font-weight:700;cursor:pointer;margin-bottom:10px}\n.b-back:hover{background:#1e2c22}\n.b-conv{background:none;border:none;color:#5fb98a;font-size:9.5px;font-weight:700;cursor:pointer;padding:0;text-decoration:underline}\n.b-convtop{background:#152019;border:1px solid #27392e;color:#cfe8d8;border-radius:8px;padding:6px 12px;font-size:11px;font-weight:700;cursor:pointer;margin:6px 0 4px;width:100%;text-align:left}\n.b-convtop:hover{background:#1e2c22}\n.crow2{border-radius:9px;padding:7px 10px;margin-bottom:6px;max-width:88%;background:#111a14;border:1px solid #1c2a22}\n.crow2.me{margin-left:auto;background:#122019;border-color:#24382c}\n.crow2.hl{border-color:#ffd166;box-shadow:0 0 0 1px #ffd166 inset}\n.crow2top{display:flex;justify-content:space-between;gap:10px;font-size:9px;color:#7d968a;margin-bottom:3px;font-family:ui-monospace,Menlo,monospace}\n.crow2text{font-size:11.5px;color:#cfe8d8;line-height:1.5}\n.gate{position:absolute;inset:0;background:#0a0e0c;display:flex;align-items:center;justify-content:center;z-index:20}\n.gate.hidden{display:none}\n.gatebox{background:#121a15;border:1px solid #223227;border-radius:16px;padding:30px 34px;text-align:center;width:280px}\n.gatelock{font-size:26px;margin-bottom:8px}\n.gatetitle{font-size:14px;font-weight:800;color:#eaf5ee;margin-bottom:4px}\n.gatesub{font-size:10.5px;color:#7d968a;margin-bottom:16px}\n.gate-input{width:100%;background:#0c1210;border:1px solid #223227;border-radius:8px;color:#eaf5ee;padding:9px 11px;font-size:13px;margin-bottom:10px;text-align:center}\n.gate-input:focus{outline:none;border-color:#3d6a4d}\n.gate-btn{width:100%;background:#1d5c3c;border:none;color:#eafff2;border-radius:8px;padding:9px;font-size:13px;font-weight:800;cursor:pointer}\n.gate-btn:hover{background:#237049}\n.gate-err{color:#ff8a7a;font-size:10.5px;margin-top:8px;min-height:14px}\n`,\n  render(d2){\n    return `<div class=\"emap\">\n      <header>\n        <h1>🗺️ Mapa de Entidades — Operação via WhatsApp <button class=\"b-refresh\">🔄</button></h1>\n        <div class=\"sub\">Lojas, processos, pedidos e pessoas extraídos e cruzados a partir das mensagens — as mensagens em si são evidência de apoio, não o produto.</div>\n      </header>\n      <div class=\"stats\" data-d2-static>\n        <div class=\"stat\"><b class=\"s-msg\">0</b><span>mensagens</span></div>\n        <div class=\"stat\"><b class=\"s-loja\">0</b><span>lojas/contatos</span></div>\n        <div class=\"stat\"><b class=\"s-proc\">0</b><span>processos ativos</span></div>\n        <div class=\"stat\"><b class=\"s-ped\">0</b><span>pedidos</span></div>\n        <div class=\"stat\"><b class=\"s-pes\">0</b><span>pessoas</span></div>\n        <div class=\"stat\"><span class=\"s-sync mono\">—</span></div>\n      </div>\n      <div class=\"tabs\" data-d2-static>\n        <div class=\"tabbtns\">\n          <button class=\"tab hot\" data-tab=\"lojas\">🏬 Lojas &amp; contatos</button>\n          <button class=\"tab\" data-tab=\"processos\">🔧 Processos</button>\n          <button class=\"tab\" data-tab=\"pedidos\">🧾 Pedidos</button>\n          <button class=\"tab\" data-tab=\"pessoas\">👤 Pessoas</button>\n        </div>\n        <input class=\"search\" type=\"text\" placeholder=\"🔎 buscar loja, pedido, processo...\" />\n      </div>\n      <div class=\"body\">\n        <div class=\"gridcol\"><div class=\"egrid\" data-d2-static></div></div>\n        <div class=\"detail\" data-d2-static><div class=\"na\">Clique num card pra ver os detalhes e as mensagens que originaram essa entidade.</div></div>\n      </div>\n      <div class=\"gate\" data-d2-static>\n        <div class=\"gatebox\">\n          <div class=\"gatelock\">🔒</div>\n          <div class=\"gatetitle\">Acesso restrito</div>\n          <div class=\"gatesub\">Dados operacionais internos da Granado</div>\n          <input type=\"password\" class=\"gate-input\" placeholder=\"Senha\" />\n          <button class=\"gate-btn\">Entrar</button>\n          <div class=\"gate-err\"></div>\n        </div>\n      </div>\n    </div>`\n  },\n  mounted(d2){\n    const gridEl=d2.$('.egrid'), detailEl=d2.$('.detail')\n    if(!gridEl||!detailEl)return\n    const RULES=[\n      {key:'pedido_travado',label:'Pedido travado',icon:'🧾',cls:'crit',re:/preso\\s*(em|no)?\\s*erp|travad[oa]\\s*no\\s*sap|pedido\\s*preso/i},\n      {key:'acesso_oms',label:'Acesso OMS',icon:'🔑',cls:'warn',re:/senha.*oms|liberar\\s*o\\s*oms|acessar\\s*o\\s*oms|oms.*senha/i},\n      {key:'checkout_sem_estoque',label:'Checkout sem estoque',icon:'📦',cls:'crit',re:/sem\\s*termos?\\s*o\\s*produto|aceito\\s*sem.*produto|indevidamente/i},\n      {key:'entrega_status',label:'EuEntrego inconsistente',icon:'🛵',cls:'warn',re:/eu\\s*entrego|euentrego/i,re2:/errone|errad|entregue|retirad[oa]|extravi/i},\n      {key:'estorno',label:'Estorno/cancelamento',icon:'💳',cls:'info',re:/estorno|reembolso|cancelad[oa]/i},\n      {key:'loja_duplicada',label:'Loja duplicada',icon:'🏬',cls:'warn',re:/duas\\s*lojas|excluir\\s*uma\\s*loja|selecionar\\s*a\\s*loja/i},\n      {key:'credencial_exposta',label:'Credencial exposta',icon:'🔐',cls:'crit',re:/@granadophebo\\.com\\.br|@phebo\\.com\\.br/i}\n    ]\n    function classify(t){ if(!t)return null; for(const r of RULES){ if(r.re2){ if(r.re.test(t)&&r.re2.test(t)) return r } else if(r.re.test(t)) return r } return null }\n    function extractOrders(t){ if(!t)return []; const m=t.match(/\\b\\d{8,}\\b/g); return m?[...new Set(m)]:[] }\n    function esc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}\n    function fmtRel(d){ try{return d2.fmt.relative(new Date(d))}catch(e){return ''} }\n    function inferTipoLoja(chatName,isGroup){\n      if(isGroup) return 'grupo'\n      if(/^Granado |Dark Store/i.test(chatName)) return 'loja'\n      return 'contato'\n    }\n    function inferTipoPessoa(senderName,chatName,fromMe){\n      if(fromMe||senderName==='Granado Oms') return 'time interno'\n      if(/rappi|ifood|mercado livre|pluggto/i.test(chatName)) return 'parceiro (marketplace)'\n      return 'loja/contato'\n    }\n    const S={items:[],lojas:{},pedidos:{},pessoas:{},processos:{},tab:'lojas',sel:null,conv:null,lastSync:null,query:''}\n    const ds=d2.data()\n    function setText(sel,v){const el=d2.$(sel);if(el)el.textContent=v}\n    function build(){\n      const lojas={},pedidos={},pessoas={},processos={}\n      for(const i of S.items){\n        const lk=i.chatName\n        const l=lojas[lk]=lojas[lk]||{key:lk,name:lk,tipo:inferTipoLoja(lk,i.isGroup),msgs:0,sinais:0,cat:{},peds:new Set(),pessoas:new Set(),last:i.createdAt,items:[]}\n        l.msgs++; l.items.push(i); if(new Date(i.createdAt)>new Date(l.last))l.last=i.createdAt\n        if(i.rule){\n          l.sinais++; l.cat[i.rule.key]=(l.cat[i.rule.key]||0)+1\n          const pk=i.rule.key\n          const pr=processos[pk]=processos[pk]||{key:pk,rule:i.rule,count:0,lojas:new Set(),peds:new Set(),first:i.createdAt,last:i.createdAt,items:[]}\n          pr.count++; pr.items.push(i); pr.lojas.add(lk)\n          for(const o of i.orders) pr.peds.add(o)\n          if(new Date(i.createdAt)>new Date(pr.last)) pr.last=i.createdAt\n          if(new Date(i.createdAt)<new Date(pr.first)) pr.first=i.createdAt\n        }\n        if(i.senderName && i.senderName!==lk) l.pessoas.add(i.senderName)\n        for(const o of i.orders){\n          l.peds.add(o)\n          const p=pedidos[o]=pedidos[o]||{key:o,order:o,loja:lk,rule:i.rule,first:i.createdAt,last:i.createdAt,count:0,items:[]}\n          p.count++; p.items.push(i)\n          if(new Date(i.createdAt)>new Date(p.last))p.last=i.createdAt\n          if(new Date(i.createdAt)<new Date(p.first))p.first=i.createdAt\n          if(!p.rule && i.rule) p.rule=i.rule\n        }\n        const sk=i.senderName||lk\n        const ps=pessoas[sk]=pessoas[sk]||{key:sk,name:sk,tipo:inferTipoPessoa(sk,lk,i.fromMe),msgs:0,sinais:0,lojas:new Set(),last:i.createdAt,items:[]}\n        ps.msgs++; ps.items.push(i); ps.lojas.add(lk)\n        if(new Date(i.createdAt)>new Date(ps.last))ps.last=i.createdAt\n        if(i.rule) ps.sinais++\n      }\n      S.lojas=lojas; S.pedidos=pedidos; S.pessoas=pessoas; S.processos=processos\n    }\n    function catPills(cat){\n      return Object.keys(cat).map(k=>{ const r=RULES.find(x=>x.key===k); if(!r)return ''; return '<span class=\"epill '+r.cls+'\">'+r.icon+' '+cat[k]+'</span>' }).join('')\n    }\n    function renderStats(){\n      setText('.s-msg',S.items.length)\n      setText('.s-loja',Object.keys(S.lojas).length)\n      setText('.s-proc',Object.keys(S.processos).length)\n      setText('.s-ped',Object.keys(S.pedidos).length)\n      setText('.s-pes',Object.keys(S.pessoas).length)\n      setText('.s-sync', S.lastSync? ('sync '+fmtRel(S.lastSync)) : '—')\n    }\n    function renderGrid(){\n      let html=''\n      const q=S.query\n      const mt=s=>!q||String(s||'').toLowerCase().includes(q)\n      if(S.tab==='lojas'){\n        const arr=Object.values(S.lojas).filter(l=>mt(l.name)).sort((a,b)=> b.sinais-a.sinais || new Date(b.last)-new Date(a.last))\n        html=arr.map(l=>'<div class=\"ecard'+(S.sel&&S.sel.kind==='loja'&&S.sel.key===l.key?' sel':'')+'\" data-kind=\"loja\" data-key=\"'+esc(l.key)+'\">'\n          +'<div class=\"ehead\"><span class=\"etipo\">'+(l.tipo==='loja'?'🏬 loja':l.tipo==='grupo'?'👥 grupo':'💬 contato')+'</span><span class=\"etime\">'+fmtRel(l.last)+'</span></div>'\n          +'<div class=\"ename\">'+esc(l.name)+'</div>'\n          +'<div class=\"erow\"><b>'+l.msgs+'</b> msgs · <b>'+l.sinais+'</b> sinais'+(l.pessoas.size?' · <b>'+l.pessoas.size+'</b> pessoas':'')+'</div>'\n          +'<div class=\"ebadges\">'+catPills(l.cat)+'</div>'\n          +(l.peds.size?'<div class=\"echips\">'+[...l.peds].map(o=>'<span class=\"echip\">#'+o+'</span>').join('')+'</div>':'')\n          +'</div>').join('')\n      }else if(S.tab==='processos'){\n        const arr=Object.values(S.processos).filter(p=>mt(p.rule.label)).sort((a,b)=> b.count-a.count || new Date(b.last)-new Date(a.last))\n        html=arr.map(p=>'<div class=\"ecard'+(S.sel&&S.sel.kind==='processo'&&S.sel.key===p.key?' sel':'')+'\" data-kind=\"processo\" data-key=\"'+esc(p.key)+'\">'\n          +'<div class=\"ehead\"><span class=\"etipo\">'+p.rule.icon+' processo</span><span class=\"etime\">'+fmtRel(p.last)+'</span></div>'\n          +'<div class=\"ename\">'+esc(p.rule.label)+'</div>'\n          +'<div class=\"erow\"><b>'+p.count+'</b> ocorrências · <b>'+p.lojas.size+'</b> lojas'+(p.peds.size?' · <b>'+p.peds.size+'</b> pedidos':'')+'</div>'\n          +'<div class=\"ebadges\"><span class=\"epill '+p.rule.cls+'\">'+p.rule.icon+' '+p.rule.label+'</span></div>'\n          +(p.lojas.size?'<div class=\"echips\">'+[...p.lojas].slice(0,4).map(l=>'<span class=\"echip\">'+esc(l)+'</span>').join('')+(p.lojas.size>4?'<span class=\"echip\">+'+(p.lojas.size-4)+'</span>':'')+'</div>':'')\n          +'</div>').join('')\n      }else if(S.tab==='pedidos'){\n        const arr=Object.values(S.pedidos).filter(p=>mt(p.order)||mt(p.loja)).sort((a,b)=> new Date(b.last)-new Date(a.last))\n        html=arr.map(p=>'<div class=\"ecard'+(S.sel&&S.sel.kind==='pedido'&&S.sel.key===p.key?' sel':'')+'\" data-kind=\"pedido\" data-key=\"'+esc(p.key)+'\">'\n          +'<div class=\"ehead\"><span class=\"etipo\">🧾 pedido</span><span class=\"etime\">'+fmtRel(p.last)+'</span></div>'\n          +'<div class=\"ename mono\">#'+p.order+'</div>'\n          +'<div class=\"erow\">loja/contexto: <b>'+esc(p.loja)+'</b></div>'\n          +'<div class=\"erow\">mencionado <b>'+p.count+'</b>x</div>'\n          +(p.rule?'<div class=\"ebadges\"><span class=\"epill '+p.rule.cls+'\">'+p.rule.icon+' '+p.rule.label+'</span></div>':'')\n          +'</div>').join('')\n      }else{\n        const arr=Object.values(S.pessoas).filter(ps=>mt(ps.name)).sort((a,b)=> b.sinais-a.sinais || new Date(b.last)-new Date(a.last))\n        html=arr.map(ps=>'<div class=\"ecard'+(S.sel&&S.sel.kind==='pessoa'&&S.sel.key===ps.key?' sel':'')+'\" data-kind=\"pessoa\" data-key=\"'+esc(ps.key)+'\">'\n          +'<div class=\"ehead\"><span class=\"etipo\">👤 '+esc(ps.tipo)+'</span><span class=\"etime\">'+fmtRel(ps.last)+'</span></div>'\n          +'<div class=\"ename\">'+esc(ps.name)+'</div>'\n          +'<div class=\"erow\"><b>'+ps.msgs+'</b> msgs em <b>'+ps.lojas.size+'</b> conversa(s)'+(ps.sinais?' · <b>'+ps.sinais+'</b> sinais':'')+'</div>'\n          +'</div>').join('')\n      }\n      gridEl.innerHTML = html || '<div class=\"na\">'+(q?'Nada encontrado pra \"'+esc(q)+'\".':'Nada aqui ainda.')+'</div>'\n    }\n    function fmtDT(d){ try{return d2.fmt.date(new Date(d))+' '+d2.fmt.time(new Date(d))}catch(e){return fmtRel(d)} }\n    function renderConversation(){\n      const chat=S.conv.chat\n      const msgs=S.items.filter(i=>i.chatName===chat).sort((a,b)=> new Date(a.createdAt)-new Date(b.createdAt))\n      let html='<button class=\"b-back\">← Voltar</button>'\n        +'<div class=\"dhead\">'+esc(chat)+'</div><div class=\"dsub\">conversa completa · '+msgs.length+' mensagens</div>'\n        +'<h3>Conversa (ordem cronológica)</h3>'\n        +msgs.map(m=>'<div class=\"crow2'+(m.fromMe?' me':'')+(m.id===S.conv.highlight?' hl':'')+'\"'+(m.id===S.conv.highlight?' data-hl=\"1\"':'')+'>'\n          +'<div class=\"crow2top\"><span>'+(m.fromMe?'time interno':esc(m.senderName||chat))+'</span><span>'+fmtDT(m.createdAt)+'</span></div>'\n          +'<div class=\"crow2text\">'+(esc(m.text)||'<i>(mídia/reação sem texto)</i>')+'</div>'\n        +'</div>').join('')\n      detailEl.innerHTML=html\n      const hl=detailEl.querySelector('[data-hl=\"1\"]')\n      if(hl && hl.scrollIntoView) hl.scrollIntoView({block:'center'})\n    }\n    function renderDetail(){\n      if(S.conv){ renderConversation(); return }\n      if(!S.sel){ detailEl.innerHTML='<div class=\"na\">Clique num card pra ver os detalhes e as mensagens que originaram essa entidade.</div>'; return }\n      let src, title, sub\n      if(S.sel.kind==='loja'){ src=S.lojas[S.sel.key]; title=src.name; sub=src.tipo+' · '+src.msgs+' mensagens · '+src.sinais+' sinais' }\n      else if(S.sel.kind==='processo'){ src=S.processos[S.sel.key]; title=src.rule.icon+' '+src.rule.label; sub=src.count+' ocorrências · '+src.lojas.size+' loja(s)'+(src.peds.size?' · '+src.peds.size+' pedido(s)':'') }\n      else if(S.sel.kind==='pedido'){ src=S.pedidos[S.sel.key]; title='Pedido #'+src.order; sub='origem: '+src.loja+' · mencionado '+src.count+'x' }\n      else { src=S.pessoas[S.sel.key]; title=src.name; sub=src.tipo+' · '+src.msgs+' mensagens' }\n      const items=[...src.items].sort((a,b)=> new Date(b.createdAt)-new Date(a.createdAt))\n      let html='<div class=\"dhead\">'+esc(title)+'</div><div class=\"dsub\">'+esc(sub)+'</div>'\n      if(S.sel.kind==='processo') html+='<div class=\"ebadges\">'+[...src.lojas].map(l=>'<span class=\"epill\">'+esc(l)+'</span>').join('')+'</div>'\n      if(S.sel.kind==='loja') html+='<button class=\"b-convtop\" data-chat=\"'+esc(title)+'\" data-id=\"\">↗ Ver conversa completa</button>'\n      html+='<h3>Evidências (mensagens)</h3>'\n        +items.map(i=>'<div class=\"evrow\"><div class=\"evtop\"><span>'+esc(i.chatName)+(i.senderName&&i.senderName!==i.chatName?' · '+esc(i.senderName):'')+'</span><span>'+fmtRel(i.createdAt)+'</span></div><div class=\"evtext\">'+(esc(i.text)||'<i>(mídia/reação sem texto)</i>')+'</div>'\n          +'<button class=\"b-conv\" data-chat=\"'+esc(i.chatName)+'\" data-id=\"'+esc(i.id)+'\">↗ ver conversa</button>'\n          +'</div>').join('')\n      detailEl.innerHTML=html\n    }\n    function renderAll(){ renderStats(); renderGrid(); renderDetail() }\n    async function load(){\n      try{\n        await ds.refresh(); await ds.loadAll()\n        const recs=ds.records()\n        S.items=recs.map(r=>{\n          const p=r.payload||{}\n          const text=(p.text&&p.text.message)||''\n          return {id:r._id,chatName:p.chatName||'(sem nome)',senderName:p.senderName||'',fromMe:!!p.fromMe,isGroup:!!p.isGroup,text,rule:classify(text),orders:extractOrders(text),createdAt:r.createdAt}\n        })\n        build(); S.lastSync=new Date(); renderAll()\n      }catch(e){ d2.log('emap load error', e) }\n    }\n    d2.on('.tab','click',e=>{\n      const b=e.target.closest('.tab'); if(!b)return\n      S.tab=b.getAttribute('data-tab'); S.sel=null; S.conv=null\n      d2.$('.tab').forEach(x=>x.classList.toggle('hot',x===b))\n      renderGrid(); renderDetail()\n    })\n    d2.on('.search','input',e=>{ S.query=(e.target.value||'').toLowerCase().trim(); renderGrid() })\n    d2.on('.ecard','click',e=>{\n      const c=e.target.closest('.ecard'); if(!c)return\n      S.conv=null\n      S.sel={kind:c.getAttribute('data-kind'),key:c.getAttribute('data-key')}\n      renderGrid(); renderDetail()\n    })\n    d2.on('.b-conv','click',e=>{\n      const b=e.target.closest('.b-conv'); if(!b)return\n      S.conv={chat:b.getAttribute('data-chat'),highlight:b.getAttribute('data-id')}\n      renderDetail()\n    })\n    d2.on('.b-convtop','click',e=>{\n      const b=e.target.closest('.b-convtop'); if(!b)return\n      S.conv={chat:b.getAttribute('data-chat'),highlight:null}\n      renderDetail()\n    })\n    d2.on('.b-back','click',()=>{ S.conv=null; renderDetail() })\n    d2.on('.b-refresh','click',()=>{load()})\n    const GATE_KEY='wamon_gate_ok_v1'\n    const gateEl=d2.$('.gate')\n    function start(){\n      load()\n      let acc=0,prev=null\n      d2.raf(t=>{ if(prev==null)prev=t; const dt=(t-prev)/1000; prev=t; acc+=dt; if(acc>20){acc=0;load()} })\n    }\n    function tryUnlock(pw){\n      if(pw==='Granado@2026@data2'){\n        try{localStorage.setItem(GATE_KEY,'1')}catch(e){}\n        if(gateEl) gateEl.classList.add('hidden')\n        start()\n        return true\n      }\n      return false\n    }\n    let unlocked=false\n    try{ unlocked = localStorage.getItem(GATE_KEY)==='1' }catch(e){}\n    if(unlocked){ if(gateEl) gateEl.classList.add('hidden'); start() }\n    d2.on('.gate-btn','click',()=>{\n      const inp=d2.$('.gate-input')\n      if(!tryUnlock(inp?inp.value:'')){\n        const err=d2.$('.gate-err'); if(err) err.textContent='Senha incorreta'\n      }\n    })\n    d2.on('.gate-input','keydown',e=>{ if(e.key==='Enter'){ const b=d2.$('.gate-btn'); if(b) b.click() } })\n  }\n}"}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}}}}}}}},{"_id":"6a8c87b8472417a8ccc3b2c9","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bAgentClient","package":"granado-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:`.app{display:flex;flex-direction:column;height:100%;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;background:#F5F0E1;color:#2B2B23}\n.hdr{background:#1E4D3B;color:#F5F0E1;padding:16px 22px;display:flex;align-items:center;justify-content:space-between;border-bottom:3px solid #B8963E;flex-shrink:0}\n.brand{font-family:Georgia,'Times New Roman',serif;font-size:21px}\n.brand .sep{color:#B8963E;margin:0 6px}\n.brand .b2b{font-size:12px;color:#E8CF8F;letter-spacing:2px;margin-left:8px;text-transform:uppercase}\n.logout-btn{background:none;border:1px solid rgba(245,240,225,.4);color:#F5F0E1;padding:6px 13px;border-radius:20px;font-size:12px;cursor:pointer;font-family:inherit}\n.logout-btn:hover{background:rgba(255,255,255,.08)}\n.login-wrap{flex:1;display:flex;align-items:center;justify-content:center;padding:24px;overflow:auto}\n.login-card{background:#fff;border-radius:16px;padding:32px;max-width:360px;width:100%;box-shadow:0 10px 28px rgba(30,77,59,.14)}\n.login-card h2{font-family:Georgia,'Times New Roman',serif;margin:0 0 4px;font-size:21px;color:#1E4D3B}\n.login-card p.sub{margin:0 0 22px;font-size:13px;color:#8a8574}\n.field{margin-bottom:14px}\n.field label{display:block;font-size:12px;margin-bottom:5px;color:#555;font-weight:600}\n.field input{width:100%;padding:11px 13px;border:1px solid #ddd6c2;border-radius:9px;font-size:14px;box-sizing:border-box;font-family:inherit}\n.field input:focus{outline:none;border-color:#1E4D3B}\n.login-btn{width:100%;padding:12px;background:#1E4D3B;color:#fff;border:none;border-radius:9px;font-size:14px;font-weight:700;cursor:pointer;margin-top:8px;font-family:inherit;letter-spacing:.3px}\n.login-btn:hover:not(:disabled){background:#163A2C}\n.login-btn:disabled{opacity:.6;cursor:default}\n.login-err{color:#a4392e;font-size:12.5px;margin-top:12px;background:#fbeceb;padding:8px 10px;border-radius:8px}\n.chat{flex:1;display:flex;flex-direction:column;min-height:0}\n.log{flex:1;overflow-y:auto;padding:18px 16px;display:flex;flex-direction:column;gap:10px}\n.msg{max-width:80%;padding:10px 14px;border-radius:15px;font-size:14px;line-height:1.5;white-space:pre-wrap}\n.msg.user{align-self:flex-end;background:#1E4D3B;color:#fff;border-bottom-right-radius:4px}\n.msg.assistant{align-self:flex-start;background:#fff;border:1px solid #e7e0cc;border-bottom-left-radius:4px;color:#2B2B23}\n.typing{align-self:flex-start;background:#fff;border:1px solid #e7e0cc;padding:10px 16px;border-radius:15px;font-size:13px;color:#9a9585;border-bottom-left-radius:4px}\n.dot{display:inline-block;width:5px;height:5px;border-radius:50%;background:#9a9585;margin:0 1px;animation:blink 1.1s infinite}\n.dot:nth-child(2){animation-delay:.15s}.dot:nth-child(3){animation-delay:.3s}\n@keyframes blink{0%,80%,100%{opacity:.25}40%{opacity:1}}\n.empty{margin:auto;text-align:center;color:#9a9585;font-size:13px;max-width:220px}\n.row{display:flex;gap:8px;padding:12px 14px;border-top:1px solid #e7e0cc;background:#fff;flex-shrink:0}\n.row input{flex:1;padding:11px 15px;border:1px solid #ddd6c2;border-radius:24px;font-size:14px;font-family:inherit;box-sizing:border-box}\n.row input:focus{outline:none;border-color:#1E4D3B}\n.row button{background:#1E4D3B;color:#fff;border:none;border-radius:50%;width:42px;height:42px;cursor:pointer;font-size:16px;flex-shrink:0}\n.row button:hover:not(:disabled){background:#163A2C}\n.row button:disabled{opacity:.5}\n@media(max-width:480px){.hdr{padding:12px 16px}.brand{font-size:18px}.login-card{padding:24px 20px}.msg{max-width:88%}}\n.widget-wrap{max-width:92%;align-self:flex-start;display:flex;flex-direction:column;gap:8px}\n.prod-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px;background:#fff;border:1px solid #e7e0cc;border-radius:15px;padding:12px;border-bottom-left-radius:4px}\n.prod-card{display:flex;flex-direction:column;background:#F5F0E1;border-radius:10px;overflow:hidden;border:1px solid #eee2c8}\n.prod-card img{width:100%;height:100px;object-fit:contain;background:#fff;padding:6px;box-sizing:border-box}\n.prod-info{padding:8px 9px 9px;display:flex;flex-direction:column;gap:5px;flex:1}\n.prod-name{font-size:12px;line-height:1.35;color:#2B2B23;min-height:32px}\n.prod-price{font-size:14px;font-weight:700;color:#1E4D3B}\n.prod-qty-row{display:flex;gap:5px;margin-top:auto}\n.prod-qty{width:38px;padding:5px 4px;border:1px solid #ddd6c2;border-radius:6px;font-size:11.5px;font-family:inherit;text-align:center;flex-shrink:0}\n.prod-add{flex:1;background:#1E4D3B;color:#fff;border:none;border-radius:7px;padding:6px 4px;font-size:11.5px;font-weight:600;cursor:pointer;font-family:inherit;min-width:0}\n.prod-add:hover{background:#163A2C}\n.prod-add:disabled{opacity:.55;cursor:default}\n.prod-card.oos{opacity:.8}\n.prod-oos-badge{font-size:9.5px;font-weight:700;color:#a4392e;background:#fbeceb;padding:2px 7px;border-radius:20px;align-self:flex-start}\n.prod-alert{flex:1;background:#B8963E;color:#fff;border:none;border-radius:7px;padding:6px 4px;font-size:11.5px;font-weight:600;cursor:pointer;font-family:inherit;min-width:0}\n.prod-alert:hover{background:#a17f30}\n.prod-alert:disabled{opacity:.55;cursor:default}\n.order-list{display:flex;flex-direction:column;gap:8px;background:#fff;border:1px solid #e7e0cc;border-radius:15px;padding:12px;border-bottom-left-radius:4px}\n.order-card{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:9px 11px;background:#F5F0E1;border-radius:9px;border:1px solid #eee2c8}\n.order-left{display:flex;flex-direction:column;gap:2px}\n.order-num{font-size:12.5px;font-weight:700;color:#1E4D3B}\n.order-date{font-size:11px;color:#8a8574}\n.order-right{display:flex;flex-direction:column;align-items:flex-end;gap:3px}\n.order-total{font-size:13px;font-weight:700;color:#2B2B23}\n.order-status{font-size:10.5px;font-weight:600;padding:2px 8px;border-radius:20px;background:#E8CF8F;color:#5c4a12;white-space:nowrap}\n.msg.assistant p{margin:0 0 6px}\n.msg.assistant p:last-child{margin-bottom:0}\n.msg.assistant ul{margin:2px 0 6px;padding-left:18px}\n.msg.assistant li{margin-bottom:3px}\n.msg.assistant li:last-child{margin-bottom:0}`,\n\n  render(d2){\n    const view = d2.state.view || 'login'\n    const header = `<header class=\"hdr\">\n      <div class=\"brand\">Granado<span class=\"sep\">|</span>Phebo<span class=\"b2b\">B2B</span></div>\n      ${view === 'chat' ? '<button class=\"logout-btn\">Sair</button>' : ''}\n    </header>`\n\n    if (view === 'login') {\n      return `<div class=\"app\">${header}\n      <div class=\"login-wrap\">\n        <div class=\"login-card\">\n          <h2>Portal B2B</h2>\n          <p class=\"sub\">Entre com seu acesso do portal Granado B2B</p>\n          <div class=\"field\"><label>E-mail</label><input class=\"f-email\" type=\"email\" placeholder=\"voce@empresa.com\" ${d2.state.loginBusy?'disabled':''}/></div>\n          <div class=\"field\"><label>Senha</label><input class=\"f-pass\" type=\"password\" placeholder=\"••••••••\" ${d2.state.loginBusy?'disabled':''}/></div>\n          <button class=\"login-btn\" ${d2.state.loginBusy?'disabled':''}>${d2.state.loginBusy?'Entrando…':'Entrar'}</button>\n          ${d2.state.loginError ? `<div class=\"login-err\">${esc(d2.state.loginError)}</div>` : ''}\n        </div>\n      </div></div>`\n    }\n\n    const msgs = d2.state.messages || []\n    const bubbles = msgs.map((m,i) => {\n      const key = `m${i}`\n      if (m.widget === 'products' && m.products && m.products.length) {\n        const cards = m.products.map(p => {\n          const oos = p.inStock === false\n          const badge = oos ? `<div class=\"prod-oos-badge\">Sem estoque</div>` : ''\n          const controls = oos\n            ? `<div class=\"prod-qty-row\"><button class=\"prod-alert\" data-name=\"${(p.name||'').replace(/\"/g,'&quot;')}\">Avise-me</button></div>`\n            : `<div class=\"prod-qty-row\"><input type=\"number\" class=\"prod-qty\" value=\"1\" min=\"1\"/><button class=\"prod-add\" data-pid=\"${p.productId}\" data-name=\"${(p.name||'').replace(/\"/g,'&quot;')}\">Adicionar</button></div>`\n          return `<div class=\"prod-card ${oos?'oos':''}\">\n          <img src=\"${p.image||''}\" loading=\"lazy\"/>\n          <div class=\"prod-info\">\n            ${badge}\n            <div class=\"prod-name\">${p.name||''}</div>\n            <div class=\"prod-price\">R$ ${p.price||'—'}</div>\n            ${controls}\n          </div>\n        </div>`\n        }).join('')\n        return `<div class=\"widget-wrap\" data-key=\"${key}\">${m.text?`<div class=\"msg assistant\">${esc(m.text)}</div>`:''}<div class=\"prod-grid\">${cards}</div></div>`\n      }\n      if (m.widget === 'orders' && m.orders && m.orders.length) {\n        const rows = m.orders.map(o => `<div class=\"order-card\">\n          <div class=\"order-left\"><div class=\"order-num\">Pedido ${o.numero}</div><div class=\"order-date\">${o.data}</div></div>\n          <div class=\"order-right\"><div class=\"order-total\">${o.total}</div><div class=\"order-status\">${o.status}</div></div>\n        </div>`).join('')\n        return `<div class=\"widget-wrap\" data-key=\"${key}\">${m.text?`<div class=\"msg assistant\">${esc(m.text)}</div>`:''}<div class=\"order-list\">${rows}</div></div>`\n      }\n      const body = m.role === 'user' ? esc(m.text) : mdToHtml(m.text)\n      return `<div class=\"msg ${m.role==='user'?'user':'assistant'}\" data-key=\"${key}\">${body}</div>`\n    }).join('')\n    const empty = msgs.length === 0 ? `<div class=\"empty\">Pergunte sobre prazos, políticas, seus pedidos, ou peça pra adicionar um produto no carrinho.</div>` : ''\n    const typing = d2.state.sendBusy ? `<div class=\"typing\">${esc(d2.state.thinking || 'Pensando')}<span class=\"dot\"></span><span class=\"dot\"></span><span class=\"dot\"></span></div>` : ''\n\n    return `<div class=\"app\">${header}\n    <div class=\"chat\">\n      <div class=\"log\">${empty}${bubbles}${typing}</div>\n      <div class=\"row\">\n        <input class=\"chat-in\" placeholder=\"Digite sua mensagem…\" ${d2.state.sendBusy?'disabled':''}/>\n        <button class=\"chat-send\" ${d2.state.sendBusy?'disabled':''}>➤</button>\n      </div>\n    </div></div>`\n  },\n\n  mounted(d2){\n    try {\n      const saved = localStorage.getItem('b2bSession')\n      const savedToken = localStorage.getItem('b2bToken')\n      if (saved) { d2.state.session = saved; d2.state.token = savedToken; d2.state.view = 'chat' }\n    } catch {}\n\n    const doLogin = async () => {\n      const email = d2.$('.f-email')?.value.trim()\n      const password = d2.$('.f-pass')?.value\n      if (!email || !password) { d2.state.loginError = 'Preencha e-mail e senha.'; return }\n      d2.state.loginBusy = true\n      d2.state.loginError = ''\n      try {\n        const res = await d2.rush('b2bLogin').invoke({ email, password })\n        const out = res?.data ?? res\n        if (out?.ok) {\n          d2.state.session = out.session\n          d2.state.token = out.token\n          try { localStorage.setItem('b2bSession', out.session); localStorage.setItem('b2bToken', out.token || '') } catch {}\n          d2.state.messages = [{ role:'assistant', text:'Login efetuado! Como posso ajudar hoje?' }]\n          d2.state.view = 'chat'\n        } else {\n          d2.state.loginError = out?.message || 'Não foi possível entrar.'\n        }\n      } catch (e) {\n        d2.state.loginError = 'Erro ao conectar. Tente novamente.'\n      } finally {\n        d2.state.loginBusy = false\n      }\n    }\n    d2.on('.login-btn','click', doLogin)\n    d2.on('.f-pass','keydown', (e)=>{ if (e.key === 'Enter') doLogin() })\n\n    const send = async () => {\n      const el = d2.$('.chat-in')\n      const text = el?.value.trim()\n      if (!text || d2.state.sendBusy) return\n      el.value = ''\n      const historyText = buildHistoryText(d2.state.messages)\n      d2.state.messages = [...(d2.state.messages||[]), { role:'user', text }]\n      d2.state.sendBusy = true\n      d2.state.thinking = 'Analisando sua mensagem…'\n      try {\n        const res = await d2.rush('b2bAgent').stream(\n          { message: text, session: d2.state.session, token: d2.state.token, history: historyText },\n          (u) => { d2.state.thinking = u?.payload?.text || d2.state.thinking }\n        )\n        const out = res?.data ?? res\n        d2.state.messages = [...d2.state.messages, {\n          role:'assistant',\n          text: out?.reply || 'Desculpa, algo deu errado.',\n          widget: out?.widget,\n          products: out?.products,\n          orders: out?.orders,\n          cartItems: out?.cartItems,\n        }]\n      } catch (e) {\n        d2.state.messages = [...d2.state.messages, { role:'assistant', text: 'Erro ao falar com o agente. Tente de novo.' }]\n      } finally {\n        d2.state.sendBusy = false\n        d2.state.thinking = null\n      }\n    }\n    d2.on('.chat-send','click', send)\n    d2.on('.chat-in','keydown', (e)=>{ if (e.key === 'Enter') send() })\n\n    d2.on('.prod-add','click', async (e, el) => {\n      const pid = el.dataset.pid\n      const name = el.dataset.name\n      const qtyEl = el.parentElement?.querySelector('.prod-qty')\n      const qty = Math.max(1, parseInt(qtyEl?.value, 10) || 1)\n      if (!pid || el.disabled) return\n      el.disabled = true\n      el.textContent = 'Adicionando…'\n      try {\n        const res = await d2.rush('b2bMontarCarrinho').invoke({ session: d2.state.session, productId: pid, qty })\n        const out = res?.data ?? res\n        d2.state.messages = [...(d2.state.messages||[]),\n          { role:'user', text: `Adicionar: ${name}` },\n          { role:'assistant', text: out?.ok ? `Adicionei ${qty}x \"${name}\" ao carrinho.` : (out?.message || 'Não consegui adicionar ao carrinho.') }\n        ]\n      } catch (err) {\n        d2.state.messages = [...(d2.state.messages||[]), { role:'assistant', text: 'Erro ao adicionar ao carrinho.' }]\n      }\n    })\n\n    d2.on('.prod-alert','click', async (e, el) => {\n      const name = el.dataset.name\n      if (el.disabled) return\n      el.disabled = true\n      el.textContent = 'Registrando…'\n      const msgText = `Me avise quando \"${name}\" voltar ao estoque`\n      const historyText = buildHistoryText(d2.state.messages)\n      d2.state.messages = [...(d2.state.messages||[]), { role:'user', text: msgText }]\n      d2.state.sendBusy = true\n      d2.state.thinking = 'Registrando alerta de estoque…'\n      try {\n        const res = await d2.rush('b2bAgent').stream(\n          { message: msgText, session: d2.state.session, token: d2.state.token, history: historyText },\n          (u) => { d2.state.thinking = u?.payload?.text || d2.state.thinking }\n        )\n        const out = res?.data ?? res\n        d2.state.messages = [...d2.state.messages, { role:'assistant', text: out?.reply || 'Não consegui registrar o alerta.' }]\n      } catch (err) {\n        d2.state.messages = [...d2.state.messages, { role:'assistant', text: 'Erro ao registrar o alerta.' }]\n      } finally {\n        d2.state.sendBusy = false\n        d2.state.thinking = null\n      }\n    })\n\n    d2.on('.logout-btn','click', ()=>{\n      try { localStorage.removeItem('b2bSession'); localStorage.removeItem('b2bToken') } catch {}\n      d2.state.session = null\n      d2.state.token = null\n      d2.state.view = 'login'\n      d2.state.messages = []\n    })\n  },\n\n  updated(d2){\n    const log = d2.$('.log')\n    if (log) log.scrollTop = log.scrollHeight\n  },\n}\nfunction esc(s){ return String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') }\nfunction buildHistoryText(messages){\n  return (messages||[]).map(m => {\n    if (m.role === 'user') return `Cliente: ${m.text}`\n    let line = `Atendente: ${m.text}`\n    if (m.widget === 'products' && m.products && m.products.length) {\n      const list = m.products.map(p => `${p.name} (id=${p.productId}, sku=${p.sku||''}, R$ ${p.price}${p.inStock===false?', SEM ESTOQUE':''})`).join('; ')\n      line += `\\n[produtos mostrados: ${list}]`\n    }\n    if (Array.isArray(m.cartItems) && m.cartItems.length) {\n      const list = m.cartItems.map(c => `${c.name} (item_id=${c.itemId}, sku=${c.sku}, qty=${c.qty})`).join('; ')\n      line += `\\n[carrinho agora: ${list}]`\n    } else if (Array.isArray(m.cartItems) && m.cartItems.length === 0 && m.role !== 'user' && /carrinho/i.test(m.text||'')) {\n      line += `\\n[carrinho agora: vazio]`\n    }\n    if (m.widget === 'orders' && m.orders && m.orders.length) {\n      const list = m.orders.map(o => `Pedido ${o.numero} (${o.data}, ${o.total}, ${o.status})`).join('; ')\n      line += `\\n[pedidos mostrados nesta resposta: ${list}]`\n    }\n    return line\n  }).join('\\n')\n}\nfunction boldify(s){ return String(s).replace(/\\*\\*(.+?)\\*\\*/g, '<strong>$1</strong>') }\nfunction mdToHtml(md){\n  if (!md) return ''\n  const lines = String(md).split('\\n')\n  let html = '', inList = false\n  for (const raw of lines) {\n    const line = raw.trim()\n    if (/^[-*]\\s+/.test(line)) {\n      if (!inList) { html += '<ul>'; inList = true }\n      html += `<li>${boldify(line.replace(/^[-*]\\s+/, ''))}</li>`\n    } else if (line) {\n      if (inList) { html += '</ul>'; inList = false }\n      html += `<p>${boldify(line)}</p>`\n    } else if (inList) {\n      html += '</ul>'; inList = false\n    }\n  }\n  if (inList) html += '</ul>'\n  return html\n}\n"}}}}}}}},{"_id":"6a90542dae612488af4c4c9e","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kB2bProgresso","package":"granado-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:`.wrap{background:#F5F0E1;color:#2B2B23;font-family:'Gill Sans','Trebuchet MS',-apple-system,sans-serif;padding:26px 30px 70px;box-sizing:border-box;max-width:1180px;margin:0 auto}\n.eyebrow{font-size:11px;letter-spacing:2.5px;text-transform:uppercase;color:#8C6F2A;margin:0 0 6px}\nh2{font-family:Georgia,'Times New Roman',serif;color:#1E4D3B;font-size:30px;margin:0 0 8px;font-weight:700}\n.lead{font-size:15px;line-height:1.55;color:#4A4A3C;max-width:800px;margin:0 0 18px}\n.obs{background:#F1EBD9;border:1px solid #D5CBAA;border-left:4px solid #B8963E;border-radius:8px;padding:13px 17px;font-size:13.5px;line-height:1.55;color:#4A4A3C;margin:0 0 18px}\n.obs b{color:#3B3A2E}\n.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;margin:0 0 22px}\n.stat{background:#FFFDF6;border:1px solid #E3D9BD;border-radius:10px;padding:14px 16px;text-align:center}\n.stat .v{font-family:Georgia,serif;font-size:26px;color:#1E4D3B}\n.stat .l{font-size:11px;letter-spacing:1px;text-transform:uppercase;color:#8C6F2A;margin-top:4px}\n.legend{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 22px}\n.lg{font-size:12px;border-radius:999px;padding:4px 12px;border:1px solid}\n.lg.i{background:#E7F0E9;color:#1E6B43;border-color:#BCD4C3}\n.lg.p{background:#FBF3DC;color:#8C6F2A;border-color:#E3D0A0}\n.lg.n{background:#F7E4E0;color:#A33B2E;border-color:#E5C0B8}\nh3.sec{display:flex;align-items:baseline;gap:10px;font-family:Georgia,serif;color:#1E4D3B;font-size:19px;margin:30px 0 12px;border-bottom:2px solid #D9C289;padding-bottom:6px}\nh3.sec .gn{margin-left:auto;font-family:'Gill Sans','Trebuchet MS',sans-serif;font-size:12px;color:#8A8672;font-weight:400}\n.cat-note{font-size:12.5px;color:#6B6753;background:#F8F2E2;border:1px dashed #C9BC97;border-radius:6px;padding:8px 10px;margin:0 0 10px;line-height:1.5}\n.opt{display:flex;gap:10px;align-items:flex-start;padding:9px 0 9px 14px;border-left:3px solid #D9C289;margin:0 0 9px;background:#FFFDF6;border-radius:0 8px 8px 0}\n.opt.n{border-left-color:#CD8B80}\n.opt.p{border-left-color:#D9A93D}\n.opt .q{font-size:13.5px;font-weight:600;color:#3B3A2E}\n.opt .a{font-size:12.5px;color:#6B6753;line-height:1.45;margin-top:2px}\n.tag{flex:0 0 auto;font-size:10.5px;letter-spacing:.5px;border-radius:999px;padding:3px 9px;margin:2px 12px 0 0;white-space:nowrap;border:1px solid}\n.tag.i{background:#E7F0E9;color:#1E6B43;border-color:#BCD4C3}\n.tag.p{background:#FBF3DC;color:#8C6F2A;border-color:#E3D0A0}\n.tag.n{background:#F7E4E0;color:#A33B2E;border-color:#E5C0B8}\n@media(max-width:640px){.wrap{padding:18px 14px 60px}h2{font-size:24px}.opt{flex-wrap:wrap}}`,\n  render(d2){\n    const T={i:'✅ implementado',p:'🟡 parcial',n:'⛔ não iniciado'}\n    const CATS=[\n      ['🌐 Canal & arquitetura',null,[\n        ['Web chat autenticado (e-mail + senha)','kB2bAgentClient + b2bLogin — sessão Magento e bearer token real','i'],\n        ['WhatsApp via SFMC (webhook Data2 ↔ SFMC)','Só existe o contrato/plano (aba Integração SFMC) — nenhum endpoint provisionado','n'],\n        ['Onboarding por CNPJ (situação · CNAE · IE)','Bot atual pressupõe conta já aprovada — não existe captura de CNPJ','n'],\n        ['Motor de 14 tabelas de preço (UF × contribuinte)','O preço já vem calculado do Magento pela conta logada, mas o bot não resolve UF/IE nem escolhe tabela — é herdado, não implementado','p']]],\n      ['🧴 Produtos e estoque',null,[\n        ['Buscar produto no catálogo','buscar_produtos faz busca viva no catálogo real','i'],\n        ['Avise-me quando chegar (sem estoque)','avisar_estoque aciona o alerta real do Magento','i'],\n        ['Explicar divisão de portfólio (mix restrito)','A busca não distingue \"não existe\" de \"não é revendido\" — sem explicação dedicada','p'],\n        ['Catálogo completo de revenda (PDF)','Não implementado','n']]],\n      ['🚚 Pedidos e entregas',null,[\n        ['Consultar meus pedidos','consultar_pedidos lista os pedidos reais da conta','i'],\n        ['Rastrear pedido','detalhes_pedido acessa a nota fiscal e o Confirma Fácil — rastreio real, cadeia OMS → Magento → NF → Confirma Fácil','i'],\n        ['Nota fiscal (2ª via PDF/XML)','Não implementado','n'],\n        ['Avaria / sobra / item faltando','Não implementado — a regra do cupom (abaixo de R$500) vs. Nota de Devolução (acima) não está no bot','n'],\n        ['Cancelamento / devolução (orientação)','Só resposta genérica da IA, sem dado real nem fluxo guiado','p']]],\n      ['💳 Pagamentos e financeiro',null,[\n        ['Crédito da empresa','consultar_credito lê o crédito real da conta','i'],\n        ['Explicar boleto bloqueado / limite insuficiente','Não implementado','n'],\n        ['2ª via de boleto','Não implementado','n'],\n        ['Split de pagamento (boleto até o limite + resto em PIX/cartão)','Ideia registrada nas macros, sem decisão fechada nem implementação','n']]],\n      ['🛒 Jornada de compra','Item novo do projeto — não existia no bot original',[\n        ['Montar carrinho (1 ou N itens)','montar_carrinho / montar_carrinho_multiplo','i'],\n        ['Ver carrinho com total real','ver_carrinho — totais REST do Magento (subtotal + impostos + total), corrigido nesta semana','i'],\n        ['Remover item do carrinho','remover_do_carrinho','i'],\n        ['Atualizar quantidade','atualizar_qty_carrinho — o endpoint updateItemQty era um falso-positivo (retornava sucesso sem salvar); corrigido para o updatePost real','i'],\n        ['Escolher forma de pagamento','Não implementado','n'],\n        ['Finalizar o pedido (checkout)','Maior gap do bot hoje: o carrinho nunca vira pedido fechado','n'],\n        ['Validar pedido mínimo por região','Não implementado no bot (o Magento pode validar no checkout, que ainda não existe aqui)','n'],\n        ['Aplicar cupom (BEMVINDO10 · B2B10 · B2B5)','Não implementado','n']]],\n      ['📝 Cadastro','Cliente sem conta aprovada — universo inteiro fora do bot atual',[\n        ['Novo cadastro (coleta de dados)','Não implementado','n'],\n        ['Cadastro rejeitado (explicar motivo)','Não implementado','n'],\n        ['Cadastro pendente (status)','Não implementado','n'],\n        ['Alterar dados da empresa','Não implementado','n']]],\n      ['🧭 Fora de escopo / roteamento',null,[\n        ['Detectar assunto fora de escopo e rotear (SAC · comercial · amenities · exportação)','Fallback é resposta genérica da IA — sem roteamento explícito por assunto','n']]]]\n    const all=CATS.flatMap(c=>c[2])\n    const count=k=>all.filter(o=>o[2]===k).length\n    const cat=c=>`<h3 class=\"sec\">${c[0]}<span class=\"gn\">${c[2].length} itens</span></h3>${c[1]?`<div class=\"cat-note\">${c[1]}</div>`:''}${c[2].map(o=>`<div class=\"opt ${o[2]}\"><div style=\"flex:1\"><div class=\"q\">${o[0]}</div><div class=\"a\">${o[1]}</div></div><span class=\"tag ${o[2]}\">${T[o[2]]}</span></div>`).join('')}`\n    return `<div class=\"wrap\">\n  <p class=\"eyebrow\">Progresso · matriz planejado × executado</p>\n  <h2>O que já existe no bot de atendimento</h2>\n  <p class=\"lead\">Cruzamento entre tudo que foi mapeado neste material (bot antigo, fluxo alvo, regras de negócio) e o que já está implementado, de verdade, no assistente de atendimento B2B em produção — para termos uma ideia clara do que já funciona e do que ainda falta.</p>\n  <div class=\"obs\">ℹ️ <b>Leitura importante.</b> O bot implementado é um <b>web chat autenticado</b> para clientes que já têm conta B2B aprovada — ele resolve pedidos, rastreio, estoque e carrinho dentro da conversa. Ele <b>não</b> é uma implementação do Fluxo Alvo (que prevê WhatsApp, onboarding por CNPJ e um motor de precificação por UF/contribuinte) — é um recorte mais restrito, mas com integrações reais e testadas em produção.</div>\n  <div class=\"stats\">\n    <div class=\"stat\"><div class=\"v\">${count('i')}</div><div class=\"l\">implementado</div></div>\n    <div class=\"stat\"><div class=\"v\">${count('p')}</div><div class=\"l\">parcial</div></div>\n    <div class=\"stat\"><div class=\"v\">${count('n')}</div><div class=\"l\">não iniciado</div></div>\n  </div>\n  <div class=\"legend\"><span class=\"lg i\">✅ implementado — em produção, dado real</span><span class=\"lg p\">🟡 parcial — existe algo, mas incompleto</span><span class=\"lg n\">⛔ não iniciado — só está no plano</span></div>\n  ${CATS.map(cat).join('')}\n</div>`\n  }\n}\n"}}}}}}}},{"_id":"6a91bc369c836b9b0a986c27","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"granado-troca-ideal","package":"granado-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@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,600;9..144,700&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;600&display=swap');\n:host{display:block;position:relative;width:100%;height:100%}\n*{box-sizing:border-box;margin:0;padding:0}\n.app{position:absolute;inset:0;display:grid;grid-template-rows:auto auto auto 1fr;background:#f4efe3;color:#20261f;font-family:'IBM Plex Sans',system-ui,sans-serif;overflow:hidden}\n.serif{font-family:'Fraunces',Georgia,serif}\nheader{padding:16px 28px 12px;border-bottom:1px solid #e2ddd0;background:#f7f4ec}\nheader h1{font-family:'Fraunces',Georgia,serif;font-size:20px;font-weight:600;color:#1e4d38}\nheader .sub{font-size:11.5px;color:#7a857c;margin-top:3px}\nheader .sub b{color:#a87f1f;font-weight:600}\n.kpis{display:flex;gap:26px;padding:10px 28px;background:#f7f4ec;border-bottom:1px solid #e2ddd0}\n.kpi b{display:block;font-family:'Fraunces',Georgia,serif;font-size:20px;font-weight:600;color:#1e4d38;line-height:1.1;font-variant-numeric:tabular-nums}\n.kpi.warn b{color:#a63a2b}\n.kpi span{font-size:9px;letter-spacing:.07em;text-transform:uppercase;color:#7a857c;font-weight:600}\n.tabs{display:flex;gap:8px;padding:9px 28px;background:#f4efe3;border-bottom:1px solid #e2ddd0;align-items:center}\n.tab{background:#fffdf8;border:1px solid #e2ddd0;color:#4a564d;border-radius:99px;padding:6px 18px;font-size:12.5px;font-weight:600;cursor:pointer;font-family:inherit}\n.tab.hot{background:#1e4d38;border-color:#1e4d38;color:#f4efe3}\n.b-export{margin-left:auto;background:#a87f1f;border:none;color:#fffdf8;border-radius:99px;padding:6px 16px;font-size:12px;font-weight:700;cursor:pointer;font-family:inherit}\n.b-export:hover{background:#8f6b17}\n.body{min-height:0;overflow:hidden}\n.torre{display:grid;grid-template-columns:380px 1fr;height:100%}\n.entrada{display:grid;grid-template-columns:1fr 430px;height:100%}\n.col{overflow-y:auto;padding:16px 20px}\n.col.left{border-right:1px solid #e2ddd0}\n.ccard{background:#fffdf8;border:1px solid #e2ddd0;border-radius:12px;padding:12px 14px;margin-bottom:10px;cursor:pointer;position:relative}\n.ccard:hover{border-color:#b9c4ba}\n.ccard.sel{border-color:#1e4d38;box-shadow:0 0 0 1px #1e4d38}\n.chead{display:flex;justify-content:space-between;align-items:baseline;gap:8px;margin-bottom:3px}\n.cid{font-family:'IBM Plex Mono',monospace;font-size:10px;color:#a87f1f;font-weight:600}\n.cwhen{font-size:10px;color:#9aa39b}\n.b-delcase{background:none;border:none;color:#cfc9b8;font-size:11px;cursor:pointer;padding:0 3px}\n.b-delcase:hover{color:#a63a2b}\n.cname{font-size:13.5px;font-weight:700;margin-bottom:2px}\n.cmeta{font-size:10.5px;color:#7a857c;margin-bottom:8px}\n.chips{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:8px}\n.chip{font-size:9px;font-weight:700;border-radius:99px;padding:2px 9px;background:#eef0e9;color:#4a564d}\n.chip.ok{background:#e2efe7;color:#2d6a4f}\n.chip.crit{background:#f7e4df;color:#a63a2b}\n.chip.gold{background:#f4ead2;color:#a87f1f}\n.chip.org{background:#1e4d38;color:#f4efe3}\n.bars{display:grid;gap:4px}\n.bar{display:grid;grid-template-columns:54px 1fr auto;gap:8px;align-items:center;font-size:8.5px}\n.bar .bl{font-weight:700;letter-spacing:.06em;text-transform:uppercase}\n.bar.p .bl{color:#1e4d38}.bar.d .bl{color:#a87f1f}\n.bt{height:5px;border-radius:99px;background:#ece8db;overflow:hidden}\n.bf{height:100%;border-radius:99px}\n.bar.p .bf{background:#1e4d38}.bar.d .bf{background:#a87f1f}\n.bar .bn{font-family:'IBM Plex Mono',monospace;font-size:8.5px;color:#9aa39b}\n.dhead{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}\n.dhead h2{font-family:'Fraunces',Georgia,serif;font-size:21px;font-weight:600}\n.dhr{display:flex;gap:8px;align-items:center;flex-wrap:wrap}\n.did{font-family:'IBM Plex Mono',monospace;font-size:11px;color:#a87f1f;font-weight:600}\n.b-walk{background:#1e4d38;border:none;color:#f4efe3;border-radius:8px;font-size:11px;font-weight:700;padding:6px 13px;cursor:pointer;font-family:inherit}\n.b-walk:hover{background:#2d6a4f}\n.b-restore{background:none;border:1px solid #e2ddd0;border-radius:8px;color:#9aa39b;font-size:10px;padding:5px 9px;cursor:pointer;font-family:inherit}\n.b-restore:hover{color:#a63a2b;border-color:#e6c2ba}\n.dmeta{font-size:11.5px;color:#7a857c;margin:4px 0 12px}\n.dmeta b{color:#1e4d38}\n.walkbar{background:#1e4d38;color:#f4efe3;border-radius:10px;padding:10px 15px;margin-bottom:14px;display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}\n.walkbar .wt{font-size:12px;font-weight:700}\n.walkbar .wp{font-family:'IBM Plex Mono',monospace;font-size:11px;opacity:.85}\n.promessa{background:#f4ead2;border:1px solid #e6d5a8;border-radius:10px;padding:10px 15px;font-size:12px;color:#6b5413;margin-bottom:16px}\n.promessa b{color:#a87f1f}\n.tls{display:grid;grid-template-columns:1fr 1fr;gap:20px}\n@media(max-width:1100px){.tls{grid-template-columns:1fr}}\n.tlbox{background:#fffdf8;border:1px solid #e2ddd0;border-radius:12px;padding:16px 16px 12px}\n.tlbox h3{font-size:10px;letter-spacing:.1em;text-transform:uppercase;font-weight:700;margin-bottom:14px;display:flex;align-items:center;gap:7px}\n.tlbox.p h3{color:#1e4d38}.tlbox.d h3{color:#a87f1f}\n.tlbox h3 i{width:9px;height:9px;border-radius:50%;font-style:normal}\n.tlbox.p h3 i{background:#1e4d38}.tlbox.d h3 i{background:#a87f1f}\n.tlbox.p{--tc:#1e4d38}.tlbox.d{--tc:#a87f1f}\n.vstep{position:relative;padding:0 0 16px 24px}\n.vstep::before{content:'';position:absolute;left:5px;top:14px;bottom:0;width:2px;background:#ece8db}\n.vstep:last-of-type::before{display:none}\n.vstep::after{content:'';position:absolute;left:0;top:3px;width:12px;height:12px;border-radius:50%;border:2px solid #cfc9b8;background:#fffdf8}\n.vstep.done::after{border-color:var(--tc);background:var(--tc)}\n.vstep.done::before{background:var(--tc)}\n.vstep.now::after{border-color:#1e4d38;box-shadow:0 0 0 3px #e2efe7}\n.vstep.now.br::after{border-color:#a63a2b;box-shadow:0 0 0 3px #f7e4df}\n.vstep.walkhl{background:#f7f4ec;border-radius:10px;padding:8px 8px 12px 24px;margin-left:-8px}\n.vtop{display:flex;justify-content:space-between;gap:8px;align-items:baseline}\n.vn{font-size:12.5px;font-weight:700;line-height:1.3}\n.vbtns{display:flex;gap:2px;align-items:center;flex-shrink:0}\n.ebtn,.mbtn{background:none;border:none;color:#b9c4ba;font-size:12px;cursor:pointer;padding:0 2px;line-height:1;font-family:inherit}\n.ebtn:hover{color:#1e4d38}\n.mbtn{font-size:11px;font-weight:700}\n.mbtn:hover{color:#a87f1f}\n.mbtn[disabled]{opacity:.25;cursor:default}\n.vo{font-size:10.5px;color:#4a564d;margin-top:2px}\n.vo b{color:#1e4d38}\n.vmora{font-size:10px;color:#4a564d;margin-top:3px}\n.vmora b{color:#6b5413;font-weight:600}\n.hbadge{display:inline-block;font-size:8.5px;font-weight:700;border-radius:99px;padding:1.5px 8px;margin-left:6px;vertical-align:1px}\n.hbadge.manual{background:#f7e4df;color:#a63a2b}\n.hbadge.parcial{background:#f4ead2;color:#a87f1f}\n.hbadge.auto{background:#e2efe7;color:#2d6a4f}\n.hbadge.inexistente{background:#e8e4d8;color:#7a857c}\n.vw{font-family:'IBM Plex Mono',monospace;font-size:9.5px;color:#9aa39b;margin-top:2px}\n.vw.done{color:#2d6a4f}\n.vw.br{color:#a63a2b;font-weight:600}\n.vfonte{font-size:10px;color:#7a857c;margin-top:5px;line-height:1.45}\n.vfonte b{color:#4a564d;font-weight:600}\n.vdores{font-size:10px;color:#a63a2b;margin-top:3px;line-height:1.5;background:#fbf1ee;border-radius:6px;padding:5px 8px;white-space:pre-line}\n.vdores b{font-weight:700}\n.b-add{width:100%;background:none;border:1px dashed #cfc9b8;border-radius:8px;color:#9aa39b;font-size:11px;font-weight:600;padding:7px;cursor:pointer;font-family:inherit;margin-top:2px}\n.b-add:hover{color:#1e4d38;border-color:#1e4d38}\n.eform{background:#f7f4ec;border:1px solid #e2ddd0;border-radius:10px;padding:12px;margin-top:4px}\n.eform .fl{font-size:9px;letter-spacing:.07em;text-transform:uppercase;color:#7a857c;font-weight:700;margin:8px 0 3px}\n.eform .fl:first-child{margin-top:0}\n.eform input,.eform textarea,.eform select{width:100%;background:#fffdf8;border:1px solid #e2ddd0;border-radius:7px;padding:6px 9px;font-size:12px;color:#20261f;font-family:inherit;resize:vertical}\n.eform input:focus,.eform textarea:focus,.eform select:focus{outline:none;border-color:#1e4d38}\n.ebtns{display:flex;gap:7px;margin-top:12px;flex-wrap:wrap}\n.b-save{flex:1;background:#1e4d38;border:none;color:#f4efe3;border-radius:7px;padding:7px;font-size:12px;font-weight:700;cursor:pointer;font-family:inherit}\n.b-cancel{background:none;border:1px solid #e2ddd0;color:#7a857c;border-radius:7px;padding:7px 12px;font-size:12px;cursor:pointer;font-family:inherit}\n.b-del{background:none;border:1px solid #e6c2ba;color:#a63a2b;border-radius:7px;padding:7px 12px;font-size:12px;cursor:pointer;font-family:inherit}\n.b-wprev,.b-wnext{background:#a87f1f;border:none;color:#fffdf8;border-radius:7px;padding:7px 14px;font-size:12px;font-weight:700;cursor:pointer;font-family:inherit}\n.b-wnext{flex:1}\n.b-wprev[disabled]{opacity:.4;cursor:default}\n.logbox{margin-top:18px}\n.logbox h3{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:#7a857c;font-weight:700;margin-bottom:8px}\n.lrow{display:grid;grid-template-columns:44px 1fr;gap:10px;padding:6px 0;border-bottom:1px solid #ece8db;font-size:11.5px}\n.lrow:last-child{border-bottom:none}\n.lt{font-family:'IBM Plex Mono',monospace;font-size:10px;color:#9aa39b;padding-top:2px}\n.lm{color:#4a564d}\n.lm .auto{font-size:8.5px;font-weight:700;background:#e2efe7;color:#2d6a4f;border-radius:99px;padding:1px 7px;margin-right:6px;vertical-align:1px}\n.na{color:#9aa39b;font-size:12px;font-style:italic;padding:24px 0;text-align:center}\n.cover{background:#fffdf8;border:1px solid #e2ddd0;border-radius:12px;padding:16px 18px;margin-bottom:14px}\n.cover h4{font-family:'Fraunces',Georgia,serif;font-size:14px;font-weight:600;color:#1e4d38;margin-bottom:10px}\n.crow{display:grid;grid-template-columns:1fr auto;gap:10px;padding:6px 0;border-bottom:1px solid #ece8db;font-size:11.5px;color:#4a564d;align-items:center}\n.crow:last-child{border-bottom:none}\n.cyes{color:#2d6a4f;font-weight:700;font-size:10.5px}\n.cno{color:#a63a2b;font-weight:700;font-size:10.5px}\n.fitem{background:#fffdf8;border:1px solid #e2ddd0;border-radius:12px;padding:14px 16px;margin-bottom:11px}\n.ftop{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px}\n.fraw{font-size:12px;color:#4a564d;font-style:italic;border-left:3px solid #e2ddd0;padding-left:11px;margin-bottom:10px;line-height:1.5}\n.farrow{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:#a87f1f;font-weight:700;margin-bottom:7px}\n.fchips{display:flex;gap:5px;flex-wrap:wrap}\n.fchip{font-family:'IBM Plex Mono',monospace;font-size:10px;background:#eef0e9;border-radius:6px;padding:2px 8px;color:#4a564d}\n.fcase{font-family:'IBM Plex Mono',monospace;font-size:10px;color:#2d6a4f;font-weight:600;margin-top:9px}\n.form{background:#fffdf8;border:1px solid #e2ddd0;border-radius:14px;padding:20px 20px 16px}\n.form h3{font-family:'Fraunces',Georgia,serif;font-size:16px;font-weight:600;color:#1e4d38;margin-bottom:4px}\n.form .fs{font-size:11px;color:#7a857c;margin-bottom:14px}\n.form .fl{font-size:9.5px;letter-spacing:.07em;text-transform:uppercase;color:#7a857c;font-weight:700;margin:10px 0 4px}\n.fi{width:100%;background:#f7f4ec;border:1px solid #e2ddd0;border-radius:8px;padding:8px 11px;font-size:12.5px;color:#20261f;font-family:inherit}\n.fi:focus{outline:none;border-color:#1e4d38}\n.fbtn{width:100%;margin-top:16px;background:#1e4d38;border:none;color:#f4efe3;border-radius:9px;padding:10px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}\n.fbtn:hover{background:#2d6a4f}\n.fnote{font-size:10px;color:#9aa39b;margin-top:9px;line-height:1.5}\n.toast{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);background:#1e4d38;color:#f4efe3;border-radius:99px;padding:9px 22px;font-size:12.5px;font-weight:700;z-index:50;opacity:0;transition:opacity .3s;pointer-events:none}\n.toast.show{opacity:1}\n`,\n  render(d2){\n    return `<div class=\"app\">\n      <header>\n        <h1>Torre de Controle de Trocas — Mundo Ideal</h1>\n        <div class=\"sub\">Casos reais mapeados na discovery. Em cada etapa: <b>dono</b>, <b>onde mora hoje</b> e as dores. Os casos marcados <b>A MAPEAR</b> (expressa via loja física) são os buracos — use <b>▶ percorrer</b> pra preenchê-los e <b>📋 copiar respostas</b> no fim.</div>\n      </header>\n      <div class=\"kpis\" data-d2-static>\n        <div class=\"kpi\"><b class=\"k-ativos\">0</b><span>casos</span></div>\n        <div class=\"kpi warn\"><b class=\"k-manual\">0</b><span>etapas manuais / inexistentes</span></div>\n        <div class=\"kpi\"><b class=\"k-auto\">0</b><span>etapas já automáticas</span></div>\n        <div class=\"kpi\"><b class=\"k-resp\">0%</b><span>etapas respondidas</span></div>\n      </div>\n      <div class=\"tabs\" data-d2-static>\n        <button class=\"tab hot\" data-v=\"torre\">🗼 Torre de Controle</button>\n        <button class=\"tab\" data-v=\"entrada\">📥 Canal de Entrada</button>\n        <button class=\"b-export\">📋 copiar respostas</button>\n      </div>\n      <div class=\"body\" data-d2-static></div>\n      <div class=\"toast\" data-d2-static></div>\n    </div>`\n  },\n  mounted(d2){\n    const bodyEl=d2.$('.body'); if(!bodyEl)return\n    const NOW=Date.now()\n    const D=n=>new Date(NOW+n*864e5).toISOString()\n    const fdm=t=>{if(!t)return'';const x=new Date(t);return String(x.getDate()).padStart(2,'0')+'/'+String(x.getMonth()+1).padStart(2,'0')}\n    const esc=s=>String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')\n    const ORIGENS={cd:'📦 CD',dark:'⚡ Darkstore',loja:'🏬 Loja'}\n    const HOJE={manual:'hoje: manual',parcial:'hoje: parcial',auto:'hoje: automático',inexistente:'hoje: não existe'}\n    const KEY='gideal_v4'\n    const AD='a definir na sessão'\n    function mk(o){return Object.assign({n:'',dono:AD,mora:AD,sla:'—',state:'next',when:null,fonte:'',dores:'',hoje:'manual',note:'',br:false},o)}\n    const DEFAULTS=[\n      {id:'TR-1042',cliente:'Marina Duarte',origem:'cd',entrega:'entrega normal',marca:'Granado',tipo:'Arrependimento',pagto:'Crédito · Adyen',valor:186,risco:'baixo',when:D(-2),canal:'TroqueCommerce',\n       status:{t:'Fluxo CD — mapeado ✔',cls:'ok'},\n       promessa:'Fluxo real mapeado: entrada pelo <b>TroqueCommerce já é automática</b>. Os gargalos são a autorização da logística (<b>hoje 5–10 dias, ideal 1 dia</b> — confirmado com o Jefferson) e a cadeia planilha OMNI+SAC → Adyen um a um.',\n       produto:[\n        mk({n:'Solicitação',dono:'Digital / SAC',mora:'TroqueCommerce',hoje:'auto',state:'done',when:D(-2),fonte:'TroqueCommerce',dores:'Cliente pode não encontrar o TroqueCommerce — origens diferentes exigem canais diferentes de contestação (pode chegar via atalhos: WhatsApp, template Boxlink).\\nCobertura: entrega normal (CD) e expressa via Darkstore SIM · expressa via LOJA não.\\n[CASO CD] cliente solicita no TroqueCommerce e recebe a etiqueta para postar nos Correios.'}),\n        mk({n:'Postagem pelo cliente',dono:'Cliente / Troque',mora:'Troque.app.br',hoje:'parcial',state:'done',when:D(-1),fonte:'Integração Troque (etiqueta emitida no caso)',dores:'até a postagem do cliente, o processo fica em HOLD'}),\n        mk({n:'Autorização de reembolso — CD [Jeferson]',dono:'Jeferson · Logística',mora:'Logística — processo manual',hoje:'manual',sla:'ideal 1d · hoje 5–10d',state:'now',note:'fila de validação manual',fonte:'Autorização automática por política; exceção manual só p/ histórico fraudulento',dores:'validação manual das evidências de troca · validação dos itens devolvidos a partir do Troque.\\nMundo ideal: dispensar a etapa p/ cliente SEM histórico fraudulento; rever a política de autorização com base no recebimento.\\nEscala: aprovação nível 1 por PESO (pedidos multi-itens) · nível 2 completa facilitada com balança + bipper.'})],\n       dinheiro:[\n        mk({n:'Logística autoriza reembolso',dono:'Logística',mora:'Magento (cancelamento manual) + SAP (cancela nota, concilia estoque) + planilha OMNI+SAC 2026.xlsx',hoje:'manual',state:'now',note:'aguardando autorização',fonte:'Autorização dispara o caso automaticamente',dores:'o reembolso NÃO inicia até a logística autorizar.\\nProcesso: pedido cancelado manualmente no Magento → SAP cancela nota e concilia estoque → dados extraídos do Magento digitados na planilha OMNI+SAC 2026.xlsx'}),\n        mk({n:'Financeiro processa reembolso',dono:'Financeiro',mora:'Planilha + painel Adyen (um a um)',hoje:'manual',sla:'ideal imediato · hoje até 24h',state:'next',when:D(2),fonte:'API Adyen disparada pelo caso',dores:'dependendo do horário da autorização, leva até 24h.\\nCartão de crédito: cada reembolso da planilha é liberado na Adyen manualmente, um a um.'})],\n       log:[{t:D(-2),a:1,m:'Solicitação criada no TroqueCommerce — etiqueta emitida'},{t:D(-1),a:1,m:'Cliente postou nos Correios — HOLD liberado'}]},\n      {id:'TR-1038',cliente:'Paulo Mendes',origem:'cd',entrega:'entrega normal',marca:'Granado',tipo:'Arrependimento',pagto:'Crédito · Adyen',valor:318,risco:'médio',when:D(-9),canal:'SAC',\n       status:{t:'Variante: cliente caiu no SAC',cls:'gold'},\n       promessa:'Mesmo fluxo do CD, mas o cliente <b>não encontrou o TroqueCommerce</b> e caiu no SAC — o caso vira troca de e-mails antes de entrar no trilho.',\n       produto:[\n        mk({n:'Solicitação',dono:'SAC',mora:'Sistema do SAC (vira e-mail)',hoje:'manual',state:'done',when:D(-9),fonte:'SAC registra direto no caso',dores:'cliente não encontrou o TroqueCommerce e caiu no SAC — caso vira troca de e-mails.\\nPERGUNTA: o SAC consegue abrir o caso no TroqueCommerce em nome do cliente?'}),\n        mk({n:'Postagem pelo cliente',dono:'Cliente / Troque',mora:'Troque.app.br',hoje:'parcial',state:'done',when:D(-8),fonte:'Troque',dores:'até a postagem, processo em HOLD'}),\n        mk({n:'Autorização de reembolso — CD [Jeferson]',dono:'Jeferson · Logística',mora:'Logística — processo manual',hoje:'manual',sla:'ideal 1d · hoje 5–10d',state:'now',note:'na fila há 8 dias',br:true,fonte:'Autorização automática por política + score',dores:'mesma fila manual — este caso está parado há 8 dias (faixa real: 5–10d)'})],\n       dinheiro:[\n        mk({n:'Logística autoriza reembolso',dono:'Logística',mora:'Magento manual + SAP + planilha OMNI+SAC 2026.xlsx',hoje:'manual',state:'next',when:D(3),fonte:'Autorização dispara o caso',dores:'não inicia até a logística autorizar'}),\n        mk({n:'Financeiro processa reembolso',dono:'Financeiro',mora:'Planilha + painel Adyen (um a um)',hoje:'manual',sla:'ideal imediato · hoje até 24h',state:'next',when:D(4),fonte:'API Adyen',dores:'um a um na Adyen'})],\n       log:[{t:D(-9),a:0,m:'Cliente abriu por e-mail no SAC — registrado manualmente'},{t:D(0),a:1,m:'Alerta: autorização parada há 8 dias (SLA ideal: 1d)'}]},\n      {id:'TR-1041',cliente:'Renata Alves',origem:'cd',entrega:'entrega normal',marca:'Care',tipo:'Defeito',pagto:'PIX parcelado · PagBrasil',valor:212,risco:'médio',when:D(-4),canal:'Shopify / SAC',\n       status:{t:'Pior caminho: Care + PagBrasil',cls:'gold'},\n       promessa:'Care (Shopify) + PIX parcelado (PagBrasil) — hoje o caminho com <b>zero automação</b> nas duas pontas.',\n       produto:[\n        mk({n:'Solicitação',dono:'Digital',mora:'Shopify (Care, separado de tudo)',hoje:'manual',state:'done',when:D(-4),fonte:'Portal pós-venda (mesmo fluxo pras 3 marcas)',dores:'Care roda no Shopify; conexão OMS↔Shopify quebrada — recusa nem aparece pro cliente.\\nPERGUNTA: o TroqueCommerce cobre Care?'}),\n        mk({n:'Autorização',dono:'Digital',mora:'Shopify + OMS (manual)',hoje:'manual',sla:'4h',state:'done',when:D(-4),fonte:'Política codificada por marca',dores:'pra Care, tudo manual — zero automação mesmo em recusa da loja'}),\n        mk({n:'Coleta agendada',dono:'Logística reversa',mora:'não existe — cliente vai ao correio',hoje:'inexistente',sla:'48h',state:'done',when:D(-3),fonte:'Agendamento automático no caso',dores:'cada item que chega exige descobrir \"de qual pedido é\"'}),\n        mk({n:'Recebimento CD',dono:'CD',mora:'Recebimento físico sem identificação',hoje:'manual',state:'now',note:'previsto hoje',fonte:'Bipagem vinculada ao caso',dores:'chega sem identificação — investigação manual por item'}),\n        mk({n:'Conferência',dono:'CD',mora:'CD + planilha do CD',hoje:'manual',sla:'3d úteis',state:'next',when:D(2),fonte:'Fila com checklist + foto',dores:'fila de meses, 1 pessoa'}),\n        mk({n:'Destino: análise Qualidade',dono:'Qualidade',mora:'não existe — itens acumulam em armários',hoje:'inexistente',state:'next',when:D(2),fonte:'Matriz devolver-vs-descartar',dores:'descarte não acontece — 2 armários lotados de devoluções'})],\n       dinheiro:[\n        mk({n:'Elegibilidade',dono:'Digital',mora:'OMS + política no papel',hoje:'manual',sla:'imediata',state:'done',when:D(-4),fonte:'Política + pedido'}),\n        mk({n:'Decisão',dono:'Financeiro',mora:'Caso a caso com a PagBrasil',hoje:'manual',sla:'24h',state:'next',when:D(3),fonte:'Resultado da conferência',dores:'pra PagBrasil, alguém resolve direto com a processadora'}),\n        mk({n:'Execução',dono:'Financeiro',mora:'Contato manual com PagBrasil',hoje:'manual',sla:'2h',state:'next',when:D(3),fonte:'API da processadora',dores:'PIX parcelado não tem automação pra NENHUMA marca'}),\n        mk({n:'Na fatura',dono:'Processadora',mora:'Fatura do cliente',hoje:'inexistente',state:'next',when:D(4),fonte:'Webhook'})],\n       log:[{t:D(-4),a:0,m:'Caso Care registrado manualmente'},{t:D(-4),a:1,m:'Cliente notificada com a previsão das duas trilhas'}]},\n      {id:'TR-1044',cliente:'Cláudia Nunes',origem:'dark',entrega:'expressa',marca:'Phebo',tipo:'Defeito (produto vazando)',pagto:'Crédito · Adyen',valor:42,risco:'baixo',when:D(-1),canal:'TroqueCommerce',\n       status:{t:'Fluxo Darkstore — mapeado ✔ (4 perguntas abertas)',cls:'gold'},\n       promessa:'Troca por defeito via Darkstore: entrada <b>✅ TroqueCommerce</b> (validado em Loyalty e EComm). O [Processo Jeferson] valida e <b>refaz o pedido no Magento na mão</b>. Quatro perguntas abertas na etapa 2.',\n       produto:[\n        mk({n:'Solicitação — defeito (produto vazando)',dono:'Digital',mora:'TroqueCommerce',hoje:'auto',state:'done',when:D(-1),fonte:'TroqueCommerce',dores:'template é enviado sempre mandando o cliente pro Troque (validado em Loyalty e EComm — ambos Darkstore)'}),\n        mk({n:'[Processo Jeferson] — validação',dono:'Jeferson · Logística',mora:'Logística — manual',hoje:'manual',sla:'ideal 1h · hoje ~30d',state:'now',note:'fila de validação',fonte:'Validação automática por foto + matriz Qualidade',dores:'validação mecânica do produto devolvido.\\nPERGUNTAS ABERTAS:\\n· no Troque dá pra escolher os produtos a substituir? se não, como isso é feito?\\n· motivos diferentes (devolução/cancelamento/defeito) recebem tratativas diferentes?\\n· quais são (e quais deveriam ser) os critérios pra abrir contestação no Troque?\\n· quais canais o Troque atende hoje?'}),\n        mk({n:'Criação do novo pedido [refazer pedido]',dono:'Logística',mora:'Magento — manual',hoje:'manual',state:'next',when:D(2),fonte:'Pedido de troca gerado automaticamente pelo caso',dores:'novo pedido criado manualmente no Magento'})],\n       dinheiro:[\n        mk({n:'Elegibilidade',dono:'Digital',mora:'OMS',hoje:'manual',sla:'imediata',state:'done',when:D(-1),fonte:'Política + foto'}),\n        mk({n:'Decisão: estorno imediato',dono:'automático (score)',mora:'não existe',hoje:'inexistente',sla:'imediata',state:'done',when:D(-1),fonte:'Score antifraude'}),\n        mk({n:'Execução',dono:'Financeiro',mora:'Painel Adyen (manual)',hoje:'manual',sla:'2h',state:'done',when:D(-1),fonte:'API'}),\n        mk({n:'Na fatura',dono:'Processadora',mora:'Fatura do cliente',hoje:'inexistente',state:'done',when:D(0),fonte:'Webhook'})],\n       log:[{t:D(-1),a:1,m:'Solicitação criada no TroqueCommerce com foto do defeito'},{t:D(0),a:0,m:'Aguardando validação do [Processo Jeferson]'}]},\n      {id:'TR-1046',cliente:'Bruna Costa',origem:'loja',entrega:'expressa',marca:'Granado',tipo:'Arrependimento',pagto:'Crédito · Adyen',valor:154,risco:'baixo',when:D(0),canal:'❓ a mapear',\n       status:{t:'A MAPEAR — expressa via loja',cls:'crit'},\n       promessa:'<b>O buraco do mapa:</b> entrega expressa que saiu de LOJA FÍSICA — o TroqueCommerce <b>não cobre</b> esse caso. Como o cliente contesta? Pra onde volta o produto? Quem autoriza? Percorra e preencha.',\n       produto:[\n        mk({n:'Solicitação',hoje:'manual',state:'now',note:'caminho desconhecido',dores:'TroqueCommerce NÃO disponível pra entrega por loja — por onde esse cliente entra hoje? Template Boxlink? WhatsApp? SAC?'}),\n        mk({n:'Devolução do produto',hoje:'manual',dores:'o produto volta pra onde: a loja que enviou, o CD ou a darkstore? quem emite a etiqueta/coleta?'}),\n        mk({n:'Conferência',hoje:'manual',dores:'quem valida a devolução quando a origem é loja? a loja tem processo e gente pra isso? passa pelo [Processo Jeferson] do CD?'}),\n        mk({n:'Estoque',hoje:'manual',dores:'o estoque volta pra loja no SAP? mesmo risco de estoque fantasma da devolução via escritório?'})],\n       dinheiro:[\n        mk({n:'Autorização de reembolso',hoje:'manual',dores:'quem autoriza quando a origem é loja — também o Jeferson (CD)? a própria loja? o digital?'}),\n        mk({n:'Execução',mora:'Painel Adyen (manual)?',hoje:'manual',dores:'a RECUSA da loja tem estorno automático (Adyen, Granado/Phebo) — e o arrependimento pós-entrega, tem?'}),\n        mk({n:'Na fatura',dono:'Processadora',mora:'Fatura do cliente',hoje:'inexistente',fonte:'Webhook'})],\n       log:[{t:D(0),a:0,m:'Caso criado na discovery pra mapear o caminho expressa-via-loja'}]},\n      {id:'TR-1047',cliente:'Eduardo Reis',origem:'loja',entrega:'expressa',marca:'Granado',tipo:'Expressa atrasada / frete',pagto:'Crédito · Adyen',valor:210,risco:'—',when:D(0),canal:'❓ a mapear',\n       status:{t:'A MAPEAR — expressa via loja',cls:'crit'},\n       promessa:'Caso real conhecido: cliente pagou <b>R$ 20 de frete expresso (promessa de 2 dias)</b>, pedido ficou 5 dias sem faturar, coleta só no 6º dia. Quem detecta, quem devolve o frete, e o cancelamento segue qual fluxo?',\n       produto:[\n        mk({n:'Pedido travado sem faturar',dono:'Fiscal',mora:'SAP + e-mail diário no Teams',hoje:'parcial',state:'done',when:D(-1),fonte:'Detecção automática de pedido pago sem nota',dores:'expressa só coleta DEPOIS de faturar; caso real: 5 dias parado, nota forçada na mão, coleta no 6º dia'}),\n        mk({n:'Detecção do atraso',mora:'hoje: a reclamação do cliente',hoje:'inexistente',state:'now',note:'cliente reclamou',dores:'ninguém detecta a expressa órfã — CEP fora de abrangência também deixa pedido sem dono e sem alerta. Quem deveria ser o dono desse alarme?'}),\n        mk({n:'Coleta e entrega',dono:'EuEntrego / loja',mora:'planilha manual de CEPs por unidade',hoje:'manual',dores:'a cobertura por raio mora numa planilha manual por loja — desalinhada do checkout'})],\n       dinheiro:[\n        mk({n:'Reembolso do frete',hoje:'manual',dores:'cliente pagou R$ 20 pela promessa de 2 dias — quem decide e quem executa o reembolso do frete?'}),\n        mk({n:'Cancelamento (se o cliente desistir)',hoje:'manual',dores:'cancelamento de expressa de loja segue qual fluxo? recusa-da-loja tem Adyen automático — e o cancelamento por atraso?'})],\n       log:[{t:D(0),a:0,m:'Caso criado na discovery a partir do caso real relatado em reunião'}]},\n      {id:'TR-1039',cliente:'Jorge Lima',origem:'loja',entrega:'retirada',marca:'Phebo',tipo:'Não retirado',pagto:'PIX · Adyen',valor:74,risco:'—',when:D(-12),canal:'—',\n       status:{t:'Resolução automática agendada',cls:'gold'},\n       promessa:'Retirada não realizada. Aviso automático no 7º dia; se não retirar até <b>'+fdm(D(18))+'</b>, o caso <b>cancela e estorna sozinho</b> — sem depender de favor.',\n       produto:[\n        mk({n:'Pedido pronto na loja',dono:'Loja',mora:'OMS + SAP',hoje:'manual',state:'done',when:D(-12),fonte:'OMS + faturamento SAP',dores:'pedido pode travar dias no SAP sem faturar; loja sabe mas não pode liberar — às vezes entrega SEM NOTA (risco chargeback)'}),\n        mk({n:'Aviso automático · 7 dias',dono:'automático',mora:'Régua de e-mail (já implementada)',hoje:'auto',state:'done',when:D(-5),fonte:'Régua do caso (WhatsApp + e-mail)',dores:'só o aviso existe — o \"cancelamento automático em 7 dias\" é mito'}),\n        mk({n:'Prazo final · 30 dias',dono:'automático',mora:'Memória da gerente da darkstore (favor informal)',hoje:'manual',state:'now',note:'resolução automática em '+fdm(D(18)),fonte:'Gatilho de tempo do caso',dores:'depende de a gerente \"quebrar um galho\" e avisar o digital; daí tudo manual: SAP + planilha do financeiro'})],\n       dinheiro:[\n        mk({n:'Elegibilidade condicional',dono:'automático',mora:'ninguém enxerga até alguém reclamar',hoje:'inexistente',state:'next',when:D(18),fonte:'Ativada pelo gatilho dos 30 dias'}),\n        mk({n:'Estorno automático',dono:'automático',mora:'Painel Adyen (manual)',hoje:'manual',sla:'2h',state:'next',when:D(18),fonte:'API'}),\n        mk({n:'Na fatura',dono:'Processadora',mora:'Fatura do cliente',hoje:'inexistente',state:'next',when:D(19),fonte:'Webhook'})],\n       log:[{t:D(-5),a:1,m:'Aviso dos 7 dias disparado automaticamente'}]},\n      {id:'TR-1040',cliente:'Balcão Barra Shopping',origem:'loja',entrega:'balcão',marca:'Granado',tipo:'Troca no balcão',pagto:'Voucher na hora',valor:59,risco:'—',when:D(0),canal:'Loja',\n       status:{t:'Resolvido na loja · 11min',cls:'ok'},\n       promessa:'A loja resolveu <b>no balcão</b>, pelo fluxo que já existe no sistema da loja — sem mandar o cliente pra casa abrir SAC.',\n       produto:[\n        mk({n:'Registro pela loja',dono:'Loja',mora:'Sistema da loja (fluxo existe, não é usado)',hoje:'inexistente',state:'done',when:D(0),fonte:'Sistema da loja (fluxo de troca que JÁ existe)',dores:'o fluxo existe mas não é usado — a loja manda o cliente voltar pra casa e abrir SAC'}),\n        mk({n:'Troca efetuada',dono:'Loja',mora:'Balcão',hoje:'manual',sla:'15min',state:'done',when:D(0),fonte:'Balcão'}),\n        mk({n:'Estoque ajustado',dono:'automático',mora:'SAP (ajuste manual, quando acontece)',hoje:'manual',state:'done',when:D(0),fonte:'Sistema da loja → SAP'})],\n       dinheiro:[\n        mk({n:'Voucher emitido na hora',dono:'Loja',mora:'Sistema da loja',hoje:'manual',sla:'imediato',state:'done',when:D(0),fonte:'Sistema da loja',dores:'estorno parcial (ex.: cartão-presente) depende de UMA pessoa específica — conhecimento tribal'})],\n       log:[{t:D(0),a:1,m:'Fluxo de balcão ativado — cliente saiu com a troca feita'}]}\n    ]\n    const COVER=[\n      {r:'📦 CD · entrega normal',ok:true,n:'cliente recebe etiqueta e posta nos Correios'},\n      {r:'⚡ Darkstore · expressa',ok:true,n:'validado em Loyalty e EComm'},\n      {r:'🏬 Loja · expressa (SFS)',ok:false,n:'A MAPEAR — casos TR-1046 / TR-1047'},\n      {r:'🏬 Loja · retirada',ok:false,n:'fluxo próprio: aviso 7d + resolução manual ~30d'},\n      {r:'🏬 Loja · balcão',ok:false,n:'sistema da loja tem o fluxo, mas não é usado'}\n    ]\n    const FEED=[\n      {canal:'TroqueCommerce',when:D(-1),raw:'Cliente abre contestação de defeito com foto — origem Darkstore identificada',out:['motivo: defeito','origem: Darkstore · expressa','etiqueta: emitida'],caso:'TR-1044 · entrada já automática hoje'},\n      {canal:'TroqueCommerce',when:D(-2),raw:'Arrependimento — pedido de entrega normal localizado pelo CPF',out:['motivo: arrependimento','origem: CD · normal','status: HOLD até postagem'],caso:'TR-1042 · entrada já automática hoje'},\n      {canal:'SAC',when:D(-9),raw:'\"quero devolver meu pedido\" — cliente não encontrou o TroqueCommerce',out:['motivo: arrependimento','origem: CD','entrada: e-mail manual'],caso:'TR-1038 · variante a corrigir'},\n      {canal:'❓ a mapear',when:D(0),raw:'Cliente de expressa entregue por LOJA quer devolver — TroqueCommerce não cobre',out:['origem: Loja · expressa','canal de entrada: ???'],caso:'TR-1046 · o buraco do mapa'}\n    ]\n    let SAVED={edits:{},created:[]}\n    try{const raw=localStorage.getItem(KEY); if(raw)SAVED=JSON.parse(raw)||SAVED}catch(e){}\n    function persist(){try{localStorage.setItem(KEY,JSON.stringify(SAVED))}catch(e){}}\n    const clone=o=>JSON.parse(JSON.stringify(o))\n    const CASES=(SAVED.created||[]).map(clone).concat(DEFAULTS.map(c=>{\n      const cc=clone(c), ed=SAVED.edits[c.id]\n      if(ed){ if(ed.produto)cc.produto=ed.produto; if(ed.dinheiro)cc.dinheiro=ed.dinheiro }\n      return cc\n    }))\n    const isCreated=id=>(SAVED.created||[]).some(x=>x.id===id)\n    function persistCase(c){\n      if(isCreated(c.id)){SAVED.created=SAVED.created.map(x=>x.id===c.id?clone(c):x)}\n      else{SAVED.edits[c.id]={produto:c.produto,dinheiro:c.dinheiro}}\n      persist()\n    }\n    const S={view:'torre',sel:CASES[0]?CASES[0].id:null,seq:1048+(SAVED.created||[]).length,edit:null,walk:null}\n    const byId=id=>CASES.find(x=>x.id===id)\n    function toast(msg){const t=d2.$('.toast');if(!t)return;t.textContent=msg;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),2200)}\n    function allSteps(c){return c.produto.map((s,i)=>({track:'produto',idx:i})).concat(c.dinheiro.map((s,i)=>({track:'dinheiro',idx:i})))}\n    function prog(steps){const t=steps.length||1,d=steps.filter(s=>s.state==='done').length;return{d,t,p:Math.round(d/t*100)}}\n    function answered(s){return s.dono!==AD&&s.mora!==AD&&s.dono&&s.mora}\n    function orgLabel(c){return (ORIGENS[c.origem]||c.origem)+(c.entrega?' · '+c.entrega:'')}\n    function renderKpis(){\n      let manual=0,auto=0,tot=0,resp=0\n      CASES.forEach(c=>[...c.produto,...c.dinheiro].forEach(s=>{tot++;if(s.hoje==='manual'||s.hoje==='inexistente')manual++;if(s.hoje==='auto')auto++;if(answered(s))resp++}))\n      const e1=d2.$('.k-ativos'),e2=d2.$('.k-manual'),e3=d2.$('.k-auto'),e4=d2.$('.k-resp')\n      if(e1)e1.textContent=CASES.length\n      if(e2)e2.textContent=manual\n      if(e3)e3.textContent=auto\n      if(e4)e4.textContent=tot?Math.round(resp/tot*100)+'%':'0%'\n    }\n    function caseCard(c){\n      const pp=prog(c.produto),pd=prog(c.dinheiro)\n      return '<div class=\"ccard'+(S.sel===c.id?' sel':'')+'\" data-id=\"'+c.id+'\">'\n        +'<div class=\"chead\"><span class=\"cid\">'+c.id+'</span><span>'\n        +(isCreated(c.id)?'<button class=\"b-delcase\" data-id=\"'+c.id+'\" title=\"excluir caso criado\">🗑</button>':'')\n        +'<span class=\"cwhen\">'+fdm(c.when)+'</span></span></div>'\n        +'<div class=\"cname\">'+esc(c.cliente)+'</div>'\n        +'<div class=\"cmeta\">'+esc(c.tipo)+' · '+esc(c.marca)+(c.valor?' · R$ '+c.valor:'')+'</div>'\n        +'<div class=\"chips\"><span class=\"chip org\">'+orgLabel(c)+'</span><span class=\"chip '+c.status.cls+'\">'+esc(c.status.t)+'</span></div>'\n        +'<div class=\"bars\">'\n        +'<div class=\"bar p\"><span class=\"bl\">produto</span><span class=\"bt\"><span class=\"bf\" style=\"width:'+pp.p+'%\"></span></span><span class=\"bn\">'+pp.d+'/'+pp.t+'</span></div>'\n        +'<div class=\"bar d\"><span class=\"bl\">dinheiro</span><span class=\"bt\"><span class=\"bf\" style=\"width:'+pd.p+'%\"></span></span><span class=\"bn\">'+pd.d+'/'+pd.t+'</span></div>'\n        +'</div></div>'\n    }\n    function isEditing(track,idx){return S.edit&&S.edit.track===track&&S.edit.idx===idx}\n    function stepHtml(s,track,idx,len){\n      if(isEditing(track,idx)){\n        const walkBtns=S.walk\n          ?'<div class=\"ebtns\"><button class=\"b-wprev\"'+(S.walk.pos===0?' disabled':'')+'>← anterior</button><button class=\"b-wnext\">'+(S.walk.pos>=S.walk.list.length-1?'salvar e concluir ✓':'salvar e próxima →')+'</button><button class=\"b-cancel b-wexit\">sair</button></div>'\n          :'<div class=\"ebtns\"><button class=\"b-save\">Salvar</button><button class=\"b-cancel\">Cancelar</button><button class=\"b-del\">Remover</button></div>'\n        return '<div class=\"vstep '+s.state+' walkhl\"><div class=\"eform\" data-track=\"'+track+'\" data-idx=\"'+idx+'\">'\n          +'<div class=\"fl\">Etapa</div><input class=\"e-n\" value=\"'+esc(s.n)+'\"/>'\n          +'<div class=\"fl\">Dono (quem responde)</div><input class=\"e-dono\" value=\"'+esc(s.dono)+'\"/>'\n          +'<div class=\"fl\">Onde mora hoje (sistema · planilha · pessoa)</div><input class=\"e-mora\" value=\"'+esc(s.mora)+'\"/>'\n          +'<div class=\"fl\">Como é hoje</div><select class=\"e-hoje\">'\n            +'<option value=\"manual\"'+(s.hoje==='manual'?' selected':'')+'>manual</option>'\n            +'<option value=\"parcial\"'+(s.hoje==='parcial'?' selected':'')+'>parcial</option>'\n            +'<option value=\"auto\"'+(s.hoje==='auto'?' selected':'')+'>automático</option>'\n            +'<option value=\"inexistente\"'+(s.hoje==='inexistente'?' selected':'')+'>não existe</option></select>'\n          +'<div class=\"fl\">SLA (ideal · hoje)</div><input class=\"e-sla\" value=\"'+esc(s.sla)+'\"/>'\n          +'<div class=\"fl\">Fonte de informação (mundo ideal)</div><textarea class=\"e-fonte\" rows=\"2\">'+esc(s.fonte)+'</textarea>'\n          +'<div class=\"fl\">Dores / perguntas abertas</div><textarea class=\"e-dores\" rows=\"4\">'+esc(s.dores)+'</textarea>'\n          +walkBtns\n          +'</div></div>'\n      }\n      let when=''\n      if(s.state==='done')when='<div class=\"vw done\">✓ '+fdm(s.when)+'</div>'\n      else if(s.state==='now')when='<div class=\"vw'+(s.br?' br':'')+'\">'+(s.br?'⚠ ':'● ')+esc(s.note||'em andamento')+'</div>'\n      else when='<div class=\"vw\">'+(s.when?'previsto '+fdm(s.when):'previsto')+'</div>'\n      return '<div class=\"vstep '+s.state+(s.br?' br':'')+'\">'\n        +'<div class=\"vtop\"><div class=\"vn\">'+esc(s.n)+'<span class=\"hbadge '+s.hoje+'\">'+HOJE[s.hoje]+'</span></div><div class=\"vbtns\">'\n        +'<button class=\"mbtn\" data-track=\"'+track+'\" data-idx=\"'+idx+'\" data-dir=\"-1\" title=\"mover pra cima\"'+(idx===0?' disabled':'')+'>▲</button>'\n        +'<button class=\"mbtn\" data-track=\"'+track+'\" data-idx=\"'+idx+'\" data-dir=\"1\" title=\"mover pra baixo\"'+(idx===len-1?' disabled':'')+'>▼</button>'\n        +'<button class=\"ebtn\" data-track=\"'+track+'\" data-idx=\"'+idx+'\" title=\"editar etapa\">✏️</button></div></div>'\n        +'<div class=\"vo\">Dono: <b>'+esc(s.dono)+'</b>'+(s.sla&&s.sla!=='—'?' · SLA '+esc(s.sla):'')+'</div>'\n        +'<div class=\"vmora\">🏠 Mora em: <b>'+esc(s.mora)+'</b></div>'\n        +when\n        +(s.fonte&&s.fonte!=='—'?'<div class=\"vfonte\"><b>Fonte (ideal):</b> '+esc(s.fonte)+'</div>':'')\n        +(s.dores&&s.dores!=='—'?'<div class=\"vdores\"><b>Dores hoje:</b> '+esc(s.dores)+'</div>':'')\n        +'</div>'\n    }\n    function detail(c){\n      const walkbar=S.walk\n        ?'<div class=\"walkbar\"><span class=\"wt\">▶ Percorrendo as etapas — preencha dono e onde mora</span><span class=\"wp\">etapa '+(S.walk.pos+1)+' de '+S.walk.list.length+'</span></div>'\n        :''\n      return '<div class=\"dhead\"><h2 class=\"serif\">'+esc(c.cliente)+'</h2><div class=\"dhr\"><span class=\"did\">'+c.id+'</span>'\n        +(S.walk?'':'<button class=\"b-walk\">▶ percorrer etapas</button>')\n        +'<button class=\"b-restore\" title=\"descarta as edições deste caso\">↺ restaurar</button></div></div>'\n        +'<div class=\"dmeta\">Origem: <b>'+orgLabel(c)+'</b> · '+esc(c.tipo)+' · '+esc(c.marca)+(c.valor?' · R$ '+c.valor:'')+(c.pagto&&c.pagto!=='—'?' · '+esc(c.pagto):'')+' · entrada: '+esc(c.canal||'—')+'</div>'\n        +walkbar\n        +'<div class=\"promessa\">'+c.promessa+'</div>'\n        +'<div class=\"tls\">'\n        +'<div class=\"tlbox p\"><h3><i></i>Trilha do produto</h3>'+c.produto.map((s,i,a)=>stepHtml(s,'produto',i,a.length)).join('')+'<button class=\"b-add\" data-track=\"produto\">+ adicionar etapa</button></div>'\n        +'<div class=\"tlbox d\"><h3><i></i>Trilha do dinheiro</h3>'+c.dinheiro.map((s,i,a)=>stepHtml(s,'dinheiro',i,a.length)).join('')+'<button class=\"b-add\" data-track=\"dinheiro\">+ adicionar etapa</button></div>'\n        +'</div>'\n        +'<div class=\"logbox\"><h3>Linha de eventos</h3>'\n        +(c.log||[]).map(l=>'<div class=\"lrow\"><span class=\"lt\">'+fdm(l.t)+'</span><span class=\"lm\">'+(l.a?'<span class=\"auto\">auto</span>':'')+esc(l.m)+'</span></div>').join('')\n        +'</div>'\n    }\n    function renderTorre(){\n      const c=byId(S.sel)||CASES[0]\n      bodyEl.innerHTML='<div class=\"torre\">'\n        +'<div class=\"col left\">'+CASES.map(caseCard).join('')+'</div>'\n        +'<div class=\"col\">'+(c?detail(c):'<div class=\"na\">Selecione um caso.</div>')+'</div>'\n        +'</div>'\n      const hl=bodyEl.querySelector('.walkhl')\n      if(hl&&hl.scrollIntoView)hl.scrollIntoView({block:'center'})\n    }\n    function renderEntrada(){\n      bodyEl.innerHTML='<div class=\"entrada\">'\n        +'<div class=\"col left\">'\n        +'<div class=\"cover\"><h4>Cobertura do TroqueCommerce por origem · entrega</h4>'\n        +COVER.map(r=>'<div class=\"crow\"><span>'+r.r+' <span style=\"color:#9aa39b\">— '+esc(r.n)+'</span></span><span class=\"'+(r.ok?'cyes':'cno')+'\">'+(r.ok?'✅ coberto':'❌ não coberto')+'</span></div>').join('')\n        +'</div>'\n        +'<div class=\"na\" style=\"text-align:left;padding:0 0 14px;font-style:normal;color:#7a857c\">Como as contestações chegam hoje:</div>'\n        +FEED.map(f=>'<div class=\"fitem\"><div class=\"ftop\"><span class=\"chip\">'+esc(f.canal)+'</span><span class=\"cwhen\">'+fdm(f.when)+'</span></div>'\n          +'<div class=\"fraw\">'+esc(f.raw)+'</div>'\n          +'<div class=\"farrow\">↓ extraído</div>'\n          +'<div class=\"fchips\">'+f.out.map(o=>'<span class=\"fchip\">'+esc(o)+'</span>').join('')+'</div>'\n          +'<div class=\"fcase\">→ '+esc(f.caso)+'</div></div>').join('')\n        +'</div>'\n        +'<div class=\"col\"><div class=\"form\">'\n        +'<h3>Nova contestação</h3><div class=\"fs\">Simule uma entrada e veja o caso nascer com as duas trilhas. Casos criados ficam salvos neste navegador (🗑 no card pra excluir).</div>'\n        +'<div class=\"fl\">Nome do cliente</div><input class=\"fi f-nome\" value=\"Cliente da sessão\"/>'\n        +'<div class=\"fl\">Origem · entrega</div><select class=\"fi f-origem\"><option value=\"cd|entrega normal\">📦 CD · entrega normal</option><option value=\"dark|expressa\">⚡ Darkstore · expressa</option><option value=\"loja|expressa\">🏬 Loja · expressa</option><option value=\"loja|retirada\">🏬 Loja · retirada</option><option value=\"loja|balcão\">🏬 Loja · balcão</option></select>'\n        +'<div class=\"fl\">Marca</div><select class=\"fi f-marca\"><option>Granado</option><option>Phebo</option><option>Care</option></select>'\n        +'<div class=\"fl\">Motivo</div><select class=\"fi f-tipo\"><option>Arrependimento</option><option>Defeito</option><option>Não retirado</option><option>Troca no balcão</option></select>'\n        +'<div class=\"fl\">Pagamento</div><select class=\"fi f-pagto\"><option>Crédito · Adyen</option><option>PIX · Adyen</option><option>PIX parcelado · PagBrasil</option></select>'\n        +'<div class=\"fl\">Valor (R$)</div><input class=\"fi f-valor\" type=\"number\" value=\"129\"/>'\n        +'<div class=\"fl\">Score de risco</div><select class=\"fi f-risco\"><option>baixo</option><option>médio</option><option>alto</option></select>'\n        +'<button class=\"fbtn\">Registrar contestação</button>'\n        +'<div class=\"fnote\">Baixo risco → no mundo ideal o dinheiro volta <b>antes</b> de o produto voltar.</div>'\n        +'</div></div></div>'\n    }\n    function genCase(f){\n      const id='TR-'+(S.seq++)\n      const low=f.risco==='baixo'\n      const api=f.pagto.indexOf('PagBrasil')>=0?'API PagBrasil':'API Adyen'\n      let produto,dinheiro,promessa,status\n      if(f.tipo==='Troca no balcão'){\n        produto=[mk({n:'Registro pela loja',dono:'Loja',mora:'Sistema da loja',state:'done',when:D(0)}),mk({n:'Troca efetuada',dono:'Loja',sla:'15min',state:'done',when:D(0)}),mk({n:'Estoque ajustado',dono:'automático',mora:'SAP',state:'done',when:D(0)})]\n        dinheiro=[mk({n:'Voucher emitido na hora',dono:'Loja',mora:'Sistema da loja',sla:'imediato',state:'done',when:D(0)})]\n        promessa='Resolvido no balcão — cliente saiu da loja com a troca feita.'\n        status={t:'Resolvido na loja',cls:'ok'}\n      }else if(f.tipo==='Não retirado'){\n        produto=[mk({n:'Pedido pronto na loja',dono:'Loja',mora:'OMS + SAP',state:'done',when:D(0)}),mk({n:'Aviso automático · 7 dias',dono:'automático',mora:'Régua de comunicação',hoje:'auto',state:'next',when:D(7)}),mk({n:'Prazo final · 30 dias',dono:'automático',mora:'Gatilho do caso',state:'next',when:D(30)})]\n        dinheiro=[mk({n:'Elegibilidade condicional',dono:'automático',state:'next',when:D(30)}),mk({n:'Estorno automático',dono:'automático',mora:api,sla:'2h',state:'next',when:D(30)}),mk({n:'Na fatura',dono:'Processadora',state:'next',when:D(31)})]\n        promessa='Se não houver retirada até <b>'+fdm(D(30))+'</b>, o caso cancela e estorna sozinho.'\n        status={t:'Resolução automática agendada',cls:'gold'}\n      }else{\n        const viaTroque=(f.origem==='cd'||f.origem==='dark')\n        produto=[\n          mk({n:'Solicitação',dono:viaTroque?'TroqueCommerce':'❓ canal a mapear',mora:viaTroque?'TroqueCommerce':AD,hoje:viaTroque?'auto':'manual',state:'done',when:D(0),dores:viaTroque?'':'TroqueCommerce não cobre expressa via loja — canal de entrada a definir'}),\n          mk({n:'Devolução do produto',dono:viaTroque?'Cliente / Troque':AD,mora:viaTroque?'Troque.app.br':AD,hoje:viaTroque?'parcial':'manual',state:'next',when:D(2)}),\n          mk({n:'Autorização de reembolso [Jeferson]',dono:'Jeferson · Logística',mora:'Logística — manual',sla:'ideal 1d · hoje 5–10d',state:'next',when:D(4)}),\n          mk({n:'Destino final',dono:'CD · Qualidade',state:'next',when:D(5)})]\n        if(low){\n          dinheiro=[mk({n:'Decisão: estorno imediato',dono:'score baixo risco',sla:'imediata',state:'done',when:D(0),note:'sem aguardar devolução'}),mk({n:'Execução',dono:api,sla:'2h',state:'now',note:'em execução · previsto hoje'}),mk({n:'Na fatura',dono:'Processadora',state:'next',when:D(1)})]\n          promessa='Score de baixo risco: reembolso <b>hoje</b>, antes de o produto voltar.'\n        }else{\n          dinheiro=[mk({n:'Logística autoriza reembolso',dono:'Logística',mora:'Magento + SAP + planilha OMNI+SAC',state:'next',when:D(5)}),mk({n:'Financeiro processa reembolso',dono:'Financeiro',mora:'Planilha + Adyen um a um',sla:'ideal imediato · hoje 24h',state:'next',when:D(6)}),mk({n:'Na fatura',dono:'Processadora',state:'next',when:D(7)})]\n          promessa='Risco '+f.risco+': o dinheiro volta após a autorização — previsão <b>'+fdm(D(6))+'</b>, já comunicada ao cliente.'\n        }\n        status={t:'No prazo · recém-criado',cls:'ok'}\n      }\n      return {id,cliente:f.nome||'Cliente da sessão',origem:f.origem,entrega:f.entrega,canal:'Simulador',marca:f.marca,tipo:f.tipo,pagto:f.pagto,valor:f.valor,risco:f.risco,when:D(0),status,promessa,produto,dinheiro,\n        log:[{t:D(0),a:1,m:'Caso criado estruturado · origem '+(ORIGENS[f.origem]||f.origem)+' · '+f.entrega}]}\n    }\n    function exportText(){\n      const L=[]\n      L.push('# Discovery Troca — respostas ('+fdm(new Date().toISOString())+')')\n      L.push('')\n      let cm=0,cp=0,ca=0,ci=0\n      CASES.forEach(c=>{\n        L.push('## '+c.id+' · '+c.cliente+' · '+orgLabel(c).replace(/[📦⚡🏬] /,'')+' · '+c.tipo+' · '+c.marca)\n        ;['produto','dinheiro'].forEach(track=>{\n          L.push('### Trilha do '+track)\n          c[track].forEach((s,i)=>{\n            if(s.hoje==='manual')cm++;else if(s.hoje==='parcial')cp++;else if(s.hoje==='auto')ca++;else ci++\n            L.push((i+1)+'. '+s.n+' — dono: '+s.dono+' | mora em: '+s.mora+' | '+HOJE[s.hoje]+(s.sla&&s.sla!=='—'?' | SLA: '+s.sla:''))\n            if(s.fonte&&s.fonte!=='—'&&s.fonte)L.push('   fonte (ideal): '+s.fonte)\n            if(s.dores&&s.dores!=='—'&&s.dores)L.push('   dores: '+s.dores.replace(/\\n/g,' / '))\n          })\n        })\n        L.push('')\n      })\n      L.push('---')\n      L.push('Resumo: '+cm+' etapas manuais · '+cp+' parciais · '+ci+' inexistentes · '+ca+' automáticas')\n      L.push('Etapas ainda sem resposta (dono/mora \"'+AD+'\"): '+CASES.reduce((n,c)=>n+[...c.produto,...c.dinheiro].filter(s=>!answered(s)).length,0))\n      return L.join('\\n')\n    }\n    function copyText(txt){\n      if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(txt).then(()=>toast('📋 Respostas copiadas — cola no chat!'),()=>toast('não consegui copiar'))}\n      else{toast('clipboard indisponível')}\n    }\n    function saveEditForm(f){\n      const c=byId(S.sel); if(!c||!S.edit)return null\n      const s=c[S.edit.track][S.edit.idx]; if(!s)return null\n      const gv=cl=>{const el=f.querySelector(cl);return el?el.value:''}\n      s.n=gv('.e-n'); s.dono=gv('.e-dono'); s.mora=gv('.e-mora'); s.sla=gv('.e-sla')||'—'\n      s.hoje=gv('.e-hoje')||s.hoje; s.fonte=gv('.e-fonte'); s.dores=gv('.e-dores')\n      persistCase(c); return s\n    }\n    function walkGo(pos){\n      const c=byId(S.sel); if(!c||!S.walk)return\n      if(pos>=S.walk.list.length){S.walk=null;S.edit=null;toast('✔ Percurso concluído — agora é só 📋 copiar respostas');renderTorre();renderKpis();return}\n      if(pos<0)pos=0\n      S.walk.pos=pos\n      const t=S.walk.list[pos]\n      S.edit={track:t.track,idx:t.idx}\n      renderTorre()\n    }\n    function renderView(){ if(S.view==='torre')renderTorre(); else renderEntrada(); renderKpis() }\n    d2.on('.tab','click',e=>{\n      const b=e.target.closest('.tab'); if(!b)return\n      S.view=b.getAttribute('data-v'); S.edit=null; S.walk=null\n      d2.$$('.tab').forEach(x=>x.classList.toggle('hot',x===b))\n      renderView()\n    })\n    d2.on('.b-export','click',()=>{copyText(exportText())})\n    d2.on('.b-delcase','click',e=>{\n      const b=e.target.closest('.b-delcase'); if(!b)return\n      e.stopPropagation()\n      const id=b.getAttribute('data-id')\n      SAVED.created=(SAVED.created||[]).filter(x=>x.id!==id); persist()\n      const i=CASES.findIndex(x=>x.id===id); if(i>=0)CASES.splice(i,1)\n      if(S.sel===id)S.sel=CASES[0]?CASES[0].id:null\n      S.edit=null; S.walk=null; renderTorre(); renderKpis()\n    })\n    d2.on('.ccard','click',e=>{\n      if(e.target.closest('.b-delcase'))return\n      const c=e.target.closest('.ccard'); if(!c)return\n      S.sel=c.getAttribute('data-id'); S.edit=null; S.walk=null; renderTorre()\n    })\n    d2.on('.b-walk','click',()=>{\n      const c=byId(S.sel); if(!c)return\n      S.walk={list:allSteps(c),pos:0}\n      walkGo(0)\n    })\n    d2.on('.b-wnext','click',e=>{\n      const f=e.target.closest('.eform'); if(!f)return\n      saveEditForm(f); renderKpis()\n      walkGo(S.walk?S.walk.pos+1:0)\n    })\n    d2.on('.b-wprev','click',e=>{\n      const b=e.target.closest('.b-wprev'); if(!b||b.disabled)return\n      const f=e.target.closest('.eform'); if(f)saveEditForm(f)\n      walkGo(S.walk?S.walk.pos-1:0)\n    })\n    d2.on('.b-wexit','click',()=>{S.walk=null;S.edit=null;renderTorre()})\n    d2.on('.ebtn','click',e=>{\n      const b=e.target.closest('.ebtn'); if(!b)return\n      S.walk=null\n      S.edit={track:b.getAttribute('data-track'),idx:Number(b.getAttribute('data-idx'))}\n      renderTorre()\n    })\n    d2.on('.mbtn','click',e=>{\n      const b=e.target.closest('.mbtn'); if(!b||b.disabled)return\n      const c=byId(S.sel); if(!c)return\n      const track=b.getAttribute('data-track'), idx=Number(b.getAttribute('data-idx')), dir=Number(b.getAttribute('data-dir'))\n      const arr=c[track], j=idx+dir\n      if(j<0||j>=arr.length)return\n      const tmp=arr[idx]; arr[idx]=arr[j]; arr[j]=tmp\n      S.edit=null; S.walk=null; persistCase(c); renderTorre()\n    })\n    d2.on('.b-add','click',e=>{\n      const b=e.target.closest('.b-add'); if(!b)return\n      const c=byId(S.sel); if(!c)return\n      const track=b.getAttribute('data-track')\n      c[track].push(mk({n:'Nova etapa',fonte:AD,dores:AD}))\n      S.walk=null; S.edit={track,idx:c[track].length-1}\n      persistCase(c); renderTorre()\n    })\n    d2.on('.b-save','click',e=>{\n      const f=e.target.closest('.eform'); if(!f)return\n      saveEditForm(f)\n      S.edit=null; renderTorre(); renderKpis()\n    })\n    d2.on('.b-cancel','click',e=>{\n      if(e.target.closest('.b-wexit'))return\n      S.edit=null; renderTorre()\n    })\n    d2.on('.b-del','click',()=>{\n      const c=byId(S.sel); if(!c||!S.edit)return\n      c[S.edit.track].splice(S.edit.idx,1)\n      S.edit=null; S.walk=null; persistCase(c); renderTorre(); renderKpis()\n    })\n    d2.on('.b-restore','click',()=>{\n      const c=byId(S.sel); if(!c)return\n      const def=DEFAULTS.find(x=>x.id===c.id)\n      if(def){ delete SAVED.edits[c.id]; persist(); c.produto=clone(def.produto); c.dinheiro=clone(def.dinheiro) }\n      S.edit=null; S.walk=null; renderTorre(); renderKpis()\n    })\n    d2.on('.fbtn','click',()=>{\n      const v=s=>{const el=d2.$(s);return el?el.value:''}\n      const og=(v('.f-origem')||'cd|entrega normal').split('|')\n      const novo=genCase({nome:v('.f-nome'),origem:og[0],entrega:og[1],marca:v('.f-marca'),tipo:v('.f-tipo'),pagto:v('.f-pagto'),valor:Number(v('.f-valor'))||0,risco:v('.f-risco')})\n      CASES.unshift(novo)\n      SAVED.created=SAVED.created||[]; SAVED.created.unshift(clone(novo)); persist()\n      S.sel=novo.id; S.view='torre'; S.edit=null; S.walk=null\n      d2.$$('.tab').forEach(x=>x.classList.toggle('hot',x.getAttribute('data-v')==='torre'))\n      renderView()\n    })\n    renderView()\n  }\n}"}}}}}}}},{"_id":"6a91c00796814ecdd8a7a9f7","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kDeckB2b","package":"granado-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 FONT_CSS = `@font-face{font-family:'AkzExt';src:url(https://granado-app.data2.media/file/EBpu8brvJpn11D4il45TS) format('opentype');font-weight:400;font-display:block}@font-face{font-family:'AkzExtLight';src:url(https://granado-app.data2.media/file/8orsK0wtYTghGT7sGMiC6) format('opentype');font-weight:300;font-display:block}`\n// Variantes por subdominio. Cada Application publicada resolve a sua pelo hostname:\n//   proposta-b2b        -> deck completo (inclui a secao Investimento)\n//   proposta-canal-b2b  -> mesmo deck, sem a parte comercial\n// ?v=<chave> forca uma variante (util no preview do builder).\nconst VARIANTS={\n  'proposta-b2b':{key:'proposta-b2b',pass:'yestemosbananas',comercial:true},\n  'proposta-canal-b2b':{key:'proposta-canal-b2b',pass:'esplendor',comercial:false}\n}\nconst DEFAULT_VARIANT=VARIANTS['proposta-b2b']\nconst OPEN=false\n\nfunction variant(){\n  try{\n    const q=(location.search||'').match(/[?&]v=([\\w-]+)/)\n    if(q&&VARIANTS[q[1]])return VARIANTS[q[1]]\n    // O host publicado tem duas formas: 'proposta-canal-b2b.granado-app.data2.app'\n    // e 'proposta-canal-b2b--granado-app.next.data2.app'. Cobrimos as duas.\n    const label=(location.hostname||'').split('.')[0]\n    if(VARIANTS[label])return VARIANTS[label]\n    const app=label.split('--')[0]\n    if(VARIANTS[app])return VARIANTS[app]\n  }catch(e){}\n  return DEFAULT_VARIANT\n}\n\nconst GATE = `<div class=\"gwrap\">\n  <img class=\"gbg\" src=\"https://granado-app.data2.media/file/gypbe6V0A-Zv7jM4xaJ-l\" alt=\"\">\n  <div class=\"gcard\">\n    <div class=\"glock\"><img src=\"https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt\" style=\"height:22px\" alt=\"Data²\"><span>+</span><img src=\"https://granado-app.data2.media/file/T6YAJeTNwwvYpaJJPQg9K\" style=\"height:14px\" alt=\"Granado\"></div>\n    <div class=\"gsub\">Proposta · Auto-atendimento do canal B2B</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>\n</div>`\n\nconst DECK = `<div class=\"deckwrap\">\n<header>\n  <img class=\"d2\" src=\"https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt\" alt=\"Data²\">\n  <img class=\"gr\" src=\"https://granado-app.data2.media/file/T6YAJeTNwwvYpaJJPQg9K\" alt=\"Granado\">\n</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\n\n<div class=\"deckroot\">\n<!-- 01 · CAPA -->\n<section class=\"capa\" id=\"capa\">\n  <div class=\"frame kb rv\">\n    <img class=\"bgimg\" src=\"https://granado-app.data2.media/file/gypbe6V0A-Zv7jM4xaJ-l\" alt=\"\">\n    <div class=\"mid\">\n      <div class=\"lockup\">\n        <img class=\"d2m\" src=\"https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt\" alt=\"Data²\">\n        <span class=\"plus\">+</span>\n        <img class=\"grm\" src=\"https://granado-app.data2.media/file/T6YAJeTNwwvYpaJJPQg9K\" alt=\"Granado\">\n      </div>\n      <div class=\"sub\">Auto-atendimento do canal B2B</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 · Agosto 2026</div>\n  </div>\n</section>\n\n<!-- ÍNDICE -->\n<section id=\"indice\">\n  <div class=\"eyebrow rv\">Índice</div>\n  <div class=\"indice rv\">\n    <a href=\"#data2\"><span class=\"n\">01</span><span class=\"t\">Quem somos</span></a>\n    <a href=\"#consultoria\"><span class=\"n\">02</span><span class=\"t\">Como trabalhamos</span></a>\n    <a href=\"#experts\"><span class=\"n\">03</span><span class=\"t\">Domain Experts</span></a>\n    <a href=\"#tese\"><span class=\"n\">04</span><span class=\"t\">A proposta</span></a>\n    <a href=\"#conceito\"><span class=\"n\">05</span><span class=\"t\">O conceito</span></a>\n    <a href=\"#arquitetura\"><span class=\"n\">06</span><span class=\"t\">Arquitetura e integração</span></a>\n    <a href=\"#pratica\"><span class=\"n\">07</span><span class=\"t\">Na prática</span></a>\n    <a href=\"#sprint1\"><span class=\"n\">08</span><span class=\"t\">Sprint 1 · O essencial</span></a>\n    <a href=\"#sprint2\"><span class=\"n\">09</span><span class=\"t\">Sprint 2 · O canal ideal</span></a>\n    <a href=\"#kpis\"><span class=\"n\">10</span><span class=\"t\">O que vamos medir</span></a>\n    <a href=\"#cronograma\"><span class=\"n\">11</span><span class=\"t\">Cronograma</span></a>\n    <a href=\"#investimento\"><span class=\"n\">12</span><span class=\"t\">Investimento</span></a>\n    <a href=\"#kickoff\"><span class=\"n\">13</span><span class=\"t\">Antes do kickoff</span></a>\n  </div>\n</section>\n\n<!-- 02 · DATA² -->\n<section id=\"data2\">\n  <div class=\"eyebrow rv\">Data² · Filosofia e tecnologia</div>\n  <div class=\"split\" style=\"align-items:end;margin-bottom:clamp(26px,5vh,52px)\">\n    <div>\n      <img class=\"logofull rv\" src=\"https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt\" alt=\"Data²\">\n      <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,46px);line-height:1.14;max-width:16ch\">Necessidade elevada à produção.</h2>\n    </div>\n    <div style=\"display:flex;flex-direction:column;align-items:flex-start;gap:clamp(20px,3.4vh,32px)\">\n      <img class=\"ring rv\" src=\"https://granado-app.data2.media/file/YHYIIb0_WelaXTRzjFKsb\" alt=\"\">\n      <p class=\"apoio destaque rv\" style=\"margin-top:0\">Construímos a Data² imaginando um mundo onde as coisas simplesmente … funcionam. Uma plataforma onde dados, lógica, interfaces e agentes de IA vivem no mesmo runtime — a ideia que o time de negócio descreve hoje é a solução que ele opera amanhã.</p>\n    </div>\n  </div>\n  <div class=\"prods rv\">\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#FA48DC\"></span>Domain</div><p>A inteligência: conversa, decide e raciocina sobre o negócio.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#4866FA\"></span>UI</div><p>Interfaces completas, geradas e evoluídas na plataforma.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#FA4848\"></span>Registry</div><p>Os dados: catálogos, tabelas, milhões de registros tipados.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#8448FA\"></span>Rush</div><p>Escuta tudo que entra: eventos, webhooks, mensagens.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#48FAFA\"></span>Connect</div><p>Integra tudo que sai: ERP, WhatsApp, APIs, e-mail.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#48FAA1\"></span>ID</div><p>Identidade, permissões e políticas de acesso.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#BFFA48\"></span>Cashier</div><p>Transaciona: pagamentos, cobrança, conciliação.</p></div>\n    <div class=\"p\"><div class=\"pn\"><span class=\"dot\" style=\"background:#FADC48\"></span>Media</div><p>Gera imagem e mídia a partir do catálogo e da marca.</p></div>\n  </div>\n</section>\n\n<!-- 03 · CONSULTORIA / SPRINTS -->\n<section id=\"consultoria\">\n  <div class=\"eyebrow rv\">Como trabalhamos</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\">Sprints que começam num KPI e terminam em produção.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Nada de projetos de um ano. Cada sprint ataca um problema medível, com escopo fechado, custo conhecido e resultado rodando em semanas.</p>\n  </div>\n  <div class=\"pilares\">\n    <div class=\"pilar rv d2\"><span class=\"k\">01 · KPI</span><div class=\"t\">Escolhemos o número que precisa mexer</div><p>Reativação, conversão, custo de atendimento. O sprint nasce de um indicador de negócio — não de um levantamento de requisitos.</p></div>\n    <div class=\"pilar rv d3\"><span class=\"k\">02 · Sprint</span><div class=\"t\">Semanas, com escopo e custo fechados</div><p>Time sênior da Data² desenha e constrói direto na plataforma. Quem projeta é quem entrega — o know-how não se perde em repasses.</p></div>\n    <div class=\"pilar rv d4\"><span class=\"k\">03 · Produção</span><div class=\"t\">Entrega medida no mesmo KPI</div><p>O sprint termina com a solução em produção e o indicador sendo acompanhado. O que funciona, escala na onda seguinte.</p></div>\n  </div>\n</section>\n\n<!-- 04 · DOMAIN EXPERTS -->\n<section id=\"experts\">\n  <div class=\"eyebrow rv\">Domain Experts</div>\n  <div class=\"split\" style=\"align-items:center\">\n    <div>\n      <h2 class=\"rv\" style=\"font-size:clamp(28px,4vw,56px);line-height:1.12;max-width:15ch\">Tecnologia, com quem conhece a operação por dentro.</h2>\n      <p class=\"apoio rv\">A cada projeto, um parceiro especialista do domínio trabalha lado a lado com o time Data². É essa combinação que nos permite ir além: redesenhar o processo por inteiro, em vez de automatizar o que já não funciona.</p>\n    </div>\n    <div class=\"expert rv d2\">\n      <div class=\"mono\">AS</div>\n      <div class=\"nome\">Ale Schwab</div>\n      <div class=\"firma\">AS.Consulting</div>\n      <p>Especialista em transformação de Customer Experience, operações e processos, conectando estratégia, tecnologia e IA para gerar eficiência, escala e crescimento. Neste projeto, integra o time Granado na jornada de descoberta.</p>\n    </div>\n  </div>\n</section>\n\n<!-- 05 · STATEMENT -->\n<section class=\"statement\" id=\"tese\">\n  <div class=\"eyebrow rv\">A proposta</div>\n  <div class=\"split\">\n    <h1 class=\"rv\">Atender bem quem o comercial não alcança.</h1>\n    <p class=\"apoio destaque rv\">Os pequenos e médios clientes do portal B2B compram sozinhos, recorrentemente — e hoje não têm com quem falar. A proposta: um canal digital que resolve de ponta a ponta, com o comercial entrando só onde ele importa.</p>\n  </div>\n</section>\n\n<!-- 04 · CONCEITO -->\n<section id=\"conceito\">\n  <div class=\"eyebrow rv\">O conceito</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(18px,3vh,34px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(25px,3.2vw,48px);line-height:1.12;max-width:15ch\">Um canal que resolve — não roteia.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O bot antigo terminava quase todo caminho em \"abra um chamado\". O novo canal termina em resposta: consulta os sistemas da Granado e responde na hora.</p>\n  </div>\n  <div class=\"pilares quatro\">\n    <div class=\"pilar rv d2\"><span class=\"k\">Resolver</span><div class=\"t\">Resposta na hora, com dado de verdade</div><p>Cadastro validado em tempo real com motivo de rejeição na tela, rastreio de pedido, segunda via de NF direto no canal.</p></div>\n    <div class=\"pilar rv d3\"><span class=\"k\">Avisar</span><div class=\"t\">A Granado fala primeiro</div><p>Boleto pendente, produto de volta ao estoque, cadastro aprovado: hoje o cliente descobre sozinho. Passa a ser avisado por WhatsApp. A Granado cuida da relação com o cliente de ponta a ponta.</p></div>\n    <div class=\"pilar rv d4\"><span class=\"k\">Vender</span><div class=\"t\">Pedido no WhatsApp em 3 cliques</div><p>Catálogo, carrinho e checkout completos dentro da conversa — inclusive split de pagamento quando o limite de boleto não cobre.</p></div>\n    <div class=\"pilar rv d5\"><span class=\"k\">Cuidar</span><div class=\"t\">Pós-vendas sem atrito</div><p>Entrega com problema, produto com defeito, item faltando, cobrança em aberto: o cliente resolve no mesmo canal em que comprou. Cada caso nasce registrado no Service Cloud.</p><div class=\"nota\">No piloto o canal recebe, coleta tudo — nota, itens e fotos — e abre o caso. A resolução automática ponta a ponta depende das integrações e é avaliada depois do piloto.</div></div>\n  </div>\n</section>\n\n<!-- ARQUITETURA -->\n<section id=\"arquitetura\">\n  <div class=\"eyebrow rv\">Arquitetura e integração</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\">Aceleramos porque a fundação já está no ar.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">A engine Data² que já roda na Granado é a mesma que vai operar este canal — motor de conversa, catálogo, carrinho, rastreio e crédito já validados em produção com dado real. Integramos por leitura; nada do que funciona hoje é alterado.</p>\n  </div>\n  <div class=\"arq rv\">\n    <div class=\"col\">\n      <div class=\"col-h\">Sistemas Granado · intocados</div>\n      <div class=\"box\"><div class=\"bn\">Magento</div><div class=\"bd\">Portal B2B, catálogo, carrinho</div></div>\n      <div class=\"box\"><div class=\"bn\">JD Edwards</div><div class=\"bd\">ERP, financeiro, boletos</div></div>\n      <div class=\"box\"><div class=\"bn\">OMS + Confirma Fácil</div><div class=\"bd\">Pedidos, NF, entrega</div></div>\n      <div class=\"box\"><div class=\"bn\">Salesforce Service Cloud</div><div class=\"bd\">Cada interação do canal vira caso no histórico do cliente</div></div>\n    </div>\n    <div class=\"link\"><span>leitura · API</span></div>\n    <div class=\"core\">\n      <div class=\"ch\">Data² · engine já publicada</div>\n      <div class=\"box\"><div class=\"bn\">Conectores de leitura</div><div class=\"bd\">Consultam os sistemas na hora da conversa — sem réplica, sem batch</div></div>\n      <div class=\"box\"><div class=\"bn\">Agente de atendimento</div><div class=\"bd\">Regras do canal, macros higienizadas, roteamento pro comercial quando o faturamento passa de R$ 50k</div></div>\n      <div class=\"box\"><div class=\"bn\">Avisos proativos</div><div class=\"bd\">Boleto pendente, estoque de volta, status de cadastro</div></div>\n    </div>\n    <div class=\"link\"><span>conversa</span></div>\n    <div class=\"col\">\n      <div class=\"col-h\">Revendedor</div>\n      <div class=\"box\"><div class=\"bn\">WhatsApp</div><div class=\"bd\">Onde o cliente já está</div></div>\n      <div class=\"box\"><div class=\"bn\">Portal B2B</div><div class=\"bd\">Mesmo atendimento, embarcado no site</div></div>\n      <div class=\"box\"><div class=\"bn\">Comercial Granado</div><div class=\"bd\">Recebe só o que é dele — faturamento acima de R$ 50k e exceções</div></div>\n    </div>\n  </div>\n</section>\n\n<!-- NA PRÁTICA · 1 -->\n<section id=\"pratica\">\n  <div class=\"eyebrow rv\">Na prática · comprar e acompanhar</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.4vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,46px);line-height:1.12;max-width:16ch\">A diferença que o revendedor sente.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Tarefas comuns do dia a dia do canal, lado a lado: o caminho de hoje e a mesma tarefa dentro da conversa.</p>\n  </div>\n  <div class=\"comps rv\">\n    <div class=\"comp\">\n      <div class=\"ctit\">Rastrear um pedido</div>\n      <div class=\"hj\">\n        <div class=\"lk\">Hoje · até 48 horas</div>\n        <ul class=\"steps\">\n          <li>O cliente abre uma solicitação no portal de chamados</li>\n          <li>O time consulta o Magento, o OMS e o Confirma Fácil, sistema por sistema</li>\n          <li>A resposta volta dentro do prazo padrão de 48 horas</li>\n        </ul>\n      </div>\n      <div class=\"cn\">\n        <div class=\"lk\">Com o canal · segundos</div>\n        <div class=\"demo\">\n          <div class=\"b cli\">Cadê meu pedido 48122?</div>\n          <div class=\"b can\">Saiu do CD ontem e está com a transportadora — previsão de entrega sexta.</div>\n        </div>\n      </div>\n    </div>\n    <div class=\"comp\">\n      <div class=\"ctit\">Montar um pedido — difusores pro escritório</div>\n      <div class=\"hj\">\n        <div class=\"lk\">Hoje · página por página</div>\n        <ul class=\"steps\">\n          <li>Pesquisar cada aroma de difusor, um por um</li>\n          <li>Adicionar item a item, com a página recarregando a cada passo</li>\n          <li>Repetir o processo inteiro pra cada variação</li>\n        </ul>\n      </div>\n      <div class=\"cn\">\n        <div class=\"lk\">Com o canal · uma conversa</div>\n        <div class=\"demo\">\n          <div class=\"b cli\">Quais aromas de difusor vocês têm?</div>\n          <div class=\"b can\">Quatro: alecrim, capim-limão, lavanda e flor de laranjeira.</div>\n          <div class=\"b cli\">Quero 10 de cada.</div>\n          <div class=\"cart\">\n            <div class=\"ch\">Carrinho · 40 itens</div>\n            <div class=\"ci\"><span>Difusor 250 ml · alecrim</span><b>10 un</b></div>\n            <div class=\"ci\"><span>Difusor 250 ml · capim-limão</span><b>10 un</b></div>\n            <div class=\"ci\"><span>Difusor 250 ml · lavanda</span><b>10 un</b></div>\n            <div class=\"ci\"><span>Difusor 250 ml · flor de laranjeira</span><b>10 un</b></div>\n            <div class=\"ci tot\"><span>Total na sua tabela de revenda</span><b>R$ 3.560</b></div>\n            <span class=\"lnk\">granado.com.br/b2b/checkout/48b2 <i>↗</i></span>\n          </div>\n        </div>\n      </div>\n    </div>\n  </div>\n</section>\n\n<!-- NA PRÁTICA · 2 -->\n<section id=\"pratica2\">\n  <div class=\"eyebrow rv\">Na prática · perguntar e desbloquear</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.4vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,46px);line-height:1.12;max-width:17ch\">O que hoje vira e-mail, chamado ou espera.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">As perguntas que o canal passa a responder na hora — com dado dos sistemas da Granado e com uma base que o time de negócios mantém sozinho.</p>\n  </div>\n  <div class=\"comps rv\">\n    <div class=\"comp\">\n      <div class=\"ctit\">“Qual é a minha tabela de preço?”</div>\n      <div class=\"hj\">\n        <div class=\"lk\">Hoje · vai e vem de e-mail</div>\n        <ul class=\"steps\">\n          <li>O cliente manda e-mail pro comercial ou pro atendimento</li>\n          <li>A resposta depende de alguém ter tempo pra procurar</li>\n        </ul>\n      </div>\n      <div class=\"cn\">\n        <div class=\"lk\">Com o canal · na hora</div>\n        <div class=\"demo\">\n          <div class=\"b cli\">Por que perfume não aparece pra mim?</div>\n          <div class=\"b can\">Perfume não entra no catálogo de revenda do seu cadastro. A sua tabela é esta, com o seu desconto já aplicado.</div>\n        </div>\n        <div class=\"cnote\">As respostas saem de uma base administrada pelo time de negócios — ele edita no painel e muda na conversa.</div>\n      </div>\n    </div>\n    <div class=\"comp\">\n      <div class=\"ctit\">Cadastro em análise, pedido já feito</div>\n      <div class=\"hj\">\n        <div class=\"lk\">Hoje · dias parado</div>\n        <ul class=\"steps\">\n          <li>Sem cadastro aprovado, o cliente não consegue comprar</li>\n          <li>A aprovação leva dias, sem status — e a compra fica pra depois</li>\n        </ul>\n      </div>\n      <div class=\"cn\">\n        <div class=\"lk\">Com o canal · compra na hora</div>\n        <div class=\"demo\">\n          <div class=\"b cli\">Meu cadastro ainda está em análise. Já consigo comprar?</div>\n          <div class=\"b can\">Consegue: eu fecho o pedido agora e ele fica reservado, esperando a aprovação do cadastro. Pedido na fila prioriza a análise — te aviso no minuto em que aprovar.</div>\n        </div>\n        <div class=\"cnote\">Quem fica pendente é o pedido, não o cliente. O pedido na fila é o que prioriza a aprovação do cadastro — e ninguém espera pra comprar. Entra na Sprint 2, junto das integrações de Receita Federal, SEFAZ e bureau.</div>\n      </div>\n    </div>\n    <div class=\"comp\">\n      <div class=\"ctit\">2ª via de boleto e nota fiscal</div>\n      <div class=\"hj\">\n        <div class=\"lk\">Hoje · chamado e prazo de 48 horas</div>\n        <ul class=\"steps\">\n          <li>Abre solicitação no portal de chamados</li>\n          <li>O time procura o documento e devolve dentro do prazo padrão</li>\n        </ul>\n      </div>\n      <div class=\"cn\">\n        <div class=\"lk\">Com o canal · segundos</div>\n        <div class=\"demo\">\n          <div class=\"b cli\">Preciso da segunda via do boleto do pedido 48122.</div>\n          <div class=\"b can\">Aqui: boleto do pedido 48122, vence sexta. A nota fiscal do pedido também está aqui.</div>\n        </div>\n      </div>\n    </div>\n  </div>\n</section>\n\n<!-- SPRINT 1 -->\n<section id=\"sprint1\">\n  <div class=\"eyebrow rv\">O que vamos construir · <b>Sprint 1</b></div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.6vh,28px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">O essencial, impecável.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Comprar, acompanhar e resolver — no WhatsApp e no portal, pra toda a base. Boa parte já roda em produção com dado real: o piloto começa daqui.</p>\n  </div>\n  <div class=\"leg rv\"><span><i class=\"ok\"></i>Validado — já roda em produção</span><span><i class=\"nv\"></i>Novo — o que vamos construir</span></div>\n  <div class=\"frentes rv\">\n    <div class=\"f\"><div class=\"fn\">Gestão de carrinhos <span class=\"ct\">5 validados · 2 novos</span></div>\n      <div class=\"fi ok\">Buscar produto no catálogo</div>\n      <div class=\"fi ok\">Avise-me quando chegar</div>\n      <div class=\"fi ok\">Montar e ver carrinho com total real</div>\n      <div class=\"fi ok\">Remover item / atualizar quantidade</div>\n      <div class=\"fi ok\">Consultar meus pedidos</div>\n      <div class=\"fi nv\">Finalizar em PIX e cartão (página de checkout)</div>\n      <div class=\"fi nv\">Pedido mínimo por região + cupom</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Pedidos e histórico <span class=\"ct\">2 validados · 3 novos</span></div>\n      <div class=\"fi ok\">Rastrear pedido (Confirma Fácil)</div>\n      <div class=\"fi ok\">Consulta de crédito</div>\n      <div class=\"fi nv\">Segunda via de nota fiscal</div>\n      <div class=\"fi nv\">Segunda via de boleto</div>\n      <div class=\"fi nv\">Boleto bloqueado por limite ou pendência</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">WhatsApp + widget web <span class=\"ct\">1 validado · 2 novos</span></div>\n      <div class=\"fi ok\">Motor de atendimento (login, sessão, ações)</div>\n      <div class=\"fi nv\">Widget embarcado no portal B2B</div>\n      <div class=\"fi nv\">Número próprio de WhatsApp</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">FAQs <span class=\"ct\">4 novos</span></div>\n      <div class=\"fi nv\">Explicar o catálogo restrito e as políticas, com dado real</div>\n      <div class=\"fi nv\">Painel de FAQ — o time Granado alimenta sozinho</div>\n      <div class=\"fi nv\">Pesquisa de satisfação (CSAT) ao fim da conversa</div>\n      <div class=\"fi nv\">Respostas fora de escopo roteadas certo</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Fluxos guiados <span class=\"ct\">3 novos</span></div>\n      <div class=\"fi nv\">Avaria, sobra ou item faltando — coleta completa</div>\n      <div class=\"fi nv\">Cancelamento e devolução guiados</div>\n      <div class=\"fi nv\">Saída manual pro portal de chamados</div>\n      <div class=\"fnote\">O bot coleta NF, itens e fotos; a decisão final segue humana, registrada no Service Cloud.</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Service Cloud <span class=\"ct\">2 novos</span></div>\n      <div class=\"fi nv\">Cada interação vira caso, no histórico do cliente</div>\n      <div class=\"fi nv\">Mapeamento das jornadas na própria sprint</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Painel de acompanhamento <span class=\"ct\">1 novo</span></div>\n      <div class=\"fi nv\">Monitoramento, alarmes e acompanhamento do canal, medindo desde o 1º dia</div>\n    </div>\n  </div>\n</section>\n\n<!-- SPRINT 2 -->\n<section id=\"sprint2\">\n  <div class=\"eyebrow rv\">O que vamos construir · <b>Sprint 2</b></div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.6vh,28px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(26px,3.4vw,48px);line-height:1.12;max-width:16ch\">O canal ideal.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O que faz o atendimento virar experiência: cadastro aprovado na hora, crédito, compra sem esforço e um humano a um toque — com as integrações novas de Receita Federal, SEFAZ e bureau de crédito.</p>\n  </div>\n  <div class=\"leg rv\"><span><i class=\"nv\"></i>Novo — o que vamos construir</span></div>\n  <div class=\"frentes rv\">\n    <div class=\"f\"><div class=\"fn\">Cadastro (CNPJ) <span class=\"ct\">5 novos</span></div>\n      <div class=\"fi nv\">Novo cadastro direto na conversa</div>\n      <div class=\"fi nv\">Rejeitado — com o motivo na hora</div>\n      <div class=\"fi nv\">Em análise — o cliente já fecha o pedido; quem espera aprovação é o pedido</div>\n      <div class=\"fi nv\">Aviso no minuto em que o cadastro aprova</div>\n      <div class=\"fi nv\">Alterar dados da empresa</div>\n      <div class=\"fnote\">Depende de desbloqueio de TI Granado: cadastro sem captcha e integrações com Receita, SEFAZ e bureau.</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Crédito <span class=\"ct\">2 novos</span></div>\n      <div class=\"fi nv\">Solicitar análise pra comprar no boleto a prazo</div>\n      <div class=\"fi nv\">Pedir aumento do limite aprovado</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Compra sem esforço <span class=\"ct\">3 novos</span></div>\n      <div class=\"fi nv\">Refazer pedido — o \"pedido de sempre\" em um toque</div>\n      <div class=\"fi nv\">Recorrência automática, no ciclo de compra do cliente</div>\n      <div class=\"fi nv\">Sugestões de produto com base no histórico de pedidos</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Transbordo humano <span class=\"ct\">1 novo</span></div>\n      <div class=\"fi nv\">Escalar pra atendente — nada fica sem resposta</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Catálogo e roteamento <span class=\"ct\">2 novos</span></div>\n      <div class=\"fi nv\">Catálogo completo de revenda em PDF</div>\n      <div class=\"fi nv\">Roteamento do que é fora de escopo</div>\n    </div>\n    <div class=\"f\"><div class=\"fn\">Na conversa</div><div class=\"demo\">\n      <div class=\"b cli\">Fecha meu pedido de sempre.</div>\n      <div class=\"b can\">Fechado: 18 itens, R$ 1.870 — link do checkout aqui. Posso repetir esse pedido todo dia 5? E o polvilho antisséptico combina com o seu mix.</div>\n    </div></div>\n  </div>\n</section>\n\n<!-- KPIS -->\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\">Cada sprint começa num número. Estes são os nossos.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Os sete indicadores já validados com Secco e Papa — o dashboard entra na Sprint 1, medindo desde o primeiro dia do piloto. Onde já existe número conhecido, ele entra como linha de base.</p>\n  </div>\n  <div class=\"kpis rv\">\n    <div class=\"k\"><div class=\"kt\">Conversão</div><p>Interações no canal que terminam em pedido fechado.</p></div>\n    <div class=\"k\"><div class=\"kt\">Incremento de receita</div><p>Pedidos originados na conversa + receita de clientes reativados.</p></div>\n    <div class=\"k\"><div class=\"kt\">Horas desoneradas</div><p>Chamados evitados × tempo médio de tratamento de um chamado pelo time de Negócios.</p></div>\n    <div class=\"k\"><div class=\"kt\">Retenção</div><p>Contatos resolvidos no próprio canal, sem abrir ticket nem intervenção humana.</p></div>\n    <div class=\"k\"><div class=\"kt\">Satisfação</div><p>Nota rápida ao fim de cada conversa; acompanhamos o % de notas altas.</p></div>\n    <div class=\"k\"><div class=\"kt\">Inadimplência</div><p>Quanto conseguimos recuperar de clientes com pagamentos em aberto que interagem pelo canal.</p></div>\n    <div class=\"k\"><div class=\"kt\">Tempo de resposta</div><p>Da pergunta do cliente à resposta que resolve.</p><div class=\"kb\">Hoje: SLA de 48h → consultas simples, imediatas</div></div>\n  </div>\n</section>\n\n<!-- CRONOGRAMA -->\n<section id=\"cronograma\">\n  <div class=\"eyebrow rv\">Cronograma</div>\n  <h2 class=\"rv\" style=\"font-size:clamp(24px,3vw,44px);margin-bottom:clamp(10px,2vh,20px)\">Kickoff em 2 de setembro. Piloto no ar dia 20.</h2>\n  <p class=\"rv lead\" style=\"color:var(--musgo);font-size:14.5px;max-width:56ch;line-height:1.7;margin-bottom:clamp(26px,5vh,54px)\">O piloto cobre a Sprint 1, com clientes reais — o revendedor sente a diferença antes do projeto acabar.</p>\n  <div class=\"ondas rv\">\n    <div class=\"onda\"><div class=\"fase\">02 de setembro</div><div class=\"t\">Kickoff</div><ul><li>Mobilização, acessos e desbloqueios do lado Granado</li><li>Mapeamento das jornadas do Service Cloud</li><li>Time de homologação definido</li></ul></div>\n    <div class=\"onda\"><div class=\"fase\">20 de setembro</div><div class=\"t\">Piloto no ar — Sprint 1</div><ul><li>Canal com clientes reais: comprar, acompanhar, resolver</li><li>Amostra de clientes e período do piloto definidos no kickoff</li><li>Homologação junto com o time Granado</li><li>Painel dos 7 KPIs medindo desde o 1º dia</li></ul></div>\n    <div class=\"onda\"><div class=\"fase\">Na sequência</div><div class=\"t\">Sprint 2 — o canal ideal</div><ul><li>Cadastro aprovado na hora, crédito e transbordo humano</li><li>Refazer pedido, recorrência automática e sugestões</li><li>Integrações novas: Receita Federal, SEFAZ, bureau</li></ul></div>\n  </div>\n</section>\n\n<!-- INVESTIMENTO -->\n<section id=\"investimento\">\n  <div class=\"eyebrow rv\">Investimento</div>\n  <div class=\"custos rv\">\n    <div class=\"row\"><div><div class=\"t\">Sprint 1 — O essencial</div><div class=\"d\">Carrinho, pedidos, FAQs, fluxos guiados, WhatsApp + widget, Service Cloud, dashboard de KPIs.</div></div><div class=\"v\">R$ 100.000</div></div>\n    <div class=\"row\"><div><div class=\"t\">Sprint 2 — O canal ideal</div><div class=\"d\">Cadastro na conversa, crédito, refazer pedido, recorrência e sugestões, transbordo humano, catálogo e roteamento.</div></div><div class=\"v\">R$ 100.000</div></div>\n    <div class=\"row\"><div><div class=\"t\">Analista Data² dedicado</div><div class=\"d\">Opcional — um analista full-time no canal: rotinas de sustentação garantidas, melhoria contínua e resposta imediata, como extensão do time Granado.</div></div><div class=\"v\">R$ 26.000<em> /mês</em></div></div>\n    <div class=\"row total\"><div><div class=\"t\">Operação do canal</div><div class=\"d\">Custo de running medido em testes reais no ambiente Granado — IA responde por 92%; infraestrutura fica abaixo de R$ 0,06 por sessão.</div></div><div class=\"v\">~R$ 0,77<em> /sessão</em></div></div>\n  </div>\n  <p class=\"cond rv\">Faturamento por sprint entregue.</p>\n</section>\n\n<!-- ANTES DO KICKOFF -->\n<section id=\"kickoff\">\n  <div class=\"eyebrow rv\">Antes do kickoff · 2 de setembro</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\">O que fechamos juntos até lá.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Nada aqui muda o escopo — são decisões e desbloqueios que deixam o caminho livre pras sprints correrem no ritmo combinado.</p>\n  </div>\n  <div class=\"pilares dois\">\n    <div class=\"pilar rv d2\"><span class=\"k\">Decidimos juntos</span>\n      <ul class=\"klist\">\n        <li><b>SFMC × WhatsApp</b> — orquestração de campanhas e trilha de cobrança de boleto: em qual sprint entra, e com que profundidade</li>\n        <li><b>Homologação do piloto</b> — quantas pessoas do time Granado, e em que momento</li>\n        <li><b>Retenção do histórico de conversa</b> — por quanto tempo guardar antes de arquivar</li>\n        <li><b>Telas de exemplo</b> — entram na próxima versão desta proposta</li>\n      </ul>\n    </div>\n    <div class=\"pilar rv d3\"><span class=\"k\">Desbloqueios do lado Granado</span>\n      <ul class=\"klist\">\n        <li><b>Tech</b> — cadastro sem captcha; sessão compartilhada entre portal e widget; envio direto à página de checkout</li>\n        <li><b>Financeiro</b> — como funciona a emissão de 2ª via de boleto e de nota fiscal</li>\n        <li><b>CRM</b> — mapear quais jornadas da conversa viram registro no Service Cloud</li>\n        <li><b>Chamados (Service Cloud)</b> — mapear as solicitações que chegam hoje pelo canal de chamados, pra definir o que o agente passa a resolver</li>\n      </ul>\n    </div>\n  </div>\n</section>\n\n<!-- 08 · FECHAMENTO -->\n<section class=\"selva fecho\" data-dark id=\"fim\">\n  <div class=\"bg kb\"><img src=\"https://granado-app.data2.media/file/gypbe6V0A-Zv7jM4xaJ-l\" alt=\"\"></div>\n  <div class=\"mid\">\n    <img class=\"ring branco rv\" style=\"width:52px\" src=\"https://granado-app.data2.media/file/YHYIIb0_WelaXTRzjFKsb\" 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\"><img class=\"d2m\" style=\"height:26px\" src=\"https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt\" alt=\"Data²\"><span class=\"plus\" style=\"font-size:20px\">+</span><img class=\"grm\" style=\"height:18px\" src=\"https://granado-app.data2.media/file/T6YAJeTNwwvYpaJJPQg9K\" alt=\"Granado\"></div>\n    <div class=\"sub rv\">gui@data2.app · data2.app</div>\n  </div>\n</section>\n</div>\n</div>`\n\n// Remove a secao Investimento e sua entrada no indice, renumerando o item seguinte.\n// O contador de slides e o nav de dots sao derivados do DOM, entao se ajustam sozinhos.\nfunction stripComercial(html){\n  const a=html.indexOf('<!-- INVESTIMENTO -->'), b=html.indexOf('<!-- ANTES DO KICKOFF -->')\n  let out=(a>-1&&b>a)?html.slice(0,a)+html.slice(b):html\n  out=out.replace('<a href=\"#investimento\"><span class=\"n\">12</span><span class=\"t\">Investimento</span></a>','')\n  out=out.replace('<a href=\"#kickoff\"><span class=\"n\">13</span>','<a href=\"#kickoff\"><span class=\"n\">12</span>')\n  return out\n}\nlet _deckSemComercial=null\nfunction deckFor(v){\n  if(v.comercial) return DECK\n  if(!_deckSemComercial) _deckSemComercial=stripComercial(DECK)\n  return _deckSemComercial\n}\n\nfunction injectFonts(){\n  try{\n    if(!document.getElementById('akz-deck-fonts')){\n      const st=document.createElement('style'); st.id='akz-deck-fonts'; st.textContent=FONT_CSS\n      document.head.appendChild(st)\n    }\n  }catch(e){}\n}\n\nfunction initDeck(d2){\n  const wrap=d2.$('.deckwrap'); if(!wrap||wrap.dataset.init) 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  const pad=n=>(n<10?'0':'')+n\n  let cur=0\n  slides.forEach((s,i)=>{const a=document.createElement('a');a.href='#'+s.id;a.setAttribute('aria-label','Slide '+(i+1));dots.appendChild(a)})\n  const links=[].slice.call(dots.children)\n  counter.textContent=pad(1)+' — '+pad(slides.length)\n  if(links[0])links[0].classList.add('on')\n  const setActive=i=>{if(i===cur)return;cur=i;counter.textContent=pad(cur+1)+' — '+pad(slides.length);links.forEach((l,k)=>l.classList.toggle('on',k===cur));wrap.classList.toggle('on-dark',slides[cur].hasAttribute('data-dark'));wrap.classList.toggle('last',cur===slides.length-1)}\n  // reveal: dispara assim que o slide encosta na viewport — funciona mesmo se o slide for mais alto que a tela; one-way\n  const revIO=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');revIO.unobserve(e.target)}})},{root:rootEl,rootMargin:'0px 0px 12% 0px',threshold:.01})\n  slides.forEach(s=>revIO.observe(s))\n  // slide ativo: pega o mais visivel, com faixas tolerantes pra nunca travar\n  const ratios=new Array(slides.length).fill(0)\n  const actIO=new IntersectionObserver(es=>{es.forEach(e=>{const i=slides.indexOf(e.target);if(i>-1)ratios[i]=e.isIntersecting?e.intersectionRatio:0});let bi=cur,bv=-1;ratios.forEach((r,i)=>{if(r>bv){bv=r;bi=i}});if(bv>0)setActive(bi)},{root:rootEl,threshold:[0,.15,.35,.55,.75,1]})\n  slides.forEach(s=>actIO.observe(s))\n  // rede de seguranca: se o observer nao rodar, mostra tudo\n  setTimeout(()=>{if(!rootEl.querySelector('section.in'))slides.forEach(s=>s.classList.add('in'))},500)\n  const rm=(typeof matchMedia==='function')&&matchMedia('(prefers-reduced-motion: reduce)').matches\n  const go=i=>{i=Math.max(0,Math.min(slides.length-1,i));slides[i].scrollIntoView({behavior:rm?'auto':'smooth'})}\n  wrap.addEventListener('click',ev=>{\n    const a=ev.target.closest&&ev.target.closest('a[href^=\"#\"]'); if(!a)return\n    ev.preventDefault()\n    const id=a.getAttribute('href').slice(1)\n    const t=slides.find(s=>s.id===id); if(t)t.scrollIntoView({behavior:rm?'auto':'smooth'})\n  })\n  window.addEventListener('keydown',e=>{\n    if(!wrap.isConnected)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\nexport default {\n  css:`\n  :host,.deckwrap{\n    --verde:#24422F;\n    --verde-fundo:#1C3526;\n    --paper:#FAFAF6;\n    --ink:#12160F;\n    --musgo:#66755F;\n    --banana:#D99A26;\n    --hibisco:#A93526;\n    --ok:#2F6E5B;\n    --linha:rgba(18,22,15,.14);\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','Helvetica Neue',Helvetica,Arial,sans-serif;\n  }\n  *{box-sizing:border-box;margin:0;padding:0}\n\n  .deckwrap{background:var(--paper);color:var(--ink);font-family:var(--lt);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}\n\n  header{position:absolute;inset:0 0 auto 0;z-index:40;display:flex;justify-content:space-between;align-items:center;padding:22px var(--mx) 34px;pointer-events:none;background:linear-gradient(to bottom,var(--paper) 52%,rgba(250,250,246,0))}\n  .deckwrap.on-dark header{background:none}\n  header img{height:16px;display:block;filter:brightness(0) invert(8%) sepia(9%) saturate(600%) hue-rotate(60deg)}\n  header .gr{height:13px}\n  .deckwrap.on-dark header img{filter:none}\n  .counter{position:absolute;z-index:40;left:var(--mx);bottom:22px;font-family:var(--lt);font-size:11px;letter-spacing:.22em;color:var(--musgo)}\n  .deckwrap.on-dark .counter{color:rgba(250,250,246,.75)}\n  nav.dots{position:absolute;z-index:40;right:clamp(14px,2.4vw,32px);top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:14px}\n  nav.dots a{width:6px;height:6px;border-radius:50%;background:var(--musgo);opacity:.35;transition:opacity .3s,transform .3s}\n  nav.dots a.on{opacity:1;transform:scale(1.5);background:var(--ink)}\n  .deckwrap.on-dark nav.dots a{background:#FAFAF6}\n  .deckwrap.on-dark nav.dots a.on{background:#FAFAF6}\n  .hint{position:absolute;z-index:40;right:var(--mx);bottom:22px;font-size:11px;letter-spacing:.22em;color:var(--musgo);text-transform:uppercase;transition:opacity .4s}\n  .deckwrap.last .hint{opacity:0}\n  .deckwrap.on-dark .hint{color:rgba(250,250,246,.6)}\n\n  section{min-height:100%;scroll-snap-align:start;position:relative;display:flex;flex-direction:column;justify-content:center;padding:clamp(84px,12vh,140px) clamp(20px,7vw,120px);overflow:hidden}\n  .eyebrow{font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--musgo);margin-bottom:clamp(20px,4vh,44px)}\n  .eyebrow b{font-weight:400;color:var(--banana)}\n\n  .rv{opacity:0;transform:translateY(26px);transition:opacity .9s cubic-bezier(.2,.6,.2,1),transform .9s cubic-bezier(.2,.6,.2,1)}\n  section.in .rv{opacity:1;transform:none}\n  section.in .rv:nth-child(2),section.in .d2{transition-delay:.12s}\n  section.in .rv:nth-child(3),section.in .d3{transition-delay:.24s}\n  section.in .rv:nth-child(4),section.in .d4{transition-delay:.36s}\n  section.in .rv:nth-child(5),section.in .d5{transition-delay:.48s}\n  section.in .rv:nth-child(6),section.in .d6{transition-delay:.6s}\n\n  .selva{color:#FAFAF6}\n  .selva .bg{position:absolute;inset:0;z-index:0}\n  .selva .bg img{width:100%;height:100%;object-fit:cover;display:block}\n  .selva .bg::after{content:'';position:absolute;inset:0;background:rgba(18,30,20,.38)}\n  .selva .mid{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;text-align:center;gap:34px}\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 .grm{height:clamp(20px,3.2vw,38px)}\n  .lockup .plus{font-family:var(--lt);font-size:clamp(22px,3vw,36px);line-height:1;opacity:.9}\n  .selva .sub{font-family:var(--lt);font-size:clamp(13px,1.4vw,17px);letter-spacing:.14em;text-transform:uppercase;opacity:.92}\n  .selva .foot{position:absolute;z-index:1;left:0;right:0;bottom:clamp(56px,9vh,88px);text-align:center;font-size:12px;letter-spacing:.26em;text-transform:uppercase;opacity:.8}\n  @media (prefers-reduced-motion:no-preference){\n    .kb img{animation:kb 36s ease-in-out infinite alternate}\n    @keyframes kb{from{transform:scale(1)}to{transform:scale(1.07)}}\n  }\n\n  h1,h2{font-family:var(--ext);font-weight:400;text-wrap:balance}\n  .statement h1{font-size:clamp(34px,5.6vw,86px);line-height:1.08;letter-spacing:-.01em;max-width:16ch}\n  .apoio{max-width:44ch;font-size:clamp(15px,1.5vw,19px);line-height:1.65;color:var(--musgo)}\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  .pilares{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3.4vw,56px);border-top:1px solid var(--linha);padding-top:clamp(26px,5vh,52px)}\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(--musgo)}\n  .pilar .k{display:block;font-size:11px;letter-spacing:.26em;color:var(--banana);text-transform:uppercase;margin-bottom:18px}\n  @media(max-width:820px){.pilares{grid-template-columns:1fr}}\n\n  .demo{border:1px solid var(--linha);border-radius:18px;padding:clamp(12px,1.6vh,18px);display:flex;flex-direction:column;gap:8px}\n  .demo .b{max-width:90%;padding:9px 13px;border-radius:14px;font-size:12.5px;line-height:1.5}\n  .demo .cli{align-self:flex-end;background:#ECEDE3;color:var(--ink);border-bottom-right-radius:4px}\n  .demo .can{align-self:flex-start;background:var(--verde);color:#FAFAF6;border-bottom-left-radius:4px}\n\n  .arq{display:grid;grid-template-columns:1fr 44px 1.15fr 44px 1fr;gap:clamp(10px,1.2vw,18px);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(--musgo);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(--musgo);margin-top:4px;line-height:1.5}\n  .arq .core{background:var(--verde);color:#FAFAF6;border-radius:18px;padding:clamp(16px,2.4vh,26px);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(--banana)}\n  .arq .core .box{border-color:rgba(250,250,246,.25)}\n  .arq .core .box .bd{color:rgba(250,250,246,.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;background:var(--musgo);opacity:.5}\n  .arq .link span{position:relative;background:var(--paper);font-size:9px;letter-spacing:.2em;text-transform:uppercase;color:var(--musgo);transform:rotate(-90deg);white-space:nowrap;padding:0 6px}\n  @media(max-width:900px){.arq{grid-template-columns:1fr}.arq .link{min-height:34px}.arq .link::before{left:50%;right:auto;top:0;bottom:0;width:1px;height:auto}.arq .link span{transform:none}}\n\n  .ondas{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(22px,3vw,48px);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;width:9px;height:9px;border-radius:50%;background:var(--banana);transform:translateY(-4px)}\n  .onda .fase{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--musgo);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(--musgo);padding:9px 0;border-bottom:1px solid var(--linha)}\n  .pilares.dois{grid-template-columns:1fr 1fr}\n  @media(max-width:820px){.ondas{grid-template-columns:1fr}.onda::before{display:none}.pilares.dois{grid-template-columns:1fr}}\n\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;padding:clamp(18px,3vh,30px) 0;border-bottom:1px solid var(--linha)}\n  .custos .row .t{font-family:var(--ext);font-size:clamp(16px,1.7vw,22px)}\n  .custos .row .d{font-size:13.5px;color:var(--musgo);margin-top:8px;max-width:52ch;line-height:1.6}\n  .custos .row .v{font-family:var(--ext);font-size:clamp(20px,2.4vw,34px);font-variant-numeric:tabular-nums;white-space:nowrap}\n  .custos .row .v em{font-style:normal;color:var(--musgo);font-family:var(--lt);font-size:.55em;letter-spacing:.1em}\n  .custos .total .v{color:var(--banana)}\n  .cond{margin-top:clamp(22px,4vh,40px);font-size:13px;line-height:1.7;color:var(--musgo);max-width:60ch}\n\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;min-height:min(58vh,640px);display:flex;align-items:center;justify-content:center;color:#FAFAF6}\n  .capa .frame .bgimg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}\n  .capa .frame::after{content:'';position:absolute;inset:0;background:rgba(18,30,20,.34);z-index:0}\n  .capa .frame .mid{position:relative;z-index:1;display:flex;flex-direction:column;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;text-transform:uppercase;opacity:.92}\n  .capa .caption{display:flex;justify-content:space-between;align-items:baseline;gap:24px;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);color:var(--ink);letter-spacing:.01em}\n  .capa .caption .meta{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--musgo)}\n\n  .expert{border:1px solid var(--linha);border-radius:20px;padding:clamp(22px,3.4vh,36px);display:flex;flex-direction:column;gap:5px;max-width:400px;justify-self:end}\n  .expert .mono{width:54px;height:54px;border-radius:50%;background:var(--verde);color:#FAFAF6;font-family:var(--ext);font-size:16px;display:flex;align-items:center;justify-content:center;margin-bottom:16px;letter-spacing:.06em}\n  .expert .nome{font-family:var(--ext);font-size:clamp(18px,1.9vw,25px)}\n  .expert .firma{font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--banana);margin:4px 0 12px}\n  .expert p{font-size:13.5px;line-height:1.68;color:var(--musgo)}\n  @media(max-width:820px){.expert{justify-self:start}}\n\n  .fecho .bg::after{background:rgba(12,22,14,.64)}\n  .fecho .mid{gap:44px}\n  .fecho .frase{font-family:var(--lt);font-size:clamp(16px,1.8vw,25px);line-height:1.65;letter-spacing:.025em;max-width:44ch;text-wrap:balance}\n  .fecho .frase i{font-style:normal;opacity:.65}\n\n  .ring{width:clamp(54px,5.4vw,80px);height:auto;display:block}\n  .ring.branco{filter:brightness(0) invert(1)}\n\n  .apoio::before{content:'';display:block;width:36px;height:2px;background:var(--banana);margin-bottom:18px}\n  .apoio.destaque{color:var(--ink);font-size:clamp(16px,1.7vw,21px);line-height:1.6}\n\n  .indice{display:grid;grid-template-columns:1fr 1fr;gap:0 clamp(28px,5vw,90px);border-top:1px solid var(--linha)}\n  .indice a{display:flex;align-items:baseline;gap:18px;padding:clamp(13px,2.2vh,22px) 0;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(--musgo);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;transition:transform .3s cubic-bezier(.2,.6,.2,1)}\n  .indice a:hover .n,.indice a:focus-visible .n{color:var(--banana)}\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  .apoio,.pilar p,.demo .b,.onda li,.custos .row .d,.cond,.arq .box .bd,.expert p,.lead{font-family:var(--sf)}\n\n  .logofull{height:clamp(38px,5.4vw,66px);width:auto;filter:brightness(0);display:block;margin-bottom:clamp(20px,3.4vh,34px)}\n  .prods{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--linha)}\n  .prods .p{padding:clamp(14px,2vh,20px) 20px clamp(14px,2vh,20px) 0;border-bottom:1px solid var(--linha)}\n  .prods .pn{display:flex;align-items:center;gap:9px;font-family:var(--ext);font-size:clamp(12.5px,1.1vw,14.5px)}\n  .prods .dot{width:8px;height:8px;border-radius:50%;flex:none}\n  .prods .p p{font-family:var(--sf);font-size:12.5px;color:var(--musgo);line-height:1.55;margin-top:6px}\n  @media(max-width:900px){.prods{grid-template-columns:1fr 1fr}}\n\n  .leg{display:flex;gap:26px;flex-wrap:wrap;margin-bottom:clamp(18px,3vh,30px);font-family:var(--sf);font-size:12.5px;color:var(--musgo)}\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(--banana)}\n  .frentes{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--linha);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;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(--musgo);white-space:nowrap;letter-spacing:.04em}\n  .frentes .fi{display:flex;gap:8px;align-items:baseline;font-family:var(--sf);font-size:12px;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;position:relative;top:-1px}\n  .frentes .fi.ok::before{background:var(--ok)}\n  .frentes .fi.nv::before{background:var(--banana)}\n  .frentes .fnote{font-family:var(--sf);font-size:11px;color:var(--musgo);margin-top:7px;line-height:1.5}\n  .frentes .demo{margin:clamp(12px,1.8vh,18px) 0;align-self:start}\n  @media(max-width:900px){.frentes{grid-template-columns:1fr}}\n\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);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;margin-bottom:12px}\n  .comp .hj .lk{color:var(--hibisco)}\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-family:var(--sf);font-size:12.5px;line-height:1.55;color:var(--musgo);display:flex;gap:10px;align-items:baseline}\n  .steps li::before{counter-increment:s;content:counter(s);font-family:var(--lt);font-size:10px;color:var(--hibisco);border:1px solid currentColor;border-radius:50%;width:17px;height:17px;display:flex;align-items:center;justify-content:center;flex:none;position:relative;top:2px}\n  @media(max-width:820px){.comp{grid-template-columns:1fr}}\n  .comp .cnote{font-family:var(--sf);font-size:11px;color:var(--musgo);margin-top:10px;line-height:1.5}\n  #pratica,#pratica2{padding-top:clamp(74px,9.5vh,116px);padding-bottom:clamp(48px,7vh,92px)}\n  .cart{align-self:stretch;background:var(--paper);border:1px solid var(--linha);border-radius:14px;padding:11px 13px;font-family:var(--sf)}\n  .cart .ch{font-family:var(--lt);font-size:9.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--musgo);margin-bottom:8px}\n  .cart .ci{display:flex;justify-content:space-between;gap:14px;font-size:11.5px;line-height:1.5;color:var(--ink);padding:3px 0}\n  .cart .ci b{font-weight:600;white-space:nowrap;color:var(--musgo)}\n  .cart .ci.tot{border-top:1px solid var(--linha);margin-top:6px;padding-top:8px}\n  .cart .ci.tot b{color:var(--ink);font-family:var(--ext);font-weight:400}\n  .cart .lnk{display:inline-block;margin-top:10px;font-size:11.5px;color:var(--verde);text-decoration:underline;text-underline-offset:3px;word-break:break-all}\n  .cart .lnk i{font-style:normal;text-decoration:none;display:inline-block;margin-left:3px}\n  .pilares.quatro{grid-template-columns:1fr 1fr;row-gap:clamp(16px,2.4vh,30px)}\n  .pilar .nota{font-family:var(--sf);font-size:11px;color:var(--musgo);margin-top:11px;padding-top:9px;border-top:1px solid var(--linha);line-height:1.5}\n  @media(max-width:820px){.pilares.quatro{grid-template-columns:1fr}}\n\n  .klist{list-style:none;margin-top:6px}\n  .klist li{font-family:var(--sf);font-size:13.5px;line-height:1.6;color:var(--ink);padding:10px 0;border-bottom:1px solid var(--linha)}\n  .klist li b{font-weight:600}\n\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-family:var(--sf);font-size:12.5px;color:var(--musgo);line-height:1.6;margin-top:8px}\n  .kpis .k .kb{font-family:var(--sf);font-size:11.5px;color:var(--banana);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  @media(max-width:820px){\n    .deckroot{scroll-snap-type:y proximity}\n    nav.dots{display:none}\n    section{padding:72px 20px 82px}\n    .counter,.hint{bottom:16px}\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 .grm{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    .eyebrow{margin-bottom:18px}\n    .fecho .frase{font-size:15px}\n  }\n\n  :focus-visible{outline:2px solid var(--banana);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  .deckwrap{position:relative;height:100vh;height:100svh;overflow:hidden}\n  .deckroot{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;scroll-snap-type:y proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;overscroll-behavior-y:contain}\n\n  .gwrap{position:relative;height:100vh;height:100svh;overflow:hidden;display:flex;align-items:center;justify-content:center;background:var(--verde-fundo);font-family:var(--lt)}\n  .gwrap .gbg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n  .gwrap::after{content:'';position:absolute;inset:0;background:rgba(12,22,14,.5)}\n  .gcard{position:relative;z-index:1;background:var(--paper);border-radius:20px;padding:40px 36px 32px;width:min(380px,88vw);text-align:center;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 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;color:var(--musgo);margin-bottom:26px}\n  .ginput{width:100%;box-sizing:border-box;padding:13px 14px;border:1px solid var(--linha);border-radius:10px;background:#fff;font-size:15px;text-align:center;outline:none;font-family:var(--sf)}\n  .ginput:focus{border-color:var(--verde);box-shadow:0 0 0 3px rgba(36,66,47,.14)}\n  .gbtn{width:100%;margin-top:12px;padding:13px;border:none;border-radius:10px;background:var(--verde);color:var(--paper);font-size:12px;letter-spacing:.24em;text-transform:uppercase;cursor:pointer;font-family:var(--lt)}\n  .gbtn:hover{background:#1C3526}\n  .gerr{display:none;color:var(--hibisco);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)}40%{transform:translateX(9px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}\n`,\n  render(d2){\n    return d2.state.unlocked ? deckFor(variant()) : GATE\n  },\n  mounted(d2){\n    injectFonts()\n    const V=variant()\n    const SKEY='deckB2bUnlocked:'+V.key\n    let ok=OPEN\n    try{ if(sessionStorage.getItem(SKEY)==='1') ok=true }catch(e){}\n    try{ if((location.search||'').indexOf('k='+V.pass)>-1) ok=true }catch(e){}\n    const check=()=>{\n      const inp=d2.$('.ginput'); if(!inp) return\n      if(inp.value===V.pass){\n        try{sessionStorage.setItem(SKEY,'1')}catch(e){}\n        d2.state.unlocked=true\n      } else {\n        const c=d2.$('.gcard'), er=d2.$('.gerr')\n        if(er)er.style.display='block'\n        if(c){c.classList.remove('shake');void c.offsetWidth;c.classList.add('shake')}\n        if(inp.select)inp.select()\n      }\n    }\n    d2.on('.gbtn','click',check)\n    d2.on('.ginput','keydown',e=>{if(e.key==='Enter')check()})\n    if(ok) d2.state.unlocked=true\n    initDeck(d2)\n  },\n  updated(d2){\n    initDeck(d2)\n  }\n}"}}}}}}}},{"_id":"6a9ace9633e56ed61838ac9b","component":"data2-core-types@Component","system":"6a62e4640613f70fa449ff5d","params":{"extends":"data2-core-primitives@Block","component":"kBcB2b","package":"granado-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 FONT_CSS = `@font-face{font-family:'AkzExt';src:url(https://granado-app.data2.media/file/EBpu8brvJpn11D4il45TS) format('opentype');font-weight:400;font-display:block}@font-face{font-family:'AkzExtLight';src:url(https://granado-app.data2.media/file/8orsK0wtYTghGT7sGMiC6) format('opentype');font-weight:300;font-display:block}`\n\nconst IMG = {\n  d2:    'https://granado-app.data2.media/file/eNKhy9aF3ky_usZNZkmWt',\n  gr:    'https://granado-app.data2.media/file/T6YAJeTNwwvYpaJJPQg9K',\n  selva: 'https://granado-app.data2.media/file/gypbe6V0A-Zv7jM4xaJ-l',\n  ring:  'https://granado-app.data2.media/file/YHYIIb0_WelaXTRzjFKsb',\n  hero:  'https://granado-app.data2.media/file/oNgHh4K7N4TIZ5VCpz9h0',\n  suite: 'https://granado-app.data2.media/file/DFy1VS0HiMz27jRIjJnBc',\n  cafe:  'https://granado-app.data2.media/file/kFYhPZ0UiU8kVfO3Wu5iY',\n  mata:  'https://granado-app.data2.media/file/27KMde4crYr9IQZIqfMRi'\n}\n\n// ---------------------------------------------------------------- dados\n// Todo numero deste business case carrega o indice da fonte que o sustenta (slide \"As fontes\").\nconst N = {\n  inv:{s1:100000, s2:100000, tot:200000, analista:26000, sessao:0.77, ia:92, infra:0.06},\n  canal:{ativos:3012, inativos:2287, baseAtiva:56.8, ticket:1920, tkUp:15.2,\n         ped:1644, pedMes:137, pedUp:31.8, brutoUp:51.8, liqUp:52.2, volUp:37.2, skus:343,\n         ppc:0.05, fatMes:263040, margMes:131520, fatAno:3156480, marg:50},\n  atend:{casos:384, horas:135, fte:1.02, custo:9009, custoAno:108114,\n         totalCasos:2739, tabs:106, demandas:62, demZero:24, foraCasos:1233, foraPct:45,\n         deson:5707, hlib:86},\n  det:[{k:'Potencial muito alto', casos:175, custo:5403, deson:3782, cap:70},\n       {k:'Potencial alto',       casos:37,  custo:1591, deson:716,  cap:45},\n       {k:'Fora do catálogo',     casos:172, custo:2016, deson:1209, cap:60}],\n  fora:[['Indisponibilidade B2B',593],['Spam',256],['Falta de retorno',135],\n        ['Caso duplicado',82],['Compra Corporativa',46],['Amenities',24]],\n  mes:[['dez/25',251],['jan/26',1111],['fev/26',169],['mar/26',289],['abr/26',214],\n       ['mai/26',25],['jun/26',13],['jul/26',250],['ago/26',405],['set/26',12]],\n  // leitura anual adotada — 137 pedidos/mes. Todos os valores sao margem de contribuicao.\n  e1:{reat:3995, conv:3946, tk:2630, inad:8000, tot:18571, pay:13, peso:26,\n      reatMes:7.6, convPed:4},\n  e2:{tot:5881, pay:37, peso:8},\n  e3:{tot:46262, pay:10, peso:65, hlib:86, hconta:8, contas:10.8, tx:15, novos:1.6,\n      tkprem:4800, unit:2400, hosp:3504, casas:16, ocup:60, meta:15, cupom:12},\n  cen:{\n    pess:{pay:11, m12:37109,  m24:515960,  b12:29399,  rec:7428,  cus:3466, pre:18505, r1:0.4, r2:0.6},\n    base:{pay:6,  m12:366201, m24:1537231, b12:70714,  rec:18571, cus:5881, pre:46262, r1:1,   r2:1},\n    otim:{pay:4,  m12:782327, m24:2854747, b12:123921, rec:33426, cus:7223, pre:83272, r1:1.8, r2:1.2}\n  },\n  // leitura mensal — mantida como cenario de upside, nunca como premissa\n  up:{pedMes:1644, fatMes:3156480, fatAno:37877760, ppc:0.55,\n      e1:134846, e2:5881, e3:46262, pay:2, m24:4343054, peso:[72,3,25]},\n  rb:{casas:16, mata:220000, cidade:'Piedade · SP'},\n  rc:{hoteis:72, estados:17, destinos:56, anos:35}\n}\n\nconst SRC = [\n  [1,'Tabulações B2B por mês.xlsx','Relatório <i>New Cases and Case Tabulation</i> do Salesforce, filtro <b>Case Record Type = B2B</b>. 2.739 casos, 106 tabulações distintas, dez/25 a set/26. Extraído em 03/09/2026 18:16 BRT.'],\n  [2,'Base_63_Demandas_Atendimento_B2B_Data2.xlsx','Catálogo de demandas do atendimento: rota, setores envolvidos, tempo estimado e potencial de desoneração. 62 linhas (o ID 7 não existe no arquivo).'],\n  [3,'Visão Atual do Canal B2B.docx','Documento estratégico do canal: performance YTD 2025, base de clientes, ticket médio, perfil de compra e o problema de percepção de mix.'],\n  [4,'Deck Proposta B2B','A proposta em si — escopo das duas sprints, investimento, os 7 KPIs e o cronograma. Vive no <b>granado-app</b> como componente <code>kDeckB2b</code>.'],\n  [5,'ANALISE_ATENDIMENTO_B2B.md','Consolidação da descoberta: regras de negócio do canal, macros, roteamentos fora de escopo e as decisões tomadas em campo na Granado.'],\n  [6,'Produtos Ativos e descrições B2B.xlsx','343 SKUs habilitados no portal, com preço sem imposto, estoque e linha.'],\n  [7,'roncodobugio.com.br · Booking.com · Roteiros de Charme','Amenities Granado, 16 casas, 220.000 m² de Mata Atlântica preservada. Associação Roteiros de Charme: 72 hotéis em 17 estados e 56 destinos, 35 anos em 2026.'],\n  [8,'Estimativa Data²','<b>Premissa, não dado.</b> Salário e encargos de mercado, tempo de toque por demanda, taxa de captura da automação, ocupação hoteleira e conversão de prospecção. Tudo o que carrega [8] é hipótese e está aberto a contestação.']\n]\n\n// ---------------------------------------------------------------- helpers\nconst brl  = v => 'R$ ' + Math.round(v).toLocaleString('pt-BR')\nconst brlk = v => 'R$ ' + Math.round(v/1000).toLocaleString('pt-BR') + ' mil'\nconst brlM = v => 'R$ ' + (v/1e6).toLocaleString('pt-BR',{minimumFractionDigits:2,maximumFractionDigits:2}) + ' mi'\nconst nb   = v => v.toLocaleString('pt-BR')\nconst s    = n => `<i class=\"s\">${n}</i>`\n\n// ---------------------------------------------------------------- graficos\nfunction gBase(){\n  const {ativos,inativos} = N.canal, tot = ativos+inativos\n  const W=900,H=104,pa=ativos/tot*W, pi=W-pa-3\n  return `<svg viewBox=\"0 0 900 ${H}\" role=\"img\" aria-label=\"Base de clientes: 3.012 ativos e 2.287 inativos\">\n    <rect x=\"0\" y=\"26\" width=\"${pa.toFixed(1)}\" height=\"34\" rx=\"2\" fill=\"var(--verde)\"/>\n    <rect x=\"${(pa+3).toFixed(1)}\" y=\"26\" width=\"${pi.toFixed(1)}\" height=\"34\" rx=\"2\" fill=\"#D6CFBC\"/>\n    <text class=\"cl\" x=\"0\" y=\"18\">${nb(ativos)} ativos · ${N.canal.baseAtiva}%</text>\n    <text class=\"cl\" x=\"${(pa+3).toFixed(1)}\" y=\"18\">${nb(inativos)} inativos · 43,2%</text>\n    <text class=\"cs\" x=\"${(pa+3).toFixed(1)}\" y=\"80\">o estoque de receita que o canal já conquistou e perdeu</text>\n  </svg>`\n}\nfunction gMes(){\n  const W=900,H=250,PL=52,PR=24,PT=26,PB=38, iw=W-PL-PR, ih=H-PT-PB, mx=1200\n  const v=N.mes, x=i=>PL+iw*i/(v.length-1), y=n=>PT+ih-(n/mx)*ih\n  let o=''\n  ;[0,300,600,900,1200].forEach(t=>{\n    o+=`<line class=\"gl\" x1=\"${PL}\" y1=\"${y(t).toFixed(1)}\" x2=\"${W-PR}\" y2=\"${y(t).toFixed(1)}\"/>`\n    o+=`<text class=\"ca\" x=\"${PL-9}\" y=\"${(y(t)+3.5).toFixed(1)}\" text-anchor=\"end\">${nb(t)}</text>`\n  })\n  o+=`<rect x=\"${x(5).toFixed(1)}\" y=\"${PT}\" width=\"${(x(6)-x(5)).toFixed(1)}\" height=\"${ih}\" fill=\"#A93526\" opacity=\".07\"/>`\n  o+=`<text class=\"cs\" x=\"${((x(5)+x(6))/2).toFixed(1)}\" y=\"${PT+16}\" text-anchor=\"middle\" fill=\"var(--hibisco)\">canal desligado</text>`\n  const pts=v.map((d,i)=>`${x(i).toFixed(1)},${y(d[1]).toFixed(1)}`)\n  o+=`<path d=\"M${x(0)},${y(0)}L${pts.join('L')}L${x(v.length-1)},${y(0)}Z\" fill=\"var(--verde)\" opacity=\".10\"/>`\n  o+=`<path d=\"M${pts.join('L')}\" fill=\"none\" stroke=\"var(--verde)\" stroke-width=\"2.5\" stroke-linejoin=\"round\"/>`\n  v.forEach((d,i)=>{\n    o+=`<circle cx=\"${x(i).toFixed(1)}\" cy=\"${y(d[1]).toFixed(1)}\" r=\"4\" fill=\"var(--verde)\" stroke=\"var(--paper)\" stroke-width=\"2\"/>`\n    o+=`<text class=\"ca\" x=\"${x(i).toFixed(1)}\" y=\"${H-PB+17}\" text-anchor=\"middle\">${d[0]}</text>`\n    o+=`<text class=\"cv\" x=\"${x(i).toFixed(1)}\" y=\"${(y(d[1])-11).toFixed(1)}\" text-anchor=\"middle\">${d[1]}</text>`\n  })\n  return `<svg viewBox=\"0 0 ${W} ${H}\" role=\"img\" aria-label=\"Casos B2B abertos por mês, dez/25 a set/26\">${o}</svg>`\n}\nfunction gFora(){\n  const v=N.fora, mx=v[0][1], W=900, rh=34\n  return `<svg viewBox=\"0 0 ${W} ${v.length*rh+8}\" role=\"img\" aria-label=\"Tabulações fora do catálogo por volume\">\n  ${v.map(([l,n],i)=>{\n    const bw=(n/mx)*(W-330), col=i<4?'var(--hibisco)':'var(--ok)'\n    return `<text class=\"cl2\" x=\"0\" y=\"${i*rh+22}\">${l}</text>\n      <rect x=\"250\" y=\"${i*rh+11}\" width=\"${bw.toFixed(1)}\" height=\"14\" rx=\"2\" fill=\"${col}\" opacity=\"${i<4?.85:.9}\"/>\n      <text class=\"cv2\" x=\"${(250+bw+9).toFixed(1)}\" y=\"${i*rh+22}\">${n}</text>`\n  }).join('')}</svg>`\n}\nfunction gBar(items, mx, W){\n  W = W||900\n  return `<svg viewBox=\"0 0 ${W} ${items.length*40+8}\" role=\"img\" aria-label=\"Composição do benefício mensal\">\n  ${items.map((it,i)=>{\n    const bw=(it.v/mx)*(W-390)\n    return `<text class=\"cl2\" x=\"0\" y=\"${i*40+20}\">${it.l}</text>\n      <text class=\"cs2\" x=\"0\" y=\"${i*40+34}\">${it.d}</text>\n      <rect x=\"270\" y=\"${i*40+8}\" width=\"${Math.max(bw,2).toFixed(1)}\" height=\"16\" rx=\"2\" fill=\"${it.c}\"/>\n      <text class=\"cv2\" x=\"${(270+Math.max(bw,2)+9).toFixed(1)}\" y=\"${i*40+21}\">${brl(it.v)}</text>`\n  }).join('')}</svg>`\n}\n// Modelo mensal do beneficio. As constantes sao as do Anexo A do business case e\n// reproduzem os marcos publicados (mes 1, 3, 6, 12, 18, 24) na leitura anual.\nconst MOD = {e1c:14576.0, e1s:332.83, e2c:5707, e2g:0.00253097, e3s:3855.17, opc:401.2}\nfunction serie(r1, r2){          // r1 = receita e nicho · r2 = captura da automacao\n  const a = MOD.e1c*r1 + MOD.e2c*r2 - MOD.opc\n  const b = MOD.e1s*r1 + MOD.e2c*r2*MOD.e2g*r1 + MOD.e3s*r1 - MOD.opc*MOD.e2g*r1\n  const out=[]\n  for(let m=1;m<=24;m++) out.push(a*m + b*m*(m+1)/2 - (m>=3 ? N.inv.tot : N.inv.s1))\n  return out\n}\nconst FLUXO = {pess:serie(N.cen.pess.r1,N.cen.pess.r2),\n               base:serie(N.cen.base.r1,N.cen.base.r2),\n               otim:serie(N.cen.otim.r1,N.cen.otim.r2)}\n\nfunction gFluxo(){\n  const M=12                                   // ate o mes 12: o payback fica legivel\n  const W=900,H=300,PL=68,PR=96,PT=22,PB=40, iw=W-PL-PR, ih=H-PT-PB\n  const S=[{k:'pess',l:'Pessimista',c:'#A9C4B4',d:'6 4',w:2},\n           {k:'base',l:'Base',      c:'#2F6E5B',d:'',   w:2.8},\n           {k:'otim',l:'Otimista',  c:'#14301F',d:'2 3',w:2}]\n  let lo=0,hi=0\n  S.forEach(x=>FLUXO[x.k].slice(0,M).forEach(v=>{lo=Math.min(lo,v);hi=Math.max(hi,v)}))\n  hi*=1.10; lo*=1.18\n  const x=m=>PL+iw*(m-1)/(M-1), y=v=>PT+ih-((v-lo)/(hi-lo))*ih\n  let o=''\n  const step = hi>2e6 ? 1e6 : (hi>7e5 ? 25e4 : 1e5)\n  for(let t=Math.ceil(lo/step)*step; t<=hi; t+=step){\n    const zero=Math.abs(t)<1\n    o+=`<line class=\"gl\" x1=\"${PL}\" y1=\"${y(t).toFixed(1)}\" x2=\"${W-PR}\" y2=\"${y(t).toFixed(1)}\" ${zero?'stroke=\"#B9B3A1\" stroke-width=\"1.5\"':''}/>`\n    o+=`<text class=\"ca ${zero?'on':''}\" x=\"${PL-10}\" y=\"${(y(t)+3.5).toFixed(1)}\" text-anchor=\"end\">${zero?'0':(t/1e3).toLocaleString('pt-BR')+' mil'}</text>`\n  }\n  ;[1,3,6,9,12].forEach(m=>o+=`<text class=\"ca ${m===12?'on':''}\" x=\"${x(m).toFixed(1)}\" y=\"${H-PB+18}\" text-anchor=\"middle\">mês ${m}</text>`)\n  S.forEach(sr=>{\n    const p=FLUXO[sr.k].slice(0,M)\n    o+=`<path d=\"M${p.map((v,i)=>`${x(i+1).toFixed(1)},${y(v).toFixed(1)}`).join('L')}\" fill=\"none\"\n      stroke=\"${sr.c}\" stroke-width=\"${sr.w}\" ${sr.d?`stroke-dasharray=\"${sr.d}\"`:''} stroke-linejoin=\"round\"/>`\n    const pm=N.cen[sr.k].pay\n    if(pm<=M){\n      o+=`<circle cx=\"${x(pm).toFixed(1)}\" cy=\"${y(p[pm-1]).toFixed(1)}\" r=\"5.5\" fill=\"${sr.c}\" stroke=\"var(--paper)\" stroke-width=\"2.5\"/>`\n      o+=`<text class=\"cpay\" x=\"${x(pm).toFixed(1)}\" y=\"${(y(p[pm-1])-14).toFixed(1)}\" text-anchor=\"middle\" fill=\"${sr.c}\">mês ${pm}</text>`\n    }\n    o+=`<text class=\"cl3\" x=\"${W-PR+10}\" y=\"${(y(p[M-1])+4).toFixed(1)}\" fill=\"${sr.c}\">${sr.l}</text>`\n  })\n  o+=`<text class=\"cs\" x=\"${PL}\" y=\"${PT-6}\">acumulado, em reais</text>`\n  return `<svg viewBox=\"0 0 ${W} ${H}\" role=\"img\" aria-label=\"Fluxo de caixa acumulado nos 12 primeiros meses, nos três cenários\">${o}</svg>`\n}\nfunction gFunil(){\n  const steps=[[N.rc.hoteis,'hotéis na associação Roteiros de Charme',7],\n               [15,'meta de conta aberta em 12 meses — 21% da associação',8],\n               [15*N.e3.hosp,'hóspedes por ano com um Granado no banheiro',8],\n               [Math.round(15*N.e3.hosp*.12),'cupons resgatados a 12% de conversão',8]]\n  const W=900, mx=steps[2][0]\n  return `<svg viewBox=\"0 0 ${W} ${steps.length*46+8}\" role=\"img\" aria-label=\"Alcance estimado do nicho premium\">\n  ${steps.map((st,i)=>{\n    const bw=Math.max((st[0]/mx)*(W-300),3)\n    return `<rect x=\"0\" y=\"${i*46+10}\" width=\"${bw.toFixed(1)}\" height=\"24\" rx=\"2\" fill=\"var(--verde)\" opacity=\"${1-i*0.18}\"/>\n      <text class=\"cv3\" x=\"${(bw+12).toFixed(1)}\" y=\"${i*46+27}\">${nb(st[0])}</text>\n      <text class=\"cs2\" x=\"${(bw+12).toFixed(1)}\" y=\"${i*46+41}\">${st[1]} [${st[2]}]</text>`\n  }).join('')}</svg>`\n}\n\n// ---------------------------------------------------------------- gate\nconst PASS='yestemosbananas', SKEY='bcB2bUnlocked', OPEN=false\nconst GATE = `<div class=\"gwrap\">\n  <img class=\"gbg\" src=\"${IMG.selva}\" alt=\"\">\n  <div class=\"gcard\">\n    <div class=\"glock\"><img src=\"${IMG.d2}\" style=\"height:22px\" alt=\"Data²\"><span>+</span><img src=\"${IMG.gr}\" style=\"height:14px\" alt=\"Granado\"></div>\n    <div class=\"gsub\">Business case · Auto-atendimento do canal B2B</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>\n</div>`\n\n// ---------------------------------------------------------------- deck\nconst DECK = `<div class=\"deckwrap\">\n<header>\n  <img class=\"d2\" src=\"${IMG.d2}\" alt=\"Data²\">\n  <img class=\"gr\" src=\"${IMG.gr}\" alt=\"Granado\">\n</header>\n<div class=\"counter\" id=\"counter\">01</div>\n<div class=\"hint\">↓ navegue</div>\n<nav class=\"dots\" id=\"dots\" aria-label=\"Slides\"></nav>\n\n<div class=\"deckroot\">\n\n<!-- 01 CAPA -->\n<section class=\"capa\" id=\"capa\">\n  <div class=\"frame kb rv\">\n    <img class=\"bgimg\" src=\"${IMG.selva}\" alt=\"\">\n    <div class=\"mid\">\n      <div class=\"lockup\">\n        <img class=\"d2m\" src=\"${IMG.d2}\" alt=\"Data²\"><span class=\"plus\">+</span>\n        <img class=\"grm\" src=\"${IMG.gr}\" alt=\"Granado\">\n      </div>\n      <div class=\"sub\">Business case · Auto-atendimento do canal B2B</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 <i>…</i> work.</div>\n    <div class=\"meta\">Business case · Setembro 2026</div>\n  </div>\n</section>\n\n<!-- 02 INDICE -->\n<section id=\"indice\">\n  <div class=\"eyebrow rv\">Índice</div>\n  <div class=\"indice rv\">\n    <a href=\"#pedido\"><span class=\"n\">01</span><span class=\"t\">O pedido</span></a>\n    <a href=\"#leitura\"><span class=\"n\">02</span><span class=\"t\">A leitura adotada</span></a>\n    <a href=\"#fontes\"><span class=\"n\">03</span><span class=\"t\">As fontes</span></a>\n    <a href=\"#canal\"><span class=\"n\">04</span><span class=\"t\">O canal hoje</span></a>\n    <a href=\"#atendimento\"><span class=\"n\">05</span><span class=\"t\">O atendimento hoje</span></a>\n    <a href=\"#buraco\"><span class=\"n\">06</span><span class=\"t\">O buraco</span></a>\n    <a href=\"#tres\"><span class=\"n\">07</span><span class=\"t\">Os três eixos</span></a>\n    <a href=\"#eixo1\"><span class=\"n\">08</span><span class=\"t\">Eixo 1 · Receita</span></a>\n    <a href=\"#eixo2\"><span class=\"n\">09</span><span class=\"t\">Eixo 2 · Tempo</span></a>\n    <a href=\"#eixo3\"><span class=\"n\">10</span><span class=\"t\">Eixo 3 · Marca</span></a>\n    <a href=\"#campanha\"><span class=\"n\">11</span><span class=\"t\">A campanha</span></a>\n    <a href=\"#nicho\"><span class=\"n\">12</span><span class=\"t\">O tamanho do nicho</span></a>\n    <a href=\"#cenarios\"><span class=\"n\">13</span><span class=\"t\">Cenários</span></a>\n    <a href=\"#fluxo\"><span class=\"n\">14</span><span class=\"t\">Fluxo de caixa</span></a>\n    <a href=\"#ciclo\"><span class=\"n\">15</span><span class=\"t\">O ciclo</span></a>\n  </div>\n</section>\n\n<!-- 03 O PEDIDO -->\n<section id=\"pedido\">\n  <div class=\"eyebrow rv\">O pedido</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.8vh,32px)\">\n    <h2 class=\"rv big\">${brlk(N.inv.tot)}, em duas sprints faturadas por entrega.${s(4)}</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O que se compra com isso é um canal B2B que <b>vende, acompanha e resolve sozinho</b> —\n    no WhatsApp e no portal, integrado ao Service Cloud. A pergunta deste documento é se esse número volta, e em quanto tempo.\n    Volta por três caminhos independentes, e o maior deles não é receita nem economia: é <b>o que o time faz com o tempo que sobra</b>.</p>\n  </div>\n  <div class=\"ondas duas rv\">\n    <div class=\"onda\"><div class=\"fase\">Sprint 1 · ${brlk(N.inv.s1)}${s(4)}</div><div class=\"t\">O essencial no ar</div>\n      <ul><li><b>Comprar:</b> busca no catálogo de ${N.canal.skus} SKUs${s(6)}, carrinho e envio direto ao checkout</li>\n      <li><b>Acompanhar:</b> status do pedido, rastreio, 2ª via de nota fiscal e de boleto</li>\n      <li><b>Resolver:</b> FAQs, fluxos guiados e abertura de caso no Service Cloud</li>\n      <li>No WhatsApp e no widget do portal, onde o cliente já está</li>\n      <li>Painel dos 7 KPIs medindo desde o 1º dia do piloto</li></ul></div>\n    <div class=\"onda\"><div class=\"fase\">Sprint 2 · ${brlk(N.inv.s2)}${s(4)}</div><div class=\"t\">O canal ideal</div>\n      <ul><li><b>Cadastro na conversa</b>, com Receita Federal, SEFAZ e bureau de crédito</li>\n      <li><b>Crédito:</b> consulta de limite e liberação sem passar pelo humano</li>\n      <li><b>Recompra:</b> refazer pedido, recorrência e sugestão por histórico</li>\n      <li>Transbordo humano com contexto — o cliente não repete nada</li>\n      <li>Só é faturada <b>depois</b> do piloto entregar nos 7 KPIs</li></ul></div>\n  </div>\n  <div class=\"nums duas rv\" style=\"margin-top:clamp(16px,2.6vh,30px)\">\n    <div class=\"nm dst\"><span class=\"k\">mês ${N.cen.base.pay}</span><span class=\"lb\">Payback no cenário base</span>\n      <span class=\"d\">Os três eixos somados, na leitura conservadora do volume. No pessimista, mês ${N.cen.pess.pay}</span></div>\n    <div class=\"nm dst\"><span class=\"k\">${brlM(N.cen.base.m24)}</span><span class=\"lb\">Retorno em 24 meses</span>\n      <span class=\"d\">Margem de contribuição incremental, já líquida do investimento e da operação</span></div>\n  </div>\n</section>\n\n<!-- 04 A LEITURA ADOTADA -->\n<section id=\"leitura\">\n  <div class=\"eyebrow rv\">Nota de método</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.8vh,32px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:16ch\">Adotamos a leitura mais conservadora do volume.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O documento do canal registra <b>${nb(N.canal.ped)} pedidos</b> sob o rótulo\n    <b>YTD (2025)</b>${s(3)}. Este business case lê isso como <b>${N.canal.pedMes} pedidos por mês</b> — é o que a fonte\n    literalmente diz. A leitura alternativa (${nb(N.up.pedMes)} <b>por mês</b>) vem de uma anotação de campo${s(5)},\n    produz um caso muito mais favorável e está tratada como <b>upside</b>, não como premissa.</p>\n  </div>\n  <div class=\"pesos rv\">\n    <div class=\"p\"><span class=\"pn\">Eixo 1 · Receita</span><span class=\"pv\">${N.e1.peso}%</span>\n      <span class=\"pbar\"><i style=\"width:${N.e1.peso}%\"></i></span>\n      <span class=\"pd\">paga sozinho no mês ${N.e1.pay}. Na leitura mensal seriam <b>${N.up.peso[0]}%</b> — é o eixo que a ambiguidade move</span></div>\n    <div class=\"p\"><span class=\"pn\">Eixo 2 · Tempo</span><span class=\"pv\">${N.e2.peso}%</span>\n      <span class=\"pbar\"><i style=\"width:${N.e2.peso}%\"></i></span>\n      <span class=\"pd\">sozinho, ${N.e2.pay} meses. Economia de atendimento não paga este projeto em nenhuma das duas leituras</span></div>\n    <div class=\"p on\"><span class=\"pn\">Eixo 3 · Marca</span><span class=\"pv\">${N.e3.peso}%</span>\n      <span class=\"pbar\"><i style=\"width:${N.e3.peso}%\"></i></span>\n      <span class=\"pd\">paga sozinho no mês ${N.e3.pay}. <b>O único eixo cuja conta não passa pelo volume de pedidos</b></span></div>\n  </div>\n  <p class=\"nota rv\"><b>Consequência da escolha:</b> o payback base sai do mês ${N.up.pay} para o <b>mês ${N.cen.base.pay}</b>,\n  e o peso dos eixos vira de cabeça para baixo. Na leitura conservadora quem carrega o caso é a marca — e é justamente por isso\n  que o documento se sustenta: <b>qualquer que seja a resposta do JD Edwards, o caso continua de pé</b>.\n  <b>Pendente:</b> confirmar o volume real de pedidos no JD Edwards — é o que fecha este slide antes do comitê.</p>\n</section>\n\n<!-- 05 FONTES -->\n<section id=\"fontes\">\n  <div class=\"eyebrow rv\">As fontes</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(18px,3vh,32px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:17ch\">Todo número aqui tem etiqueta.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O índice em cinza ao lado de cada valor aponta para uma destas oito fontes.\n    O que é <b>estimativa</b> carrega [8] — e nada que carrega [8] deve ser aceito sem contestação.</p>\n  </div>\n  <div class=\"fontes rv\">\n    ${SRC.map(([n,t,d])=>`<div class=\"f\"><span class=\"fn\">${n}</span><div><div class=\"ft\">${t}</div><div class=\"fd\">${d}</div></div></div>`).join('')}\n  </div>\n</section>\n\n<!-- 06 O CANAL HOJE -->\n<section id=\"canal\">\n  <div class=\"eyebrow rv\">O canal hoje</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.6vh,30px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:17ch\">Um canal que cresce rápido e perde metade da base.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Faturamento bruto <b>+${N.canal.brutoUp}%</b>${s(3)} e ticket médio\n    <b>+${N.canal.tkUp}%</b>${s(3)} no ano. E, ao mesmo tempo, <b>${nb(N.canal.inativos)} clientes inativos</b>${s(3)} —\n    gente que já se cadastrou, já comprou e parou. A margem do canal hoje é de <b>${brl(N.canal.margMes)}/mês</b>${s(3)}${s(8)}.</p>\n  </div>\n  <div class=\"kfour rv\">\n    <div class=\"kf\"><span class=\"k\">${brl(N.canal.ticket)}</span><span class=\"lb\">Ticket médio${s(3)}</span><span class=\"d\">+${N.canal.tkUp}% no ano</span></div>\n    <div class=\"kf\"><span class=\"k\">${N.canal.pedMes}<em style=\"font-style:normal;font-size:.44em;color:var(--musgo)\"> /mês</em></span><span class=\"lb\">Pedidos${s(3)}</span><span class=\"d\">${nb(N.canal.ped)} no ano, +${N.canal.pedUp}% · ver “A leitura adotada”</span></div>\n    <div class=\"kf\"><span class=\"k\">${N.canal.baseAtiva}%</span><span class=\"lb\">Base ativa${s(3)}</span><span class=\"d\">${nb(N.canal.ativos)} de ${nb(N.canal.ativos+N.canal.inativos)}</span></div>\n    <div class=\"kf\"><span class=\"k\">${N.canal.skus}</span><span class=\"lb\">SKUs no portal${s(6)}</span><span class=\"d\">mix restrito por decisão${s(3)}</span></div>\n  </div>\n  <div class=\"chart rv\">${gBase()}</div>\n</section>\n\n<!-- 07 O ATENDIMENTO HOJE -->\n<section id=\"atendimento\">\n  <div class=\"eyebrow rv\">O atendimento hoje</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:16ch\">${nb(N.atend.totalCasos)} casos reais, não estimados.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Cruzamos as <b>${N.atend.tabs} tabulações</b> que o Salesforce realmente usou${s(1)}\n    com as <b>${N.atend.demandas} demandas</b> do catálogo de atendimento${s(2)}. Daí sai o custo — e o tempo — que o canal consome hoje.</p>\n  </div>\n  <div class=\"kfour rv\">\n    <div class=\"kf\"><span class=\"k\">${N.atend.casos}</span><span class=\"lb\">Casos por mês${s(1)}</span><span class=\"d\">média dos 7 meses com canal ativo</span></div>\n    <div class=\"kf\"><span class=\"k\">${N.atend.horas} h</span><span class=\"lb\">Horas por mês${s(1)}${s(2)}${s(8)}</span><span class=\"d\">triagem + resolução + handoffs</span></div>\n    <div class=\"kf\"><span class=\"k\">${N.atend.fte.toLocaleString('pt-BR',{minimumFractionDigits:2})}</span><span class=\"lb\">FTE-equivalente${s(8)}</span><span class=\"d\">pouco mais de uma pessoa</span></div>\n    <div class=\"kf dst\"><span class=\"k\">${brl(N.atend.custo)}</span><span class=\"lb\">Por mês${s(8)}</span><span class=\"d\">${brl(N.atend.custoAno)} por ano</span></div>\n  </div>\n  <div class=\"chart rv\">${gMes()}</div>\n  <p class=\"nota rv\">Maio e junho de 2026 quase zerados${s(1)} não são queda de demanda: a Granado <b>desativou o bot por estar desatualizado</b>${s(5)}.\n  O canal está desligado enquanto este documento é escrito.</p>\n</section>\n\n<!-- 08 O BURACO -->\n<section id=\"buraco\">\n  <div class=\"eyebrow rv\">O buraco</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:18ch\">${N.atend.foraPct}% da fila não é demanda. É fila.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\"><b>${nb(N.atend.foraCasos)} dos ${nb(N.atend.totalCasos)} casos</b>${s(1)} não correspondem\n    a nenhuma das ${N.atend.demandas} demandas do catálogo. E não é cauda longa dispersa — são quatro motivos.</p>\n  </div>\n  <div class=\"chart rv\">${gFora()}</div>\n  <div class=\"twocol rv\">\n    <p class=\"nota\">Os quatro primeiros — em vermelho — somam <b>1.066 casos</b>${s(1)}: 39% de tudo que entrou no canal.\n    Isso é o <b>custo de não ter atendimento</b>, não o custo de atender. É a fatia mais fácil de eliminar.</p>\n    <p class=\"nota\">Os dois últimos — em verde — são o oposto: <b>${N.fora[4][1]} pedidos de compra corporativa</b> e\n    <b>${N.fora[5][1]} de amenities</b>${s(1)}, hoje <b>roteados para fora do canal</b>${s(5)}.\n    Demanda premium chegando e sendo recusada. Guarde este número: ele volta no eixo 3.</p>\n  </div>\n</section>\n\n<!-- 09 OS TRES EIXOS -->\n<section class=\"statement\" id=\"tres\">\n  <div class=\"eyebrow rv\">Os três eixos</div>\n  <div class=\"split\">\n    <h1 class=\"rv\">Receita, tempo e marca.</h1>\n    <p class=\"apoio destaque rv\">Cada eixo abaixo carrega o investimento inteiro — os ${brlk(N.inv.tot)} — no seu próprio payback.\n    É o teste mais duro que se pode fazer: nenhum deles se apoia nos outros dois. Somados, pagam no <b>mês ${N.cen.base.pay}</b>.</p>\n  </div>\n  <div class=\"eixos rv\">\n    <div class=\"ex\"><span class=\"n\">01 · ${N.e1.peso}% do benefício</span><div class=\"t\">Receita</div><p>Reativar quem parou, converter quem trava, subir o ticket.</p><span class=\"pay\">${brl(N.e1.tot)}/mês · paga sozinho no mês ${N.e1.pay}</span></div>\n    <div class=\"ex\"><span class=\"n\">02 · ${N.e2.peso}% do benefício</span><div class=\"t\">Tempo</div><p>${N.atend.hlib} horas por mês que saem da triagem.</p><span class=\"pay warn\">${brl(N.e2.tot)}/mês · sozinho, ${N.e2.pay} meses</span></div>\n    <div class=\"ex dst\"><span class=\"n\">03 · ${N.e3.peso}% do benefício</span><div class=\"t\">Marca</div><p>Essas horas viram prospecção de nicho premium.</p><span class=\"pay\">${brl(N.e3.tot)}/mês · paga sozinho no mês ${N.e3.pay}</span></div>\n  </div>\n</section>\n\n<!-- 10 EIXO 1 RECEITA -->\n<section id=\"eixo1\">\n  <div class=\"eyebrow rv\">Eixo 1 · <b>Receita</b></div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:17ch\">Receita nova, medida em margem.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Quatro linhas, todas em <b>margem de contribuição</b>${s(8)} — nunca em faturamento bruto.\n    É a única medida comparável com o investimento.</p>\n  </div>\n  <div class=\"chart rv\">${gBar([\n    {l:'Reativação da base inativa', d:`${N.e1.reatMes.toLocaleString('pt-BR',{minimumFractionDigits:1})} clientes por mês dos ${nb(N.canal.inativos)} inativos [3][8] · acumula`, v:N.e1.reat, c:'var(--verde)'},\n    {l:'Uplift de conversão',        d:`+${N.e1.convPed} pedidos/mês: cadastro pendente, boleto travado no limite, dúvida sem resposta [8]`, v:N.e1.conv, c:'var(--verde)'},\n    {l:'Uplift de ticket médio',     d:'refazer pedido, recorrência e sugestão por histórico [4][8]', v:N.e1.tk, c:'var(--verde)'},\n    {l:'Inadimplência recuperada',   d:'KPI do deck [4], sem base para dimensionar — entrada conservadora [8]', v:N.e1.inad, c:'#8C9A84'}\n  ], N.e1.inad)}</div>\n  <div class=\"tot rv\"><span class=\"tl\">Total no mês 12 · ${N.e1.peso}% do benefício</span><span class=\"tv\">${brl(N.e1.tot)}<em> /mês</em></span>\n    <span class=\"tn\">contra uma margem atual de ${brl(N.canal.margMes)}/mês${s(3)}${s(8)} — um acréscimo de 14,1%. Paga sozinho no mês ${N.e1.pay}.\n    <b>É o eixo mais sensível à ambiguidade do volume</b>: na leitura mensal ele salta para ${brl(N.up.e1)}/mês e paga sozinho no mês ${N.up.pay}.</span></div>\n</section>\n\n<!-- 11 EIXO 2 TEMPO -->\n<section id=\"eixo2\">\n  <div class=\"eyebrow rv\">Eixo 2 · <b>Tempo</b></div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:18ch\">Cortar custo de atendimento não paga este projeto.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O atendimento do canal custa <b>${brl(N.atend.custo)} por mês</b>${s(8)}:\n    ${N.atend.casos} casos e pouco mais de <b>um FTE</b>. Mesmo capturando a maior parte disso, a economia sozinha levaria\n    <b>${N.e2.pay} meses</b> para devolver os ${brlk(N.inv.tot)}. Este eixo entra no caso pelo tempo que ele <b>libera</b>,\n    não pelo dinheiro que ele economiza.</p>\n  </div>\n  <div class=\"chart rv\">${gBar(N.det.map(d=>({\n    l:d.k, d:`${d.casos} casos/mês · ${brl(d.custo)}/mês de custo · captura de ${d.cap}% [8]`,\n    v:d.deson, c:'var(--banana)'})), N.det[0].deson)}</div>\n  <div class=\"split\" style=\"align-items:start;margin-top:clamp(18px,3vh,34px)\">\n    <div class=\"tot rv\" style=\"margin:0\"><span class=\"tl\">Desoneração no mês 12 · ${N.e2.peso}% do benefício</span>\n      <span class=\"tv\">${brl(N.e2.tot)}<em> /mês</em></span>\n      <span class=\"tn\">${N.atend.hlib} horas por mês${s(8)} que saem da triagem e da consulta de status</span></div>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O que importa aqui são as <b>${N.atend.hlib} horas</b>. Elas saem da fila\n    de triagem e entram na prospecção do nicho premium. É lá — no eixo 3 — que esse tempo vira receita.</p>\n  </div>\n</section>\n\n<!-- 12 EIXO 3 MARCA -->\n<section class=\"ph\" id=\"eixo3\">\n  <div class=\"phgrid\">\n    <div class=\"phtxt\">\n      <div class=\"eyebrow rv\">Eixo 3 · <b>Marca</b> — ${N.e3.peso}% do caso</div>\n      <h2 class=\"rv\" style=\"font-size:clamp(23px,3vw,42px);line-height:1.12;max-width:19ch\">As horas liberadas viram uma base que ninguém está atendendo.</h2>\n      <p class=\"apoio rv\">O comercial prioriza quem compra volume${s(3)}. Um pedido de ${brl(50000)} vai para o representante${s(5)} —\n      abaixo disso, ninguém liga. Mas existe uma faixa que <b>nunca vai chegar a ${brl(50000)}</b> e ainda assim vale muito:\n      o cliente de nicho premium, que coloca a marca na mão de gente que talvez nunca tenha comprado Granado.</p>\n      <p class=\"apoio rv\">O <b>Ronco do Bugio</b> é o exemplo: pousada boutique em ${N.rb.cidade}, ${N.rb.casas} casas,\n      ${nb(N.rb.mata)} m² de Mata Atlântica preservada, membro do Roteiros de Charme — e <b>já usa amenities Granado</b>${s(7)}.\n      Não é prospecção fria. É uma relação que existe e não é trabalhada.</p>\n      <div class=\"quote rv\">A demanda já bate na porta: <b>${N.fora[5][1]} casos de amenities</b> e\n        <b>${N.fora[4][1]} de compra corporativa</b>${s(1)} entraram no canal nos últimos meses — e foram\n        <b>encaminhados para fora</b>${s(5)}. Este é o eixo que <b>não depende do número ambíguo</b>: ele sai das\n        horas liberadas e do ticket premium, não do volume atual do canal.</div>\n    </div>\n    <div class=\"phimgs rv\">\n      <figure class=\"a\"><img src=\"${IMG.hero}\" alt=\"Casa do Ronco do Bugio na Mata Atlântica\"><figcaption>Ronco do Bugio · ${N.rb.cidade}${s(7)}</figcaption></figure>\n      <figure class=\"b\"><img src=\"${IMG.suite}\" alt=\"Interior de uma das casas\"></figure>\n      <figure class=\"c\"><img src=\"${IMG.mata}\" alt=\"Mata Atlântica no entorno da pousada\"></figure>\n    </div>\n  </div>\n</section>\n\n<!-- 13 A CAMPANHA -->\n<section class=\"ph rev\" id=\"campanha\">\n  <div class=\"phgrid\">\n    <div class=\"phtxt\">\n      <div class=\"eyebrow rv\">A campanha</div>\n      <h2 class=\"rv\" style=\"font-size:clamp(23px,3vw,42px);line-height:1.12;max-width:18ch\">Um cupom transforma awareness em número.</h2>\n      <div class=\"pitch rv\">“Ofereça cupons de desconto dos seus amenities Granado para seus hóspedes\n      continuarem essa experiência em casa — e ganhe desconto nos seus pedidos.”</div>\n      <p class=\"apoio rv\">O hóspede toma banho com Granado numa situação de altíssima conveniência e prazer, e sai\n      com um cupom. O cupom é <b>único por hotel</b>: cada resgate diz de qual pousada veio, quanto gerou e quanto\n      vale devolver em desconto no próximo pedido do hotel.</p>\n      <p class=\"apoio rv\">Isso resolve o problema clássico do brand marketing — <b>mídia espontânea que ninguém\n      consegue medir</b>. Aqui ela vira uma linha rastreável: hóspedes alcançados, cupons resgatados, receita B2C\n      atribuída, e um motivo comercial concreto para o hotel comprar mais.</p>\n      <p class=\"nota rv\">A receita B2C dos cupons <b>não entra em nenhuma conta deste business case</b>. O payback do eixo 3\n      é calculado só com os pedidos B2B dos hotéis conquistados. O alcance de marca fica como upside não contabilizado.</p>\n    </div>\n    <div class=\"phimgs rv\">\n      <figure class=\"a\"><img src=\"${IMG.cafe}\" alt=\"Café da manhã na pousada\"><figcaption>O contexto em que a marca é experimentada${s(7)}</figcaption></figure>\n      <div class=\"mini\">\n        <div class=\"m\"><span class=\"mk\">${nb(N.e3.hosp)}</span><span class=\"ml\">hóspedes por ano em uma pousada de ${N.e3.casas} casas${s(7)}${s(8)}</span>\n          <span class=\"md\">${N.e3.casas} casas × 365 dias × ${N.e3.ocup}% de ocupação ÷ 2 noites de estadia × 2 hóspedes</span></div>\n      </div>\n    </div>\n  </div>\n</section>\n\n<!-- 14 O NICHO -->\n<section id=\"nicho\">\n  <div class=\"eyebrow rv\">O tamanho do nicho</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(14px,2.2vh,26px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:17ch\">O nicho tem lista, e a lista é pública.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O Ronco do Bugio pertence ao <b>Roteiros de Charme</b>: ${N.rc.hoteis} hotéis\n    em ${N.rc.estados} estados e ${N.rc.destinos} destinos${s(7)}. Não é um mercado a descobrir — é um cadastro a trabalhar.</p>\n  </div>\n  <div class=\"chart rv\">${gFunil()}</div>\n  <div class=\"calc rv\">\n    <div class=\"c\"><span class=\"ck\">${N.e3.hlib} h</span><span class=\"cl\">liberadas por mês${s(8)}</span></div>\n    <div class=\"op\">÷</div>\n    <div class=\"c\"><span class=\"ck\">${N.e3.hconta} h</span><span class=\"cl\">por conta trabalhada${s(8)}</span></div>\n    <div class=\"op\">=</div>\n    <div class=\"c\"><span class=\"ck\">${N.e3.contas.toLocaleString('pt-BR',{minimumFractionDigits:1})}</span><span class=\"cl\">contas por mês</span></div>\n    <div class=\"op\">×</div>\n    <div class=\"c\"><span class=\"ck\">${N.e3.tx}%</span><span class=\"cl\">de conversão${s(8)}</span></div>\n    <div class=\"op\">=</div>\n    <div class=\"c dst\"><span class=\"ck\">${N.e3.novos.toLocaleString('pt-BR',{minimumFractionDigits:1})}</span><span class=\"cl\">novos clientes/mês</span></div>\n  </div>\n  <div class=\"tot rv\"><span class=\"tl\">Eixo 3 no mês 12 · ${N.e3.peso}% do benefício</span><span class=\"tv\">${brl(N.e3.tot)}<em> /mês</em></span>\n    <span class=\"tn\">${N.e3.novos.toLocaleString('pt-BR',{minimumFractionDigits:1})} clientes/mês acumulados × ${brl(N.e3.tkprem)} de ticket premium${s(8)} × ${N.canal.marg}% de margem${s(8)} ·\n    paga sozinho no mês ${N.e3.pay} — <b>o payback mais rápido dos três</b>. As quatro premissas desta conta são estimativas${s(8)},\n    e a proposta é validá-las com 3 contas piloto no D+48, antes de liberar a Sprint 2.</span></div>\n</section>\n\n<!-- 15 CENARIOS -->\n<section id=\"cenarios\">\n  <div class=\"eyebrow rv\">Cenários</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(16px,2.6vh,30px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:16ch\">Três cenários. Nenhum negativo.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">O <b>pessimista</b> corta as premissas de receita e de nicho a 40% e a captura\n    da automação a 60%. O <b>otimista</b> multiplica por 1,8 e 1,2. Só premissas ${s(8)} são alteradas — nenhum dado observado se move.</p>\n  </div>\n  <div class=\"cen rv\">\n    <div class=\"row hd\"><span>Cenário</span><span>Benefício mês 12</span><span>Payback</span><span>Acumulado 12 m</span><span>Acumulado 24 m</span><span>Retorno</span></div>\n    ${[['Pessimista','pess','receita e nicho a 40%, captura a 60%'],\n       ['Base','base','as premissas descritas aqui, na leitura anual'],\n       ['Otimista','otim','receita e nicho a 180%, captura a 120%']].map(([nm,k,sub])=>{\n      const c=N.cen[k]\n      return `<div class=\"row ${k==='base'?'on':''}\">\n        <span><b>${nm}</b><em>${sub}</em></span>\n        <span>${brl(c.b12)}</span>\n        <span class=\"pill ${k}\">mês ${c.pay}</span>\n        <span>${brl(c.m12)}</span>\n        <span><b>${brlM(c.m24)}</b></span>\n        <span>${Math.round(c.m24/N.inv.tot*100).toLocaleString('pt-BR')}%</span></div>`\n    }).join('')}\n  </div>\n  <p class=\"nota rv\">A distância entre o pessimista e o otimista é grande porque as premissas [8] são muitas.\n  O que <b>não</b> muda entre eles é o sinal: em 24 meses, os três cenários devolvem mais do que custaram.</p>\n</section>\n\n<!-- 16 FLUXO -->\n<section id=\"fluxo\">\n  <div class=\"eyebrow rv\">Fluxo de caixa</div>\n  <div class=\"split\" style=\"align-items:start;margin-bottom:clamp(10px,1.8vh,20px)\">\n    <h2 class=\"rv\" style=\"font-size:clamp(24px,3.2vw,44px);line-height:1.12;max-width:17ch\">Onde a linha cruza o zero, o projeto se pagou.</h2>\n    <p class=\"apoio rv\" style=\"margin-top:0\">Os três eixos somados, menos investimento e operação${s(4)}, mês a mês,\n    nos <b>12 primeiros meses</b>. O ponto em cada linha marca o payback. O degrau até o mês 3 é a Sprint 2,\n    faturada só depois do gate do piloto.</p>\n  </div>\n  <div class=\"chart rv\">${gFluxo()}</div>\n  <div class=\"legend rv\">\n    <span><i class=\"l1\"></i>Pessimista — paga no mês ${N.cen.pess.pay}, ${brl(N.cen.pess.m12)} no mês 12</span>\n    <span><i class=\"l2\"></i>Base — paga no mês ${N.cen.base.pay}, ${brl(N.cen.base.m12)} no mês 12</span>\n    <span><i class=\"l3\"></i>Otimista — paga no mês ${N.cen.otim.pay}, ${brl(N.cen.otim.m12)} no mês 12</span>\n  </div>\n</section>\n\n<!-- 16 O CICLO -->\n<section class=\"statement ciclo\" id=\"ciclo\">\n  <div class=\"eyebrow rv\">O que estamos propondo de verdade</div>\n  <div class=\"split\">\n    <h1 class=\"rv\">Não é tirar um problema da frente. É um ciclo.</h1>\n    <p class=\"apoio destaque rv\">Investimentos assim mostram resultado em semanas, não em anos — e é justamente por isso\n    que não devem ser tratados como evento pontual. O sprint que desonera o atendimento libera o tempo que abre a base\n    premium; a base premium gera mídia rastreável, que gera receita, que financia o próximo sprint.</p>\n  </div>\n  <div class=\"loop rv\">\n    <div class=\"lp\"><span class=\"n\">01</span><div class=\"t\">Escolhe um KPI</div><p>O sprint nasce de um número que precisa mexer${s(4)} — não de um levantamento de requisitos.</p></div>\n    <div class=\"ar\">→</div>\n    <div class=\"lp\"><span class=\"n\">02</span><div class=\"t\">Entrega em produção</div><p>Escopo e custo fechados, semanas em vez de trimestres${s(4)}.</p></div>\n    <div class=\"ar\">→</div>\n    <div class=\"lp\"><span class=\"n\">03</span><div class=\"t\">Mede no mesmo KPI</div><p>O painel dos 7 indicadores desde o 1º dia do piloto${s(4)}.</p></div>\n    <div class=\"ar\">→</div>\n    <div class=\"lp on\"><span class=\"n\">04</span><div class=\"t\">Financia a próxima onda</div><p>O que funciona escala. O canal deixa de ser ferramenta operacional e passa a ser motor de crescimento${s(3)}.</p></div>\n  </div>\n  <p class=\"nota rv\" style=\"margin-top:clamp(16px,2.6vh,28px)\">É a maneira correta de crescer: escalando e inovando, com\n  cada onda pagando a seguinte. Este business case é a primeira volta do ciclo — não a última.</p>\n</section>\n\n<!-- 17 FECHO -->\n<section class=\"selva fecho\" data-dark id=\"fim\">\n  <div class=\"bg\"><img src=\"${IMG.selva}\" alt=\"\"></div>\n  <div class=\"mid\">\n    <img class=\"ring branco rv\" style=\"width:52px\" src=\"${IMG.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\"><img class=\"d2m\" style=\"height:26px\" src=\"${IMG.d2}\" alt=\"Data²\"><span class=\"plus\" style=\"font-size:20px\">+</span><img class=\"grm\" style=\"height:18px\" src=\"${IMG.gr}\" alt=\"Granado\"></div>\n    <div class=\"sub rv\">gui@data2.app · data2.app</div>\n  </div>\n</section>\n\n</div>\n</div>`\n\nfunction injectFonts(){\n  try{\n    if(!document.getElementById('akz-bc-fonts')){\n      const st=document.createElement('style'); st.id='akz-bc-fonts'; st.textContent=FONT_CSS\n      document.head.appendChild(st)\n    }\n  }catch(e){}\n}\n\nfunction initDeck(d2){\n  const wrap=d2.$('.deckwrap'); if(!wrap||wrap.dataset.init) return\n  wrap.dataset.init='1'\n  const root=wrap.querySelector('.deckroot')\n  const slides=[].slice.call(root.querySelectorAll('section'))\n  const dots=wrap.querySelector('.dots'), counter=wrap.querySelector('.counter')\n  const pad=n=>(n<10?'0':'')+n\n  let cur=0\n  slides.forEach((sl,i)=>{const a=document.createElement('a');a.href='#'+sl.id;a.setAttribute('aria-label','Slide '+(i+1));dots.appendChild(a)})\n  const links=[].slice.call(dots.children)\n  counter.textContent=pad(1)+' — '+pad(slides.length)\n  if(links[0])links[0].classList.add('on')\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  const revIO=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');revIO.unobserve(e.target)}})},{root:root,rootMargin:'0px 0px 12% 0px',threshold:.01})\n  slides.forEach(sl=>revIO.observe(sl))\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; ratios.forEach((r,i)=>{if(r>bv){bv=r;bi=i}})\n    if(bv>0)setActive(bi)\n  },{root:root,threshold:[0,.15,.35,.55,.75,1]})\n  slides.forEach(sl=>actIO.observe(sl))\n  setTimeout(()=>{if(!root.querySelector('section.in'))slides.forEach(sl=>sl.classList.add('in'))},500)\n  const rm=(typeof matchMedia==='function')&&matchMedia('(prefers-reduced-motion: reduce)').matches\n  const go=i=>{i=Math.max(0,Math.min(slides.length-1,i));slides[i].scrollIntoView({behavior:rm?'auto':'smooth'})}\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(sl=>sl.id===a.getAttribute('href').slice(1))\n    if(t)t.scrollIntoView({behavior:rm?'auto':'smooth'})\n  })\n  window.addEventListener('keydown',e=>{\n    if(!wrap.isConnected)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\nexport default {\n  css:`\n  :host,.deckwrap{\n    --verde:#24422F; --verde-fundo:#1C3526; --paper:#FAFAF6; --ink:#12160F;\n    --musgo:#66755F; --banana:#D99A26; --hibisco:#A93526; --ok:#2F6E5B;\n    --linha:rgba(18,22,15,.14); --linha-2:rgba(18,22,15,.26);\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','Helvetica Neue',Helvetica,Arial,sans-serif;\n    --mono:ui-monospace,SFMono-Regular,Menlo,monospace;\n  }\n  *{box-sizing:border-box;margin:0;padding:0}\n  .deckwrap{background:var(--paper);color:var(--ink);font-family:var(--lt);\n    -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;\n    position:relative;height:100vh;height:100svh;overflow:hidden}\n  .deckroot{position:absolute;inset:0;overflow-y:auto;overflow-x:hidden;\n    scroll-snap-type:y proximity;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;\n    overscroll-behavior-y:contain}\n\n  header{position:absolute;inset:0 0 auto 0;z-index:40;display:flex;justify-content:space-between;\n    align-items:center;padding:22px var(--mx) 34px;pointer-events:none;\n    background:linear-gradient(to bottom,var(--paper) 52%,rgba(250,250,246,0))}\n  .deckwrap.on-dark header{background:none}\n  header img{height:16px;display:block;filter:brightness(0) invert(8%) sepia(9%) saturate(600%) hue-rotate(60deg)}\n  header .gr{height:13px}\n  .deckwrap.on-dark header img{filter:none}\n  .counter{position:absolute;z-index:40;left:var(--mx);bottom:22px;font-size:11px;\n    letter-spacing:.22em;color:var(--musgo)}\n  .deckwrap.on-dark .counter{color:rgba(250,250,246,.75)}\n  nav.dots{position:absolute;z-index:40;right:clamp(12px,2vw,28px);top:50%;transform:translateY(-50%);\n    display:flex;flex-direction:column;gap:11px}\n  nav.dots a{width:6px;height:6px;border-radius:50%;background:var(--musgo);opacity:.35;\n    transition:opacity .3s,transform .3s}\n  nav.dots a.on{opacity:1;transform:scale(1.5);background:var(--ink)}\n  .deckwrap.on-dark nav.dots a{background:#FAFAF6}\n  .hint{position:absolute;z-index:40;right:var(--mx);bottom:22px;font-size:11px;letter-spacing:.22em;\n    color:var(--musgo);text-transform:uppercase;transition:opacity .4s}\n  .deckwrap.last .hint{opacity:0}\n  .deckwrap.on-dark .hint{color:rgba(250,250,246,.6)}\n\n  section{min-height:100%;scroll-snap-align:start;position:relative;display:flex;\n    flex-direction:column;justify-content:center;padding:clamp(80px,11vh,132px) clamp(20px,7vw,120px);\n    overflow:hidden}\n  .eyebrow{font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--musgo);\n    margin-bottom:clamp(18px,3.4vh,38px)}\n  .eyebrow b{font-weight:400;color:var(--banana)}\n  h1,h2{font-family:var(--ext);font-weight:400;letter-spacing:-.01em}\n  h2.big{font-size:clamp(28px,4vw,54px);line-height:1.1;max-width:17ch}\n  .split{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(24px,4vw,72px)}\n  @media(max-width:900px){.split{grid-template-columns:1fr;gap:18px}}\n  .apoio{font-family:var(--sf);font-size:14.5px;line-height:1.72;color:var(--musgo);max-width:52ch}\n  .apoio b{color:var(--ink);font-weight:600}\n  .apoio.destaque{font-size:16px;color:var(--ink)}\n  .lead{font-family:var(--sf);color:var(--musgo);font-size:14px;max-width:58ch;line-height:1.7;\n    margin-bottom:clamp(22px,4vh,46px)}\n  .nota{font-family:var(--sf);font-size:12.5px;line-height:1.65;color:var(--musgo);max-width:74ch;\n    margin-top:clamp(14px,2.4vh,26px);padding-top:12px;border-top:1px solid var(--linha)}\n  .nota b{color:var(--ink);font-weight:600}\n  .twocol{display:grid;grid-template-columns:1fr 1fr;gap:clamp(20px,3vw,48px)}\n  @media(max-width:820px){.twocol{grid-template-columns:1fr;gap:6px}}\n\n  .rv{opacity:0;transform:translateY(24px);\n    transition:opacity .85s cubic-bezier(.2,.6,.2,1),transform .85s cubic-bezier(.2,.6,.2,1)}\n  section.in .rv{opacity:1;transform:none}\n  section.in .rv:nth-child(2){transition-delay:.07s}\n  section.in .rv:nth-child(3){transition-delay:.13s}\n  section.in .rv:nth-child(4){transition-delay:.19s}\n\n  /* fonte / source chip */\n  .s{font-family:var(--mono);font-style:normal;font-size:9px;vertical-align:super;\n    color:var(--musgo);opacity:.85;margin-left:2px;letter-spacing:0}\n  .s::before{content:'['}.s::after{content:']'}\n\n  /* capa */\n  .capa{justify-content:flex-end;padding:clamp(60px,10vh,110px) var(--mx) clamp(40px,9vh,84px)}\n  .capa .frame{position:relative;flex:1;min-height:58svh;border-radius:30px;overflow:hidden;\n    display:flex;align-items:center;justify-content:center}\n  .capa .bgimg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n  .capa .frame::after{content:'';position:absolute;inset:0;background:rgba(12,22,14,.42)}\n  .capa .mid{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;\n    gap:24px;text-align:center;padding:0 24px}\n  .capa .lockup{display:flex;align-items:center;gap:18px}\n  .capa .lockup .d2m{height:36px}\n  .capa .lockup .grm{height:22px}\n  .capa .lockup .plus{color:#FAFAF6;font-size:22px}\n  .capa .sub{color:#FAFAF6;font-size:11.5px;letter-spacing:.24em;text-transform:uppercase}\n  .capa .caption{display:flex;justify-content:space-between;align-items:baseline;gap:26px;\n    padding:22px 4px 0}\n  .capa .frase-cap{font-family:var(--ext);font-size:clamp(16px,2vw,25px);line-height:1.35;max-width:30ch}\n  .capa .meta{font-family:var(--sf);font-size:12px;color:var(--musgo);white-space:nowrap}\n  .capa .frase-cap i{font-style:normal;color:var(--banana)}\n  @media(max-width:820px){.capa .caption{flex-direction:column;gap:10px}}\n\n  /* indice */\n  .indice{display:grid;grid-template-columns:1fr 1fr;column-gap:clamp(24px,5vw,90px);\n    border-top:1px solid var(--linha)}\n  .indice a{display:flex;align-items:baseline;gap:16px;padding:11px 0;\n    border-bottom:1px solid var(--linha);text-decoration:none;color:var(--ink)}\n  .indice a:hover{color:var(--ok)}\n  .indice .n{font-family:var(--mono);font-size:10.5px;color:var(--musgo);min-width:20px}\n  .indice .t{font-family:var(--ext);font-size:clamp(13px,1.2vw,15.5px)}\n  @media(max-width:820px){.indice{grid-template-columns:1fr}}\n\n  /* pesos — nota de metodo */\n  .pesos{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2.6vw,40px);\n    border-top:1px solid var(--linha-2)}\n  .pesos .p{padding:clamp(14px,2.4vh,24px) 0 clamp(14px,2.4vh,24px);border-bottom:1px solid var(--linha)}\n  .pesos .pn{font-family:var(--sf);font-size:11px;letter-spacing:.14em;text-transform:uppercase;\n    color:var(--musgo);display:block}\n  .pesos .pv{font-family:var(--ext);font-size:clamp(30px,4vw,54px);line-height:1;letter-spacing:-.02em;\n    display:block;margin-top:10px}\n  .pesos .p.on .pv{color:var(--ok)}\n  .pesos .pbar{display:block;height:4px;background:var(--linha);margin-top:14px;border-radius:2px;overflow:hidden}\n  .pesos .pbar i{display:block;height:100%;background:var(--linha-2);border-radius:2px}\n  .pesos .p.on .pbar i{background:var(--ok)}\n  .pesos .pd{font-family:var(--sf);font-size:11.5px;color:var(--musgo);line-height:1.55;\n    display:block;margin-top:12px}\n  .pesos .pd b{color:var(--ink);font-weight:600}\n  @media(max-width:820px){.pesos{grid-template-columns:1fr;gap:0}}\n\n  /* statement */\n  .statement h1{font-size:clamp(30px,5.2vw,68px);line-height:1.04;max-width:17ch}\n\n  /* numeros */\n  .nums{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--linha-2)}\n  .nums.duas{grid-template-columns:1fr 1fr}\n  .nums .nm{padding:clamp(16px,2.6vh,26px) 24px clamp(16px,2.6vh,26px) 0;\n    border-bottom:1px solid var(--linha);border-right:1px solid var(--linha)}\n  .nums .nm:last-child{border-right:0}\n  .nums .nm.dst{background:rgba(47,110,91,.055)}\n  .nums .k{font-family:var(--ext);font-size:clamp(24px,2.9vw,40px);line-height:1;display:block;\n    letter-spacing:-.02em}\n  .nums .nm.dst .k{color:var(--ok)}\n  .nums .lb{font-family:var(--sf);font-size:12px;display:block;margin-top:10px;font-weight:600}\n  .nums .d{font-family:var(--sf);font-size:11.5px;color:var(--musgo);display:block;margin-top:5px;\n    line-height:1.5}\n  @media(max-width:900px){.nums{grid-template-columns:1fr 1fr}.nums .nm{padding-right:18px}}\n  @media(max-width:560px){.nums{grid-template-columns:1fr}}\n\n  .kfour{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--linha-2);\n    margin-bottom:clamp(16px,2.8vh,32px)}\n  .kfour .kf{padding:clamp(13px,2.2vh,22px) 22px clamp(13px,2.2vh,22px) 0;\n    border-bottom:1px solid var(--linha);border-right:1px solid var(--linha)}\n  .kfour .kf:last-child{border-right:0}\n  .kfour .kf.dst .k{color:var(--ok)}\n  .kfour .k{font-family:var(--ext);font-size:clamp(21px,2.5vw,34px);line-height:1;display:block;\n    letter-spacing:-.02em}\n  .kfour .lb{font-family:var(--sf);font-size:11.5px;display:block;margin-top:9px;font-weight:600}\n  .kfour .d{font-family:var(--sf);font-size:11px;color:var(--musgo);display:block;margin-top:4px;line-height:1.45}\n  @media(max-width:900px){.kfour{grid-template-columns:1fr 1fr}}\n\n  /* fontes */\n  .fontes{display:grid;grid-template-columns:1fr 1fr;column-gap:clamp(24px,4vw,64px);\n    border-top:1px solid var(--linha)}\n  .fontes .f{display:grid;grid-template-columns:24px 1fr;gap:12px;padding:11px 0;\n    border-bottom:1px solid var(--linha)}\n  .fontes .fn{font-family:var(--mono);font-size:11px;color:var(--musgo);padding-top:2px}\n  .fontes .ft{font-family:var(--ext);font-size:13px;line-height:1.35}\n  .fontes .fd{font-family:var(--sf);font-size:11.5px;color:var(--musgo);line-height:1.55;margin-top:4px}\n  .fontes .fd b{color:var(--ink);font-weight:600}\n  .fontes .fd code{font-family:var(--mono);font-size:10.5px}\n  @media(max-width:900px){.fontes{grid-template-columns:1fr}}\n\n  /* charts */\n  .chart{width:100%;overflow-x:auto}\n  .chart svg{display:block;width:100%;height:auto;min-width:520px;overflow:visible}\n  .gl{stroke:var(--linha);stroke-width:1}\n  .ca{font-family:var(--mono);font-size:9.5px;fill:var(--musgo)}\n  .ca.on{fill:var(--ink);font-weight:600}\n  .cv{font-family:var(--mono);font-size:9.5px;fill:var(--ink)}\n  .cl{font-family:var(--sf);font-size:11.5px;fill:var(--ink);font-weight:600}\n  .cs{font-family:var(--sf);font-size:10.5px;fill:var(--musgo)}\n  .cl2{font-family:var(--sf);font-size:12.5px;fill:var(--ink)}\n  .cs2{font-family:var(--sf);font-size:10.5px;fill:var(--musgo)}\n  .cv2{font-family:var(--mono);font-size:12px;fill:var(--ink);font-weight:600}\n  .cv3{font-family:var(--ext);font-size:15px;fill:var(--ink)}\n  .cl3{font-family:var(--sf);font-size:11.5px;font-weight:600}\n  .cpay{font-family:var(--sf);font-size:10.5px;font-weight:600}\n\n  .legend{display:flex;flex-wrap:wrap;gap:6px 28px;margin-top:14px;font-family:var(--sf);\n    font-size:11.5px;color:var(--musgo)}\n  .legend span{display:flex;align-items:center;gap:8px}\n  .legend i{width:18px;height:0;border-top-width:2.5px;display:block}\n  .legend .l1{border-top:2.5px dashed #A9C4B4}\n  .legend .l2{border-top:2.8px solid #2F6E5B}\n  .legend .l3{border-top:2.5px dotted #14301F}\n\n  /* eixos cards */\n  .eixos{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(14px,2vw,26px);\n    margin-top:clamp(26px,5vh,54px)}\n  .eixos .ex{border-top:2px solid var(--ink);padding-top:15px}\n  .eixos .ex.dst{border-top-color:var(--ok)}\n  .eixos .ex.dst .t{color:var(--ok)}\n  .eixos .n{font-family:var(--mono);font-size:11px;color:var(--musgo)}\n  .eixos .t{font-family:var(--ext);font-size:clamp(17px,1.9vw,24px);margin:7px 0 8px}\n  .eixos p{font-family:var(--sf);font-size:12.5px;color:var(--musgo);line-height:1.6}\n  .eixos .pay{display:inline-block;margin-top:12px;font-family:var(--sf);font-size:11px;\n    font-weight:600;color:var(--ok);border:1px solid var(--ok);border-radius:2px;padding:3px 9px}\n  .eixos .pay.warn{color:var(--banana);border-color:var(--banana)}\n  @media(max-width:820px){.eixos{grid-template-columns:1fr;gap:16px}}\n\n  /* total */\n  .tot{margin-top:clamp(16px,2.8vh,30px);padding-top:14px;border-top:2px solid var(--ink);\n    display:flex;flex-direction:column;gap:3px}\n  .tot .tl{font-family:var(--sf);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--musgo)}\n  .tot .tv{font-family:var(--ext);font-size:clamp(24px,3vw,38px);line-height:1.05;letter-spacing:-.02em;color:var(--ok)}\n  .tot .tv em{font-style:normal;font-size:.42em;color:var(--musgo);letter-spacing:0}\n  .tot .tn{font-family:var(--sf);font-size:11.5px;color:var(--musgo);line-height:1.55;max-width:62ch;margin-top:3px}\n\n  /* slides com foto */\n  .ph{padding:clamp(74px,10vh,120px) clamp(20px,5vw,88px)}\n  .phgrid{display:grid;grid-template-columns:1.02fr .98fr;gap:clamp(24px,4vw,64px);align-items:center}\n  .ph.rev .phgrid{grid-template-columns:.98fr 1.02fr}\n  .ph.rev .phtxt{order:2}.ph.rev .phimgs{order:1}\n  @media(max-width:960px){.phgrid,.ph.rev .phgrid{grid-template-columns:1fr}\n    .ph.rev .phtxt{order:1}.ph.rev .phimgs{order:2}}\n  .phtxt h2{margin-bottom:14px}\n  .phtxt .apoio{margin-top:12px}\n  .phimgs{display:grid;grid-template-columns:1fr 1fr;gap:10px}\n  .phimgs figure{margin:0;position:relative;overflow:hidden;border-radius:3px;background:#E8E5DA}\n  .phimgs figure.a{grid-column:1 / -1}\n  .phimgs img{display:block;width:100%;height:100%;object-fit:cover}\n  .phimgs figure.a img{max-height:min(38svh,330px)}\n  .phimgs figure.b img,.phimgs figure.c img{height:min(20svh,170px)}\n  .phimgs figcaption{position:absolute;left:0;right:0;bottom:0;padding:16px 12px 9px;\n    font-family:var(--sf);font-size:10.5px;color:#FAFAF6;\n    background:linear-gradient(to top,rgba(12,22,14,.72),rgba(12,22,14,0))}\n  .phimgs figcaption .s{color:rgba(250,250,246,.8)}\n  .quote{font-family:var(--sf);font-size:13px;line-height:1.65;color:var(--ink);\n    margin-top:clamp(14px,2.4vh,24px);padding:13px 16px;background:rgba(217,154,38,.11);\n    border-left:2px solid var(--banana);border-radius:0 2px 2px 0}\n  .quote b{font-weight:600}\n  .pitch{font-family:var(--ext);font-size:clamp(14.5px,1.6vw,20px);line-height:1.5;\n    color:var(--verde);margin:14px 0 4px;padding:16px 18px;background:rgba(47,110,91,.08);\n    border-radius:3px}\n  .mini{display:grid;gap:10px}\n  .mini .m{border-top:2px solid var(--ink);padding-top:12px}\n  .mini .mk{font-family:var(--ext);font-size:clamp(22px,2.6vw,34px);line-height:1;display:block;letter-spacing:-.02em}\n  .mini .ml{font-family:var(--sf);font-size:12px;font-weight:600;display:block;margin-top:8px;line-height:1.45}\n  .mini .md{font-family:var(--sf);font-size:10.5px;color:var(--musgo);display:block;margin-top:5px;line-height:1.5}\n\n  /* calc */\n  .calc{display:flex;align-items:flex-end;gap:clamp(8px,1.6vw,22px);flex-wrap:wrap;\n    margin-top:clamp(18px,3vh,32px)}\n  .calc .c{border-top:2px solid var(--linha-2);padding-top:9px;min-width:96px}\n  .calc .c.dst{border-top-color:var(--ok)}\n  .calc .ck{font-family:var(--ext);font-size:clamp(18px,2.1vw,28px);line-height:1;display:block;letter-spacing:-.02em}\n  .calc .c.dst .ck{color:var(--ok)}\n  .calc .cl{font-family:var(--sf);font-size:11px;color:var(--musgo);display:block;margin-top:6px;line-height:1.4}\n  .calc .op{font-family:var(--ext);font-size:17px;color:var(--musgo);padding-bottom:16px}\n\n  /* cenarios */\n  .cen{border-top:1px solid var(--linha-2)}\n  .cen .row{display:grid;grid-template-columns:1.7fr 1fr .8fr 1fr 1fr .7fr;gap:12px;\n    align-items:center;padding:12px 0;border-bottom:1px solid var(--linha);\n    font-family:var(--mono);font-size:12.5px}\n  .cen .row span{text-align:right}\n  .cen .row span:first-child{text-align:left;font-family:var(--sf)}\n  .cen .row.hd{font-family:var(--sf);font-size:10px;letter-spacing:.1em;text-transform:uppercase;\n    color:var(--musgo);padding:9px 0}\n  .cen .row.hd span:first-child{font-family:var(--sf)}\n  .cen .row.on{background:rgba(47,110,91,.06)}\n  .cen .row b{font-weight:600}\n  .cen .row span:first-child b{font-family:var(--ext);font-weight:400;font-size:14px;display:block}\n  .cen .row em{font-style:normal;font-family:var(--sf);font-size:10.5px;color:var(--musgo);display:block;margin-top:2px}\n  .cen .pill{font-family:var(--sf);font-size:11px;font-weight:600;border:1px solid;\n    border-radius:2px;padding:2px 8px;justify-self:end;white-space:nowrap}\n  .cen .pill.pess{color:var(--banana);border-color:var(--banana)}\n  .cen .pill.base{color:var(--ok);border-color:var(--ok)}\n  .cen .pill.otim{color:var(--ok);border-color:var(--ok)}\n  @media(max-width:900px){.cen .row{grid-template-columns:1fr 1fr;row-gap:4px}\n    .cen .row span{text-align:left}.cen .row.hd{display:none}.cen .pill{justify-self:start}}\n\n  /* ondas */\n  .ondas{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(14px,2vw,26px)}\n  .ondas.duas{grid-template-columns:1fr 1fr;gap:clamp(20px,3.4vw,56px)}\n  .ondas.duas .t{font-size:clamp(16px,1.8vw,22px)}\n  .ondas.duas li{font-size:12.5px;padding:6px 0 6px 13px}\n  .ondas .onda{border-top:2px solid var(--ink);padding-top:14px}\n  .ondas .fase{font-family:var(--mono);font-size:11px;color:var(--ok);font-weight:600}\n  .ondas .t{font-family:var(--ext);font-size:clamp(14px,1.5vw,18px);margin:7px 0 10px;line-height:1.25}\n  .ondas ul{list-style:none}\n  .ondas li{font-family:var(--sf);font-size:11.5px;line-height:1.55;color:var(--musgo);\n    padding:5px 0 5px 12px;position:relative}\n  .ondas li::before{content:'';position:absolute;left:0;top:12px;width:4px;height:4px;\n    border-radius:50%;background:var(--linha-2)}\n  .ondas li b{color:var(--ink);font-weight:600}\n  @media(max-width:900px){.ondas{grid-template-columns:1fr 1fr}}\n  @media(max-width:560px){.ondas{grid-template-columns:1fr}}\n\n  /* ciclo */\n  .loop{display:flex;align-items:stretch;gap:clamp(6px,1.2vw,16px);margin-top:clamp(24px,4.4vh,50px);\n    flex-wrap:wrap}\n  .loop .lp{flex:1 1 180px;border-top:2px solid var(--linha-2);padding-top:13px}\n  .loop .lp.on{border-top-color:var(--ok)}\n  .loop .n{font-family:var(--mono);font-size:10.5px;color:var(--musgo)}\n  .loop .t{font-family:var(--ext);font-size:clamp(14px,1.5vw,18px);margin:6px 0 8px;line-height:1.25}\n  .loop .lp.on .t{color:var(--ok)}\n  .loop p{font-family:var(--sf);font-size:11.5px;color:var(--musgo);line-height:1.6}\n  .loop .ar{font-family:var(--ext);font-size:16px;color:var(--linha-2);align-self:center;padding-top:12px}\n  @media(max-width:820px){.loop .ar{display:none}}\n\n  /* fecho */\n  .selva{background:var(--verde-fundo);color:#FAFAF6}\n  .selva .bg{position:absolute;inset:0}\n  .selva .bg img{width:100%;height:100%;object-fit:cover}\n  .selva::after{content:'';position:absolute;inset:0;background:rgba(12,22,14,.55)}\n  .fecho .mid{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;\n    text-align:center;gap:clamp(20px,3.6vh,38px)}\n  .fecho .frase{font-family:var(--ext);font-size:clamp(17px,2.4vw,32px);line-height:1.35;max-width:26ch}\n  .fecho .frase i{font-style:normal;color:var(--banana)}\n  .fecho .lockup{display:flex;align-items:center;gap:15px}\n  .fecho .lockup .plus{color:#FAFAF6}\n  .fecho .sub{font-family:var(--sf);font-size:12px;color:rgba(250,250,246,.72)}\n  .ring.branco{filter:brightness(0) invert(1)}\n\n  @media(max-width:900px){\n    .deckroot{scroll-snap-type:y proximity}\n    nav.dots{display:none}\n    section{padding:70px 20px 78px}\n    .counter,.hint{bottom:16px}\n    .eyebrow{margin-bottom:16px}\n    .chart svg{min-width:460px}\n  }\n  /* gate */\n  .gwrap{position:relative;height:100vh;height:100svh;overflow:hidden;display:flex;align-items:center;\n    justify-content:center;background:var(--verde-fundo);font-family:var(--lt)}\n  .gwrap .gbg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n  .gwrap::after{content:'';position:absolute;inset:0;background:rgba(12,22,14,.5)}\n  .gcard{position:relative;z-index:1;background:var(--paper);border-radius:20px;padding:40px 36px 32px;\n    width:min(380px,88vw);text-align:center;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 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(--musgo);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;font-family:var(--sf)}\n  .ginput:focus{border-color:var(--verde);box-shadow:0 0 0 3px rgba(36,66,47,.14)}\n  .gbtn{width:100%;margin-top:12px;padding:13px;border:none;border-radius:10px;background:var(--verde);\n    color:var(--paper);font-size:12px;letter-spacing:.24em;text-transform:uppercase;cursor:pointer;font-family:var(--lt)}\n  .gbtn:hover{background:#1C3526}\n  .gerr{display:none;color:var(--hibisco);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)}40%{transform:translateX(9px)}\n    60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}\n\n  :focus-visible{outline:2px solid var(--banana);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  render(d2){ return d2.state.unlocked ? DECK : GATE },\n  mounted(d2){\n    injectFonts()\n    let ok=OPEN\n    try{ if(sessionStorage.getItem(SKEY)==='1') ok=true }catch(e){}\n    try{ if((location.search||'').indexOf('k='+PASS)>-1) ok=true }catch(e){}\n    const check=()=>{\n      const inp=d2.$('.ginput'); if(!inp) return\n      if(inp.value===PASS){\n        try{ sessionStorage.setItem(SKEY,'1') }catch(e){}\n        d2.state.unlocked=true\n      } else {\n        const c=d2.$('.gcard'), er=d2.$('.gerr')\n        if(er) er.style.display='block'\n        if(c){ c.classList.remove('shake'); void c.offsetWidth; c.classList.add('shake') }\n        if(inp.select) inp.select()\n      }\n    }\n    d2.on('.gbtn','click',check)\n    d2.on('.ginput','keydown',e=>{ if(e.key==='Enter') check() })\n    if(ok) d2.state.unlocked=true\n    initDeck(d2)\n  },\n  updated(d2){ initDeck(d2) }\n}"}}}}}}}},{"_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":"6a63145ee395e9f0a470a8a6","component":"data2-rush@HTTPListener","reference":"fontEmbed","system":"6a62e4640613f70fa449ff5d","params":{}},{"_id":"6a62ff52d29000874041a118","component":"data2-rush@HTTPListener","reference":"farmaceutico","system":"6a62e4640613f70fa449ff5d","params":{}},{"_id":"6a7b3cb1366af6efea29ba54","component":"data2-rush@HTTPListener","reference":"b2bChatTurn","system":"6a62e4640613f70fa449ff5d","params":{}},{"_id":"6a84722ee63cca502f902b1f","component":"data2-rush@HTTPListener","reference":"scr-dados","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8da95966a3dd835dcedf8c","component":"data2-rush@HTTPListener","reference":"scr-varredura","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a847299d03c638697cc8d95","component":"data2-rush@HTTPListener","reference":"scr-sugerir-resposta","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8472e555257896c23dc00d","component":"data2-rush@HTTPListener","reference":"scr-atualizar-caso","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8472b862e28978c05418bb","component":"data2-rush@HTTPListener","reference":"scr-gerar-insight","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a84723d45ad40122b090b84","component":"data2-rush@HTTPListener","reference":"scr-classificar","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8c86292073c0d5a8cf4f2b","component":"data2-rush@HTTPListener","reference":"b2bLogin","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8c863985088162648c142b","component":"data2-rush@HTTPListener","reference":"b2bAgent","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}},{"_id":"6a8c8632e047ad6ec4b4fc0f","component":"data2-rush@HTTPListener","reference":"b2bMontarCarrinho","system":"6a62e4640613f70fa449ff5d","params":{},"deleteMetadata":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a9acf9125f539ae3fcc8a58","component":"data2-core-primitives@Artboard","system":"6a62e4640613f70fa449ff5d","displayName":"Business Case B2B","params":{"title":{"component":"data2-core-primitives@String","params":{"content":"Business Case B2B"}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}},"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0}}},"reference":"TknpjwteoL3d4g4XRuS7e","deleteMetadata":{}},{"_id":"d56a6e06a7abab573ada06f5","parent":"6a9acf9125f539ae3fcc8a58","component":"granado-app@kBcB2b","system":"6a62e4640613f70fa449ff5d","displayName":"Business Case B2B","params":{"super":{"position":{"component":"data2-core-types@Position","params":{"x":0,"y":0,"freeTransform":true,"left":true,"right":false,"top":true,"bottom":false,"growH":true,"growV":true}},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1440,"height":900}}}},"reference":"YB2PlkeP495fGNh8Urvlq","deleteMetadata":{}}]}},{"_id":"5fc4faeec8da12be8bf3d08a","component":"data2-core@Data2TableDataSource","system":"5e3b796d6228227ca637d0f4","params":{"paginate":{"default":1000},"super":{},"table":"fileComponentInstances","schema":[{"component":"data2-core-types@Field","params":{"key":"component","title":"Component","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"system","title":"System","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"displayName","title":"Display Name","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"params","title":"Params","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"reference","title":"Reference","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":{"component":"data2-core-primitives@String","params":{"content":"fileId"}},"title":"File ID","component":"data2-core-primitives@ObjectId"},"break":true}],"hooks":{"before":{"all":[{"hook":"softDelete"}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"timestamp","options":{"fields":["createdAt","updatedAt"]}}],"update":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}],"patch":[{"hook":"timestamp","options":{"fields":["updatedAt"]}}]},"after":{"all":[{"hook":"fileUrl","options":{"downloadField":"params.fileUrl","uploadField":"params.uploadFileUrl","enhanceSchema":false}}],"find":[{"hook":"useCache","options":{"additionalGroupKeys":["$and.0.system","$and.1.system"],"skipCache":true}}],"create":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"update":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"patch":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}],"remove":[{"hook":"invalidateCache","options":{"additionalGroupKeys":["system"],"secondaryUniqueKeys":["reference"]}},{"hook":"invalidateEnvCache"}]}},"multi":["create"],"useEstimatedDocumentCount":true},"reference":"FileComponentInstancesTableDataSource","updatedAt":"2022-12-19T20:42:51.837Z","createdAt":"2020-11-30T13:59:52.294Z"},{"reference":"ApplicationsTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a9acf9eaf1c24d190db4836","displayName":"Business Case B2B — Granado","reference":"_61gWDB52Q6a9UoSP_HZP","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"business-case-b2b"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"TknpjwteoL3d4g4XRuS7e"}}},"system":"6a62e4640613f70fa449ff5d","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6a7b3dd1b124b6ebcc070c7b","component":"data2-core@Store","system":"6a62e4640613f70fa449ff5d","params":{"initialData":{"history":[],"pendingSlot":null,"input":"","sending":false}},"reference":"b2bChatStore"},{"_id":"6a846cb2d03c638697cc7a20","component":"data2-core@DataSet","system":"6a62e4640613f70fa449ff5d","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"b2b-sequencer"}}},"reference":"b2b-sequencerDataSet","deleteMetadata":{}},{"_id":"6a8492981a5ebade92544eed","component":"data2-core@DataSet","system":"6a62e4640613f70fa449ff5d","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"b2b-integracoes"}}},"reference":"b2b-integracoesDataSet","deleteMetadata":{}},{"_id":"6a84a8650245d9638aea6511","component":"data2-core@DataSet","system":"6a62e4640613f70fa449ff5d","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"b2b-precos-base"}}},"reference":"b2b-precos-baseDataSet","deleteMetadata":{}},{"_id":"6a846cb1b419cf072832e8c7","component":"data2-core@Data2TableDataSource","system":"6a62e4640613f70fa449ff5d","params":{"table":"b2b-sequencer","schema":[{"component":"data2-core-types@Field","params":{"key":"key","title":"Chave","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"titulo","title":"Título","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"descricao","title":"Descrição","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Agrada o cliente (0-3)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"negocio","title":"Agrada o negócio (0-3)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"esforco","title":"Esforço (0-3)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"grupo_override","title":"Grupo manual (0=auto)","component":"data2-core-primitives@Number"}}]},"reference":"b2b-sequencer","deleteMetadata":{}},{"_id":"6a8492984914c296337490dd","component":"data2-core@Data2TableDataSource","system":"6a62e4640613f70fa449ff5d","params":{"table":"b2b-integracoes","schema":[{"component":"data2-core-types@Field","params":{"key":"key","title":"Chave","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"sistema","title":"Sistema externo","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nome","title":"Integração","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"capacidade","title":"Capacidade","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"objetivo","title":"Para que serve","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"dados","title":"Dados / endpoints necessários","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"criticidade","title":"Criticidade","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"status","title":"Status","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"anotacoes","title":"Anotações","component":"data2-core-primitives@String"}}]},"reference":"b2b-integracoes","deleteMetadata":{}},{"_id":"6a84a86500cfd069693af25d","component":"data2-core@Data2TableDataSource","system":"6a62e4640613f70fa449ff5d","params":{"table":"b2b-precos-base","schema":[{"component":"data2-core-types@Field","params":{"key":"sku","title":"SKU (EAN)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nome","title":"Produto","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"linha","title":"Linha","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"marca","title":"Marca","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"base","title":"Preço base (R$)","component":"data2-core-primitives@Number"}}]},"reference":"b2b-precos-base","deleteMetadata":{}}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}