{"context":{"domain":"babi-lf"},"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":"6ab58b1260cbe7aa719c6d7b","name":"Lucas Fontana","domain":"babi-lf","visibility":"private","updatedAt":"2026-09-24T20:41:54.351Z","createdAt":"2026-09-24T20:41:54.351Z"}]}},{"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":"6ab5b717d41d6b6b0d5623ac","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Footer","package":"babi-lf","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 DEFAULT_COLUMNS = [\n  { title: 'A loja', links: [\n    { label: 'Sobre a Auto Super', href: '#' },\n    { label: 'Perguntas frequentes', href: '#' },\n    { label: 'Fale com a Auto Super', href: '#' },\n    { label: 'Minha conta', href: '#' }\n  ] },\n  { title: 'Políticas', links: [\n    { label: 'Trocas e devoluções', href: '#' },\n    { label: 'Envio e entrega', href: '#' },\n    { label: 'Política de privacidade', short: 'Privacidade', href: '#' },\n    { label: 'Termos e condições de venda', short: 'Termos de venda', href: '#' }\n  ] },\n  { title: 'Atendimento (SAC)', plain: true, links: [\n    { label: '[E-MAIL DO SAC]' },\n    { label: '[WHATSAPP DO SAC]' },\n    { label: '[HORÁRIO DE ATENDIMENTO]' }\n  ] }\n];\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n// Os blocos do rodapé nas telas ainda trazem os rótulos antigos no parâmetro `columns`: aqui eles já saem com o nome novo.\nconst fixLabel = (s) => String(s == null ? '' : s).replace('Sobre o Lucas Fontana', 'Sobre a Auto Super').replace('Fale com o Lucas', 'Fale com a Auto Super');\n\nconst svg = (inner) => `<svg class=\"ic\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${inner}</svg>`;\nconst ICON_IG = svg('<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/><circle cx=\"12\" cy=\"12\" r=\"4\"/><circle cx=\"17.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/>');\nconst ICON_YT = svg('<rect x=\"2.5\" y=\"5\" width=\"19\" height=\"14\" rx=\"4\"/><path d=\"m10 9 5 3-5 3V9Z\"/>');\n\n// Rotas renomeadas: links antigos gravados nas páginas passam a apontar para os novos endereços\nconst HREF_FIX = { '/sobre': '/sobre-a-auto-super', '/sobre-o-lucas': '/sobre-a-auto-super', '/fale-com-o-lucas': '/fale-com-a-auto-super', '/minha-conta': '/perfil/meus-dados' };\nconst fixHref = (h) => HREF_FIX[String(h || '')] || h;\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.lf-footer {\n  --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff;\n  --grey-200: #c9cacd; --grey-300: #a7a9ad; --graphite-700: #34363a; --line-inverse: #26282b;\n  container: lf / inline-size;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  background: var(--ink); color: var(--grey-200); width: 100%;\n  min-height: 100%; display: flex; flex-direction: column;\n}\n.lf-footer > .in { flex: 1 0 auto; }\n.lf-footer a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.rule { height: 6px; display: flex; }\n.rule span { flex: 1; background: var(--red-500); }\n.rule i { width: 240px; background: repeating-conic-gradient(var(--white) 0 25%, var(--ink) 0 50%) 0 0 / 6px 6px; }\n.in { padding: 72px 96px 40px; display: flex; flex-direction: column; gap: 56px; }\n.top { display: flex; gap: 72px; }\n.brand { flex: 0 0 340px; display: flex; flex-direction: column; gap: 20px; }\n\n.logo { --h: 56px; display: inline-flex; align-items: center; gap: calc(var(--h) * .215); color: var(--white); text-decoration: none; flex-shrink: 0; align-self: flex-start; }\n.logo-shield { display: block; height: var(--h); width: auto; flex: none; }\n.logo-words { display: flex; flex-direction: column; gap: calc(var(--h) * .06); }\n.logo-name { font-size: calc(var(--h) * .385); font-weight: 900; font-stretch: 105%; font-style: italic; line-height: 1; white-space: nowrap; color: var(--white); }\n.logo-sub { display: flex; align-items: center; gap: calc(var(--h) * .09); }\n.logo-rule { flex: 1; height: max(2px, calc(var(--h) * .023)); background: var(--red-500); }\n.logo-shop { font-size: calc(var(--h) * .162); font-weight: 800; letter-spacing: calc(var(--h) * .081); margin-right: calc(var(--h) * -.081); line-height: 1; color: var(--grey-300); white-space: nowrap; }\n\n.about { margin: 0; font-size: 14px; line-height: 1.6; }\n.social { display: flex; gap: 10px; }\n.social a { width: 44px; height: 44px; border: 1px solid var(--graphite-700); color: var(--grey-200); display: flex; align-items: center; justify-content: center; }\n.social a:hover { border-color: var(--white); color: var(--white); }\n.ic { width: 18px; height: 18px; flex: none; }\n\n.cols { flex: 1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 32px; }\n.col { display: flex; flex-direction: column; gap: 14px; font-size: 14px; }\n.col h3 { margin: 0; font-size: 12px; font-weight: 700; letter-spacing: .2em; color: var(--white); text-transform: uppercase; }\n.col a { color: var(--grey-200); text-decoration: none; }\n.col a:hover { color: var(--white); }\n.short { display: none; }\n\n.legal { border-top: 1px solid var(--line-inverse); padding-top: 28px; display: flex; justify-content: space-between; align-items: center; gap: 24px; font-size: 13px; color: var(--grey-300); }\n.legal strong { color: var(--white); font-weight: 600; }\n.addr::before { content: ' · '; }\n\n@container lf (max-width: 1199px) {\n  .rule { height: 5px; }\n  .rule i { width: 160px; }\n  .in { padding: 56px 40px 32px; gap: 40px; }\n  .top { flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 24px; }\n  .brand { flex: 1 1 100%; flex-direction: row; justify-content: space-between; align-items: center; gap: 24px; }\n  .about { display: none; }\n  .cols { flex: 1 1 100%; gap: 24px; }\n  .col { gap: 12px; } .col h3 { font-size: 11px; }\n  .legal { flex-direction: column; align-items: flex-start; gap: 8px; padding-top: 24px; font-size: 12px; line-height: 1.5; }\n  .logo { --h: 48px; }\n}\n@container lf (max-width: 767px) {\n  .rule i { width: 96px; background-size: 5px 5px; }\n  .in { padding: 48px 20px 32px; gap: 36px; }\n  .brand { flex-direction: column; align-items: flex-start; gap: 16px; }\n  .about { display: block; }\n  .cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 20px; }\n  .col { gap: 4px; }\n  .col h3 { padding-bottom: 6px; }\n  .col a { min-height: 36px; display: flex; align-items: center; }\n  .col-contact { gap: 10px; }\n  .legal { gap: 10px; }\n  .addr { display: block; } .addr::before { content: none; }\n  .long { display: none; } .short { display: inline; }\n  .logo { --h: 42px; }\n}\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const cols = Array.isArray(p.columns) && p.columns.length ? p.columns : DEFAULT_COLUMNS;\n    const about = p.about || 'Os produtos que aparecem no canal, com a curadoria de quem testa de verdade.';\n    const company = p.company || '[RAZÃO SOCIAL] · CNPJ [00.000.000/0000-00]';\n    const address = p.address || '[ENDEREÇO COMPLETO]';\n    const home = p.homeHref || '#';\n    const ig = p.instagram === null ? null : (p.instagram || '#');\n    const yt = p.youtube === null ? null : (p.youtube || '#');\n\n    const colsHtml = cols.map((c, i) => {\n      const links = (c.links || []).map((l, k) => {\n        const text = l.short\n          ? `<span class=\"long\">${esc(fixLabel(l.label))}</span><span class=\"short\">${esc(l.short)}</span>`\n          : esc(fixLabel(l.label));\n        return c.plain\n          ? `<span data-key=\"c${i}l${k}\">${text}</span>`\n          : `<a data-key=\"c${i}l${k}\" href=\"${esc(fixHref(l.href || '#'))}\">${text}</a>`;\n      }).join('');\n      return `<div class=\"col${c.plain ? ' col-contact' : ''}\" data-key=\"c${i}\"><h3>${esc(c.title)}</h3>${links}</div>`;\n    }).join('');\n\n    return `\n<footer class=\"lf-footer\">\n  <div class=\"rule\" aria-hidden=\"true\"><span></span><i></i></div>\n  <div class=\"in\">\n    <div class=\"top\">\n      <div class=\"brand\">\n        <a class=\"logo\" href=\"${esc(home)}\" aria-label=\"Auto Super Shop — início\">\n          <img class=\"logo-shield\" src=\"https://babi-lf.data2.media/file/7JdlktektKrT-sjFK5yjP\" alt=\"\" aria-hidden=\"true\" draggable=\"false\">\n          <span class=\"logo-words\" aria-hidden=\"true\"><span class=\"logo-name\">LOJA OFICIAL</span><span class=\"logo-sub\"><span class=\"logo-rule\"></span><span class=\"logo-shop\">SHOP</span><span class=\"logo-rule\"></span></span></span>\n        </a>\n        <p class=\"about\">${esc(about)}</p>\n        <div class=\"social\">\n          ${ig !== null ? `<a href=\"${esc(ig)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"Instagram da Auto Super\">${ICON_IG}</a>` : ''}\n          ${yt !== null ? `<a href=\"${esc(yt)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"YouTube da Auto Super\">${ICON_YT}</a>` : ''}\n        </div>\n      </div>\n      <div class=\"cols\">${colsHtml}</div>\n    </div>\n    <div class=\"legal\">\n      <span>${esc(company)}<span class=\"addr\">${esc(address)}</span></span>\n      <span>Pagamento: <strong>Pix</strong> · <strong>Cartão de crédito</strong></span>\n    </div>\n  </div>\n</footer>`;\n  }\n};"}},"about":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"about"}}}},"address":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"address"}}}},"columns":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"columns"}}}},"company":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"company"}}}},"homeHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"homeHref"}}}},"instagram":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"instagram"}}}},"youtube":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"youtube"}}}}}}}}}},{"_id":"6ab5ba1fb58135ae6e6d7daa","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AnnouncementBar","package":"babi-lf","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 DEFAULT_ITEMS = ['Pagamentos em PIX ou Cartão', 'Enviamos para todo o Brasil', 'Loja oficial do canal'];\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.lf-ab {\n  --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff;\n  container: lf / inline-size; width: 100%;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n}\n.in { height: 40px; display: flex; align-items: center; justify-content: center; gap: 28px; background: var(--ink); color: var(--white);\n  font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; overflow: hidden; }\n.sep { width: 14px; height: 2px; background: var(--red-500); transform: skewX(-30deg); flex: none; }\n.edge { display: none; }\n@container lf (max-width: 1199px) { .in { gap: 20px; font-size: 11px; } .in > .x3 { display: none; } }\n@container lf (max-width: 767px) {\n  .in { height: 36px; gap: 10px; letter-spacing: .12em; }\n  .in > .item, .in > .sep { display: none; }\n  .in > .primary { display: inline; }\n  .in > .edge { display: block; width: 12px; }\n}\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const items = Array.isArray(p.items) && p.items.length ? p.items : DEFAULT_ITEMS;\n    const n = Number(p.primary);\n    const primary = Number.isInteger(n) && n >= 0 && n < items.length ? n : Math.min(1, items.length - 1);\n    let out = '<span class=\"sep edge\" aria-hidden=\"true\"></span>';\n    items.forEach((t, i) => {\n      const third = i >= 2 ? ' x3' : '';\n      if (i) out += `<span class=\"sep${third}\" aria-hidden=\"true\"></span>`;\n      out += `<span class=\"item${third}${i === primary ? ' primary' : ''}\" data-key=\"i${i}\">${esc(t)}</span>`;\n    });\n    out += '<span class=\"sep edge\" aria-hidden=\"true\"></span>';\n    return `<div class=\"lf-ab\" role=\"region\" aria-label=\"Avisos da loja\"><div class=\"in\">${out}</div></div>`;\n  }\n};"}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"primary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primary"}}}}}}}}}},{"_id":"6ab5be22d41d6b6b0d5630b6","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Select","package":"babi-lf","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 DEFAULT_OPTIONS = [\n  { value: 'relevancia', label: 'Relevância' },\n  { value: 'menor-preco', label: 'Menor preço' },\n  { value: 'maior-preco', label: 'Maior preço' },\n  { value: 'mais-vendidos', label: 'Mais vendidos' }\n];\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n\nconst CHEVRON = '<svg class=\"ic\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M6 9l6 6 6-6\"/></svg>';\n\nconst TABLET = (p) => `\n${p} .box { height: 48px; }\n${p} .label .long { display: none; }\n${p} .label .short { display: inline; }`;\nconst MOBILE = (p) => `\n${p} { display: flex; width: 100%; }\n${p} .label { display: none; }\n${p} .prefix { display: inline; font-weight: 500; }\n${p} .box { flex: 1; min-width: 0; padding: 0 8px; font-size: 13px; gap: 4px; }\n${p} .box .ic { width: 14px; height: 14px; }`;\n\nconst norm = (list) => (Array.isArray(list) && list.length ? list : DEFAULT_OPTIONS).map((o) =>\n  o && typeof o === 'object' ? { value: String(o.value != null ? o.value : o.label), label: String(o.label != null ? o.label : o.value) } : { value: String(o), label: String(o) });\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.lf-select {\n  --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --line-strong: #d9d9d6;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  position: relative; display: inline-flex; align-items: center; gap: 12px; min-width: 0; max-width: 100%;\n}\n.label { font-size: 14px; color: var(--grey-500); white-space: nowrap; }\n.short { display: none; }\n.box { position: relative; display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 44px; min-width: 160px;\n  padding: 0 12px 0 16px; border: 1px solid var(--line-strong); border-radius: 2px; background: var(--white);\n  font-size: 15px; font-weight: 700; color: var(--ink); white-space: nowrap; }\n.box > .cur { min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n.box .ic { width: 16px; height: 16px; flex: none; }\n.box select { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; font-size: 16px; margin: 0; border: 0; }\n.box:focus-within { border-color: var(--ink); outline: 2px solid var(--red-500); outline-offset: 2px; }\n.prefix { display: none; }\n${TABLET('.v-tablet')}\n${TABLET('.v-mobile')}\n${MOBILE('.v-mobile')}\n@media (max-width: 1199px) { ${TABLET('.v-auto')} }\n@media (max-width: 767px) { ${MOBILE('.v-auto')} }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('select', 'change', (e, el) => {\n      d2.state.value = el.value;\n      try { d2.emit('onChange', el.value); } catch (err) { /* nenhum handler ligado */ }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const opts = norm(p.options);\n    const label = p.label || 'Ordenar por';\n    const shortLabel = p.shortLabel || 'Ordenar';\n    const layout = ['desktop', 'tablet', 'mobile'].indexOf(p.layout) >= 0 ? p.layout : 'auto';\n    const want = d2.state.value != null ? d2.state.value : (p.value != null && p.value !== '' ? String(p.value) : opts[0].value);\n    const cur = opts.filter((o) => o.value === String(want))[0] || opts[0];\n\n    const options = opts.map((o, i) =>\n      `<option data-key=\"o${i}\" value=\"${esc(o.value)}\"${o.value === cur.value ? ' selected' : ''}>${esc(o.label)}</option>`).join('');\n\n    return `\n<div class=\"lf-select v-${layout}\">\n  <span class=\"label\" aria-hidden=\"true\"><span class=\"long\">${esc(label)}</span><span class=\"short\">${esc(shortLabel)}</span></span>\n  <span class=\"box\">\n    <span class=\"cur\"><span class=\"prefix\">${esc(shortLabel)}: </span>${esc(cur.label)}</span>\n    ${CHEVRON}\n    <select aria-label=\"${esc(label)}\">${options}</select>\n  </span>\n</div>`;\n  }\n};"}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"options":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"options"}}}},"shortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortLabel"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6ab5c0802086c2e434179cac","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Button","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Button — Loja Lucas Fontana\n// Um único componente para todos os botões da loja, controlado por parâmetros.\n// `glyph` põe um ícone à esquerda do texto (\"Assistir no YouTube\" com o ícone do YouTube).\n// `ghost` é o contorno branco para fundos escuros (SupportCard, cards escuros): transparente, inverte no hover.\nconst VARIANTS = ['primary', 'brand', 'light', 'outline', 'ghost'];\nconst SIZES = ['sm', 'md', 'lg', 'hero', 'inset'];\nconst WIDTHS = ['auto', 'mobile', 'always'];\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst pick = (v, list, fb) => (list.indexOf(String(v)) >= 0 ? String(v) : fb);\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\n\nconst ARROW = '<svg class=\"ic\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14M13 6l6 6-6 6\"/></svg>';\n\n// Ícones à esquerda: mesmos desenhos do componente Icon (grade de 24px, traço 1.6)\nconst GLYPHS = {\n  youtube: '<rect x=\"2.5\" y=\"5\" width=\"19\" height=\"14\" rx=\"4\"/><path d=\"m10 9 5 3-5 3V9Z\"/>',\n  instagram: '<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/><circle cx=\"12\" cy=\"12\" r=\"4\"/><circle cx=\"17.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/>',\n  play: '<path d=\"M8 5.5v13l10-6.5-10-6.5Z\" fill=\"currentColor\" stroke=\"none\"/>',\n  message: '<path d=\"M4 5h16v11H9l-5 4V5Z\"/>',\n  briefcase: '<rect x=\"3\" y=\"7\" width=\"18\" height=\"13\" rx=\"1\"/><path d=\"M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2\"/><path d=\"M3 13h18\"/>',\n  bag: '<path d=\"M5 8h14l-1.2 12.2a1 1 0 0 1-1 .8H7.2a1 1 0 0 1-1-.8L5 8Z\"/><path d=\"M9 8V6a3 3 0 0 1 6 0v2\"/>',\n  user: '<circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 21c1.5-4 4.5-6 8-6s6.5 2 8 6\"/>',\n  truck: '<path d=\"M3 6h11v10H3zM14 9h4l3 3v4h-7\"/><circle cx=\"7\" cy=\"17.5\" r=\"1.8\"/><circle cx=\"17\" cy=\"17.5\" r=\"1.8\"/>'\n};\nconst glyphSvg = (n) => (n && GLYPHS[n]\n  ? `<svg class=\"ic g\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${GLYPHS[n]}</svg>`\n  : '');\n\n// Regras de tablet (<1200) e mobile (<768), aplicadas pela variante fixa ou pela largura da tela (auto)\nconst TABLET = (p) => `\n${p} .btn.s-hero { height: 52px; padding: 0 28px; font-size: 13px; gap: 12px; }\n${p} .btn.s-hero .ic { width: 16px; height: 16px; }\n${p} .btn.s-inset { height: 36px; padding: 0 14px; font-size: 12px; letter-spacing: .08em; }`;\nconst MOBILE = (p) => `\n${p} .btn.w-mobile { display: flex; width: 100%; padding: 0 20px; }\n${p} .long { display: none; }\n${p} .short { display: inline; }`;\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.wrap { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --graphite-800: #2a2b2e; --grey-100: #ececea;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: block; width: 100%; }\n.btn { display: inline-flex; align-items: center; justify-content: center; gap: 12px; height: 48px; padding: 0 24px;\n  border: 1.5px solid transparent; border-radius: 2px; font-family: inherit; font-size: 13px; font-weight: 700;\n  letter-spacing: .1em; line-height: 1; text-transform: uppercase; text-decoration: none; cursor: pointer; white-space: nowrap;\n  transition: background-color .15s ease, color .15s ease, border-color .15s ease; margin: 0; }\n.btn:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.btn .ic { width: 16px; height: 16px; flex: none; }\n.btn .ic.g { width: 18px; height: 18px; margin-right: -2px; }\n.short { display: none; }\n\n.s-sm { height: 44px; padding: 0 18px; }\n.s-lg { height: 52px; padding: 0 28px; }\n.s-hero { height: 56px; padding: 0 32px; font-size: 14px; gap: 14px; }\n.s-hero .ic { width: 18px; height: 18px; }\n.s-inset { height: 40px; padding: 0 20px; border: 0; }\n\n.w-always { display: flex; width: 100%; }\n\n.v-primary { background: var(--ink); color: var(--white); }\n.v-primary:hover { background: var(--graphite-800); color: var(--white); }\n.v-brand { background: var(--red-500); color: var(--white); }\n.v-brand:hover { background: var(--ink); color: var(--white); }\n.v-light { background: var(--white); color: var(--ink); }\n.v-light:hover { background: var(--grey-100); color: var(--ink); }\n.v-outline { background: var(--white); color: var(--ink); border-color: var(--ink); }\n.v-outline:hover { background: var(--ink); color: var(--white); }\n.v-ghost { background: transparent; color: var(--white); border-color: var(--white); }\n.v-ghost:hover { background: var(--white); color: var(--ink); }\n.v-ghost:focus-visible { outline-color: var(--white); }\n.btn.is-disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }\n\n${TABLET('.l-tablet')}\n${TABLET('.l-mobile')}\n${MOBILE('.l-mobile')}\n@media (max-width: 1199px) { ${TABLET('.l-auto')} }\n@media (max-width: 767px) { ${MOBILE('.l-auto')} }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('.btn', 'click', (e, el) => {\n      if (el.classList.contains('is-disabled')) { e.preventDefault(); return; }\n      if (el.tagName === 'BUTTON') {\n        try { d2.emit('onClick'); } catch (err) { /* nenhum handler ligado */ }\n      }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const label = p.label || 'Botão';\n    const variant = pick(p.variant, VARIANTS, 'primary');\n    const size = pick(p.size, SIZES, 'md');\n    const width = pick(p.fullWidth, WIDTHS, 'auto');\n    const layout = pick(p.layout, LAYOUTS, 'auto');\n    const disabled = truthy(p.isDisabled);\n    const arrow = truthy(p.arrow);\n    const href = p.href ? String(p.href) : '';\n\n    const text = p.shortLabel\n      ? `<span class=\"long\">${esc(label)}</span><span class=\"short\">${esc(p.shortLabel)}</span>`\n      : `<span>${esc(label)}</span>`;\n    const cls = `btn v-${variant} s-${size} w-${width}${disabled ? ' is-disabled' : ''}`;\n    const inner = `${glyphSvg(p.glyph)}${text}${arrow ? ARROW : ''}`;\n\n    const el = href\n      ? `<a class=\"${cls}\" href=\"${esc(href)}\"${truthy(p.newTab) ? ' target=\"_blank\" rel=\"noopener\"' : ''}${disabled ? ' aria-disabled=\"true\" tabindex=\"-1\"' : ''}>${inner}</a>`\n      : `<button type=\"button\" class=\"${cls}\"${disabled ? ' disabled' : ''}>${inner}</button>`;\n\n    return `<div class=\"wrap l-${layout}\">${el}</div>`;\n  }\n};"}},"arrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arrow"}}}},"fullWidth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"fullWidth"}}}},"glyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"glyph"}}}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"isDisabled":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"isDisabled"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"newTab":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"newTab"}}}},"onClick":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClick"}}}},"shortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortLabel"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}},"variant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"variant"}}}}}}}}}},{"_id":"6ab5c22eb58135ae6e6d8a73","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Doc","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Documentação — Loja Lucas Fontana\n// Renderiza um texto em formato markdown simples (títulos, listas, tabelas, código) no visual da loja.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n\nconst inline = (s) => esc(s)\n  .replace(/`([^`]+)`/g, '<code>$1</code>')\n  .replace(/\\*\\*([^*]+)\\*\\*/g, '<b>$1</b>');\n\nfunction md(src) {\n  const lines = String(src || '').replace(/\\r/g, '').split('\\n');\n  let html = '', i = 0;\n  while (i < lines.length) {\n    const l = lines[i];\n    if (!l.trim()) { i++; continue; }\n    if (l.startsWith('```')) {\n      const buf = []; i++;\n      while (i < lines.length && !lines[i].startsWith('```')) { buf.push(lines[i]); i++; }\n      i++;\n      html += `<pre>${esc(buf.join('\\n'))}</pre>`;\n      continue;\n    }\n    if (l.startsWith('### ')) { html += `<h4>${inline(l.slice(4))}</h4>`; i++; continue; }\n    if (l.startsWith('## ')) { html += `<h3>${inline(l.slice(3))}</h3>`; i++; continue; }\n    if (l.startsWith('> ')) {\n      const buf = [];\n      while (i < lines.length && lines[i].startsWith('> ')) { buf.push(inline(lines[i].slice(2))); i++; }\n      html += `<div class=\"note\">${buf.join('<br>')}</div>`;\n      continue;\n    }\n    if (l.trim().startsWith('|')) {\n      const rows = [];\n      while (i < lines.length && lines[i].trim().startsWith('|')) { rows.push(lines[i].trim()); i++; }\n      const cells = (r) => r.replace(/^\\||\\|$/g, '').split('|').map((c) => c.trim());\n      const body = rows.filter((r, k) => !(k === 1 && /^\\|?[\\s:-]+\\|/.test(r)));\n      const [head, ...rest] = body;\n      html += '<div class=\"tw\"><table><thead><tr>' + cells(head).map((c) => `<th>${inline(c)}</th>`).join('') + '</tr></thead><tbody>' +\n        rest.map((r) => '<tr>' + cells(r).map((c) => `<td>${inline(c)}</td>`).join('') + '</tr>').join('') + '</tbody></table></div>';\n      continue;\n    }\n    if (l.startsWith('- ')) {\n      const buf = [];\n      while (i < lines.length && lines[i].startsWith('- ')) { buf.push(`<li>${inline(lines[i].slice(2))}</li>`); i++; }\n      html += `<ul>${buf.join('')}</ul>`;\n      continue;\n    }\n    const buf = [];\n    while (i < lines.length && lines[i].trim() && !/^(#|- |\\||> |```)/.test(lines[i])) { buf.push(inline(lines[i])); i++; }\n    html += `<p>${buf.join(' ')}</p>`;\n  }\n  return html;\n}\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.doc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-100: #ececea; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  background: var(--white); color: var(--ink); width: 100%; min-height: 100%; }\n.rule { height: 6px; background: var(--red-500); }\n.in { padding: 48px 56px 64px; max-width: 1040px; }\n.eyebrow { margin: 0 0 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; color: var(--grey-500); }\n.eyebrow b { color: var(--red-500); }\nh1 { margin: 0 0 12px; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; text-transform: uppercase; letter-spacing: -.01em; }\n.type { display: inline-block; margin: 0 0 28px; padding: 6px 10px; background: var(--ink); color: var(--white); font: 700 13px/1 ui-monospace, SFMono-Regular, Menlo, monospace; }\nh3 { margin: 40px 0 14px; padding-top: 24px; border-top: 1px solid var(--line); font-size: 13px; font-weight: 800; letter-spacing: .24em; text-transform: uppercase; }\nh4 { margin: 24px 0 10px; font-size: 16px; font-weight: 700; }\np { margin: 0 0 14px; font-size: 16px; line-height: 1.6; color: var(--grey-700); }\np b, li b, td b { color: var(--ink); }\nul { margin: 0 0 16px; padding-left: 20px; }\nli { margin: 0 0 8px; font-size: 15px; line-height: 1.55; color: var(--grey-700); }\nli::marker { color: var(--red-500); }\ncode { font: 600 13px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; background: var(--grey-100); color: var(--ink); padding: 2px 6px; border-radius: 2px; }\npre { margin: 0 0 16px; padding: 16px 18px; background: var(--ink); color: #e9e9e7; font: 13px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; overflow-x: auto; }\n.tw { overflow-x: auto; margin: 0 0 16px; }\ntable { width: 100%; border-collapse: collapse; font-size: 14px; }\nth { text-align: left; padding: 0 12px 10px 0; border-bottom: 2px solid var(--ink); font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; }\ntd { padding: 12px 12px 12px 0; border-bottom: 1px solid var(--line); vertical-align: top; line-height: 1.5; color: var(--grey-700); }\ntd:first-child { white-space: nowrap; }\n.note { margin: 0 0 16px; padding: 14px 16px; border-left: 4px solid var(--red-500); background: var(--grey-50); font-size: 14px; line-height: 1.55; color: var(--grey-700); }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    return `<div class=\"doc\"><div class=\"rule\"></div><div class=\"in\">\n      <p class=\"eyebrow\"><b>Doc</b> — Componentes UI</p>\n      <h1>${esc(p.heading || 'Componente')}</h1>\n      ${p.typeId ? `<span class=\"type\">${esc(p.typeId)}</span>` : ''}\n      ${md(p.content)}\n    </div></div>`;\n  }\n};"}},"content":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"content"}}}},"heading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"heading"}}}},"typeId":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"typeId"}}}}}}}}}},{"_id":"6ab5c4bfa44198fd3fbf3e1b","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Logo","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Logo — Auto Super Shop\n// Escudo da Auto Super (imagem no Drive \"Ícones\") + \"LOJA OFICIAL\" em itálico, uma linha vermelha, \"SHOP\" e outra linha vermelha.\n// Proporções do logo aprovado: escudo 86×130, texto 50px, linhas de 3px, SHOP 21px com espaçamento de 10,5px.\n// Tudo escala pela altura do escudo (--h): desktop 56px, compact 52px (menu da Gestão), tablet 48px, mobile 42px.\n// Sem `layout`, vale a largura da tela (media query). `tone: 'dark'` deixa o texto branco, para fundos escuros.\nconst SHIELD = 'https://babi-lf.data2.media/file/7JdlktektKrT-sjFK5yjP';\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst LAYOUTS = ['desktop', 'compact', 'tablet', 'mobile'];\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.wrap { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b; --grey-300: #a7a9ad; --h: 56px;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; align-items: center; height: 100%; padding: 0 8px; }\n.l-compact { --h: 52px; }\n.l-tablet { --h: 48px; }\n.l-mobile { --h: 42px; justify-content: center; }\n@media (max-width: 1199px) { .l-auto { --h: 48px; } }\n@media (max-width: 767px) { .l-auto { --h: 42px; justify-content: center; } }\n.logo { display: inline-flex; align-items: center; gap: calc(var(--h) * .215); color: var(--ink); text-decoration: none; flex-shrink: 0; }\n.logo:focus-visible { outline: 2px solid var(--red-500); outline-offset: 4px; }\n.shield { display: block; height: var(--h); width: auto; flex: none; }\n.words { display: flex; flex-direction: column; gap: calc(var(--h) * .06); }\n.name { font-size: calc(var(--h) * .385); font-weight: 900; font-stretch: 105%; font-style: italic; letter-spacing: 0; line-height: 1; white-space: nowrap; color: var(--ink); }\n.sub { display: flex; align-items: center; gap: calc(var(--h) * .09); }\n.rule { flex: 1; height: max(2px, calc(var(--h) * .023)); background: var(--red-500); }\n.shop { font-size: calc(var(--h) * .162); font-weight: 800; letter-spacing: calc(var(--h) * .081); margin-right: calc(var(--h) * -.081); line-height: 1; color: var(--grey-700); white-space: nowrap; }\n.dark .name { color: var(--white); }\n.dark .shop { color: var(--grey-300); }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const tone = p.tone === 'dark' ? 'dark' : 'light';\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'auto';\n    const inner = `<img class=\"shield\" src=\"${SHIELD}\" alt=\"\" aria-hidden=\"true\" draggable=\"false\">` +\n      `<span class=\"words\" aria-hidden=\"true\"><span class=\"name\">LOJA OFICIAL</span><span class=\"sub\"><span class=\"rule\"></span><span class=\"shop\">SHOP</span><span class=\"rule\"></span></span></span>`;\n    const cls = `logo${tone === 'dark' ? ' dark' : ''}`;\n    const el = truthy(p.noLink)\n      ? `<span class=\"${cls}\" role=\"img\" aria-label=\"Auto Super Shop\">${inner}</span>`\n      : `<a class=\"${cls}\" href=\"${esc(p.href || '/')}\" aria-label=\"Auto Super Shop — início\">${inner}</a>`;\n    return `<div class=\"wrap l-${layout}\">${el}</div>`;\n  }\n};\n"}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"noLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"noLink"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6ab66f325f5a9fd2e7a7bd0c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Badge","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Badge — Loja Lucas Fontana\n// Selos quadrados em maiúsculas: status, parceria e tópicos.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst TONES = ['ink', 'red', 'outline'];\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\n\nconst TABLET = (p) => `\n${p} .badge.outline { font-size: 10px; padding: 5px 8px; }`;\nconst MOBILE = (p) => `\n${p} .badge { font-size: 9px; padding: 4px 6px; letter-spacing: .1em; }\n${p} .badge.status { padding: 4px 7px; letter-spacing: .08em; }\n${p} .badge.outline { font-size: 10px; padding: 5px 8px; }\n${p} .long { display: none; }\n${p} .short { display: inline; }`;\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.wrap { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; align-items: center; height: 100%; }\n.badge { display: inline-flex; align-items: center; font-size: 10px; font-weight: 800; letter-spacing: .12em; line-height: 1.2;\n  text-transform: uppercase; padding: 5px 8px; color: var(--white); background: var(--ink); white-space: nowrap; }\n.badge.red { background: var(--red-500); }\n.badge.outline { background: transparent; border: 1px solid var(--grey-700); font-weight: 700; font-size: 11px; padding: 5px 9px; }\n.badge.status { font-size: 11px; padding: 6px 10px; letter-spacing: .1em; }\n.short { display: none; }\n${TABLET('.l-tablet')}\n${TABLET('.l-mobile')}\n${MOBILE('.l-mobile')}\n@media (max-width: 1199px) { ${TABLET('.l-auto')} }\n@media (max-width: 767px) { ${MOBILE('.l-auto')} }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const label = p.label || 'Badge';\n    const tone = TONES.indexOf(p.tone) >= 0 ? p.tone : 'ink';\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'auto';\n    const cls = `badge${tone !== 'ink' ? ' ' + tone : ''}${truthy(p.isStatus) ? ' status' : ''}`;\n    const text = p.shortLabel\n      ? `<span class=\"long\">${esc(label)}</span><span class=\"short\">${esc(p.shortLabel)}</span>`\n      : esc(label);\n    return `<div class=\"wrap l-${layout}\"><span class=\"${cls}\">${text}</span></div>`;\n  }\n};"}},"isStatus":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"isStatus"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"shortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortLabel"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6ab6a330b39f683eb1b33564","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Icon","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Icon — Loja Lucas Fontana\n// Os 32 ícones de contorno do design system (grade de 24px, pontas arredondadas).\n// Um componente só: o parâmetro `name` escolhe o desenho. Use name = \"all\" para ver o catálogo.\nconst ICONS = {\n    search: [['circle', { cx: 11, cy: 11, r: 7 }], ['path', { d: 'm20 20-3.5-3.5' }]],\n    user: [['circle', { cx: 12, cy: 8, r: 4 }], ['path', { d: 'M4 21c1.5-4 4.5-6 8-6s6.5 2 8 6' }]],\n    bag: [['path', { d: 'M5 8h14l-1.2 12.2a1 1 0 0 1-1 .8H7.2a1 1 0 0 1-1-.8L5 8Z' }], ['path', { d: 'M9 8V6a3 3 0 0 1 6 0v2' }]],\n    menu: [['path', { d: 'M4 7h16M4 12h16M4 17h10' }]],\n    'arrow-right': [['path', { d: 'M5 12h14M13 6l6 6-6 6' }]],\n    'chevron-left': [['path', { d: 'M15 5l-7 7 7 7' }]],\n    'chevron-right': [['path', { d: 'M9 5l7 7-7 7' }]],\n    image: [['rect', { x: 3, y: 5, width: 18, height: 14, rx: 1 }], ['circle', { cx: 9, cy: 10, r: 2 }], ['path', { d: 'm21 16-5-5-9 8' }]],\n    message: [['path', { d: 'M4 5h16v11H9l-5 4V5Z' }]],\n    briefcase: [['rect', { x: 3, y: 7, width: 18, height: 13, rx: 1 }], ['path', { d: 'M9 7V5a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2' }], ['path', { d: 'M3 13h18' }]],\n    instagram: [['rect', { x: 3, y: 3, width: 18, height: 18, rx: 5 }], ['circle', { cx: 12, cy: 12, r: 4 }], ['circle', { cx: 17.5, cy: 6.5, r: 0.8, fill: 'currentColor' }]],\n    youtube: [['rect', { x: 2.5, y: 5, width: 19, height: 14, rx: 4 }], ['path', { d: 'm10 9 5 3-5 3V9Z' }]],\n    filter: [['path', { d: 'M4 8h9M17 8h3M4 16h3M11 16h9' }], ['circle', { cx: 15, cy: 8, r: 2 }], ['circle', { cx: 9, cy: 16, r: 2 }]],\n    close: [['path', { d: 'M6 6l12 12M18 6 6 18' }]],\n    check: [['path', { d: 'm5 12.5 4.5 4.5L19 7.5' }]],\n    'chevron-down': [['path', { d: 'M6 9l6 6 6-6' }]],\n    'search-x': [['circle', { cx: 11, cy: 11, r: 7 }], ['path', { d: 'm20 20-3.5-3.5M8.5 8.5l5 5M13.5 8.5l-5 5' }]],\n    truck: [['path', { d: 'M3 6h11v10H3zM14 9h4l3 3v4h-7' }], ['circle', { cx: 7, cy: 17.5, r: 1.8 }], ['circle', { cx: 17, cy: 17.5, r: 1.8 }]],\n    card: [['rect', { x: 3, y: 5.5, width: 18, height: 13, rx: 1 }], ['path', { d: 'M3 10h18M7 15h4' }]],\n    swap: [['path', { d: 'M4 8h14l-3-3M20 16H6l3 3' }]],\n    play: [['path', { d: 'M8 5.5v13l10-6.5-10-6.5Z', fill: 'currentColor', stroke: 'none' }]],\n    plus: [['path', { d: 'M12 5v14M5 12h14' }]],\n    minus: [['path', { d: 'M5 12h14' }]],\n    pin: [['path', { d: 'M12 21s-7-6.2-7-11.5A7 7 0 0 1 19 9.5C19 14.8 12 21 12 21Z' }], ['circle', { cx: 12, cy: 9.5, r: 2.5 }]],\n    logout: [['path', { d: 'M10 4H5v16h5M14 8l4 4-4 4M18 12H9' }]],\n    lock: [['rect', { x: 5, y: 10.5, width: 14, height: 10, rx: 1 }], ['path', { d: 'M8.5 10.5V7.5a3.5 3.5 0 0 1 7 0v3' }]],\n    eye: [['path', { d: 'M2.5 12S6 5.5 12 5.5 21.5 12 21.5 12 18 18.5 12 18.5 2.5 12 2.5 12Z' }], ['circle', { cx: 12, cy: 12, r: 3 }]],\n    'eye-off': [['path', { d: 'M4 4l16 16M10.6 5.6c.5-.1.9-.1 1.4-.1 6 0 9.5 6.5 9.5 6.5a17 17 0 0 1-3 3.6M6.5 7.4A16 16 0 0 0 2.5 12S6 18.5 12 18.5c1.6 0 3-.4 4.2-1M9.9 9.9a3 3 0 0 0 4.2 4.2' }]],\n    copy: [['rect', { x: 8.5, y: 8.5, width: 11, height: 11, rx: 1 }], ['path', { d: 'M15.5 8.5v-3a1 1 0 0 0-1-1h-9a1 1 0 0 0-1 1v9a1 1 0 0 0 1 1h3' }]],\n    'arrow-left': [['path', { d: 'M19 12H5M11 6l-6 6 6 6' }]],\n    mail: [['rect', { x: 3, y: 5.5, width: 18, height: 13, rx: 1 }], ['path', { d: 'm3.5 6.5 8.5 7 8.5-7' }]],\n    trash: [['path', { d: 'M4 7h16M9 7V4.5h6V7M6.5 7l.9 12.6a1 1 0 0 0 1 .9h7.2a1 1 0 0 0 1-.9L17.5 7M10 11v6M14 11v6' }]],\n    star: [['path', { d: 'M12 2.8l2.8 6 6.4.6-4.8 4.3 1.4 6.4L12 16.8 6.2 20.1l1.4-6.4L2.8 9.4l6.4-.6z', fill: 'currentColor', stroke: 'none' }]]\n  };\nconst BOLD = { 'arrow-right': 1, 'chevron-left': 1, 'chevron-right': 1, 'chevron-down': 1, close: 1, check: 1, plus: 1, minus: 1, 'arrow-left': 1 };\nconst MEDIUM = { menu: 1, search: 1, 'search-x': 1, filter: 1 };\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n\nfunction svg(name, size, stroke) {\n  const parts = ICONS[name] || [];\n  const sw = stroke || (BOLD[name] ? 2 : MEDIUM[name] ? 1.8 : 1.6);\n  const inner = parts.map(([tag, attrs]) =>\n    `<${tag} ${Object.keys(attrs).map((k) => `${k}=\"${esc(attrs[k])}\"`).join(' ')}/>`).join('');\n  return `<svg class=\"ic\" width=\"${size}\" height=\"${size}\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"${sw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">${inner}</svg>`;\n}\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.wrap { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }\n.ic { display: block; flex: none; }\n.all { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 0; background: #ffffff; border-top: 1px solid #e4e4e2; border-left: 1px solid #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; width: 100%; }\n.cell { background: #ffffff; border-right: 1px solid #e4e4e2; border-bottom: 1px solid #e4e4e2; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; padding: 22px 8px 16px; }\n.cell code { font: 600 12px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace; color: #45474b; }\n`,\n\n  render(d2) {\n    const p = d2.params || {};\n    const color = p.iconColor || '#0b0b0c';\n    const size = Number(p.size) > 0 ? Number(p.size) : 24;\n    const stroke = Number(p.strokeWidth) > 0 ? Number(p.strokeWidth) : 0;\n    if (p.name === 'all') {\n      const cells = Object.keys(ICONS).map((n) =>\n        `<div class=\"cell\" data-key=\"${esc(n)}\">${svg(n, size, stroke)}<code>${esc(n)}</code></div>`).join('');\n      return `<div class=\"all\" style=\"color:${esc(color)}\">${cells}</div>`;\n    }\n    const name = ICONS[p.name] ? p.name : 'bag';\n    return `<div class=\"wrap\" style=\"color:${esc(color)}\">${svg(name, size, stroke)}</div>`;\n  }\n};"}},"iconColor":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconColor"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}},"strokeWidth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"strokeWidth"}}}}}}}}}},{"_id":"6ab6a8e0356aa1fc96e964ea","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"SearchBar","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// SearchBar — Loja Lucas Fontana\n// Campo de busca cinza com ícone de lupa e botão \"Buscar\" embutido à direita.\n// Reaproveita os componentes Button e Icon da loja pelos parâmetros `button` e `searchIcon`.\n// `size: \"lg\"` é a busca grande das páginas (FAQ): 56px (52px no tablet/mobile), fundo branco, borda ink de 1,5px, texto 16px.\n// `showButton: false` tira o botão \"Buscar\" (no PageHeader do FAQ a busca filtra enquanto a pessoa digita).\n// `value`: texto inicial do campo. `showClear`: mostra o botão de limpar. `liveDelay`: pausa (ms) antes de avisar enquanto digita (padrão 150).\n// `syncTopic`: tópico do bus em que outro componente pode trocar o texto do campo.\n// `busTopic` avisa outros componentes da página a cada letra digitada (o FaqLayout escuta \"faq:search\").\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\n\n// O parâmetro de componente chega como o componente vivo; o d2.render usa a referência dele.\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n\nconst COMPACT = (p) => `\n${p} .field { height: 44px; padding: 0 4px 0 14px; gap: 10px; }\n${p} input { height: 40px; }\n${p}.lg .field { height: 52px; padding: 0 4px 0 14px; }\n${p}.lg.nobtn .field { padding-right: 14px; }\n${p}.lg input { height: 48px; }`;\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.sb { --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-500: #6b6e73; --line-strong: #d9d9d6;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; align-items: center; width: 100%; height: 100%; }\n.field { display: flex; align-items: center; gap: 12px; width: 100%; height: 48px; padding: 0 4px 0 18px;\n  background: var(--grey-50); border: 1px solid var(--line-strong); border-radius: 2px; min-width: 0; margin: 0; }\n.field:focus-within { border-color: var(--ink); }\ninput { flex: 1; min-width: 0; height: 44px; border: 0; outline: none; background: transparent; margin: 0; padding: 0;\n  font: 400 15px/1.2 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; color: var(--ink); -webkit-appearance: none; appearance: none; }\ninput::placeholder { color: var(--grey-500); }\ninput::-webkit-search-cancel-button { -webkit-appearance: none; }\n.nobtn .field { padding-right: 18px; }\n.clr { display: none; flex: none; align-items: center; justify-content: center; width: 28px; height: 28px; margin: 0 2px 0 0; padding: 0; border: 0; background: transparent; cursor: pointer; border-radius: 50%; }\n.has .clr { display: flex; }\n.clr:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }\n\n/* Tamanho grande (busca das páginas) */\n.lg .field { height: 56px; background: var(--white); border: 1.5px solid var(--ink); padding: 0 4px 0 18px; }\n.lg.nobtn .field { padding-right: 14px; }\n.lg input { height: 52px; font-size: 16px; }\n${COMPACT('.l-tablet')}\n${COMPACT('.l-mobile')}\n@media (max-width: 1199px) { ${COMPACT('.l-auto')} }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    // Placeholder curto quando o campo fica estreito (menos de 420px), como no design system\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const narrow = e[0].contentRect.width < 420;\n        if (d2.state.narrow !== narrow) d2.state.narrow = narrow;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n    d2.on('form', 'submit', (e) => { e.preventDefault(); submit(d2); });\n    d2.on('input', 'keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); submit(d2); } });\n    // Enquanto digita: avisa quem estiver ouvindo (onInput e o tópico do bus), com uma pausa curta\n    let t = null;\n    d2.on('input', 'input', (e, el) => {\n      setVal(d2, el.value);\n      if (t) clearTimeout(t);\n      t = setTimeout(() => {\n        const q = String(el.value || '').trim();\n        try { d2.emit('onInput', q); } catch (err) { /* nenhum handler ligado */ }\n        const topic = (d2.params || {}).busTopic;\n        if (topic && d2.bus) d2.bus.emit(String(topic), q);\n      }, Number((d2.params || {}).liveDelay) > 0 ? Number((d2.params || {}).liveDelay) : 150);\n    });\n    d2.onUnmount(() => { if (t) clearTimeout(t); });\n    // Botão de limpar (showClear): esvazia o campo e avisa quem escuta\n    d2.on('[data-clear]', 'click', () => {\n      const el = d2.$('input');\n      if (el) { el.value = ''; el.focus(); }\n      setVal(d2, '');\n      if (t) clearTimeout(t);\n      try { d2.emit('onInput', ''); } catch (err) { /* nenhum handler ligado */ }\n      const topic = (d2.params || {}).busTopic;\n      if (topic && d2.bus) d2.bus.emit(String(topic), '');\n    });\n    // syncTopic: outro componente muda a busca (ex.: a lista limpa o filtro) e o campo acompanha, se não estiver em uso\n    const sync = (d2.params || {}).syncTopic;\n    if (sync && d2.bus && typeof d2.bus.on === 'function') {\n      d2.bus.on(String(sync), (v) => {\n        const el = d2.$('input');\n        if (!el) return;\n        const rn = el.getRootNode ? el.getRootNode() : null;\n        if (rn && rn.activeElement === el) return;\n        const nv = String(v == null ? '' : v);\n        if (el.value !== nv) { el.value = nv; setVal(d2, nv); }\n      });\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'auto';\n    const lg = p.size === 'lg';\n    const withButton = p.showButton == null ? true : truthy(p.showButton);\n    const placeholder = d2.state.narrow\n      ? (p.shortPlaceholder || 'Buscar produto ou marca')\n      : (p.placeholder || 'Busque por produto, categoria ou marca');\n    const compact = layout === 'tablet' || layout === 'mobile';\n\n    const iconRef = refOf(d2.param('searchIcon'));\n    const buttonRef = withButton ? refOf(d2.param('button')) : '';\n    const icon = iconRef\n      ? d2.render(iconRef, { name: 'search', size: lg ? 20 : 18, iconColor: lg ? '#0b0b0c' : '#6b6e73' },\n          { key: 'ic', width: lg ? 20 : 18, height: lg ? 20 : 18, style: 'flex:none' })\n      : '';\n    const button = buttonRef\n      ? d2.render(buttonRef,\n          { label: p.buttonLabel || 'Buscar', variant: 'primary', size: 'inset', fullWidth: 'auto', layout: layout === 'auto' ? '' : layout },\n          { key: 'btn', width: 'auto', height: compact ? 36 : 40, style: 'flex:none', onClick: () => submit(d2) })\n      : '';\n\n    const stored = d2.root && d2.root.__lfVal !== undefined ? d2.root.__lfVal : null;\n    const cur = String(stored != null ? stored : (p.value == null ? '' : p.value));\n    const clearIcon = truthy(p.showClear) && iconRef ? d2.render(iconRef, { name: 'close', size: 16, iconColor: '#6b6e73' }, { key: 'ic-x', width: 16, height: 16, style: 'flex:none' }) : '';\n    const clear = truthy(p.showClear) ? `<button type=\"button\" class=\"clr\" data-clear aria-label=\"Limpar busca\">${clearIcon}</button>` : '';\n    return `<div class=\"sb l-${layout}${lg ? ' lg' : ''}${buttonRef ? '' : ' nobtn'}\">\n      <form class=\"field${cur ? ' has' : ''}\" role=\"search\" onsubmit=\"return false\">\n        ${icon}\n        <input type=\"search\" name=\"q\" aria-label=\"${esc(p.ariaLabel || 'Buscar produtos')}\" placeholder=\"${esc(placeholder)}\" autocomplete=\"off\" value=\"${esc(cur)}\">\n        ${clear}${button}\n      </form>\n    </div>`;\n  }\n};\n\n// Guarda o texto digitado fora do estado reativo (não redesenha) e mostra/esconde o botão de limpar\nfunction setVal(d2, v) {\n  try {\n    if (d2.root) d2.root.__lfVal = v;\n    const f = d2.$('.field');\n    if (f) f.classList.toggle('has', !!v);\n  } catch (err) { /* sem DOM */ }\n}\n\nfunction submit(d2) {\n  const el = d2.$('input');\n  const q = el ? String(el.value || '').trim() : '';\n  try { d2.emit('onSearch', q); } catch (err) { /* nenhum handler ligado */ }\n  const topic = (d2.params || {}).busTopic;\n  if (topic && d2.bus) d2.bus.emit(String(topic), q);\n}"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"busTopic":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"busTopic"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"buttonLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"buttonLabel"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"onSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSearch"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"searchIcon":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchIcon"}}}},"shortPlaceholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortPlaceholder"}}}},"showButton":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showButton"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}}}}}}}},{"_id":"6ab6bcb197b00074cbeff455","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Header","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Header — Loja Lucas Fontana\n// Cabeçalho da loja nas três telas. Não desenha logo, busca, botão nem ícones:\n// reaproveita Logo, SearchBar, Button e Icon pelos parâmetros `logo`, `searchBar`, `button` e `iconComponent`.\n// O número da sacola vem da sacola do navegador (`lf-sacola`) e atualiza sozinho; `cartCount` só vale sem acesso a ela.\n// Logado: \"Olá, [Nome]\" no lugar de \"Entre ou cadastre-se\" e o \"Sair\" — no desktop, um link ao lado de\n// \"Minha conta\"; no tablet e no mobile, o ícone de pessoa abre uma barra curta abaixo do topo com \"Minha conta\" e \"Sair\".\n// Loja: com `sessionService` (minhaSessao) ligado, o Header pergunta quem está logado ao abrir a página (guarda o último\n// resultado no navegador para não piscar) e ignora o `userName` fixo; \"Sair\" encerra a sessão pela `authStrategy`\n// (lojaAuth) e atualiza a página. Sem esses serviços (previews), vale o `userName` e \"Sair\" só avisa `onLogout`.\n// O menu de categorias vem do serviço `categoriesService` (categoriasLoja); se falhar, vale `categories` ou o padrão.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst LISTA = '/lista-de-produtos/q';\nconst DEFAULT_CATS = [\n  { label: 'Performance', href: LISTA + '?categoria=performance' },\n  { label: 'Cuidado automotivo', href: LISTA + '?categoria=cuidado-automotivo' },\n  { label: 'Vestuário', href: LISTA + '?categoria=vestuario' },\n  { label: 'Acessórios', href: LISTA + '?categoria=acessorios' }\n];\n// Busca: na página da lista os resultados atualizam sozinhos (o SearchBar avisa pelo bus); nas outras páginas vai para a lista com ?q=\nconst onLista = () => { try { return String(location.pathname || '').indexOf('/lista-de-produtos') >= 0; } catch (e) { return false; } };\nconst urlQ = () => { try { return new URLSearchParams(location.search).get('q') || ''; } catch (e) { return ''; } };\nconst goSearch = (q) => {\n  if (onLista()) return;\n  const t = String(q || '').trim();\n  if (typeof window !== 'undefined') window.location.assign(LISTA + (t ? '?q=' + encodeURIComponent(t) : ''));\n};\nconst SESS_KEY = 'lf-sessao';\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst slug = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res);\n\nlet LOADED = null; let LOADING = null;\nfunction cats(v) {\n  if (LOADED) return LOADED;\n  let list = v;\n  if (typeof list === 'string') list = list.split(',').map((s) => s.trim()).filter(Boolean);\n  if (!Array.isArray(list) || !list.length) return DEFAULT_CATS;\n  return list.map((c) => typeof c === 'string'\n    ? { label: c, href: LISTA + '?categoria=' + slug(c) }\n    : { label: c.label || c.name || '', href: c.href || LISTA + '?categoria=' + slug(c.label || c.name) });\n}\nconst readSess = () => { try { const j = JSON.parse(sessionStorage.getItem(SESS_KEY) || 'null'); return j && typeof j === 'object' ? j : null; } catch (e) { return null; } };\nconst writeSess = (v) => { try { if (v) sessionStorage.setItem(SESS_KEY, JSON.stringify(v)); else sessionStorage.removeItem(SESS_KEY); } catch (e) { /* sem storage */ } };\n\n// O parâmetro de componente chega como o componente vivo; o d2.render usa a referência dele.\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n\nfunction detect(w) { return w >= 1200 ? 'desktop' : w >= 768 ? 'tablet' : 'mobile'; }\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.hd { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  background: var(--white); color: var(--ink); width: 100%; position: relative; }\n.out { display: inline-flex; align-items: center; gap: 6px; height: 44px; padding: 0 8px 0 14px; margin-left: 2px; border: 0; border-left: 1px solid var(--line);\n  background: transparent; color: var(--grey-500); font-family: inherit; font-size: 12px; font-weight: 700; line-height: 1; letter-spacing: .1em; text-transform: uppercase; cursor: pointer; }\n.out .slot { width: 18px; height: 18px; pointer-events: none; }\n.out:hover { color: var(--red-500); }\n.out:focus-visible, .umenu a:focus-visible, .umenu button:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.umenu { position: absolute; right: 8px; z-index: 5; display: flex; align-items: stretch; height: 44px; background: var(--white);\n  border: 1px solid var(--line); box-shadow: 0 6px 16px rgba(11, 11, 12, .12); }\n.umenu a, .umenu button { display: inline-flex; align-items: center; gap: 8px; padding: 0 16px; border: 0; background: transparent; color: var(--ink);\n  font-family: inherit; font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; white-space: nowrap; cursor: pointer; }\n.umenu button { border-left: 1px solid var(--line); }\n.umenu .slot { width: 18px; height: 18px; pointer-events: none; }\n.umenu a:hover, .umenu button:hover { color: var(--red-500); }\n.l-tablet .umenu { top: 72px; right: 40px; }\n.l-mobile .umenu { top: 64px; right: 8px; }\n.row { display: flex; align-items: center; gap: 32px; height: 88px; padding: 0 96px; }\n.logo { flex: none; height: 56px; width: 300px; margin-left: -8px; }\n.search { flex: 1; min-width: 0; height: 48px; max-width: 640px; margin: 0 auto; }\n.actions { display: flex; align-items: center; gap: 8px; flex: none; }\n.ibtn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px;\n  background: transparent; border: 0; border-radius: 2px; padding: 0; margin: 0; color: var(--ink); cursor: pointer; text-decoration: none; }\n.ibtn:hover { color: var(--red-500); }\n.ibtn:focus-visible, .acct:focus-visible, .nav a:focus-visible, .all:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.ibtn .slot { width: 24px; height: 24px; pointer-events: none; }\n.count { position: absolute; top: 4px; right: 2px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;\n  background: var(--red-500); color: var(--white); font-size: 11px; font-weight: 700; line-height: 18px; text-align: center; pointer-events: none; }\n.acct { display: inline-flex; align-items: center; gap: 10px; height: 44px; padding: 0 8px; color: var(--ink); text-decoration: none; background: transparent; border: 0; cursor: pointer; font-family: inherit; }\n.acct .slot { width: 24px; height: 24px; flex: none; pointer-events: none; }\n.acct .txt { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; text-align: left; }\n.acct .hint { font-size: 12px; color: var(--grey-500); line-height: 1; white-space: nowrap; max-width: 180px; overflow: hidden; text-overflow: ellipsis; }\n.acct .lbl { font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; line-height: 1; white-space: nowrap; }\n.acct:hover .lbl { color: var(--red-500); }\n.navrow { display: flex; align-items: center; justify-content: space-between; gap: 24px; height: 48px; padding: 0 96px; border-top: 1px solid var(--line); box-shadow: inset 0 -1px 0 var(--line); }\n.nav { display: flex; align-items: center; gap: 32px; height: 100%; min-width: 0; overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }\n.nav::-webkit-scrollbar { display: none; }\n.nav a { position: relative; display: inline-flex; align-items: center; height: 100%; flex: none; color: var(--ink); text-decoration: none;\n  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }\n.nav a:hover { color: var(--red-500); }\n.nav a.on::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--red-500); }\n.all { display: inline-flex; align-items: center; gap: 8px; flex: none; min-height: 44px; color: var(--ink); text-decoration: none;\n  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }\n.all span { border-bottom: 2px solid var(--red-500); padding-bottom: 3px; }\n.all:hover { color: var(--red-500); }\n.all .slot { width: 16px; height: 16px; pointer-events: none; }\n\n/* Tablet: linha de 72px, botão de menu, busca, ícones; nav rola */\n.l-tablet .row { height: 72px; padding: 0 40px; gap: 20px; }\n.l-tablet .row { gap: 12px; }\n.l-tablet .logo { width: 268px; height: 48px; margin-left: 0; }\n.l-tablet .search { height: 44px; max-width: none; }\n.l-tablet .actions { gap: 0; margin-right: -10px; }\n.l-tablet .row > .ibtn:first-child { margin-left: -10px; }\n.l-tablet .navrow { height: 44px; padding: 0 0 0 40px; }\n.l-tablet .nav { gap: 28px; padding-right: 40px; }\n\n/* Mobile: menu · logo central · ícones; busca em linha própria; nav rola */\n.l-mobile .row { display: grid; grid-template-columns: 44px 1fr 88px; height: 64px; padding: 0 8px; gap: 0; }\n.l-mobile .left { justify-self: start; }\n.l-mobile .logo { width: 240px; height: 42px; justify-self: center; margin-left: 0; }\n.l-mobile .actions { justify-self: end; gap: 0; }\n.l-mobile .srow { padding: 0 20px 12px; }\n.l-mobile .srow .search { height: 44px; max-width: none; }\n.l-mobile .navrow { height: 44px; padding: 0 0 0 20px; }\n.l-mobile .nav { gap: 24px; padding-right: 20px; }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    // Sem `layout` fixo, o header mede a própria largura (como a container query do design system)\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const w = detect(e[0].contentRect.width);\n        if (d2.state.auto !== w) d2.state.auto = w;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n    const fire = (name, payload) => { try { d2.emit(name, payload); } catch (err) { /* nenhum handler ligado */ } };\n\n    // Sessão da loja: mostra o último resultado guardado e confirma com o servidor\n    const sessSvc = d2.param('sessionService');\n    if (sessSvc) {\n      const cached = readSess();\n      if (cached) d2.state.sess = cached;\n      d2.rush(sessSvc).invoke({}).then((res) => {\n        const r = unwrap(res) || {};\n        const v = { logado: !!r.logado, nome: r.logado ? String(r.nome || '') : '' };\n        writeSess(v.logado ? v : null);\n        d2.state.sess = v;\n      }).catch(() => { /* mantém o que havia */ });\n    }\n\n    const logout = async () => {\n      d2.state.um = false;\n      fire('onLogout');\n      const auth = d2.param('authStrategy');\n      if (!auth) return;\n      try { await d2.auth(auth).logout(); } catch (e) { /* a sessão pode já ter expirado */ }\n      writeSess(null);\n      d2.state.sess = { logado: false, nome: '' };\n      window.location.assign('/');\n    };\n\n    d2.on('[data-act=\"menu\"]', 'click', () => fire('onMenu'));\n    d2.on('[data-act=\"account\"]', 'click', (e, el) => {\n      if (d2.state.um) d2.state.um = false;\n      fire('onAccount');\n      if (!d2.param('sessionService')) return;\n      e.preventDefault();\n      const logged = !!(d2.state.sess && d2.state.sess.logado);\n      window.location.assign(logged ? ((d2.params || {}).accountHref || '/perfil/meus-pedidos') : '/login');\n    });\n    d2.on('[data-act=\"umenu\"]', 'click', () => { d2.state.um = !d2.state.um; });\n    d2.on('[data-act=\"logout\"]', 'click', () => { logout(); });\n    d2.on('[data-act=\"cart\"]', 'click', () => { fire('onCart'); if (d2.param('sessionService')) window.location.assign('/sacola'); });\n    // Contagem real da sacola (localStorage `lf-sacola`), atualizada quando qualquer página mexe nela\n    const readCount = () => { try { const b = JSON.parse(localStorage.getItem('lf-sacola') || '[]'); return Array.isArray(b) ? b.reduce((s, x) => s + (Number(x && x.qty) || 0), 0) : 0; } catch (e) { return null; } };\n    const sync = () => { const n = readCount(); if (n != null && d2.state.bag !== n) d2.state.bag = n; };\n    sync();\n    const onBag = (e) => { const n = e && e.detail && e.detail.count != null ? Number(e.detail.count) : readCount(); if (n != null && d2.state.bag !== n) d2.state.bag = n; };\n    const onStorage = (e) => { if (!e || e.key === 'lf-sacola') sync(); };\n    window.addEventListener('lf-sacola', onBag); window.addEventListener('storage', onStorage);\n    d2.onUnmount(() => { window.removeEventListener('lf-sacola', onBag); window.removeEventListener('storage', onStorage); });\n    d2.on('.nav a', 'click', (e, el) => fire('onCategory', el.dataset.slug));\n    // Menu vem da tabela Categorias (ativas com destaque no menu); se a busca falhar, vale o parâmetro/padrão\n    const catSvc = d2.param('categoriesService');\n    if (!LOADED && catSvc) {\n      if (!LOADING) {\n        LOADING = d2.rush(catSvc).invoke({}).then((res) => {\n          const rows = unwrap(res) || [];\n          LOADED = (Array.isArray(rows) ? rows : []).filter((c) => c && c.ativa !== false && Number(c.posicao_menu) > 0).sort((a, b) => Number(a.posicao_menu) - Number(b.posicao_menu)).map((c) => ({ label: c.nome, href: LISTA + '?categoria=' + c.slug }));\n          if (!LOADED.length) LOADED = null;\n        }).catch(() => { LOADING = null; });\n      }\n      const pr = LOADING;\n      if (pr) pr.then(() => { d2.state.catsTick = Date.now(); });\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    void d2.state.catsTick;\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : (d2.state.auto || 'desktop');\n    const desk = layout === 'desktop';\n    const mob = layout === 'mobile';\n\n    const iconMod = refOf(d2.param('iconComponent'));\n    const logoMod = refOf(d2.param('logo'));\n    const searchMod = refOf(d2.param('searchBar'));\n    const buttonMod = refOf(d2.param('button'));\n\n    const ic = (name, key, size) => iconMod\n      ? d2.render(iconMod, { name, size: size || 24, iconColor: 'currentColor' },\n          { key: 'ic-' + key, width: size || 24, height: size || 24, class: 'slot' })\n      : '';\n\n    const logo = logoMod\n      ? `<div class=\"logo\">${d2.render(logoMod, { layout, href: p.homeHref || '/' }, { key: 'logo' })}</div>` : '';\n\n    const search = searchMod\n      ? `<div class=\"search\">${d2.render(searchMod,\n          { layout, button: buttonMod, searchIcon: iconMod, shortPlaceholder: layout === 'tablet' ? (p.tabletPlaceholder || 'Buscar produto') : undefined, busTopic: 'loja:search', syncTopic: 'loja:q', liveDelay: 300, showClear: true, value: urlQ() },\n          { key: 'search', onSearch: (q) => { try { d2.emit('onSearch', q); } catch (err) { /* sem handler */ } goSearch(q); } })}</div>`\n      : '';\n\n    // Quem está logado: com o serviço de sessão ligado vale só a sessão; senão, o `userName` fixo (previews)\n    const sessOn = !!d2.param('sessionService');\n    const sess = d2.state.sess;\n    const userName = sessOn ? (sess && sess.logado ? (sess.nome || 'cliente') : '') : (p.userName || '');\n\n    const rawCount = d2.state.bag != null ? d2.state.bag : p.cartCount;\n    const count = Number(rawCount) > 0 ? Math.min(Number(rawCount), 99) : 0;\n    const cart = `<button type=\"button\" class=\"ibtn\" data-act=\"cart\" aria-label=\"Sacola${count ? ', ' + count + (count === 1 ? ' item' : ' itens') : ''}\">${ic('bag', 'bag')}${count ? `<span class=\"count\">${count}</span>` : ''}</button>`;\n    const logged = !!userName;\n    const userIcon = logged\n      ? `<button type=\"button\" class=\"ibtn\" data-act=\"umenu\" aria-label=\"Minha conta e sair\" aria-expanded=\"${d2.state.um ? 'true' : 'false'}\" aria-controls=\"umenu\">${ic('user', 'user')}</button>`\n      : `<button type=\"button\" class=\"ibtn\" data-act=\"account\" aria-label=\"Entrar na sua conta\">${ic('user', 'user')}</button>`;\n    const menu = `<button type=\"button\" class=\"ibtn\" data-act=\"menu\" aria-label=\"Abrir menu\">${ic('menu', 'menu')}</button>`;\n\n    const hint = logged ? 'Olá, ' + userName : (p.accountHint || 'Entre ou cadastre-se');\n    const acct = `<button type=\"button\" class=\"acct\" data-act=\"account\">${ic('user', 'user-d')}<span class=\"txt\"><span class=\"hint\">${esc(hint)}</span><span class=\"lbl\">Minha conta</span></span></button>`;\n    const out = logged ? `<button type=\"button\" class=\"out\" data-act=\"logout\">${ic('logout', 'out', 18)}Sair</button>` : '';\n    const umenu = logged && !desk && d2.state.um\n      ? `<div class=\"umenu\" id=\"umenu\" role=\"menu\"><a role=\"menuitem\" href=\"${esc(p.accountHref || '/perfil/meus-pedidos')}\" data-act=\"account\">${ic('user', 'um-user', 18)}Minha conta</a><button type=\"button\" role=\"menuitem\" data-act=\"logout\">${ic('logout', 'um-out', 18)}Sair</button></div>`\n      : '';\n\n    const list = cats(p.categories);\n    const active = slug(p.activeCategory);\n    const allLabel = p.allProductsLabel || 'Todos os produtos';\n    const allHref = p.allProductsHref || LISTA;\n    const links = list.map((c) => {\n      const s = slug(c.label);\n      const on = s === active;\n      return `<a href=\"${esc(c.href)}\" data-key=\"${esc(s)}\" data-slug=\"${esc(s)}\" class=\"${on ? 'on' : ''}\"${on ? ' aria-current=\"page\"' : ''}>${esc(c.label)}</a>`;\n    }).join('');\n    const allOn = active === 'todos';\n    const allInNav = `<a href=\"${esc(allHref)}\" data-key=\"todos\" data-slug=\"todos\" class=\"${allOn ? 'on' : ''}\"${allOn ? ' aria-current=\"page\"' : ''}>${esc(allLabel)}</a>`;\n\n    const navrow = desk\n      ? `<div class=\"navrow\"><nav class=\"nav\" aria-label=\"Categorias\">${links}</nav><a class=\"all\" href=\"${esc(allHref)}\"><span>${esc(allLabel)}</span>${ic('arrow-right', 'arrow', 16)}</a></div>`\n      : `<div class=\"navrow\"><nav class=\"nav\" aria-label=\"Categorias\">${allInNav}${links}</nav></div>`;\n\n    let top;\n    if (desk) {\n      top = `<div class=\"row\">${logo}${search}<div class=\"actions\">${acct}${out}${cart}</div></div>`;\n    } else if (mob) {\n      top = `<div class=\"row\"><div class=\"left\">${menu}</div>${logo}<div class=\"actions\">${userIcon}${cart}</div></div><div class=\"srow\">${search}</div>`;\n    } else {\n      top = `<div class=\"row\">${menu}${logo}${search}<div class=\"actions\">${userIcon}${cart}</div></div>`;\n    }\n\n    return `<header class=\"hd l-${layout}\">${top}${navrow}${umenu}</header>`;\n  }\n};"}},"accountHint":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accountHint"}}}},"accountHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accountHref"}}}},"activeCategory":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"activeCategory"}}}},"allProductsHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"allProductsHref"}}}},"allProductsLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"allProductsLabel"}}}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cartCount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartCount"}}}},"categories":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"categories"}}}},"categoriesService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"categoriesService"}}}},"homeHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"homeHref"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"onAccount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAccount"}}}},"onCart":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCart"}}}},"onCategory":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCategory"}}}},"onLogout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLogout"}}}},"onMenu":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onMenu"}}}},"onSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSearch"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"sessionService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sessionService"}}}},"userName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"userName"}}}}}}}}}},{"_id":"6ab6c6edc37038941e4f0cad","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Hero","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Hero — Auto Super Shop\n// Carrossel de abertura da home. Não desenha botão nem ícones:\n// reaproveita Button (CTA) e Icon (setas e placeholder de foto) pelos parâmetros `button` e `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst DEFAULT_SLIDES = [\n  {\n    eyebrow: 'Loja oficial do canal',\n    title: 'Testado no canal.\\nAprovado na garagem.',\n    shortTitle: 'Testado no canal.\\nAprovado na garagem.',\n    text: 'Os produtos que a Auto Super usa nos vídeos, agora direto da garagem para a sua. Assiste primeiro. Compra depois.',\n    shortText: 'O que aparece nos vídeos, tá aqui na loja.',\n    ctaLabel: 'Ver mais vendidos', ctaShortLabel: 'Mais vendidos', ctaHref: '/produtos?ordem=mais-vendidos'\n  },\n  {\n    eyebrow: 'Performance BPower',\n    title: 'Peça boa.\\nEnviada pela BPower.',\n    shortTitle: 'Peça boa.\\nEnviada pela BPower.',\n    text: 'A linha de performance da parceira, com a garantia da loja oficial e parcelamento no cartão.',\n    shortText: 'Linha de performance com a garantia da loja oficial.',\n    ctaLabel: 'Conhecer a linha', ctaShortLabel: 'Conhecer', ctaHref: '/c/performance-bpower'\n  },\n  {\n    eyebrow: 'Limpeza automotiva',\n    title: 'O que não sai\\nda garagem da Auto Super.',\n    shortTitle: 'O que não sai\\nda garagem.',\n    text: 'Shampoo, pretinho e tudo o que deixa o carro no ponto de gravação.',\n    shortText: 'Tudo o que deixa o carro no ponto de gravação.',\n    ctaLabel: 'Ver limpeza', ctaShortLabel: 'Ver limpeza', ctaHref: '/c/limpeza-automotiva'\n  }\n];\nconst PHOTO = { desktop: '1440 × 580', tablet: '1536 × 1040', mobile: '780 × 1120' };\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst lines = (s) => esc(s).split(/\\n|\\\\n/).join('<br>');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst detect = (w) => (w >= 1200 ? 'desktop' : w >= 768 ? 'tablet' : 'mobile');\nconst pad = (n) => (n < 10 ? '0' + n : String(n));\n\nfunction slidesOf(v) {\n  let list = v;\n  if (typeof list === 'string') { try { list = JSON.parse(list); } catch (e) { list = null; } }\n  return Array.isArray(list) && list.length ? list : DEFAULT_SLIDES;\n}\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.hero { --red-500: #c8102e; --red-300: #f0707f; --ink: #0b0b0c; --white: #ffffff; --graphite-800: #2a2b2e; --graphite-900: #1c1d1f; --graphite-950: #121314;\n  --text-inv-2: #b9bbbe;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  position: relative; width: 100%; height: 580px; overflow: hidden; background: var(--ink); color: var(--white); touch-action: pan-y; }\n.slide { position: absolute; inset: 0; opacity: 0; visibility: hidden; transition: opacity .45s ease, visibility 0s linear .45s; }\n.slide.on { opacity: 1; visibility: visible; transition: opacity .45s ease; }\n\n/* Foto */\n.photo { position: absolute; top: 0; right: 0; bottom: 0; width: 56%; background: var(--graphite-900); overflow: hidden; }\n.photo::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 240px; z-index: 1; background: linear-gradient(90deg, var(--ink), rgba(11,11,12,0)); pointer-events: none; }\n.photo img { display: block; width: 100%; height: 100%; object-fit: cover; }\n.ph { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: #6b6e73;\n  background: repeating-linear-gradient(-18deg, var(--graphite-900) 0 22px, var(--graphite-800) 22px 24px); }\n.ph .slot { width: 40px; height: 40px; }\n.l-desktop .ph { padding-left: 32%; }\n.l-tablet .ph, .l-mobile .ph { justify-content: flex-start; padding-top: 64px; }\n.l-tablet .title .t-long, .l-tablet .title .t-short, .l-mobile .title .t-long, .l-mobile .title .t-short { white-space: normal; }\n.ph span { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }\n.checker { position: absolute; top: 0; right: 0; z-index: 2; width: 120px; height: 60px; pointer-events: none;\n  background: repeating-conic-gradient(var(--white) 0 25%, var(--ink) 0 50%) 0 0 / 60px 60px; }\n\n/* Texto */\n.copy { position: absolute; top: 0; bottom: 0; left: 0; width: 62%; padding: 0 48px 0 96px; display: flex; flex-direction: column; justify-content: center; z-index: 1; }\n.title .t-long, .title .t-short { white-space: nowrap; }\n.eyebrow { display: flex; align-items: center; gap: 12px; margin: 0 0 20px; color: var(--red-300); font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; }\n.eyebrow b { font-weight: 800; letter-spacing: .1em; }\n.eyebrow i { display: block; width: 24px; height: 2px; background: var(--red-500); transform: skewX(-30deg); }\n.title { margin: 0; font-size: 64px; line-height: .98; font-weight: 800; font-style: italic; font-stretch: 118%; letter-spacing: -.005em; text-transform: uppercase; }\n.lead { margin: 20px 0 0; max-width: 460px; font-size: 17px; line-height: 1.5; color: var(--text-inv-2); }\n.cta { margin-top: 32px; height: 56px; width: auto; align-self: flex-start; }\n.t-short, .x-short { display: none; }\n\n/* Controles */\n.ctrl { position: absolute; right: 96px; bottom: 40px; display: flex; align-items: center; gap: 24px; z-index: 2; }\n.dots { display: flex; align-items: center; }\n.dot { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; margin: 0; border: 0; background: transparent; cursor: pointer; }\n.dot span { display: block; width: 24px; height: 3px; background: rgba(255,255,255,.4); transition: width .2s ease, background-color .2s ease; }\n.dot.on span { width: 36px; background: var(--red-500); }\n.dot:hover span { background: var(--white); }\n.dot.on:hover span { background: var(--red-500); }\n.arrows { display: flex; gap: 8px; }\n.arr { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; margin: 0; background: transparent;\n  border: 1.5px solid rgba(255,255,255,.4); border-radius: 2px; color: var(--white); cursor: pointer; }\n.arr:hover { background: var(--white); color: var(--ink); border-color: var(--white); }\n.arr .slot { width: 20px; height: 20px; pointer-events: none; }\n.dot:focus-visible, .arr:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }\n\n/* Tablet: foto atrás, texto embaixo, sem setas */\n.l-tablet { height: 520px; }\n.l-tablet .photo { width: 100%; left: 0; }\n.l-tablet .photo::before { display: none; }\n.l-tablet .photo::after { content: ''; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(11,11,12,.92) 0%, rgba(11,11,12,.6) 42%, rgba(11,11,12,0) 75%); }\n.l-tablet .copy { top: auto; width: 100%; padding: 0 40px 88px; justify-content: flex-end; }\n.l-tablet .title { font-size: 48px; font-stretch: 104%; }\n.l-tablet .lead { font-size: 16px; margin-top: 16px; max-width: 520px; }\n.l-tablet .eyebrow { margin-bottom: 16px; }\n.l-tablet .cta { margin-top: 24px; height: 52px; }\n.l-tablet .ctrl { left: 30px; right: auto; bottom: 20px; }\n.l-tablet .arrows { display: none; }\n\n/* Mobile: foto atrás, texto embaixo, CTA com largura total */\n.l-mobile { height: 560px; }\n.l-mobile .photo { width: 100%; left: 0; }\n.l-mobile .photo::before { display: none; }\n.l-mobile .photo::after { content: ''; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(11,11,12,.95) 0%, rgba(11,11,12,.7) 45%, rgba(11,11,12,0) 78%); }\n.l-mobile .checker { width: 96px; height: 48px; background-size: 48px 48px; }\n.l-mobile .copy { top: auto; width: 100%; padding: 0 20px 76px; justify-content: flex-end; }\n.l-mobile .eyebrow { margin-bottom: 14px; font-size: 11px; letter-spacing: .24em; gap: 10px; }\n.l-mobile .title { font-size: 34px; line-height: 1; font-stretch: 112%; }\n.l-mobile .lead { font-size: 15px; margin-top: 12px; }\n.l-mobile .cta { margin-top: 20px; height: 52px; width: 100%; align-self: stretch; }\n.l-mobile .t-long, .l-mobile .x-long { display: none; }\n.l-mobile .t-short, .l-mobile .x-short { display: inline; }\n.l-mobile .ctrl { left: 50%; right: auto; transform: translateX(-50%); bottom: 16px; }\n.l-mobile .arrows { display: none; }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const w = detect(e[0].contentRect.width);\n        if (d2.state.auto !== w) d2.state.auto = w;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n    const total = () => slidesOf(d2.params && d2.params.slides).length;\n    const go = (i) => {\n      const n = total();\n      const next = ((i % n) + n) % n;\n      if (d2.state.i !== next) {\n        d2.state.i = next;\n        try { d2.emit('onSlideChange', next); } catch (err) { /* sem handler */ }\n      }\n    };\n    const cur = () => d2.state.i || 0;\n    d2.on('.arr.prev', 'click', () => { go(cur() - 1); restart(); });\n    d2.on('.arr.next', 'click', () => { go(cur() + 1); restart(); });\n    d2.on('.dot', 'click', (e, el) => { go(Number(el.dataset.i)); restart(); });\n    d2.on('.hero', 'keydown', (e) => {\n      if (e.key === 'ArrowLeft') { go(cur() - 1); restart(); }\n      if (e.key === 'ArrowRight') { go(cur() + 1); restart(); }\n    });\n\n    // Arrastar para o lado troca o slide (celular e tablet)\n    let x0 = null;\n    d2.on('.hero', 'pointerdown', (e) => { x0 = e.clientX; });\n    d2.on('.hero', 'pointerup', (e) => {\n      if (x0 == null) return;\n      const dx = e.clientX - x0; x0 = null;\n      if (Math.abs(dx) > 48) { go(cur() + (dx < 0 ? 1 : -1)); restart(); }\n    });\n\n    // Troca automática opcional (autoplay em segundos; 0 desliga). Pausa ao passar o mouse.\n    let timer = null;\n    const stop = () => { if (timer) { clearInterval(timer); timer = null; } };\n    const start = () => {\n      stop();\n      const s = Number(d2.params && d2.params.autoplay) || 0;\n      const reduce = typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n      if (s > 0 && !reduce && total() > 1) timer = setInterval(() => go(cur() + 1), s * 1000);\n    };\n    function restart() { start(); }\n    d2.on('.hero', 'mouseover', stop);\n    d2.on('.hero', 'mouseout', (e) => { const h = d2.$('.hero'); if (!h || !e.relatedTarget || !h.contains(e.relatedTarget)) start(); });\n    start();\n    d2.onUnmount(stop);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : (d2.state.auto || 'desktop');\n    const slides = slidesOf(p.slides);\n    const n = slides.length;\n    const active = Math.min(d2.state.i || 0, n - 1);\n    const iconRef = refOf(d2.param('iconComponent'));\n    const buttonRef = refOf(d2.param('button'));\n    const mob = layout === 'mobile';\n\n    const ic = (name, key, size, color) => iconRef\n      ? d2.render(iconRef, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, class: 'slot' })\n      : '';\n\n    const html = slides.map((s, i) => {\n      const img = (layout === 'mobile' && s.imageMobile) || (layout !== 'desktop' && s.imageTablet) || s.image || '';\n      const photo = img\n        ? `<img src=\"${esc(img)}\" alt=\"${esc(s.imageAlt || '')}\"${i === 0 ? '' : ' loading=\"lazy\"'}>`\n        : `<div class=\"ph\">${ic('image', 'ph-' + i, 40, '#6b6e73')}<span>Foto ${PHOTO[layout]}</span></div>`;\n      const cta = buttonRef && s.ctaLabel\n        ? `<div class=\"cta\">${d2.render(buttonRef, {\n            label: mob && s.ctaShortLabel ? s.ctaShortLabel : s.ctaLabel,\n            variant: 'brand', size: 'hero', fullWidth: 'mobile', arrow: true, layout, href: s.ctaHref || ''\n          }, { key: 'cta-' + i, onClick: () => { try { d2.emit('onCta', { index: i, href: s.ctaHref || '' }); } catch (err) { /* sem handler */ } } })}</div>`\n        : '';\n      const shortT = s.shortTitle || s.title;\n      const shortX = s.shortText || s.text;\n      return `<div class=\"slide${i === active ? ' on' : ''}\" data-key=\"s${i}\" role=\"group\" aria-roledescription=\"slide\" aria-label=\"${i + 1} de ${n}\"${i === active ? '' : ' aria-hidden=\"true\"'}>\n        <div class=\"photo\">${photo}<div class=\"checker\" aria-hidden=\"true\"></div></div>\n        <div class=\"copy\">\n          ${s.eyebrow ? `<p class=\"eyebrow\"><i></i>${esc(s.eyebrow)}</p>` : ''}\n          <h${i === 0 ? 1 : 2} class=\"title\"><span class=\"t-long\">${lines(s.title)}</span><span class=\"t-short\">${lines(shortT)}</span></h${i === 0 ? 1 : 2}>\n          ${s.text ? `<p class=\"lead\"><span class=\"x-long\">${esc(s.text)}</span><span class=\"x-short\">${esc(shortX)}</span></p>` : ''}\n          ${cta}\n        </div>\n      </div>`;\n    }).join('');\n\n    const dots = n > 1 ? `<div class=\"dots\">${slides.map((s, i) =>\n      `<button type=\"button\" class=\"dot${i === active ? ' on' : ''}\" data-i=\"${i}\" data-key=\"d${i}\" aria-label=\"Ir para o slide ${i + 1}\"${i === active ? ' aria-current=\"true\"' : ''}><span></span></button>`).join('')}</div>` : '';\n    const arrows = n > 1 && layout === 'desktop'\n      ? `<div class=\"arrows\"><button type=\"button\" class=\"arr prev\" aria-label=\"Slide anterior\">${ic('chevron-left', 'al', 20)}</button><button type=\"button\" class=\"arr next\" aria-label=\"Próximo slide\">${ic('chevron-right', 'ar', 20)}</button></div>`\n      : '';\n\n    return `<section class=\"hero l-${layout}\" aria-roledescription=\"carrossel\" aria-label=\"Destaques da loja\" tabindex=\"-1\">${html}${n > 1 ? `<div class=\"ctrl\">${dots}${arrows}</div>` : ''}</section>`;\n  }\n};"}},"autoplay":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoplay"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"onSlideChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSlideChange"}}}},"slides":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"slides"}}}}}}}}}},{"_id":"6ab6c9e2df879c35ebaf3043","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CategoryCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CategoryCard — Loja Lucas Fontana\n// Card de categoria da home: fundo escuro, numeral vermelho, nome largo e a seta num quadrado vermelho.\n// Não desenha ícones: reaproveita o Icon da loja pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst detect = (w) => (w >= 280 ? 'desktop' : w >= 200 ? 'tablet' : 'mobile');\nconst numeral = (v) => {\n  if (v == null || v === '') return '';\n  const n = Number(v);\n  return Number.isFinite(n) ? (n < 10 ? '0' + n : String(n)) : String(v);\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.card { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --graphite-800: #2a2b2e; --graphite-900: #1c1d1f; --text-inv-2: #b9bbbe;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  position: relative; display: flex; flex-direction: column; justify-content: space-between; width: 100%; height: 100%; min-height: 160px;\n  padding: 28px; overflow: hidden; border-radius: 0; background: var(--graphite-900); color: var(--white); text-decoration: none; cursor: pointer; }\n.card.t-ink { background: var(--ink); }\n.card:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n\n.bg { position: absolute; inset: 0; z-index: 0; }\n.bg img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; }\n.bg::after { content: ''; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(11,11,12,.92) 0%, rgba(11,11,12,.45) 55%, rgba(11,11,12,.25) 100%); }\n.card:hover .bg img { transform: scale(1.04); }\n\n.num { position: relative; z-index: 1; margin: 0; font-size: 44px; line-height: 1; font-weight: 800; font-style: italic; font-stretch: 125%; color: var(--red-500); }\n.foot { position: relative; z-index: 1; display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }\n.words { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n.name { margin: 0; font-size: 22px; line-height: 1.05; font-weight: 800; font-stretch: 108%; text-transform: uppercase; letter-spacing: 0; overflow-wrap: normal; word-break: normal; hyphens: none; }\n.cap { margin: 0; font-size: 14px; line-height: 1.35; color: var(--text-inv-2); }\n.go { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; background: var(--red-500); color: var(--white);\n  transition: background-color .15s ease, color .15s ease; }\n.go .slot { width: 20px; height: 20px; pointer-events: none; }\n.card:hover .go { background: var(--white); color: var(--ink); }\n.card:hover .name { color: var(--white); }\n\n/* Tablet */\n.l-tablet { padding: 24px; }\n.l-tablet .num { font-size: 36px; font-stretch: 120%; }\n.l-tablet .name { font-size: 20px; font-stretch: 108%; }\n.l-tablet .cap { font-size: 13px; }\n\n/* Mobile: seta some, tudo menor */\n.l-mobile { padding: 16px; min-height: 140px; }\n.l-mobile .num { font-size: 26px; font-stretch: 115%; }\n.l-mobile .words { gap: 4px; }\n.l-mobile .name { font-size: 16px; font-stretch: 104%; }\n.l-mobile .cap { font-size: 12px; }\n.l-mobile .go { display: none; }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    // Sem `layout` fixo, o card segue a largura da própria coluna da grade\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const w = detect(e[0].contentRect.width);\n        if (d2.state.auto !== w) d2.state.auto = w;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n    d2.on('.card', 'click', (e, el) => {\n      if (el.tagName !== 'A') {\n        try { d2.emit('onSelect', d2.params && d2.params.slug); } catch (err) { /* sem handler */ }\n      }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : (d2.state.auto || 'desktop');\n    const tone = p.tone === 'ink' ? 'ink' : 'graphite';\n    const label = p.label || 'Limpeza automotiva';\n    const cap = layout === 'mobile' && p.shortCaption ? p.shortCaption : (p.caption == null ? '24 produtos' : p.caption);\n    const num = numeral(p.number == null ? 1 : p.number);\n    const iconRef = refOf(d2.param('iconComponent'));\n    const arrow = iconRef\n      ? `<span class=\"go\" aria-hidden=\"true\">${d2.render(iconRef, { name: 'arrow-right', size: 20, iconColor: 'currentColor' }, { key: 'arrow', width: 20, height: 20, class: 'slot' })}</span>`\n      : '';\n    const bg = p.image ? `<div class=\"bg\"><img src=\"${esc(p.image)}\" alt=\"\" loading=\"lazy\"></div>` : '';\n    const inner = `${bg}${num ? `<p class=\"num\" aria-hidden=\"true\">${esc(num)}</p>` : '<span></span>'}\n      <div class=\"foot\"><div class=\"words\"><p class=\"name\">${esc(label)}</p>${cap ? `<p class=\"cap\">${esc(cap)}</p>` : ''}</div>${arrow}</div>`;\n    const cls = `card t-${tone} l-${layout}`;\n    return p.href\n      ? `<a class=\"${cls}\" href=\"${esc(p.href)}\" aria-label=\"${esc(label)}${cap ? ', ' + esc(cap) : ''}\">${inner}</a>`\n      : `<button type=\"button\" class=\"${cls}\" style=\"border:0;text-align:left;font:inherit\" aria-label=\"${esc(label)}${cap ? ', ' + esc(cap) : ''}\">${inner}</button>`;\n  }\n};"}},"caption":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"caption"}}}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onSelect":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSelect"}}}},"shortCaption":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortCaption"}}}},"slug":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"slug"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6ab6ca7fff43b91b61b9b044","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Price","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Price — Loja Lucas Fontana\n// Bloco de preço: preço cheio grande, valor no Pix e parcelamento no cartão.\n// Dois tamanhos: `card` (ProductCard) e `buybox` (página de produto).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst SIZES = ['card', 'buybox'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst num = (v) => { const n = typeof v === 'string' ? Number(v.replace(/\\./g, '').replace(',', '.')) : Number(v); return Number.isFinite(n) ? n : 0; };\nconst brl = (n) => 'R$\\u00a0' + n.toFixed(2).replace('.', ',').replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.pr { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --grey-700: #45474b;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 4px; color: var(--ink); }\np { margin: 0; }\n.old { font-size: 13px; color: var(--grey-500); text-decoration: line-through; line-height: 1.2; }\n.main { font-size: 22px; line-height: 1.1; font-weight: 800; font-stretch: 110%; letter-spacing: -.005em; white-space: nowrap; }\n.sub { font-size: 13px; line-height: 1.4; color: var(--grey-700); }\n.sub b { font-weight: 700; color: var(--ink); }\n.sub .br { display: none; }\n.muted .main, .muted .sub b { color: var(--grey-500); }\n\n/* card: tablet e mobile */\n.s-card.l-tablet .main { font-size: 20px; }\n.s-card.l-mobile .main { font-size: 17px; font-stretch: 106%; }\n.s-card.l-mobile .sub { font-size: 12px; line-height: 1.35; }\n.s-card.l-mobile .dot { display: none; }\n.s-card.l-mobile .sub .br { display: block; }\n\n/* buybox */\n.s-buybox { gap: 6px; }\n.s-buybox .old { font-size: 15px; }\n.s-buybox .main { font-size: 36px; }\n.s-buybox .pix { font-size: 17px; font-weight: 700; color: var(--ink); line-height: 1.3; margin-top: 4px; }\n.s-buybox .pix span { color: var(--red-500); }\n.s-buybox .inst { font-size: 14px; color: var(--grey-700); line-height: 1.4; }\n.s-buybox.l-tablet .main { font-size: 32px; }\n.s-buybox.l-mobile .main { font-size: 28px; font-stretch: 106%; }\n.s-buybox.l-mobile .pix { font-size: 16px; }\n.s-buybox.l-mobile .inst { font-size: 13px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const size = SIZES.indexOf(p.size) >= 0 ? p.size : 'card';\n    const price = num(p.price == null ? 89.9 : p.price);\n    const disc = p.pixDiscount == null || p.pixDiscount === '' ? 10 : num(p.pixDiscount);\n    const pix = p.pixPrice != null && p.pixPrice !== '' ? num(p.pixPrice) : Math.round(price * (100 - disc)) / 100;\n    const n = Math.max(1, Math.round(num(p.installments == null ? 3 : p.installments)));\n    const old = num(p.oldPrice);\n    const showPix = pix > 0 && pix < price;\n    const cls = `pr s-${size} l-${layout}${truthy(p.muted) ? ' muted' : ''}`;\n    const oldHtml = old > price ? `<p class=\"old\"><span class=\"sr\" style=\"position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)\">De </span>${brl(old)}</p>` : '';\n\n    if (size === 'buybox') {\n      return `<div class=\"${cls}\">${oldHtml}\n        <p class=\"main\">${brl(price)}</p>\n        ${showPix ? `<p class=\"pix\"><span>${brl(pix)}</span> no Pix</p>` : ''}\n        ${n > 1 ? `<p class=\"inst\">ou em até ${n}x no cartão (juros, se houver, no pagamento)</p>` : ''}\n      </div>`;\n    }\n    const card = n > 1 ? `ou em até ${n}x no cartão` : 'no cartão';\n    return `<div class=\"${cls}\">${oldHtml}\n      <p class=\"main\">${brl(price)}</p>\n      <p class=\"sub\">${showPix ? `<b>${brl(pix)} no Pix</b><span class=\"dot\"> · </span><span class=\"br\"></span>` : ''}${esc(card)}</p>\n    </div>`;\n  }\n};"}},"installments":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"installments"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"muted":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"muted"}}}},"oldPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"oldPrice"}}}},"pixDiscount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixDiscount"}}}},"pixPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixPrice"}}}},"price":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"price"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}}}}}}}},{"_id":"6ab6caa02283ab0cf1c3c187","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ProductCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ProductCard — Loja Lucas Fontana\n// Card de produto: foto quadrada, selos, nome, preço e o botão de compra.\n// Não desenha preço, botão, selos nem ícones: reaproveita Price, Button, Badge e Icon\n// pelos parâmetros `priceComponent`, `button`, `badge` e `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst detect = (w) => (w >= 290 ? 'desktop' : w >= 200 ? 'tablet' : 'mobile');\nconst fire = (d2, name, payload) => { try { d2.emit(name, payload); } catch (err) { /* sem handler */ } };\n\n// ---- \"Adicionar à sacola\": grava na sacola do navegador (mesma chave da página Sacola) e avisa a pessoa ----\nconst bagAdd = (sku, qty) => {\n  try {\n    const raw = JSON.parse(localStorage.getItem('lf-sacola') || '[]');\n    const l = Array.isArray(raw) ? raw : [];\n    const f = l.find((y) => y && y.sku === sku);\n    if (f) f.qty = (Number(f.qty) || 0) + qty; else l.push({ sku, qty });\n    localStorage.setItem('lf-sacola', JSON.stringify(l));\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: l.reduce((s, y) => s + (Number(y.qty) || 0), 0) } }));\n    return true;\n  } catch (e) { return false; }\n};\nlet toastTimer = null;\nconst toast = (msg, ok) => {\n  try {\n    let el = document.getElementById('lf-toast');\n    if (!el) { el = document.createElement('div'); el.id = 'lf-toast'; el.setAttribute('role', 'status'); el.setAttribute('aria-live', 'polite'); document.body.appendChild(el); }\n    const mobile = window.innerWidth < 640;\n    el.style.cssText = `position:fixed;z-index:2147483000;${mobile ? 'left:16px;right:16px;bottom:16px;' : 'top:24px;right:24px;max-width:420px;'}background:#0b0b0c;color:#fff;padding:16px 20px;font-family:Archivo,system-ui,sans-serif;font-size:14px;line-height:1.4;display:flex;gap:14px;align-items:center;box-shadow:0 8px 24px rgba(0,0,0,.25);border-left:4px solid ${ok ? '#2e9e5b' : '#c8102e'}`;\n    el.innerHTML = `<span style=\"flex:1\">${esc(msg)}</span>${ok ? '<a href=\"/sacola\" style=\"color:#fff;font-weight:700;text-decoration:underline;white-space:nowrap\">Ver sacola</a>' : ''}`;\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(() => { el.style.display = 'none'; }, 4000);\n  } catch (e) { /* sem DOM */ }\n};\nconst addCard = (d2) => {\n  const p = d2.params || {};\n  const sku = String(p.sku || '');\n  if (!sku || d2.state.add === 'done') return;\n  if (bagAdd(sku, 1)) {\n    d2.state.add = 'done';\n    toast(`${String(p.name || 'Produto')} foi adicionado à sacola.`, true);\n    setTimeout(() => { d2.state.add = 'idle'; }, 2500);\n  } else toast('Não deu pra adicionar agora. Tenta de novo.', false);\n};\n\n// ---- \"Me avise quando chegar\" (mesmo fluxo do BuyBox; o slug vem do href /produto/:slug) ----\nconst MAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst NET_ERR = 'Não deu pra salvar agora. Confere sua conexão e tenta de novo.';\nconst MAIL_ERR = 'Confere o e-mail: falta o @ ou o domínio.';\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res) || {};\nconst slugOf = (d2) => { const m = String(((d2.params || {}).href) || '').match(/\\/produto\\/([^\\/?#]+)/); return m ? m[1] : ''; };\n// Avisos já pedidos neste navegador (slug → data), por 30 dias; sai quando o produto volta a ter estoque\nconst LS_KEY = 'lf-avisos';\nconst LS_TTL = 30 * 24 * 3600 * 1000;\nconst readAvisos = () => { try { const o = JSON.parse(localStorage.getItem(LS_KEY) || '{}'); return o && typeof o === 'object' && !Array.isArray(o) ? o : {}; } catch (e) { return {}; } };\nconst hasAviso = (slug) => { if (!slug) return false; const t = Number(readAvisos()[slug]); return !!t && (Date.now() - t) < LS_TTL; };\nconst saveAviso = (slug) => { try { const o = readAvisos(); o[slug] = Date.now(); localStorage.setItem(LS_KEY, JSON.stringify(o)); } catch (e) { /* sem storage */ } };\nconst dropAviso = (slug) => { try { const o = readAvisos(); if (o[slug]) { delete o[slug]; localStorage.setItem(LS_KEY, JSON.stringify(o)); } } catch (e) { /* sem storage */ } };\nconst sendLead = async (d2, email, fromForm, website) => {\n  const slug = slugOf(d2);\n  d2.state.ns = fromForm ? 'formSending' : 'sending';\n  d2.state.nerr = '';\n  let out = {};\n  try {\n    out = unwrap(await d2.rush('leadAvisoSalvar').invoke({ slug, email: email || '', website: website || '' }));\n  } catch (err) { out = { ok: false, error: NET_ERR }; }\n  if (out && out.ok) { saveAviso(slug); d2.state.ns = 'done'; return; }\n  d2.state.nerr = (out && out.error) || NET_ERR;\n  d2.state.ns = fromForm ? 'form' : 'idle';\n};\nconst startNotify = async (d2) => {\n  if ((d2.state.ns || 'idle') !== 'idle') return;\n  d2.state.ns = 'checking';\n  d2.state.nerr = '';\n  let s = {};\n  try { s = unwrap(await d2.rush('minhaSessaoContato').invoke({})); } catch (err) { s = {}; }\n  if (s && s.logado) await sendLead(d2, '', false, '');\n  else d2.state.ns = 'form';\n};\nconst submitNotify = async (d2) => {\n  if ((d2.state.ns || '') !== 'form') return;\n  const inp = d2.$ ? d2.$('.nf input[name=\"email\"]') : null;\n  const email = String(inp ? inp.value : '').trim();\n  if (!MAIL_RE.test(email)) { d2.state.nerr = MAIL_ERR; return; }\n  const hp = d2.$ ? d2.$('.nf .hp') : null;\n  await sendLead(d2, email, true, hp ? hp.value : '');\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.pc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-100: #ececea; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; width: 100%; height: 100%; background: var(--white); color: var(--ink); border-radius: 0; }\n.pc.outlined { border: 1px solid var(--line); transition: border-color .15s ease; }\n.pc.outlined:hover { border-color: var(--ink); }\n\n.media { position: relative; display: block; width: 100%; flex: 1 1 0; min-height: 120px; background: var(--grey-100); overflow: hidden; }\n.media img { display: block; width: 100%; height: 100%; object-fit: cover; transition: transform .4s ease; }\n.pc:hover .media img { transform: scale(1.03); }\n.media:focus-visible, .name a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.ph { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #a7a9ad; }\n.ph .slot { width: 40px; height: 40px; }\n.oos .media img, .oos .ph { opacity: .45; }\n.tags { position: absolute; top: 12px; left: 12px; right: 12px; display: flex; flex-wrap: wrap; gap: 6px; pointer-events: none; }\n.tag { height: 24px; }\n.soon { position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 12px; background: var(--ink); color: var(--white);\n  font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; text-align: center; }\n\n.body { display: flex; flex-direction: column; flex: none; padding: 16px 0 0; }\n.outlined .body { padding: 16px; }\n.brand { margin: 0 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); }\n.name { margin: 0; font-size: 15px; line-height: 1.35; font-weight: 600; min-height: calc(1.35em * 2);\n  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }\n.name a { color: inherit; text-decoration: none; }\n.name a:hover { color: var(--red-500); }\n.price { margin-top: 12px; }\n.act { margin-top: auto; padding-top: 16px; height: 60px; flex: none; }\n\n/* Tablet */\n.l-tablet .name { font-size: 15px; }\n\n/* Mobile */\n.l-mobile .tags { top: 8px; left: 8px; right: 8px; gap: 4px; }\n.l-mobile .tag { height: 20px; }\n.l-mobile .soon { padding: 8px; font-size: 10px; letter-spacing: .1em; }\n.l-mobile .body { padding-top: 12px; }\n.l-mobile.outlined .body { padding: 12px; }\n.l-mobile .brand { font-size: 10px; margin-bottom: 4px; }\n.l-mobile .name { font-size: 13px; }\n.l-mobile .price { margin-top: 8px; }\n.l-mobile .act { padding-top: 12px; height: 56px; }\n\n/* Me avise quando chegar: campo de e-mail dentro do card */\n.pc .act.open { height: auto; }\n.abtn { display: block; height: 44px; }\n.nf { position: relative; display: flex; flex-direction: column; gap: 8px; width: 100%; }\n.nf input[name=\"email\"] { width: 100%; height: 44px; padding: 0 12px; border: 1px solid #c9c9c6; border-radius: 0; background: var(--white); color: var(--ink); font: inherit; font-size: 14px; }\n.nf input[name=\"email\"]:focus { outline: none; border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }\n.nerr { margin: 0; font-size: 12px; line-height: 1.35; color: var(--red-500); }\n.act.open > .abtn { margin-top: 8px; }\n.nf .hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    // Sem `layout` fixo, o card segue a largura da própria coluna da grade\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const w = detect(e[0].contentRect.width);\n        if (d2.state.auto !== w) d2.state.auto = w;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n    d2.on('.media, .name a', 'click', () => fire(d2, 'onOpen', d2.params && d2.params.sku));\n    // Produto com estoque: some o aviso guardado. Esgotado: clique delegado (o Button por callback não disparou no app publicado)\n    if (!truthy((d2.params || {}).outOfStock)) { const s0 = slugOf(d2); if (s0) dropAviso(s0); }\n    d2.on('[data-act]', 'click', (e, el) => {\n      const a = el.getAttribute('data-act');\n      if (a === 'add') { addCard(d2); }\n      else if (a === 'notify') { fire(d2, 'onNotify', d2.params && d2.params.sku); startNotify(d2); }\n      else if (a === 'notify-submit') submitNotify(d2);\n    });\n    d2.on('.nf input[name=\"email\"]', 'keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); submitNotify(d2); } });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : (d2.state.auto || 'desktop');\n    const mob = layout === 'mobile';\n    const oos = truthy(p.outOfStock);\n    const outlined = p.variant === 'outlined';\n    const name = p.name || 'Shampoo automotivo neutro';\n    const href = p.href || '#';\n    const sku = p.sku || '';\n\n    const priceRef = refOf(d2.param('priceComponent'));\n    const buttonRef = refOf(d2.param('button'));\n    const badgeRef = refOf(d2.param('badge'));\n    const iconRef = refOf(d2.param('iconComponent'));\n\n    const photo = p.image\n      ? `<img src=\"${esc(p.image)}\" alt=\"${esc(p.imageAlt || name)}\" loading=\"lazy\">`\n      : `<div class=\"ph\">${iconRef ? d2.render(iconRef, { name: 'image', size: 40, iconColor: '#a7a9ad' }, { key: 'ph', width: 40, height: 40, class: 'slot' }) : ''}</div>`;\n\n    // Selos: parceiro (preto) + selo livre (vermelho). Sem estoque, o selo livre sai.\n    const tags = [];\n    if (p.partner) tags.push({ k: 'partner', label: p.partner, tone: 'ink' });\n    if (p.tag && !oos) tags.push({ k: 'tag', label: p.tag, tone: 'red' });\n    const tagHtml = badgeRef && tags.length\n      ? `<div class=\"tags\">${tags.map((t) => `<div class=\"tag\">${d2.render(badgeRef, { label: t.label, tone: t.tone, layout }, { key: 'b-' + t.k, width: 'auto', height: mob ? 20 : 24 })}</div>`).join('')}</div>`\n      : '';\n\n    const price = priceRef\n      ? `<div class=\"price\">${d2.render(priceRef, {\n          price: p.price, pixPrice: p.pixPrice, pixDiscount: p.pixDiscount, installments: p.installments, oldPrice: p.oldPrice,\n          size: 'card', layout, muted: oos\n        }, { key: 'price', height: 'auto' })}</div>`\n      : '';\n\n    let btn = '';\n    if (buttonRef && !oos) {\n      const added = d2.state.add === 'done';\n      btn = `<div class=\"act\"><span class=\"abtn\" data-act=\"add\">${d2.render(buttonRef,\n        { label: added ? '✓ Adicionado à sacola' : (p.addLabel || 'Adicionar à sacola'), shortLabel: added ? '✓ Adicionado' : 'Adicionar', variant: added ? 'outline' : 'primary', size: 'sm', fullWidth: 'always', layout, isDisabled: added },\n        { key: added ? 'added' : 'add', height: 44 })}</span></div>`;\n    } else if (buttonRef) {\n      const slug = slugOf(d2);\n      const ns = d2.state.ns || (hasAviso(slug) ? 'done' : 'idle');\n      const nerr = d2.state.nerr ? String(d2.state.nerr) : '';\n      const err = nerr ? `<p class=\"nerr\" role=\"alert\">${esc(nerr)}</p>` : '';\n      const aBtn = (key, label, short, variant, name, disabled) => `<span class=\"abtn\" data-act=\"${name}\">${d2.render(buttonRef,\n        { label, shortLabel: short, variant, size: 'sm', fullWidth: 'always', layout, isDisabled: !!disabled }, { key, height: 44 })}</span>`;\n      if (ns === 'form' || ns === 'formSending') {\n        const sending = ns === 'formSending';\n        btn = `<div class=\"act open\"><div class=\"nf\"><input type=\"email\" name=\"email\" placeholder=\"seuemail@exemplo.com\" autocomplete=\"email\" aria-label=\"Seu e-mail\"${sending ? ' disabled' : ''}>${err}<input class=\"hp\" type=\"text\" name=\"website\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\">${aBtn('nsub-' + ns, sending ? 'Enviando…' : 'Avisar-me', sending ? 'Enviando…' : 'Avisar-me', 'primary', 'notify-submit', sending)}</div></div>`;\n      } else if (ns === 'done') {\n        btn = `<div class=\"act\">${aBtn('ndone', 'Aviso combinado', 'Combinado', 'outline', 'none', true)}</div>`;\n      } else {\n        const busy = ns === 'checking' || ns === 'sending';\n        btn = `<div class=\"act${nerr ? ' open' : ''}\">${aBtn('notify-' + ns, busy ? 'Enviando…' : (p.notifyLabel || 'Me avise quando chegar'), busy ? 'Enviando…' : 'Avise-me', 'outline', 'notify', busy)}${err}</div>`;\n      }\n    }\n\n    return `<article class=\"pc l-${layout}${outlined ? ' outlined' : ''}${oos ? ' oos' : ''}\">\n      <a class=\"media\" href=\"${esc(href)}\" tabindex=\"-1\" aria-hidden=\"true\">${photo}${tagHtml}${oos ? `<span class=\"soon\">${esc(p.soonLabel || 'Volta já já')}</span>` : ''}</a>\n      <div class=\"body\">\n        ${p.brand ? `<p class=\"brand\">${esc(p.brand)}</p>` : ''}\n        <h3 class=\"name\"><a href=\"${esc(href)}\">${esc(name)}</a></h3>\n        ${price}\n        ${btn}\n      </div>\n    </article>`;\n  }\n};"}},"addLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addLabel"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"brand":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"brand"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"imageAlt":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imageAlt"}}}},"installments":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"installments"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"notifyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyLabel"}}}},"oldPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"oldPrice"}}}},"onAdd":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAdd"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onOpen"}}}},"outOfStock":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"outOfStock"}}}},"partner":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"partner"}}}},"pixDiscount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixDiscount"}}}},"pixPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixPrice"}}}},"price":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"price"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"sku":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sku"}}}},"soonLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soonLabel"}}}},"tag":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tag"}}}},"variant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"variant"}}}}}}}}}},{"_id":"6ab6cd8b97b00074cbf00e09","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FeaturePanel","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FeaturePanel — Loja Lucas Fontana\n// Painel escuro de destaque (ex.: parceria BPower): eyebrow numerado, título largo, texto,\n// lista de vantagens, CTA e foto com o xadrez no canto inferior direito.\n// Não desenha botão nem ícones: reaproveita Button e Icon pelos parâmetros `button` e `iconComponent`.\n// O texto de apoio chega por `body` (o nome `text` é reservado do bloco base do Data2).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst DEFAULT_POINTS = ['Enviado direto pela BPower', 'Garantia da loja oficial', 'Até 6x sem juros no cartão'];\nconst PHOTO = { desktop: '624 × 520', tablet: '688 × 387', mobile: '350 × 262' };\n\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst lines = (s) => esc(s).split(/\\n|\\\\n/).join('<br>');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst detect = (w) => (w >= 1000 ? 'desktop' : w >= 600 ? 'tablet' : 'mobile');\nconst pad = (v) => { const n = Number(v); return Number.isFinite(n) && v !== '' && v != null ? (n < 10 ? '0' + n : String(n)) : String(v || ''); };\nfunction listOf(v) {\n  let l = v;\n  if (typeof l === 'string') l = l.split('\\n').map((s) => s.trim()).filter(Boolean);\n  return Array.isArray(l) ? l : DEFAULT_POINTS;\n}\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.fp { --red-500: #c8102e; --red-400: #e23a52; --ink: #0b0b0c; --white: #ffffff; --graphite-800: #2a2b2e; --graphite-900: #1c1d1f; --graphite-950: #121314;\n  --text-inv-2: #b9bbbe;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: minmax(0, 1fr); width: 100%; height: 100%; min-height: 520px; background: var(--graphite-900); color: var(--white); border-radius: 0; overflow: hidden; }\n.fp.t-ink { background: var(--ink); }\n.fp.rev .photo { order: -1; }\np, h2, ul { margin: 0; }\n\n.copy { display: flex; flex-direction: column; justify-content: center; padding: 48px 56px; min-width: 0; }\n.eyebrow { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; color: var(--red-400); font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; }\n.eyebrow b { font-weight: 800; letter-spacing: .1em; }\n.eyebrow i { display: block; width: 24px; height: 2px; background: var(--red-500); transform: skewX(-30deg); }\n.title { font-size: 44px; line-height: .98; font-weight: 800; font-style: italic; font-stretch: 118%; text-transform: uppercase; letter-spacing: -.005em; }\n.t-short { display: none; }\n.text { margin-top: 18px; max-width: 480px; font-size: 16px; line-height: 1.5; color: var(--text-inv-2); }\n.points { list-style: none; padding: 0; margin-top: 20px; display: flex; flex-direction: column; gap: 10px; }\n.points li { display: flex; align-items: center; gap: 12px; font-size: 15px; font-weight: 600; line-height: 1.3; }\n.points .slot { flex: none; width: 20px; height: 20px; }\n.cta { margin-top: 28px; height: 52px; align-self: flex-start; }\n\n.photo { position: relative; min-height: 0; height: 100%; background: var(--graphite-800); overflow: hidden; }\n.photo img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }\n.ph { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px; color: #6b6e73;\n  background: repeating-linear-gradient(-18deg, var(--graphite-800) 0 22px, #34353a 22px 24px); }\n.ph .slot { width: 40px; height: 40px; }\n.ph span { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }\n.checker { position: absolute; right: 0; bottom: 0; width: 120px; height: 60px; pointer-events: none;\n  background: repeating-conic-gradient(var(--white) 0 25%, var(--ink) 0 50%) 0 0 / 60px 60px; }\n\n/* Tablet: foto em cima, texto embaixo */\n.l-tablet { grid-template-columns: 1fr; grid-template-rows: auto 1fr; min-height: 0; }\n.l-tablet .photo { order: -1; aspect-ratio: 16 / 9; height: auto; }\n.l-tablet .copy { padding: 40px; justify-content: flex-start; }\n.l-tablet .title { font-size: 40px; font-stretch: 112%; }\n.l-tablet .text { max-width: 560px; }\n.l-tablet .cta { margin-top: 28px; }\n\n/* Mobile: empilhado, CTA com largura total */\n.l-mobile { grid-template-columns: 1fr; grid-template-rows: auto 1fr; min-height: 0; }\n.l-mobile .photo { order: -1; aspect-ratio: 4 / 3; height: auto; }\n.l-mobile .checker { width: 96px; height: 48px; background-size: 48px 48px; }\n.l-mobile .copy { padding: 28px 20px 32px; justify-content: flex-start; }\n.l-mobile .eyebrow { margin-bottom: 14px; font-size: 11px; letter-spacing: .24em; gap: 10px; }\n.l-mobile .title { font-size: 30px; line-height: 1; font-stretch: 110%; }\n.l-mobile .t-long { display: none; }\n.l-mobile .t-short { display: inline; }\n.l-mobile .text { margin-top: 14px; font-size: 15px; }\n.l-mobile .points { margin-top: 20px; gap: 10px; }\n.l-mobile .points li { font-size: 14px; }\n.l-mobile .cta { margin-top: 24px; width: 100%; align-self: stretch; }\n`,\n\n  mounted(d2, root) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver((e) => {\n        const w = detect(e[0].contentRect.width);\n        if (d2.state.auto !== w) d2.state.auto = w;\n      });\n      ro.observe(root);\n      d2.onUnmount(() => ro.disconnect());\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : (d2.state.auto || 'desktop');\n    const mob = layout === 'mobile';\n    const tone = p.tone === 'ink' ? 'ink' : 'graphite';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const buttonRef = refOf(d2.param('button'));\n\n    const eyebrow = p.eyebrow == null ? 'Parceria oficial' : p.eyebrow;\n    const num = p.number == null ? '02' : pad(p.number);\n    const title = p.title || 'Peça de pista.\\nNa sua garagem.';\n    const shortTitle = p.shortTitle || title;\n    const body = p.body == null ? 'A linha de performance da BPower Motorsport. Produtos enviados diretamente pela BPower, com a garantia da loja oficial.' : p.body;\n    const points = listOf(p.points);\n    const ctaLabel = p.ctaLabel == null ? 'Conhecer a linha BPower' : p.ctaLabel;\n\n    const ic = (name, key, size, color) => iconRef\n      ? d2.render(iconRef, { name, size, iconColor: color }, { key, width: size, height: size, class: 'slot' })\n      : '';\n\n    const imgSrc = (layout === 'mobile' && p.imageMobile) || (layout !== 'desktop' && p.imageTablet) || p.image || '';\n    const photo = imgSrc\n      ? `<img src=\"${esc(imgSrc)}\" alt=\"${esc(p.imageAlt || '')}\" loading=\"lazy\">`\n      : `<div class=\"ph\">${ic('image', 'ph', 40, '#6b6e73')}<span>Foto ${PHOTO[layout]}</span></div>`;\n\n    const cta = buttonRef && ctaLabel\n      ? `<div class=\"cta\">${d2.render(buttonRef, {\n          label: mob && p.ctaShortLabel ? p.ctaShortLabel : ctaLabel,\n          variant: p.ctaVariant === 'light' ? 'light' : 'brand', size: 'lg', fullWidth: 'mobile', arrow: true, layout, href: p.ctaHref || ''\n        }, { key: 'cta', onClick: () => { try { d2.emit('onCta', p.ctaHref || ''); } catch (err) { /* sem handler */ } } })}</div>`\n      : '';\n\n    return `<section class=\"fp l-${layout} t-${tone}${truthy(p.reverse) ? ' rev' : ''}\">\n      <div class=\"copy\">\n        ${eyebrow ? `<p class=\"eyebrow\"><i></i>${esc(eyebrow)}</p>` : ''}\n        <h2 class=\"title\"><span class=\"t-long\">${lines(title)}</span><span class=\"t-short\">${lines(shortTitle)}</span></h2>\n        ${body ? `<p class=\"text\">${esc(body)}</p>` : ''}\n        ${points.length ? `<ul class=\"points\">${points.map((t, i) => `<li data-key=\"pt${i}\">${ic('check', 'ck' + i, 20, '#e23a52')}<span>${esc(t)}</span></li>`).join('')}</ul>` : ''}\n        ${cta}\n      </div>\n      <div class=\"photo\">${photo}<div class=\"checker\" aria-hidden=\"true\"></div></div>\n    </section>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"ctaHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaHref"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"ctaShortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaShortLabel"}}}},"ctaVariant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaVariant"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"imageAlt":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imageAlt"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"points":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"points"}}}},"reverse":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"reverse"}}}},"shortTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shortTitle"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6ab6ce10cf6e94d42983237c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"InfoCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// InfoCard — Loja Lucas Fontana\n// Card de informação (frete, troca, atendimento…): ícone, título, texto e CTA opcional.\n// Claro (cinza) ou escuro (preto, com o xadrez no canto). Um card só, tudo por parâmetro.\n// Não desenha botão, link nem ícones: reaproveita Button, TextLink e Icon pelos parâmetros `button`, `link` e `iconComponent`.\n// `ctaKind` escolhe o CTA: \"button\" (padrão, botão com contorno/branco) ou \"link\" (TextLink sublinhado, como na página Sobre).\n// O texto de apoio chega por `body` (o nome `text` é reservado do bloco base do Data2).\n// Rotas renomeadas: links antigos gravados nas páginas passam a apontar para os novos endereços\nconst HREF_FIX = { '/sobre': '/sobre-a-auto-super', '/sobre-o-lucas': '/sobre-a-auto-super', '/fale-com-o-lucas': '/fale-com-a-auto-super', '/contato-comercial': '/fale-com-a-auto-super' };\nconst fixHref = (h) => HREF_FIX[String(h || '')] || h;\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.ic { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-700: #45474b; --text-inv-2: #b9bbbe;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  position: relative; display: flex; flex-direction: column; width: 100%; height: 100%; padding: 36px; overflow: hidden;\n  background: var(--grey-50); color: var(--ink); border-radius: 0; }\n.ic.dark { background: var(--ink); color: var(--white); }\np, h3 { margin: 0; }\n.glyph { width: 32px; height: 32px; flex: none; margin-bottom: 28px; }\n.title { font-size: 24px; line-height: 1.08; font-weight: 800; font-stretch: 115%; text-transform: uppercase; letter-spacing: .005em; }\n.text { margin-top: 12px; font-size: 15px; line-height: 1.55; color: var(--grey-700); max-width: 440px; }\n.dark .text { color: var(--text-inv-2); }\n.cta { margin-top: auto; padding-top: 28px; height: 72px; align-self: flex-start; }\n.cta.as-link { padding-top: 24px; height: 68px; }\n.checker { position: absolute; top: 0; right: 0; width: 60px; height: 60px; pointer-events: none;\n  background: repeating-conic-gradient(var(--white) 0 25%, var(--ink) 0 50%) 0 0 / 60px 60px; }\n.dark .checker { background: repeating-conic-gradient(var(--white) 0 25%, #2a2b2e 0 50%) 0 0 / 60px 60px; }\n\n/* Tablet */\n.l-tablet { padding: 32px; }\n.l-tablet .glyph { margin-bottom: 24px; }\n.l-tablet .title { font-size: 22px; font-stretch: 112%; }\n\n/* Card largo do tablet: ícone e texto à esquerda, CTA à direita */\n.wide.l-tablet { flex-direction: row; align-items: center; gap: 28px; }\n.wide.l-tablet .glyph { margin: 0; }\n.wide.l-tablet .words { flex: 1; min-width: 0; }\n.wide.l-tablet .cta { margin: 0; padding: 0; height: 48px; flex: none; align-self: center; }\n.wide.l-tablet .cta.as-link { height: 44px; }\n.wide.l-tablet .checker { width: 48px; height: 48px; background-size: 48px 48px; }\n\n/* Mobile: botão com largura total; o link continua do tamanho do texto */\n.l-mobile { padding: 28px 24px; }\n.l-mobile .glyph { width: 28px; height: 28px; margin-bottom: 20px; }\n.l-mobile .title { font-size: 20px; font-stretch: 108%; }\n.l-mobile .text { font-size: 14px; margin-top: 10px; }\n.l-mobile .cta { width: 100%; align-self: stretch; padding-top: 24px; height: 68px; }\n.l-mobile .cta.as-link { width: auto; align-self: flex-start; padding-top: 20px; height: 64px; }\n.l-mobile .checker { width: 48px; height: 48px; background-size: 48px 48px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const mob = layout === 'mobile';\n    const dark = p.tone === 'dark';\n    const wide = truthy(p.wide);\n    const iconRef = refOf(d2.param('iconComponent'));\n    const buttonRef = refOf(d2.param('button'));\n    const linkRef = refOf(d2.param('link'));\n    const asLink = p.ctaKind === 'link' && !!linkRef;\n\n    const glyphName = p.glyph || 'truck';\n    const title = p.title || 'Frete para todo o Brasil';\n    const body = p.body == null ? 'A gente envia para qualquer CEP. Você vê o prazo e o valor antes de fechar o pedido.' : p.body;\n    const ctaLabel = p.ctaLabel || '';\n    const label = mob && p.ctaShortLabel ? p.ctaShortLabel : ctaLabel;\n    const size = mob ? 28 : 32;\n    const ctaHref = fixHref(p.ctaHref || '');\n    const fire = () => { try { d2.emit('onCta', ctaHref); } catch (err) { /* sem handler */ } };\n\n    const glyph = iconRef\n      ? `<div class=\"glyph\">${d2.render(iconRef, { name: glyphName, size, iconColor: 'currentColor' }, { key: 'glyph', width: size, height: size })}</div>`\n      : '';\n\n    let cta = '';\n    if (ctaLabel && asLink) {\n      cta = `<div class=\"cta as-link\">${d2.render(linkRef, {\n        label, href: ctaHref, tone: dark ? 'light' : 'dark', layout\n      }, { key: 'cta-link', onClick: fire })}</div>`;\n    } else if (ctaLabel && buttonRef) {\n      cta = `<div class=\"cta\">${d2.render(buttonRef, {\n        label, variant: dark ? 'light' : 'outline', size: 'sm', fullWidth: 'mobile', arrow: true, layout, href: ctaHref\n      }, { key: 'cta', onClick: fire })}</div>`;\n    }\n\n    return `<article class=\"ic l-${layout}${dark ? ' dark' : ''}${wide ? ' wide' : ''}\">\n      ${dark ? '<div class=\"checker\" aria-hidden=\"true\"></div>' : ''}\n      ${glyph}\n      <div class=\"words\"><h3 class=\"title\">${esc(title)}</h3>${body ? `<p class=\"text\">${esc(body)}</p>` : ''}</div>\n      ${cta}\n    </article>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"ctaHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaHref"}}}},"ctaKind":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaKind"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"ctaShortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaShortLabel"}}}},"glyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"glyph"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"link":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"link"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}},"wide":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"wide"}}}}}}}}}},{"_id":"6ab6d0c42086c2e4341913c7","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"SectionHeader","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// SectionHeader — Loja Lucas Fontana\n// Cabeçalho das seções: eyebrow com traço vermelho (\"— Categorias\") ou kicker vermelho (\"— Por que comprar aqui\"),\n// título largo em caixa alta e um link opcional à direita (\"Ver tudo\"). No mobile o link some (a seção mostra um botão embaixo).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst pad = (v) => { const n = Number(v); return v !== '' && v != null && Number.isFinite(n) ? (n < 10 ? '0' + n : String(n)) : String(v || ''); };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.sh { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; align-items: flex-end; justify-content: space-between; gap: 32px; width: 100%; color: var(--ink); }\np, h2 { margin: 0; }\n.words { min-width: 0; }\n.eyebrow { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; color: var(--grey-500); }\n.eyebrow b { color: var(--red-500); font-weight: 800; letter-spacing: .1em; }\n.eyebrow i { font-style: normal; color: var(--grey-500); letter-spacing: 0; }\n.eyebrow .dash { display: block; width: 20px; height: 2px; background: var(--red-500); }\n.kicker { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { font-size: 44px; line-height: 1; font-weight: 800; font-stretch: 115%; letter-spacing: -.01em; text-transform: uppercase; }\n.link { flex: none; display: inline-flex; align-items: center; min-height: 44px; color: var(--ink); text-decoration: none;\n  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; white-space: nowrap; }\n.link span { border-bottom: 2px solid var(--red-500); padding-bottom: 3px; }\n.link:hover { color: var(--red-500); }\n.link:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.l-tablet .title { font-size: 36px; }\n.l-mobile .eyebrow { font-size: 11px; letter-spacing: .26em; margin-bottom: 12px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; margin-bottom: 12px; }\n.l-mobile .title { font-size: 28px; line-height: 1.02; font-stretch: 112%; }\n.l-mobile .link { display: none; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('.link', 'click', () => { try { d2.emit('onAction', d2.params && d2.params.actionHref); } catch (err) { /* sem handler */ } });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const num = '';\n    const eyebrow = p.eyebrow == null ? 'Categorias' : p.eyebrow;\n    const title = p.title || 'Por onde a gente começa?';\n    const lead = num ? `<b>${esc(num)}</b><i>—</i>` : '<span class=\"dash\"></span>';\n    const top = !eyebrow ? ''\n      : truthy(p.kicker) ? `<p class=\"kicker\">${esc(eyebrow)}</p>`\n      : `<p class=\"eyebrow\">${lead}${esc(eyebrow)}</p>`;\n    return `<div class=\"sh l-${layout}\">\n      <div class=\"words\">\n        ${top}\n        <h2 class=\"title\">${esc(title)}</h2>\n      </div>\n      ${p.actionLabel ? `<a class=\"link\" href=\"${esc(p.actionHref || '#')}\"><span>${esc(p.actionLabel)}</span></a>` : ''}\n    </div>`;\n  }\n};"}},"actionHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"actionHref"}}}},"actionLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"actionLabel"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"kicker":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kicker"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onAction":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAction"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6ab6e5dee92b0a717903157e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"docCreateOrder","package":"babi-lf","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    .doc { box-sizing:border-box; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif; background:#f5f5f3; color:#0f172a; min-height:100%; height:100%; overflow-y:auto; padding:48px 24px; }\n    .doc *{ box-sizing:border-box; }\n    .wrap{ max-width:920px; margin:0 auto; }\n    .h1{ font-size:34px; font-weight:800; letter-spacing:-0.02em; margin:0 0 8px; }\n    .sub{ font-size:16px; color:#475569; line-height:1.55; margin:0 0 14px; }\n    .badge{ display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:700; color:#b45309; background:#fef3c7; border:1px solid #fde68a; padding:5px 12px; border-radius:999px; text-transform:uppercase; letter-spacing:.05em; }\n    .card{ background:#fff; border:1px solid #e7e7e4; border-radius:16px; padding:22px 26px; margin-top:20px; box-shadow:0 1px 2px rgba(15,23,42,.04); }\n    .card h2{ font-size:19px; font-weight:700; margin:0 0 12px; }\n    .card p{ font-size:14px; line-height:1.6; color:#475569; margin:0 0 4px; }\n    .flow{ font-size:14px; font-weight:600; color:#0f172a; line-height:1.6; margin:0 0 12px; }\n    .chip{ display:inline-block; background:#eef2ff; color:#4338ca; border-radius:6px; padding:1px 7px; font-weight:600; font-family:\"Roboto Mono\",ui-monospace,monospace; font-size:12.5px; }\n    ul{ margin:8px 0 0; padding-left:20px; }\n    li{ font-size:14px; line-height:1.6; color:#475569; margin:4px 0; }\n    code.k{ font-family:\"Roboto Mono\",ui-monospace,monospace; background:#f1f5f9; padding:1px 6px; border-radius:5px; font-size:13px; color:#0f172a; }\n    pre{ background:#0f172a; color:#e2e8f0; border-radius:10px; padding:18px 20px; overflow-x:auto; font-family:\"Roboto Mono\",ui-monospace,SFMono-Regular,monospace; font-size:12.5px; line-height:1.6; margin:12px 0 0; white-space:pre; }\n    table{ width:100%; border-collapse:collapse; margin-top:6px; }\n    th,td{ text-align:left; padding:8px 10px; font-size:13px; border-bottom:1px solid #eef0f2; vertical-align:top; }\n    th{ color:#64748b; font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }\n    td .kk{ font-family:\"Roboto Mono\",ui-monospace,monospace; color:#0f172a; font-weight:600; }\n    .req{ color:#dc2626; font-size:10px; font-weight:700; margin-left:4px; }\n    .warn{ color:#b45309; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; font-size:13px; font-weight:600; margin-top:12px; }\n    .muted{ color:#64748b; font-size:13px; line-height:1.6; margin-top:12px; }\n  `,\n  render() {\n    return `\n      <div class=\"doc\"><div class=\"wrap\">\n        <div class=\"h1\">Criar Order — Mercado Pago</div>\n        <p class=\"sub\">Guia de integração do endpoint interno de criação de pedidos (Checkout Pro Orders). Use como referência para chamar essa criação de qualquer lugar do app.</p>\n        <span class=\"badge\">● Ambiente de TESTE (sandbox)</span>\n\n        <div class=\"card\">\n          <h2>Arquitetura</h2>\n          <p class=\"flow\">Cliente &rarr; Rush Listener (<span class=\"chip\">createOrderHandler</span>) &rarr; HTTP Request (<span class=\"chip\">createOrder</span>) &rarr; API Mercado Pago (POST /v1/orders)</p>\n          <p>Componentes no workspace <b>Mercado Pago</b>:</p>\n          <ul>\n            <li><b>Key Vault</b> <code class=\"k\">mercado-pago-test</code> — guarda <code class=\"k\">ACCESS_TOKEN</code> (token da API) e <code class=\"k\">CreateOrderRush</code> (chave de acesso ao endpoint).</li>\n            <li><b>HTTP Client</b> <code class=\"k\">mercadoPago</code> — base <code class=\"k\">https://api.mercadopago.com</code></li>\n            <li><b>HTTP Request</b> <code class=\"k\">createOrder</code> — POST /v1/orders (dinâmico: recebe data + headers).</li>\n            <li><b>Rush Listener</b> <code class=\"k\">createOrderHandler</code> — ponto de entrada: valida a chave, gera a idempotência, monta o body e chama a API.</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Endpoint</h2>\n          <table>\n            <tr><td class=\"kk\">Método</td><td>POST</td></tr>\n            <tr><td class=\"kk\">URL</td><td>https://babi-lf.data2apis.com/rush/createOrderHandler</td></tr>\n            <tr><td class=\"kk\">Content-Type</td><td>application/json</td></tr>\n            <tr><td class=\"kk\">Authorization</td><td>&lt;chave CreateOrderRush&gt;</td></tr>\n          </table>\n        </div>\n\n        <div class=\"card\">\n          <h2>Autenticação</h2>\n          <p>O handler valida a chave <b>antes</b> de criar qualquer order:</p>\n          <ul>\n            <li>Lê o valor de <code class=\"k\">CreateOrderRush</code> no Key Vault (resolvido em runtime dentro do handler).</li>\n            <li>Compara com o header <code class=\"k\">Authorization</code> enviado na chamada.</li>\n            <li>Se não bater (ou faltar) &rarr; responde <code class=\"k\">{ \"error\": \"Unauthorized\" }</code> e <b>não cria order</b>.</li>\n          </ul>\n          <p class=\"muted\">Aceita o header nos dois formatos: <code class=\"k\">Authorization: &lt;chave&gt;</code> ou <code class=\"k\">Authorization: Bearer &lt;chave&gt;</code>. Nunca coloque a chave no front-end público — ela deve vir do backend/vault.</p>\n        </div>\n\n        <div class=\"card\">\n          <h2>Corpo da requisição (body)</h2>\n          <table>\n            <tr><th>Campo</th><th>Tipo</th><th>Descrição</th></tr>\n            <tr><td class=\"kk\">total_amount<span class=\"req\">OBRIGATÓRIO</span></td><td>string</td><td>Valor total do pedido. Ex: \"1000.00\"</td></tr>\n            <tr><td class=\"kk\">external_reference</td><td>string</td><td>Seu identificador do pedido.</td></tr>\n            <tr><td class=\"kk\">payer_email</td><td>string</td><td>E-mail do comprador.</td></tr>\n            <tr><td class=\"kk\">items[]<span class=\"req\">OBRIGATÓRIO</span></td><td>array</td><td>Cada item: title (string), unit_price (string), quantity (número).</td></tr>\n          </table>\n          <p class=\"muted\">Definidos automaticamente pelo handler (não enviar): <code class=\"k\">type=\"online\"</code>, <code class=\"k\">processing_mode=\"manual\"</code>, <code class=\"k\">X-Idempotency-Key</code> (único por chamada) e o Authorization do token da API.</p>\n          <div class=\"warn\">⚠️ NÃO envie <code class=\"k\">unit_measure</code> nem <code class=\"k\">total_amount</code> DENTRO de items — a API rejeita (erro unsupported_properties).</div>\n        </div>\n\n        <div class=\"card\">\n          <h2>Exemplo de chamada</h2>\n          <pre>curl -X POST 'https://babi-lf.data2apis.com/rush/createOrderHandler' \\\\\n  -H 'Content-Type: application/json' \\\\\n  -H 'Authorization: SUA_CHAVE_CreateOrderRush' \\\\\n  -d '{\n    \"total_amount\": \"1000.00\",\n    \"external_reference\": \"order_pro_123\",\n    \"payer_email\": \"buyer@email.com\",\n    \"items\": [\n      { \"title\": \"Meu produto\", \"unit_price\": \"1000.00\", \"quantity\": 1 }\n    ]\n  }'</pre>\n        </div>\n\n        <div class=\"card\">\n          <h2>Resposta de sucesso</h2>\n          <p>Retorna <code class=\"k\">{ \"data\": { \"idempotency_key\": \"...\", \"status\": 201, \"order\": { ... } } }</code>:</p>\n          <table>\n            <tr><th>Campo</th><th>Descrição</th></tr>\n            <tr><td class=\"kk\">idempotency_key</td><td>Chave única gerada pelo handler para esta criação.</td></tr>\n            <tr><td class=\"kk\">status</td><td>Status HTTP devolvido pelo Mercado Pago. <b>201</b> = order criada; 4xx/5xx = erro (os detalhes vêm em <code class=\"k\">order</code>).</td></tr>\n            <tr><td class=\"kk\">order</td><td>A order completa retornada pelo Mercado Pago (ou o corpo do erro).</td></tr>\n          </table>\n          <p style=\"margin-top:12px\">Campos úteis em <code class=\"k\">order</code>:</p>\n          <pre>{\n  \"id\": \"ORDTST01M3D5S9EW932A8S9MNXGYW775\",\n  \"status\": \"created\",\n  \"total_amount\": \"1000.00\",\n  \"checkout_url\": \"https://www.mercadopago.com.br/checkout/v1/redirect?order_id=...\"\n}</pre>\n          <p class=\"muted\">Redirecione o comprador para o <code class=\"k\">checkout_url</code> para concluir o pagamento. Guarde o <code class=\"k\">order.id</code> para consultar o status depois (endpoint <code class=\"k\">getOrderHandler</code>).</p>\n          <div class=\"warn\">⚠️ Mudança de formato: antes a order vinha em <code class=\"k\">order.data</code>, junto com dados internos da chamada. Agora vem direto em <code class=\"k\">order</code> (ex.: <code class=\"k\">order.id</code>, <code class=\"k\">order.checkout_url</code>). Ajuste quem lia <code class=\"k\">order.data.*</code>.</div>\n        </div>\n\n        <div class=\"card\">\n          <h2>Erros comuns</h2>\n          <ul>\n            <li><code class=\"k\">Unauthorized</code> — chave ausente/errada no header Authorization. Nenhuma order é criada.</li>\n            <li><code class=\"k\">unsupported_properties</code> — campos não suportados no body (ex.: unit_measure / total_amount dentro de items).</li>\n            <li><code class=\"k\">idempotency_key_already_used</code> — não deve ocorrer: o handler gera uma chave única por chamada.</li>\n            <li><code class=\"k\">status: 401</code> — problema com o ACCESS_TOKEN do vault (credencial inválida ou vencida).</li>\n            <li>Sempre confira o campo <code class=\"k\">status</code>: se não for 201, a order não foi criada e <code class=\"k\">order</code> traz o erro do Mercado Pago.</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Como reutilizar em outros lugares</h2>\n          <p>De um botão no app (onClick) ou de outro Rush handler, chame o listener diretamente:</p>\n          <pre>@createOrderHandler.invoke({\n  body: { total_amount, external_reference, payer_email, items },\n  headers: { Authorization: &lt;chave CreateOrderRush&gt; }\n})</pre>\n          <p class=\"muted\">Endpoints relacionados (mesma chave): <code class=\"k\">getOrderHandler</code> (consultar uma order pelo ID) e <code class=\"k\">searchOrdersHandler</code> (buscar orders por período e filtros).</p>\n          <p class=\"muted\">De um serviço externo (backend/curl): use a URL do endpoint com o header Authorization. Regra de ouro: a chave <code class=\"k\">CreateOrderRush</code> vem do vault/backend, nunca hardcoded no front público.</p>\n        </div>\n\n      </div></div>\n    `\n  }\n}"}}}}}}}},{"_id":"6ab6ee94449a23c1c1376d4f","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"docGetOrder","package":"babi-lf","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    .doc { box-sizing:border-box; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif; background:#f5f5f3; color:#0f172a; min-height:100%; height:100%; overflow-y:auto; padding:48px 24px; }\n    .doc *{ box-sizing:border-box; }\n    .wrap{ max-width:920px; margin:0 auto; }\n    .h1{ font-size:34px; font-weight:800; letter-spacing:-0.02em; margin:0 0 8px; }\n    .sub{ font-size:16px; color:#475569; line-height:1.55; margin:0 0 14px; }\n    .badge{ display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:700; color:#b45309; background:#fef3c7; border:1px solid #fde68a; padding:5px 12px; border-radius:999px; text-transform:uppercase; letter-spacing:.05em; }\n    .card{ background:#fff; border:1px solid #e7e7e4; border-radius:16px; padding:22px 26px; margin-top:20px; box-shadow:0 1px 2px rgba(15,23,42,.04); }\n    .card h2{ font-size:19px; font-weight:700; margin:0 0 12px; }\n    .card p{ font-size:14px; line-height:1.6; color:#475569; margin:0 0 4px; }\n    .flow{ font-size:14px; font-weight:600; color:#0f172a; line-height:1.6; margin:0 0 12px; }\n    .chip{ display:inline-block; background:#eef2ff; color:#4338ca; border-radius:6px; padding:1px 7px; font-weight:600; font-family:\"Roboto Mono\",ui-monospace,monospace; font-size:12.5px; }\n    ul{ margin:8px 0 0; padding-left:20px; }\n    li{ font-size:14px; line-height:1.6; color:#475569; margin:4px 0; }\n    code.k{ font-family:\"Roboto Mono\",ui-monospace,monospace; background:#f1f5f9; padding:1px 6px; border-radius:5px; font-size:13px; color:#0f172a; }\n    pre{ background:#0f172a; color:#e2e8f0; border-radius:10px; padding:18px 20px; overflow-x:auto; font-family:\"Roboto Mono\",ui-monospace,SFMono-Regular,monospace; font-size:12.5px; line-height:1.6; margin:12px 0 0; white-space:pre; }\n    table{ width:100%; border-collapse:collapse; margin-top:6px; }\n    th,td{ text-align:left; padding:8px 10px; font-size:13px; border-bottom:1px solid #eef0f2; vertical-align:top; }\n    th{ color:#64748b; font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }\n    td .kk, td.kk{ font-family:\"Roboto Mono\",ui-monospace,monospace; color:#0f172a; font-weight:600; }\n    .req{ color:#dc2626; font-size:10px; font-weight:700; margin-left:4px; }\n    .warn{ color:#b45309; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; font-size:13px; font-weight:600; margin-top:12px; }\n    .muted{ color:#64748b; font-size:13px; line-height:1.6; margin-top:12px; }\n  `,\n  render() {\n    return `\n      <div class=\"doc\"><div class=\"wrap\">\n        <div class=\"h1\">Consultar Order — Mercado Pago</div>\n        <p class=\"sub\">Guia de integração do endpoint interno de consulta de pedidos (Checkout Pro Orders). Use para buscar todos os dados e o status atual de uma order a partir do ID retornado na criação.</p>\n        <span class=\"badge\">● Ambiente de TESTE (sandbox)</span>\n\n        <div class=\"card\">\n          <h2>Arquitetura</h2>\n          <p class=\"flow\">Cliente &rarr; Rush Listener (<span class=\"chip\">getOrderHandler</span>) &rarr; HTTP Request (<span class=\"chip\">getOrder</span>) &rarr; API Mercado Pago (GET /v1/orders/{order_id})</p>\n          <p>Componentes no workspace <b>Mercado Pago</b>:</p>\n          <ul>\n            <li><b>Key Vault</b> <code class=\"k\">mercado-pago-test</code> — guarda <code class=\"k\">ACCESS_TOKEN</code> (token da API) e <code class=\"k\">CreateOrderRush</code> (chave de acesso ao endpoint, a mesma do Criar Order).</li>\n            <li><b>HTTP Client</b> <code class=\"k\">mercadoPago</code> — base <code class=\"k\">https://api.mercadopago.com</code></li>\n            <li><b>HTTP Request</b> <code class=\"k\">getOrder</code> — GET /v1/orders/{order_id} (dinâmico: recebe url + headers).</li>\n            <li><b>Rush Listener</b> <code class=\"k\">getOrderHandler</code> — ponto de entrada: valida a chave, valida o order_id, injeta o token e chama a API.</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Endpoint</h2>\n          <table>\n            <tr><td class=\"kk\">Método</td><td>GET (também aceita POST com o order_id no body)</td></tr>\n            <tr><td class=\"kk\">URL</td><td>https://babi-lf.data2apis.com/rush/getOrderHandler?order_id=&lt;ID&gt;</td></tr>\n            <tr><td class=\"kk\">Authorization</td><td>&lt;chave CreateOrderRush&gt;</td></tr>\n          </table>\n        </div>\n\n        <div class=\"card\">\n          <h2>Autenticação</h2>\n          <p>O handler valida a chave <b>antes</b> de consultar qualquer order:</p>\n          <ul>\n            <li>Lê o valor de <code class=\"k\">CreateOrderRush</code> no Key Vault (resolvido em runtime dentro do handler).</li>\n            <li>Compara com o header <code class=\"k\">Authorization</code> enviado na chamada.</li>\n            <li>Se não bater (ou faltar) &rarr; responde <code class=\"k\">{ \"error\": \"Unauthorized\" }</code> e <b>não chama o Mercado Pago</b>.</li>\n          </ul>\n          <p class=\"muted\">Aceita o header nos dois formatos: <code class=\"k\">Authorization: &lt;chave&gt;</code> ou <code class=\"k\">Authorization: Bearer &lt;chave&gt;</code>. Nunca coloque a chave no front-end público — ela deve vir do backend/vault. A resposta traz dados do comprador e do pagamento, por isso o endpoint é protegido.</p>\n        </div>\n\n        <div class=\"card\">\n          <h2>Parâmetros</h2>\n          <table>\n            <tr><th>Campo</th><th>Onde</th><th>Descrição</th></tr>\n            <tr><td class=\"kk\">order_id<span class=\"req\">OBRIGATÓRIO</span></td><td>query (ou body)</td><td>ID da order retornado no <code class=\"k\">order.id</code> da criação. Ex: \"ORDTST01M3D7PXZ6XEYQFCMY7HY9VSA4\"</td></tr>\n          </table>\n          <p class=\"muted\">Definido automaticamente pelo handler (não enviar): o Authorization com o <code class=\"k\">ACCESS_TOKEN</code> da API do Mercado Pago.</p>\n          <div class=\"warn\">⚠️ O order_id só pode ter letras e números. Qualquer outro caractere (/, espaço, ?, etc.) é recusado com Bad Request antes de chamar a API.</div>\n        </div>\n\n        <div class=\"card\">\n          <h2>Exemplo de chamada</h2>\n          <pre>curl -X GET 'https://babi-lf.data2apis.com/rush/getOrderHandler?order_id=ORDTST01M3D7PXZ6XEYQFCMY7HY9VSA4' \\\\\n  -H 'Authorization: SUA_CHAVE_CreateOrderRush'</pre>\n        </div>\n\n        <div class=\"card\">\n          <h2>Resposta de sucesso</h2>\n          <p>Retorna <code class=\"k\">{ \"data\": { \"status\": 200, \"order\": { ... } } }</code>, com a order completa do Mercado Pago. Campos mais úteis em <code class=\"k\">order</code>:</p>\n          <pre>{\n  \"id\": \"ORDTST01M3D7PXZ6XEYQFCMY7HY9VSA4\",\n  \"status\": \"created\",\n  \"status_detail\": \"...\",\n  \"external_reference\": \"order_auth_test\",\n  \"total_amount\": \"1000.00\",\n  \"checkout_url\": \"https://www.mercadopago.com.br/checkout/v1/redirect?order_id=...\",\n  \"items\": [ { \"title\": \"Meu produto\", \"unit_price\": \"1000.00\", \"quantity\": 1 } ]\n}</pre>\n          <p class=\"muted\">Use <code class=\"k\">order.status</code> para saber em que pé está o pedido: <code class=\"k\">created</code> (criada, aguardando pagamento), <code class=\"k\">processed</code> (pagamento processado) ou <code class=\"k\">action_required</code> (precisa de ação do integrador). O campo <code class=\"k\">status</code> fora da order é o status HTTP devolvido pelo Mercado Pago.</p>\n        </div>\n\n        <div class=\"card\">\n          <h2>Erros comuns</h2>\n          <ul>\n            <li><code class=\"k\">Unauthorized</code> — chave ausente/errada no header Authorization. O Mercado Pago não é chamado.</li>\n            <li><code class=\"k\">Bad Request</code> — order_id ausente ou com caracteres inválidos.</li>\n            <li><code class=\"k\">status: 404</code> / <code class=\"k\">order_not_found</code> — o ID não existe (ou é de outro ambiente/conta).</li>\n            <li><code class=\"k\">status: 401</code> — problema com o ACCESS_TOKEN do vault (credencial inválida ou vencida).</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Como reutilizar em outros lugares</h2>\n          <p>De um botão no app (onClick) ou de outro Rush handler, chame o listener diretamente:</p>\n          <pre>@getOrderHandler.invoke({\n  body: { order_id },\n  headers: { Authorization: &lt;chave CreateOrderRush&gt; }\n})</pre>\n          <p class=\"muted\">Fluxo típico: guarde o <code class=\"k\">order.id</code> retornado pelo Criar Order e use este endpoint para checar o status depois que o comprador volta do checkout. Regra de ouro: a chave <code class=\"k\">CreateOrderRush</code> vem do vault/backend, nunca hardcoded no front público.</p>\n        </div>\n\n      </div></div>\n    `\n  }\n}"}}}}}}}},{"_id":"6ab6ef326b9804cbc48b26d3","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"docSearchOrders","package":"babi-lf","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    .doc { box-sizing:border-box; font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif; background:#f5f5f3; color:#0f172a; min-height:100%; height:100%; overflow-y:auto; padding:48px 24px; }\n    .doc *{ box-sizing:border-box; }\n    .wrap{ max-width:920px; margin:0 auto; }\n    .h1{ font-size:34px; font-weight:800; letter-spacing:-0.02em; margin:0 0 8px; }\n    .sub{ font-size:16px; color:#475569; line-height:1.55; margin:0 0 14px; }\n    .badge{ display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:700; color:#b45309; background:#fef3c7; border:1px solid #fde68a; padding:5px 12px; border-radius:999px; text-transform:uppercase; letter-spacing:.05em; }\n    .card{ background:#fff; border:1px solid #e7e7e4; border-radius:16px; padding:22px 26px; margin-top:20px; box-shadow:0 1px 2px rgba(15,23,42,.04); }\n    .card h2{ font-size:19px; font-weight:700; margin:0 0 12px; }\n    .card p{ font-size:14px; line-height:1.6; color:#475569; margin:0 0 4px; }\n    .flow{ font-size:14px; font-weight:600; color:#0f172a; line-height:1.6; margin:0 0 12px; }\n    .chip{ display:inline-block; background:#eef2ff; color:#4338ca; border-radius:6px; padding:1px 7px; font-weight:600; font-family:\"Roboto Mono\",ui-monospace,monospace; font-size:12.5px; }\n    ul{ margin:8px 0 0; padding-left:20px; }\n    li{ font-size:14px; line-height:1.6; color:#475569; margin:4px 0; }\n    code.k{ font-family:\"Roboto Mono\",ui-monospace,monospace; background:#f1f5f9; padding:1px 6px; border-radius:5px; font-size:13px; color:#0f172a; }\n    pre{ background:#0f172a; color:#e2e8f0; border-radius:10px; padding:18px 20px; overflow-x:auto; font-family:\"Roboto Mono\",ui-monospace,SFMono-Regular,monospace; font-size:12.5px; line-height:1.6; margin:12px 0 0; white-space:pre; }\n    table{ width:100%; border-collapse:collapse; margin-top:6px; }\n    th,td{ text-align:left; padding:8px 10px; font-size:13px; border-bottom:1px solid #eef0f2; vertical-align:top; }\n    th{ color:#64748b; font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:.04em; }\n    td .kk, td.kk{ font-family:\"Roboto Mono\",ui-monospace,monospace; color:#0f172a; font-weight:600; }\n    .req{ color:#dc2626; font-size:10px; font-weight:700; margin-left:4px; }\n    .warn{ color:#b45309; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; font-size:13px; font-weight:600; margin-top:12px; }\n    .muted{ color:#64748b; font-size:13px; line-height:1.6; margin-top:12px; }\n  `,\n  render() {\n    return `\n      <div class=\"doc\"><div class=\"wrap\">\n        <div class=\"h1\">Buscar Orders — Mercado Pago</div>\n        <p class=\"sub\">Guia de integração do endpoint interno de busca de pedidos (Checkout Pro Orders). Use para listar orders por período de criação, com filtros opcionais e paginação.</p>\n        <span class=\"badge\">● Ambiente de TESTE (sandbox)</span>\n\n        <div class=\"card\">\n          <h2>Arquitetura</h2>\n          <p class=\"flow\">Cliente &rarr; Rush Listener (<span class=\"chip\">searchOrdersHandler</span>) &rarr; HTTP Request (<span class=\"chip\">searchOrders</span>) &rarr; API Mercado Pago (GET /v1/orders)</p>\n          <p>Componentes no workspace <b>Mercado Pago</b>:</p>\n          <ul>\n            <li><b>Key Vault</b> <code class=\"k\">mercado-pago-test</code> — guarda <code class=\"k\">ACCESS_TOKEN</code> (token da API) e <code class=\"k\">CreateOrderRush</code> (chave de acesso ao endpoint, a mesma do Criar Order).</li>\n            <li><b>HTTP Client</b> <code class=\"k\">mercadoPago</code> — base <code class=\"k\">https://api.mercadopago.com</code></li>\n            <li><b>HTTP Request</b> <code class=\"k\">searchOrders</code> — GET /v1/orders (dinâmico: recebe url + headers).</li>\n            <li><b>Rush Listener</b> <code class=\"k\">searchOrdersHandler</code> — ponto de entrada: valida a chave, valida os filtros, monta a URL de busca, injeta o token e chama a API.</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Endpoint</h2>\n          <table>\n            <tr><td class=\"kk\">Método</td><td>GET (também aceita POST com os filtros no body)</td></tr>\n            <tr><td class=\"kk\">URL</td><td>https://babi-lf.data2apis.com/rush/searchOrdersHandler?begin_date=...&amp;end_date=...</td></tr>\n            <tr><td class=\"kk\">Authorization</td><td>&lt;chave CreateOrderRush&gt;</td></tr>\n          </table>\n        </div>\n\n        <div class=\"card\">\n          <h2>Autenticação</h2>\n          <p>O handler valida a chave <b>antes</b> de buscar qualquer order:</p>\n          <ul>\n            <li>Lê o valor de <code class=\"k\">CreateOrderRush</code> no Key Vault (resolvido em runtime dentro do handler).</li>\n            <li>Compara com o header <code class=\"k\">Authorization</code> enviado na chamada.</li>\n            <li>Se não bater (ou faltar) &rarr; responde <code class=\"k\">{ \"error\": \"Unauthorized\" }</code> e <b>não chama o Mercado Pago</b>.</li>\n          </ul>\n          <p class=\"muted\">Aceita o header nos dois formatos: <code class=\"k\">Authorization: &lt;chave&gt;</code> ou <code class=\"k\">Authorization: Bearer &lt;chave&gt;</code>. Nunca coloque a chave no front-end público — a busca devolve orders de todos os compradores.</p>\n        </div>\n\n        <div class=\"card\">\n          <h2>Filtros</h2>\n          <table>\n            <tr><th>Campo</th><th>Formato</th><th>Descrição</th></tr>\n            <tr><td class=\"kk\">begin_date<span class=\"req\">OBRIGATÓRIO</span></td><td>RFC 3339</td><td>Início do período de criação. Ex: \"2026-09-25T00:00:00Z\"</td></tr>\n            <tr><td class=\"kk\">end_date<span class=\"req\">OBRIGATÓRIO</span></td><td>RFC 3339</td><td>Fim do período de criação. Ex: \"2026-09-26T00:00:00Z\"</td></tr>\n            <tr><td class=\"kk\">external_reference</td><td>string</td><td>Seu identificador do pedido (o mesmo enviado no Criar Order). Letras, números, _ ou -, até 64.</td></tr>\n            <tr><td class=\"kk\">status</td><td>string</td><td>Status da order: <code class=\"k\">created</code>, <code class=\"k\">processed</code>, <code class=\"k\">action_required</code>…</td></tr>\n            <tr><td class=\"kk\">limit</td><td>número</td><td>Quantidade por página.</td></tr>\n            <tr><td class=\"kk\">offset</td><td>número</td><td>A partir de qual resultado (paginação).</td></tr>\n          </table>\n          <p class=\"muted\">Pode enviar os filtros na query (GET) ou no body (POST). O handler cuida do encoding das datas e injeta o Authorization com o <code class=\"k\">ACCESS_TOKEN</code> — não enviar.</p>\n          <div class=\"warn\">⚠️ Qualquer filtro fora do formato é recusado com Bad Request antes de chamar a API. Datas precisam do horário e do fuso (Z ou ±hh:mm).</div>\n        </div>\n\n        <div class=\"card\">\n          <h2>Exemplo de chamada</h2>\n          <pre>curl -X GET 'https://babi-lf.data2apis.com/rush/searchOrdersHandler?begin_date=2026-09-25T00:00:00Z&amp;end_date=2026-09-26T00:00:00Z&amp;status=created&amp;limit=10' \\\\\n  -H 'Authorization: SUA_CHAVE_CreateOrderRush'</pre>\n        </div>\n\n        <div class=\"card\">\n          <h2>Resposta de sucesso</h2>\n          <p>Retorna <code class=\"k\">{ \"data\": { \"status\": 200, \"result\": { \"data\": [ ... ], \"paging\": { ... } } } }</code>:</p>\n          <table>\n            <tr><th>Campo</th><th>Descrição</th></tr>\n            <tr><td class=\"kk\">status</td><td>Status HTTP devolvido pelo Mercado Pago. <b>200</b> = busca ok; 4xx/5xx = erro (detalhes em <code class=\"k\">result</code>).</td></tr>\n            <tr><td class=\"kk\">result.data[]</td><td>Lista de orders encontradas.</td></tr>\n            <tr><td class=\"kk\">result.paging</td><td>total, total_pages, offset e limit.</td></tr>\n          </table>\n          <pre>{\n  \"status\": 200,\n  \"result\": {\n    \"data\": [\n      {\n        \"id\": \"ORDTST01M3D7PXZ6XEYQFCMY7HY9VSA4\",\n        \"external_reference\": \"order_auth_test\",\n        \"status\": \"created\",\n        \"total_amount\": \"1000.00\",\n        \"created_date\": \"2026-09-25T21:31:59.86Z\",\n        \"items\": [ { \"title\": \"Meu produto\", \"unit_price\": \"1000.00\", \"quantity\": 1 } ]\n      }\n    ],\n    \"paging\": { \"total\": \"3\", \"total_pages\": \"1\", \"offset\": \"0\", \"limit\": \"20\" }\n  }\n}</pre>\n        </div>\n\n        <div class=\"card\">\n          <h2>Erros comuns</h2>\n          <ul>\n            <li><code class=\"k\">Unauthorized</code> — chave ausente/errada no header Authorization. O Mercado Pago não é chamado.</li>\n            <li><code class=\"k\">Bad Request</code> — begin_date/end_date ausentes ou algum filtro fora do formato.</li>\n            <li><code class=\"k\">status: 400</code> / <code class=\"k\">invalid_parameter</code> — o Mercado Pago recusou algum valor (ex.: período inválido).</li>\n            <li><code class=\"k\">status: 401</code> — problema com o ACCESS_TOKEN do vault (credencial inválida ou vencida).</li>\n          </ul>\n        </div>\n\n        <div class=\"card\">\n          <h2>Como reutilizar em outros lugares</h2>\n          <p>De um botão no app (onClick) ou de outro Rush handler, chame o listener diretamente:</p>\n          <pre>@searchOrdersHandler.invoke({\n  body: { begin_date, end_date, status, external_reference, limit, offset },\n  headers: { Authorization: &lt;chave CreateOrderRush&gt; }\n})</pre>\n          <p class=\"muted\">Usos típicos: relatório de vendas do dia, conciliação por <code class=\"k\">external_reference</code> e listar orders pendentes (<code class=\"k\">status=created</code>). Para detalhes completos de uma order, use o <code class=\"k\">getOrderHandler</code> com o <code class=\"k\">id</code>. Regra de ouro: a chave <code class=\"k\">CreateOrderRush</code> vem do vault/backend, nunca hardcoded no front público.</p>\n        </div>\n\n      </div></div>\n    `\n  }\n}"}}}}}}}},{"_id":"6ab92bb295248f62d4cc596d","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"TextLink","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// TextLink — Loja Lucas Fontana\n// O link em caixa alta com o sublinhado vermelho de 2px (\"Ver tudo\", \"Deixar uma mensagem\", \"Conheça a história\").\n// A área de toque tem no mínimo 44px de altura; só o texto é sublinhado.\n// `variant: inline` é o link discreto em caixa normal para ações utilitárias (\"Limpar tudo\", \"Limpar filtros\").\nconst LAYOUTS = ['desktop', 'tablet', 'mobile', 'auto'];\nconst TONES = ['dark', 'light', 'red'];\nconst VARIANTS = ['default', 'inline'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst pick = (v, list, fb) => (list.indexOf(String(v)) >= 0 ? String(v) : fb);\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst ARROW = '<svg class=\"ic\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14M13 6l6 6-6 6\"/></svg>';\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.wrap { --red-500: #c8102e; --red-300: #ff4d62; --ink: #0b0b0c; --white: #ffffff;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; display: block; }\n.lk { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; margin: 0; padding: 0; background: none; border: 0; cursor: pointer;\n  font-family: inherit; font-size: 13px; font-weight: 700; letter-spacing: .1em; line-height: 1.2; text-transform: uppercase;\n  color: var(--ink); text-decoration: none; white-space: nowrap; transition: color .15s ease; }\n.lk > span { border-bottom: 2px solid var(--red-500); padding-bottom: 4px; }\n.lk .ic { width: 16px; height: 16px; flex: none; }\n.lk:hover { color: var(--red-500); }\n.lk:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.t-light { color: var(--white); }\n.t-light:hover { color: var(--red-300); }\n.t-light:focus-visible { outline-color: var(--white); }\n.t-red { color: var(--red-500); }\n.t-red > span { border: 0; padding: 0; }\n.t-red:hover { color: var(--ink); }\n.v-inline { min-height: 32px; font-size: 14px; font-weight: 600; letter-spacing: 0; text-transform: none; }\n.v-inline > span { border-bottom: 1.5px solid currentColor; padding-bottom: 1px; }\n.l-tablet .lk:not(.v-inline), .l-mobile .lk:not(.v-inline) { font-size: 12px; }\n@media (max-width: 1199px) { .l-auto .lk:not(.v-inline) { font-size: 12px; } }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('.lk', 'click', (e, el) => {\n      if (el.tagName === 'BUTTON') { try { d2.emit('onClick'); } catch (err) { /* sem handler */ } }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = pick(p.layout, LAYOUTS, 'auto');\n    const tone = pick(p.tone, TONES, 'dark');\n    const variant = pick(p.variant, VARIANTS, 'default');\n    const label = p.label || 'Ver tudo';\n    const href = p.href ? String(p.href) : '';\n    const cls = `lk t-${tone} v-${variant}`;\n    const inner = `<span>${esc(label)}</span>${truthy(p.arrow) ? ARROW : ''}`;\n    const el = href\n      ? `<a class=\"${cls}\" href=\"${esc(href)}\"${truthy(p.newTab) ? ' target=\"_blank\" rel=\"noopener\"' : ''}>${inner}</a>`\n      : `<button type=\"button\" class=\"${cls}\">${inner}</button>`;\n    return `<div class=\"wrap l-${layout}\">${el}</div>`;\n  }\n};"}},"arrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"arrow"}}}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"newTab":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"newTab"}}}},"onClick":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClick"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}},"variant":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"variant"}}}}}}}}}},{"_id":"6ab92bdae69f22645a56e5a3","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Breadcrumb","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Breadcrumb — Loja Lucas Fontana\n// A trilha acima do título da página: \"Início / Cuidado automotivo\".\n// Passos anteriores são links cinza; a página atual é preta, 600, e não é link.\n// No mobile fica numa linha só e rola para o lado (nomes de produto longos); `scroll: false` deixa quebrar.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst norm = (it) => (it && typeof it === 'object' ? { label: it.label || '', href: it.href || '' } : { label: String(it || ''), href: '' });\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.bc { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; width: 100%; }\nol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 14px; line-height: 1.4; }\nli { display: flex; align-items: center; gap: 8px; color: var(--grey-500); }\nli + li::before { content: '/'; color: var(--grey-500); }\na { color: var(--grey-500); text-decoration: none; }\na:hover { color: var(--red-500); }\na:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n[aria-current] { color: var(--ink); font-weight: 600; }\n.l-mobile ol { font-size: 13px; }\n.scroll ol { flex-wrap: nowrap; overflow-x: auto; white-space: nowrap; scrollbar-width: thin; padding-bottom: 6px; }\n.scroll li { flex-shrink: 0; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('a', 'click', (e, el) => { try { d2.emit('onNavigate', el.getAttribute('href')); } catch (err) { /* sem handler */ } });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    let items = list(p.items).map(norm).filter((i) => i.label);\n    if (!items.length) items = [{ label: 'Início', href: '/' }, { label: 'Página atual', href: '' }];\n    const scroll = layout === 'mobile' && !(p.scroll === false || p.scroll === 'false');\n    const last = items.length - 1;\n    const lis = items.map((it, i) => {\n      if (i === last) return `<li><span aria-current=\"page\">${esc(it.label)}</span></li>`;\n      return it.href ? `<li><a href=\"${esc(it.href)}\">${esc(it.label)}</a></li>` : `<li><span>${esc(it.label)}</span></li>`;\n    }).join('');\n    return `<nav class=\"bc l-${layout}${scroll ? ' scroll' : ''}\" aria-label=\"Você está em\"><ol>${lis}</ol></nav>`;\n  }\n};"}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onNavigate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNavigate"}}}},"scroll":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"scroll"}}}}}}}}}},{"_id":"6ab92bf97a2786dc7b34d7f6","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AboutHero","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AboutHero — Auto Super Shop\n// A abertura de \"Sobre a Auto Super\": breadcrumb, kicker vermelho, título em duas linhas com a segunda em\n// vermelho itálico (\"Prazer, / Auto Super.\"), apresentação curta e dois botões, ao lado de um retrato\n// com a régua vermelha embaixo. Não desenha breadcrumb nem botão: usa Breadcrumb e Button por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst IMG = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"1\"/><circle cx=\"9\" cy=\"10\" r=\"2\"/><path d=\"m21 16-5-5-9 8\"/></svg>';\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.ah { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-300: #a7a9ad; --grey-700: #45474b; --graphite-900: #1c1d1f;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  width: 100%; height: 100%; background: var(--white); color: var(--ink); }\np, h1 { margin: 0; }\n.in { padding: 56px 96px 88px; display: grid; grid-template-columns: minmax(0, 1fr) 560px; gap: 64px; align-items: center; }\n.copy { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n.crumb { height: 24px; margin-bottom: 8px; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { margin: 4px 0 8px; font-size: 54px; font-weight: 800; font-stretch: 118%; line-height: .98; letter-spacing: -.015em; text-transform: uppercase; }\n.title em { display: block; font-style: italic; color: var(--red-500); }\n.text { max-width: 640px; font-size: 18px; line-height: 1.6; color: var(--grey-700); }\n.actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 12px; }\n.act { flex: none; height: 52px; }\n.media { position: relative; aspect-ratio: 560 / 600; background: var(--graphite-900); border-bottom: 6px solid var(--red-500);\n  display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.ph { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 0 24px; text-align: center;\n  color: var(--grey-300); font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; }\n.ph svg { width: 28px; height: 28px; }\n\n.l-tablet .in { padding: 40px 40px 64px; grid-template-columns: minmax(0, 1fr) 300px; gap: 40px; }\n.l-tablet .title { font-size: 40px; font-stretch: 115%; letter-spacing: -.01em; }\n.l-tablet .text { font-size: 17px; }\n.l-tablet .media { border-bottom-width: 5px; }\n\n.l-mobile .in { padding: 24px 20px 48px; grid-template-columns: minmax(0, 1fr); gap: 32px; }\n.l-mobile .crumb { height: 30px; margin-bottom: 4px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 34px; font-stretch: 110%; line-height: 1; }\n.l-mobile .text { font-size: 15px; }\n.l-mobile .actions { flex-direction: column; }\n.l-mobile .act { width: 100%; }\n.l-mobile .media { aspect-ratio: 4 / 5; border-bottom-width: 5px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const mob = layout === 'mobile';\n    const bcRef = refOf(d2.param('breadcrumb'));\n    const btnRef = refOf(d2.param('button'));\n\n    const crumbs = list(p.crumbs).length ? list(p.crumbs) : [{ label: 'Início', href: '/' }, { label: 'Sobre a Auto Super' }];\n    const eyebrow = p.eyebrow == null ? 'Sobre a Auto Super' : p.eyebrow;\n    const title = p.title || 'Prazer,';\n    const accent = p.titleAccent == null ? 'Auto Super.' : p.titleAccent;\n    const body = p.body == null ? '[Apresentação curta da Auto Super em 2 ou 3 frases: quem é, o que faz no canal e o jeito de falar de carro.]' : p.body;\n    const pLabel = p.primaryLabel == null ? 'Assistir no YouTube' : p.primaryLabel;\n    const sLabel = p.secondaryLabel == null ? 'Ver a loja' : p.secondaryLabel;\n\n    const btn = (key, label, variant, glyph, href, evt) => d2.render(btnRef, {\n      label, variant, size: 'lg', glyph: glyph || '', fullWidth: mob ? 'always' : 'auto', layout, href: href || '', newTab: /^https?:/.test(href || '')\n    }, { key, onClick: () => { try { d2.emit(evt, href || ''); } catch (err) { /* sem handler */ } } });\n\n    const actions = btnRef && (pLabel || sLabel) ? `<div class=\"actions\">\n        ${pLabel ? `<div class=\"act\">${btn('primary', pLabel, 'brand', p.primaryGlyph == null ? 'youtube' : p.primaryGlyph, p.primaryHref, 'onPrimary')}</div>` : ''}\n        ${sLabel ? `<div class=\"act\">${btn('secondary', sLabel, 'outline', '', p.secondaryHref || '/', 'onSecondary')}</div>` : ''}\n      </div>` : '';\n\n    const crumb = bcRef ? `<div class=\"crumb\">${d2.render(bcRef, { items: crumbs, layout }, { key: 'bc' })}</div>` : '';\n    const media = p.image\n      ? `<img src=\"${esc(p.image)}\" alt=\"${esc(p.imageAlt || 'Auto Super')}\">`\n      : `<div class=\"ph\">${IMG}<span>${esc(p.placeholder || 'Foto da Auto Super')}</span></div>`;\n\n    return `<section class=\"ah l-${layout}\"><div class=\"in\">\n      <div class=\"copy\">\n        ${crumb}\n        ${eyebrow ? `<p class=\"kicker\">${esc(eyebrow)}</p>` : ''}\n        <h1 class=\"title\">${esc(title)}${accent ? `<em>${esc(accent)}</em>` : ''}</h1>\n        ${body ? `<p class=\"text\">${esc(body)}</p>` : ''}\n        ${actions}\n      </div>\n      <div class=\"media\">${media}</div>\n    </div></section>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"crumbs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"crumbs"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"imageAlt":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imageAlt"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onPrimary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPrimary"}}}},"onSecondary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSecondary"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"primaryGlyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryGlyph"}}}},"primaryHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryHref"}}}},"primaryLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryLabel"}}}},"secondaryHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"secondaryHref"}}}},"secondaryLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"secondaryLabel"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"titleAccent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"titleAccent"}}}}}}}}}},{"_id":"6ab92c098b1ad6cc3e92d8e2","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"StatBand","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// StatBand — Loja Lucas Fontana\n// Faixa preta com os números do canal: cada número tem uma régua vermelha de 3px à esquerda,\n// o valor em display itálico largo e um rótulo cinza. Xadrez discreto no canto superior direito.\n// 4 lado a lado no desktop; 2 × 2 no tablet e no mobile.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst DEFAULTS = [\n  { value: '[000 mil]', label: 'inscritos no YouTube' },\n  { value: '[000 mil]', label: 'seguidores no Instagram' },\n  { value: '[00 mi]', label: 'de visualizações' },\n  { value: '[000]', label: 'vídeos publicados' }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.sb { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-300: #a7a9ad;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  position: relative; width: 100%; height: 100%; background: var(--ink); color: var(--white); overflow: hidden; }\n.checker { position: absolute; top: 0; right: 0; width: 180px; height: 60px; pointer-events: none;\n  background: repeating-conic-gradient(rgba(255,255,255,.06) 0 25%, transparent 0 50%) 0 0 / 30px 30px; }\n.in { position: relative; padding: 56px 96px; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px; margin: 0; list-style: none; }\n.stat { display: flex; flex-direction: column; gap: 6px; padding-left: 16px; border-left: 3px solid var(--red-500); min-width: 0; }\n.stat b { font-size: 40px; font-weight: 800; font-style: italic; font-stretch: 125%; line-height: 1; white-space: nowrap; }\n.stat span { font-size: 14px; line-height: 1.35; color: var(--grey-300); }\n.l-tablet .in { padding: 48px 40px; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 32px 24px; }\n.l-mobile .in { padding: 40px 20px; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 28px 16px; }\n.l-mobile .stat { padding-left: 12px; }\n.l-mobile .stat b { font-size: 26px; font-stretch: 115%; }\n.l-mobile .stat span { font-size: 13px; }\n.l-mobile .checker { width: 96px; height: 48px; background-size: 24px 24px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const given = list(p.items).filter((i) => i && typeof i === 'object');\n    const items = (given.length ? given : DEFAULTS).slice(0, 4);\n    return `<section class=\"sb l-${layout}\" aria-label=\"O canal em números\">\n      <div class=\"checker\" aria-hidden=\"true\"></div>\n      <ul class=\"in\">${items.map((s) => `<li class=\"stat\"><b>${esc(s.value)}</b><span>${esc(s.label)}</span></li>`).join('')}</ul>\n    </section>`;\n  }\n};"}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}}}}}}}},{"_id":"6ab92c21b6119616261ffb00","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"MediaText","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// MediaText — Loja Lucas Fontana\n// Uma foto ao lado de um kicker vermelho, um título em caixa alta e um ou dois parágrafos\n// (\"A história / Da garagem pro canal. Do canal pra sua garagem.\").\n// Duas colunas iguais no desktop e no tablet (`flip` põe a foto à direita); empilhado, foto primeiro, no mobile.\n// O bloco ocupa só a largura do conteúdo: posicione-o no gutter da página (96 / 40 / 20px).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : [v]; } catch (e) { return [v]; } }\n  return [];\n};\nconst IMG = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"5\" width=\"18\" height=\"14\" rx=\"1\"/><circle cx=\"9\" cy=\"10\" r=\"2\"/><path d=\"m21 16-5-5-9 8\"/></svg>';\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.mt { --red-500: #c8102e; --ink: #0b0b0c; --grey-100: #ececea; --grey-500: #6b6e73; --grey-700: #45474b;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  width: 100%; height: 100%; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 64px; align-items: center; color: var(--ink); background: #ffffff; }\np, h2 { margin: 0; }\n.media { position: relative; aspect-ratio: 592 / 460; background: var(--grey-100); display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.ph { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 0 24px; text-align: center;\n  color: var(--grey-500); font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase; }\n.ph svg { width: 28px; height: 28px; }\n.copy { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { margin: 0 0 4px; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.copy p.body { font-size: 17px; line-height: 1.65; color: var(--grey-700); }\n.flip .media { order: 2; }\n.l-tablet { gap: 40px; }\n.l-tablet .title { font-size: 32px; }\n.l-tablet .copy p.body { font-size: 16px; }\n.l-mobile { grid-template-columns: minmax(0, 1fr); gap: 28px; align-items: start; align-content: start; }\n.l-mobile.flip .media { order: 0; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 28px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .copy p.body { font-size: 15px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const eyebrow = p.eyebrow == null ? 'A história' : p.eyebrow;\n    const title = p.title || 'Da garagem pro canal. Do canal pra sua garagem.';\n    let paras = list(p.paragraphs).map((x) => String(x || '')).filter(Boolean);\n    if (!paras.length && p.paragraphs == null) paras = ['[Primeiro parágrafo]', '[Segundo parágrafo]'];\n    const media = p.image\n      ? `<img src=\"${esc(p.image)}\" alt=\"${esc(p.imageAlt || '')}\">`\n      : `<div class=\"ph\">${IMG}<span>${esc(p.placeholder || 'Foto')}</span></div>`;\n    return `<div class=\"mt l-${layout}${truthy(p.flip) ? ' flip' : ''}\">\n      <div class=\"media\">${media}</div>\n      <div class=\"copy\">\n        ${eyebrow ? `<p class=\"kicker\">${esc(eyebrow)}</p>` : ''}\n        <h2 class=\"title\">${esc(title)}</h2>\n        ${paras.map((t) => `<p class=\"body\">${esc(t)}</p>`).join('')}\n      </div>\n    </div>`;\n  }\n};"}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"flip":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"flip"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"imageAlt":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imageAlt"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"paragraphs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"paragraphs"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6ab92c2fd638cff4260db230","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ValueCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ValueCard — Auto Super Shop\n// Um motivo numerado para comprar aqui: numeral vermelho em itálico (\"01\"), título em caixa alta e um texto curto,\n// num card branco com borda fina. Use três lado a lado sobre uma Section cinza (tone \"alt\").\n// O texto chega por `body` (o nome `text` é reservado do bloco base do Data2).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst pad = (v) => { const n = Number(v); return v !== '' && v != null && Number.isFinite(n) ? (n < 10 ? '0' + n : String(n)) : String(v || ''); };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.vc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 10px; width: 100%; height: 100%; padding: 28px;\n  background: var(--white); border: 1px solid var(--line); color: var(--ink); }\np, h3 { margin: 0; }\n.num { font-size: 34px; font-weight: 800; font-style: italic; font-stretch: 125%; line-height: 1; color: var(--red-500); }\n.title { margin: 4px 0 0; font-size: 20px; font-weight: 800; font-stretch: 112%; line-height: 1.1; text-transform: uppercase; }\n.text { font-size: 15px; line-height: 1.6; color: var(--grey-700); }\n.l-tablet { padding: 22px; }\n.l-tablet .num, .l-mobile .num { font-size: 28px; }\n.l-tablet .title, .l-mobile .title { font-size: 17px; }\n.l-tablet .text, .l-mobile .text { font-size: 14px; }\n.l-mobile { padding: 22px 20px; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const num = pad(p.number == null ? '1' : p.number);\n    const title = p.title || 'Testado de verdade';\n    const body = p.body == null ? 'O que entra na loja é o que a Auto Super usa, testa e mostra nos vídeos.' : p.body;\n    return `<article class=\"vc l-${layout}\">\n      ${num ? `<p class=\"num\" aria-hidden=\"true\">${esc(num)}</p>` : ''}\n      <h3 class=\"title\">${esc(title)}</h3>\n      ${body ? `<p class=\"text\">${esc(body)}</p>` : ''}\n    </article>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6ab92c3be69f22645a56e5fa","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Section","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Section — Loja Lucas Fontana\n// A faixa de uma seção de página: aplica o fundo (branco ou cinza \"alt\"), o gutter (96 / 40 / 20px),\n// o respiro de topo (120 / 88 / 64px; na faixa cinza 96 / 64 / 48px) e desenha o SectionHeader no lugar certo.\n// O conteúdo (cards, grids…) é posicionado por cima, na página, logo abaixo do cabeçalho — veja o Doc.\n// Não desenha o cabeçalho: usa o SectionHeader pelo parâmetro `sectionHeader`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst TOP = { desktop: 120, tablet: 88, mobile: 64 };\nconst TOP_ALT = { desktop: 96, tablet: 64, mobile: 48 };\nconst GUTTER = { desktop: 96, tablet: 40, mobile: 20 };\nconst HEAD = { desktop: 80, tablet: 72, mobile: 90 };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.sec { width: 100%; height: 100%; background: #ffffff; }\n.sec.alt { background: #f5f5f3; }\n.head { width: 100%; }\n`,\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const alt = p.tone === 'alt';\n    const top = truthy(p.flush) ? 0 : (alt ? TOP_ALT[layout] : TOP[layout]);\n    const gutter = GUTTER[layout];\n    const h = Number(p.headerHeight) > 0 ? Number(p.headerHeight) : HEAD[layout];\n    const shRef = refOf(d2.param('sectionHeader'));\n    const header = shRef && (p.title || p.eyebrow)\n      ? `<div class=\"head\" style=\"height:${h}px\">${d2.render(shRef, {\n          number: p.number || '', eyebrow: p.eyebrow || '', title: p.title || '', kicker: truthy(p.kicker),\n          actionLabel: p.actionLabel || '', actionHref: p.actionHref || '', layout\n        }, { key: 'sh', onAction: () => { try { d2.emit('onAction', p.actionHref || ''); } catch (err) { /* sem handler */ } } })}</div>`\n      : '';\n    return `<section class=\"sec${alt ? ' alt' : ''}\" style=\"padding:${top}px ${gutter}px 0\">${header}</section>`;\n  }\n};"}},"actionHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"actionHref"}}}},"actionLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"actionLabel"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"flush":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"flush"}}}},"headerHeight":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"headerHeight"}}}},"kicker":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kicker"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onAction":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAction"}}}},"sectionHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sectionHeader"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6aba776d6c715c0b701ea1e0","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Accordion","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Accordion — Loja Lucas Fontana\n// Linhas de pergunta e resposta do FAQ (e, depois, das abas da página de produto no mobile).\n// Pergunta 17px bold com um quadrado de 28px à direita: \"+\" em ink fechado, \"−\" em vermelho aberto.\n// Resposta em 15px cinza, hairline entre as linhas. Cada pergunta é um botão dentro de um título, com aria-expanded.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent` (sem ele, cai num traço próprio igual).\n// `query` filtra as perguntas (sem acento, sem maiúscula) e abre as que batem — é o que a busca do FAQ usa.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst nums = (v) => {\n  if (Array.isArray(v)) return v.map(Number).filter((n) => !isNaN(n));\n  if (typeof v === 'number') return [v];\n  if (typeof v === 'string' && v.trim()) {\n    try { const j = JSON.parse(v); if (Array.isArray(j)) return j.map(Number).filter((n) => !isNaN(n)); if (typeof j === 'number') return [j]; } catch (e) { /* lista separada por vírgula */ }\n    return v.split(',').map((s) => Number(s.trim())).filter((n) => !isNaN(n));\n  }\n  return [];\n};\nconst fold = (s) => String(s == null ? '' : s).normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase().trim();\nconst paras = (t) => String(t == null ? '' : t).split(/\\n\\s*\\n/).map((x) => x.trim()).filter(Boolean)\n  .map((x) => `<p>${esc(x).replace(/\\n/g, '<br>')}</p>`).join('');\nconst SVG = {\n  plus: '<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M12 5v14M5 12h14\"/></svg>',\n  minus: '<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M5 12h14\"/></svg>'\n};\nconst DEMO = [\n  { question: 'Quais formas de pagamento vocês aceitam?', answer: 'Pix e cartão de crédito, com parcelamento em até [N]x. Não trabalhamos com boleto.' },\n  { question: 'É seguro comprar aqui?', answer: '[Resposta]' },\n  { question: 'Preciso ter conta para comprar?', answer: '[Resposta]' }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.acc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  width: 100%; color: var(--ink); }\nh3, h4, p { margin: 0; }\n.item { border-bottom: 1px solid var(--line); }\n.q { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 16px; min-height: 60px; padding: 14px 0; margin: 0;\n  border: 0; background: transparent; font: 700 17px/1.35 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n  color: var(--ink); text-align: left; cursor: pointer; transition: color .15s ease; }\n.q:hover { color: var(--red-500); }\n.q:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.tg { flex: none; width: 28px; height: 28px; background: var(--ink); color: var(--white); display: flex; align-items: center; justify-content: center; }\n.q[aria-expanded=\"true\"] .tg { background: var(--red-500); }\n.tg svg { display: block; }\n.a { padding: 0 44px 20px 0; font-size: 15px; line-height: 1.65; color: var(--grey-700); }\n.a[hidden] { display: none; }\n.a p { margin: 0 0 10px; }\n.a p:last-child { margin: 0; }\n.none { padding: 20px 0; font-size: 15px; color: var(--grey-500); }\n.l-mobile .q { font-size: 16px; }\n.l-mobile .a { padding-right: 0; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('.q', 'click', (e, el) => {\n      const k = Number(el.dataset.i);\n      const p = d2.params || {};\n      const searching = !!fold(p.query);\n      const isOpen = el.getAttribute('aria-expanded') === 'true';\n      if (searching) {\n        // Durante a busca tudo que bate começa aberto; guardamos só o que a pessoa fechou.\n        const q = fold(p.query);\n        const st = d2.state.closedQ;\n        const closed = st && st.q === q ? st.list.slice() : [];\n        d2.state.closedQ = { q, list: isOpen ? closed.concat([k]) : closed.filter((x) => x !== k) };\n      } else {\n        const cur = (d2.state.open || nums(p.defaultOpen)).slice();\n        d2.state.open = isOpen ? cur.filter((x) => x !== k) : (truthy(p.single) ? [k] : cur.concat([k]));\n      }\n      try { d2.emit('onToggle', { index: k, open: !isOpen }); } catch (err) { /* sem handler */ }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const all = (list(p.items).length ? list(p.items) : DEMO)\n      .map((it) => ({ question: (it && it.question) || '', answer: (it && it.answer) || '' }))\n      .filter((it) => it.question);\n    const hl = Number(p.headingLevel) === 4 ? 'h4' : 'h3';\n    const iconMod = refOf(d2.param('iconComponent'));\n    const uid = 'acc'; // ids vivem no shadow root de cada instância, não colidem\n\n    // Linhas abertas: o que a pessoa abriu/fechou, ou `defaultOpen` enquanto ninguém mexeu.\n    const opened = d2.state.open || nums(p.defaultOpen);\n    const q = fold(p.query);\n    const st = d2.state.closedQ;\n    const closedQ = st && st.q === q ? st.list : [];\n\n    const rows = all.map((it, k) => ({ it, k }))\n      .filter(({ it }) => !q || fold(it.question).indexOf(q) >= 0 || fold(it.answer).indexOf(q) >= 0);\n    if (!rows.length) {\n      return `<div class=\"acc l-${layout}\"><p class=\"none\">${esc(p.emptyLabel || 'Nenhuma pergunta por aqui.')}</p></div>`;\n    }\n    const openSet = q ? rows.map((r) => r.k).filter((k) => closedQ.indexOf(k) < 0) : opened;\n\n    const html = rows.map(({ it, k }) => {\n      const isOpen = openSet.indexOf(k) >= 0;\n      const glyph = iconMod\n        ? d2.render(iconMod, { name: isOpen ? 'minus' : 'plus', size: 14, iconColor: 'currentColor' }, { key: 'ic-' + k + (isOpen ? '-m' : '-p'), width: 14, height: 14 })\n        : SVG[isOpen ? 'minus' : 'plus'];\n      return `<div class=\"item\" data-key=\"${k}\">\n        <${hl}><button type=\"button\" class=\"q\" data-i=\"${k}\" aria-expanded=\"${isOpen ? 'true' : 'false'}\" aria-controls=\"${uid}-${k}\">\n          <span>${esc(it.question)}</span><span class=\"tg\" aria-hidden=\"true\">${glyph}</span>\n        </button></${hl}>\n        <div class=\"a\" id=\"${uid}-${k}\"${isOpen ? '' : ' hidden'}>${paras(it.answer)}</div>\n      </div>`;\n    }).join('');\n    return `<div class=\"acc l-${layout}\">${html}</div>`;\n  }\n};"}},"defaultOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"defaultOpen"}}}},"emptyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyLabel"}}}},"headingLevel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"headingLevel"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onToggle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onToggle"}}}},"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"query"}}}},"single":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"single"}}}}}}}}}},{"_id":"6aba777c976d61457441e04f","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FaqGroup","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FaqGroup — Loja Lucas Fontana\n// Um tema do FAQ: título em caixa alta (24px, 20px no mobile) sobre uma régua ink de 2px, e o Accordion logo abaixo.\n// `anchor` é o id do tema: o menu \"Temas\" do FaqLayout usa para pular até ele.\n// Não desenha as perguntas: usa o Accordion pelo parâmetro `accordion` (e repassa o Icon por `iconComponent`).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst DEMO = [\n  { question: 'Quais formas de pagamento vocês aceitam?', answer: 'Pix e cartão de crédito, com parcelamento em até [N]x. Não trabalhamos com boleto.' },\n  { question: 'É seguro comprar aqui?', answer: '[Resposta]' },\n  { question: 'Preciso ter conta para comprar?', answer: '[Resposta]' }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.grp { --ink: #0b0b0c;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; width: 100%; color: var(--ink); scroll-margin-top: 24px; }\n.gt { margin: 0; padding-bottom: 12px; border-bottom: 2px solid var(--ink); font-size: 24px; font-weight: 800; font-stretch: 115%;\n  line-height: 1.05; letter-spacing: .005em; text-transform: uppercase; color: var(--ink); overflow-wrap: break-word; }\n.l-mobile .gt { font-size: 20px; font-stretch: 112%; }\n`,\n\n  mounted() {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const accMod = refOf(d2.param('accordion'));\n    const iconMod = refOf(d2.param('iconComponent'));\n    const items = list(p.items).length ? list(p.items) : DEMO;\n    const title = p.title == null ? 'Compras e pagamento' : p.title;\n    const anchor = p.anchor ? String(p.anchor) : '';\n    const tid = anchor ? anchor + '-t' : 'faq-group-t';\n\n    const acc = accMod\n      ? d2.render(accMod, {\n          items, defaultOpen: p.defaultOpen == null ? [] : p.defaultOpen, single: truthy(p.single),\n          query: p.query || '', layout, headingLevel: 3, iconComponent: iconMod\n        }, { key: 'acc' + (p.query ? '-q-' + String(p.query).toLowerCase() : ''), style: 'height:auto', onToggle: (e) => { try { d2.emit('onToggle', e); } catch (err) { /* sem handler */ } } })\n      : '';\n\n    return `<section class=\"grp l-${layout}\"${anchor ? ` id=\"${esc(anchor)}\"` : ''} aria-labelledby=\"${esc(tid)}\">\n      <h2 class=\"gt\" id=\"${esc(tid)}\">${esc(title)}</h2>\n      ${acc}\n    </section>`;\n  }\n};"}},"accordion":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accordion"}}}},"anchor":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"anchor"}}}},"defaultOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"defaultOpen"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onToggle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onToggle"}}}},"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"query"}}}},"single":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"single"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6aba778f6c715c0b701ea228","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"SupportCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// SupportCard — Auto Super Shop\n// O card escuro \"Não achou sua resposta?\" do FAQ: xadrez discreto no canto, título, uma linha apontando o SAC,\n// botão vermelho (WhatsApp do SAC), botão de contorno branco (e-mail) e uma nota pequena com link no rodapé.\n// Não desenha botão: usa o Button pelo parâmetro `button` (variantes `brand` e `ghost`).\n// O texto de apoio chega por `body` (o nome `text` é reservado do bloco base do Data2).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst dflt = (v, d) => (v == null ? d : v);\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.sc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-200: #c9cacd; --grey-300: #a7a9ad;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  position: relative; display: flex; flex-direction: column; gap: 14px; width: 100%; height: 100%; padding: 28px 24px;\n  background: var(--ink); color: var(--white); overflow: hidden; }\nh2, p { margin: 0; position: relative; }\n.checker { position: absolute; top: 0; right: 0; width: 72px; height: 48px; pointer-events: none;\n  background: repeating-conic-gradient(rgba(255,255,255,.08) 0 25%, transparent 0 50%) 0 0 / 16px 16px; }\n.title { font-size: 22px; font-weight: 800; font-stretch: 112%; line-height: 1.05; text-transform: uppercase; padding-right: 48px; }\n.body { font-size: 14px; line-height: 1.55; color: var(--grey-200); }\n.btns { position: relative; display: flex; flex-direction: column; gap: 10px; margin-top: 2px; }\n.btn { width: 100%; height: 48px; }\n.foot { font-size: 13px; line-height: 1.5; color: var(--grey-300); }\n.foot a { display: inline-block; color: var(--white); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }\n.foot a:hover { color: var(--red-500); }\n.foot a:focus-visible { outline: 2px solid var(--white); outline-offset: 2px; }\n\n/* Tablet: o card vai para o fim da página, largo; os botões ficam lado a lado */\n.l-tablet { padding: 32px; }\n.l-tablet .body { max-width: 520px; }\n.l-tablet .btns { flex-direction: row; }\n.l-tablet .btn { flex: 1 1 0; }\n\n/* Mobile: igual ao desktop, empilhado */\n.l-mobile .title { font-size: 20px; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    d2.on('.foot a', 'click', (e, el) => { try { d2.emit('onFootLink', el.getAttribute('href')); } catch (err) { /* sem handler */ } });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const btnMod = refOf(d2.param('button'));\n\n    const title = dflt(p.title, 'Não achou sua resposta?');\n    const body = dflt(p.body, 'Fala com o SAC — é o caminho mais rápido para dúvidas sobre pedido, troca ou entrega.');\n    const pLabel = dflt(p.primaryLabel, 'WhatsApp do SAC');\n    const sLabel = dflt(p.secondaryLabel, '[E-mail do SAC]');\n    const footText = dflt(p.footText, 'Quer mandar um recado pra Auto Super?');\n    const footLink = dflt(p.footLinkLabel, 'Fale com a Auto Super');\n    const footHref = p.footLinkHref || '/fale-com-a-auto-super';\n\n    const btn = (key, label, variant, glyph, href, evt) => `<div class=\"btn\">${d2.render(btnMod, {\n      label, variant, size: 'md', fullWidth: 'always', glyph: glyph || '', layout, href: href || '', newTab: /^https?:/.test(href || '')\n    }, { key, height: 48, onClick: () => { try { d2.emit(evt, href || ''); } catch (err) { /* sem handler */ } } })}</div>`;\n\n    const btns = btnMod && (pLabel || sLabel) ? `<div class=\"btns\">\n        ${pLabel ? btn('primary', pLabel, 'brand', dflt(p.primaryGlyph, 'message'), p.primaryHref, 'onPrimary') : ''}\n        ${sLabel ? btn('secondary', sLabel, 'ghost', '', p.secondaryHref, 'onSecondary') : ''}\n      </div>` : '';\n\n    const foot = footText || footLink\n      ? `<p class=\"foot\">${esc(footText)}${footText && footLink ? '<br>' : ''}${footLink ? `<a href=\"${esc(footHref)}\">${esc(footLink)}</a>` : ''}</p>`\n      : '';\n\n    return `<section class=\"sc l-${layout}\">\n      <span class=\"checker\" aria-hidden=\"true\"></span>\n      ${title ? `<h2 class=\"title\">${esc(title)}</h2>` : ''}\n      ${body ? `<p class=\"body\">${esc(body)}</p>` : ''}\n      ${btns}\n      ${foot}\n    </section>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"footLinkHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"footLinkHref"}}}},"footLinkLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"footLinkLabel"}}}},"footText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"footText"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onFootLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onFootLink"}}}},"onPrimary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPrimary"}}}},"onSecondary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSecondary"}}}},"primaryGlyph":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryGlyph"}}}},"primaryHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryHref"}}}},"primaryLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"primaryLabel"}}}},"secondaryHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"secondaryHref"}}}},"secondaryLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"secondaryLabel"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6aba77a6f7e943b8aee8e432","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"PageHeader","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// PageHeader — Loja Lucas Fontana\n// A faixa de título que abre as páginas de listagem e as institucionais (categoria, busca, FAQ):\n// breadcrumb, kicker vermelho com régua curta (\"— Ajuda\"), título 44/36/28px, uma linha de descrição\n// e, no desktop, a contagem à direita (`meta`). Uma hairline fecha a faixa.\n// Com `showSearch`, põe o SearchBar grande embaixo da descrição (é o FAQ), sem o botão \"Buscar\".\n// Não desenha breadcrumb nem busca: usa Breadcrumb e SearchBar por parâmetro (e repassa Icon e Button para a busca).\n// A descrição chega por `description` (o nome `text` é reservado do bloco base do Data2).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst PAD = { desktop: '32px 96px 40px', tablet: '28px 40px 32px', mobile: '24px 20px 28px' };\nconst CRUMB_H = { desktop: 24, tablet: 24, mobile: 30 };\nconst SEARCH_H = { desktop: 56, tablet: 52, mobile: 52 };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.pgh { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  width: 100%; height: 100%; background: var(--white); border-bottom: 1px solid var(--line); color: var(--ink); }\np, h1 { margin: 0; }\n.in { display: flex; flex-direction: column; gap: 16px; }\n.crumb { width: 100%; }\n.row { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; }\n.txt { display: flex; flex-direction: column; gap: 10px; min-width: 0; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; color: var(--ink); overflow-wrap: break-word; }\n.desc { margin-top: 6px; font-size: 17px; line-height: 1.5; color: var(--grey-700); max-width: 640px; }\n.meta { flex-shrink: 0; font-size: 14px; color: var(--grey-500); padding-bottom: 2px; }\n.search { width: 100%; max-width: 656px; margin-top: 8px; }\n\n.l-tablet .in { gap: 14px; }\n.l-tablet .title { font-size: 36px; }\n.l-tablet .desc { font-size: 16px; }\n\n.l-mobile .in { gap: 12px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 28px; font-stretch: 112%; line-height: 1.02; letter-spacing: 0; }\n.l-mobile .desc { font-size: 15px; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const bcRef = refOf(d2.param('breadcrumb'));\n    const sbRef = refOf(d2.param('searchBar'));\n    const iconRef = refOf(d2.param('searchIcon'));\n    const btnRef = refOf(d2.param('button'));\n\n    const crumbs = list(p.crumbs).length ? list(p.crumbs) : [{ label: 'Início', href: '/' }, { label: p.title || 'Perguntas frequentes' }];\n    const eyebrow = p.eyebrow == null ? 'Ajuda' : p.eyebrow;\n    const title = p.title || 'Perguntas frequentes';\n    const desc = p.description == null ? 'As dúvidas que mais chegam por aqui, respondidas sem enrolação.' : p.description;\n    const showSearch = p.showSearch == null ? true : truthy(p.showSearch);\n\n    const crumb = bcRef\n      ? `<div class=\"crumb\" style=\"height:${CRUMB_H[layout]}px\">${d2.render(bcRef, { items: crumbs, layout }, {\n          key: 'bc', onNavigate: (h) => { try { d2.emit('onNavigate', h); } catch (err) { /* sem handler */ } } })}</div>`\n      : '';\n\n    const search = showSearch && sbRef\n      ? `<div class=\"search\" style=\"height:${SEARCH_H[layout]}px\">${d2.render(sbRef, {\n          layout, size: 'lg', showButton: false, searchIcon: iconRef, button: btnRef,\n          placeholder: p.searchPlaceholder || 'Busque sua dúvida: frete, troca, pagamento...',\n          shortPlaceholder: p.searchShortPlaceholder || 'Busque sua dúvida',\n          ariaLabel: p.searchLabel || 'Buscar nas perguntas frequentes',\n          busTopic: p.searchTopic == null ? 'faq:search' : p.searchTopic\n        }, { key: 'search', onSearch: (q) => { try { d2.emit('onSearch', q); } catch (err) { /* sem handler */ } } })}</div>`\n      : '';\n\n    const meta = layout === 'desktop' && p.meta ? `<span class=\"meta\">${esc(p.meta)}</span>` : '';\n\n    return `<div class=\"pgh l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      ${crumb}\n      <div class=\"row\">\n        <div class=\"txt\">\n          ${eyebrow ? `<p class=\"kicker\">${esc(eyebrow)}</p>` : ''}\n          <h1 class=\"title\">${esc(title)}</h1>\n          ${desc ? `<p class=\"desc\">${esc(desc)}</p>` : ''}\n        </div>\n        ${meta}\n      </div>\n      ${search}\n    </div></div>`;\n  }\n};"}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"crumbs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"crumbs"}}}},"description":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"description"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"meta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"meta"}}}},"onNavigate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNavigate"}}}},"onSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSearch"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"searchIcon":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchIcon"}}}},"searchLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchLabel"}}}},"searchPlaceholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchPlaceholder"}}}},"searchShortPlaceholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchShortPlaceholder"}}}},"searchTopic":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchTopic"}}}},"showSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showSearch"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6aba77d12c4424598ddc5a99","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FaqLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FaqLayout — Auto Super Shop\n// O corpo da página de perguntas frequentes.\n// Desktop: coluna de 264px à esquerda, presa no scroll, com o menu \"Temas\" (tema atual preenchido em ink)\n// e o SupportCard embaixo; as perguntas (FaqGroups) à direita.\n// Tablet e mobile: os temas viram uma fileira de chips que rola para o lado, acima das perguntas,\n// e o SupportCard vai para o fim.\n// O tema atual acompanha a rolagem da página; clicar num tema rola até ele.\n// Busca: escuta o tópico `searchTopic` (padrão \"faq:search\", o mesmo que o PageHeader manda) ou recebe `query`,\n// mostra só os temas e perguntas que batem e, se nada bater, a mensagem de \"não achamos\".\n// Não desenha tema, pergunta nem card: usa FaqGroup, Accordion, Icon, SupportCard e Button por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst fold = (s) => String(s == null ? '' : s).normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase().trim();\nconst PAD = { desktop: '40px 96px 120px', tablet: '32px 40px 88px', mobile: '24px 20px 64px' };\n\nconst DEMO = [\n  { id: 'compras', title: 'Compras e pagamento', open: [0], items: [\n    { question: 'Quais formas de pagamento vocês aceitam?', answer: 'Pix e cartão de crédito, com parcelamento em até [N]x. Não trabalhamos com boleto.' },\n    { question: 'É seguro comprar aqui?', answer: '[Resposta]' },\n    { question: 'Preciso ter conta para comprar?', answer: '[Resposta]' }] },\n  { id: 'entrega', title: 'Entrega e rastreio', items: [\n    { question: 'Vocês entregam para todo o Brasil?', answer: '[Resposta]' },\n    { question: 'Como acompanho meu pedido?', answer: '[Resposta]' },\n    { question: 'Por que meu pedido veio em mais de uma entrega?', answer: '[Resposta]' }] },\n  { id: 'trocas', title: 'Trocas e devoluções', items: [\n    { question: 'Posso desistir da compra?', answer: '[Resposta]' },\n    { question: 'O produto chegou com defeito. E agora?', answer: '[Resposta]' }] },\n  { id: 'produtos', title: 'Produtos', items: [\n    { question: 'O produto que eu quero está esgotado. Vai voltar?', answer: '[Resposta]' },\n    { question: 'Como sei se a peça serve no meu carro?', answer: '[Resposta]' }] },\n  { id: 'conta', title: 'Minha conta', items: [\n    { question: 'Esqueci minha senha. Como faço?', answer: '[Resposta]' },\n    { question: 'Como altero meus dados ou endereço?', answer: '[Resposta]' }] }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.faq { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-500: #6b6e73; --grey-700: #45474b;\n  --line: #e4e4e2; --line-strong: #d9d9d6;\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased;\n  width: 100%; background: var(--white); color: var(--ink); }\np, h2 { margin: 0; }\n.in { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 56px; align-items: start; }\n.side { position: sticky; top: 24px; display: flex; flex-direction: column; gap: 32px; }\n.nav { display: flex; flex-direction: column; }\n.nt { margin: 0 0 12px; font-size: 12px; font-weight: 800; letter-spacing: .24em; text-transform: uppercase; color: var(--ink); }\n.topic { display: flex; align-items: center; width: 100%; min-height: 48px; padding: 0 16px; margin: 0; border: 0; background: transparent;\n  font: 700 15px/1.25 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; color: var(--ink); text-align: left; cursor: pointer; }\n.topic:hover { background: var(--grey-50); }\n.topic[aria-current=\"true\"] { background: var(--ink); color: var(--white); }\n.topic:focus-visible, .chip:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.main { display: flex; flex-direction: column; gap: 32px; min-width: 0; }\n.chips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding-bottom: 4px; }\n.chips::-webkit-scrollbar { display: none; }\n.chip { flex: none; display: inline-flex; align-items: center; height: 44px; padding: 0 16px; margin: 0; border: 1px solid var(--line-strong);\n  border-radius: 0; background: var(--white); font: 700 14px/1 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;\n  color: var(--ink); white-space: nowrap; cursor: pointer; }\n.chip:hover { border-color: var(--ink); }\n.chip[aria-current=\"true\"] { background: var(--ink); border-color: var(--ink); color: var(--white); }\n.groups { display: flex; flex-direction: column; gap: 48px; }\n.g { scroll-margin-top: 24px; }\n.help-m { margin-top: 16px; }\n.empty { display: flex; flex-direction: column; gap: 10px; padding: 32px 0 8px; border-top: 2px solid var(--ink); }\n.empty .k { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; color: var(--red-500); }\n.empty .k::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.empty h2 { font-size: 24px; font-weight: 800; font-stretch: 115%; line-height: 1.1; text-transform: uppercase; overflow-wrap: anywhere; }\n.empty p { font-size: 15px; line-height: 1.6; color: var(--grey-700); max-width: 520px; }\n\n.l-tablet .in, .l-mobile .in { grid-template-columns: minmax(0, 1fr); gap: 32px; }\n.l-mobile .in { gap: 28px; }\n.l-mobile .main { gap: 28px; }\n.l-mobile .groups { gap: 40px; }\n.l-mobile .empty h2 { font-size: 20px; }\n`,\n\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font';\n      l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    // Busca vinda do PageHeader (ou de qualquer SearchBar com o mesmo busTopic)\n    const topic = (d2.params || {}).searchTopic;\n    const t = topic == null ? 'faq:search' : String(topic);\n    if (t && d2.bus && typeof d2.bus.on === 'function') d2.bus.on(t, (q) => { const v = String(q == null ? '' : q); if (d2.state.q !== v) d2.state.q = v; });\n\n    // Clique num tema: marca e rola até ele (a rolagem suspende o acompanhamento por um instante)\n    d2.on('[data-topic]', 'click', (e, el) => {\n      const id = el.getAttribute('data-topic');\n      d2.state.active = id;\n      if (d2.root) d2.root.__lfLock = Date.now() + 900;\n      const target = d2.$(`.g[data-g=\"${id}\"]`);\n      if (target && typeof target.scrollIntoView === 'function') target.scrollIntoView({ behavior: 'smooth', block: 'start' });\n      try { d2.emit('onTopic', id); } catch (err) { /* sem handler */ }\n    });\n\n    // Tema atual acompanha a rolagem: o tema cujo topo passou da faixa de cima da tela\n    if (typeof IntersectionObserver !== 'undefined') {\n      const io = new IntersectionObserver((entries) => {\n        if (d2.root && d2.root.__lfLock && Date.now() < d2.root.__lfLock) return;\n        const hit = entries.filter((en) => en.isIntersecting)\n          .sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top)[0];\n        if (hit) {\n          const id = hit.target.getAttribute('data-g');\n          if (id && d2.state.active !== id) d2.state.active = id;\n        }\n      }, { rootMargin: '-15% 0px -70% 0px', threshold: 0 });\n      const watch = () => { io.disconnect(); d2.$$('.g[data-g]').forEach((el) => io.observe(el)); };\n      d2.onUnmount(() => io.disconnect());\n      // guarda o \"watch\" fora do estado reativo\n      d2.root.__lfWatch = watch;\n      watch();\n    }\n\n    // Link direto para um tema: /perguntas-frequentes#trocas abre a página já no tema\n    try {\n      const h = typeof location !== 'undefined' ? decodeURIComponent(String(location.hash || '').replace(/^#/, '')) : '';\n      if (h) {\n        setTimeout(() => {\n          const el = d2.$(`.g[data-g=\"${h.replace(/\"/g, '')}\"]`);\n          if (el) { d2.state.active = h; if (d2.root) d2.root.__lfLock = Date.now() + 900; el.scrollIntoView({ block: 'start' }); }\n        }, 300);\n      }\n    } catch (err) { /* sem hash */ }\n  },\n\n  updated(d2) {\n    if (d2.root && typeof d2.root.__lfWatch === 'function') d2.root.__lfWatch();\n    // mantém o chip do tema atual visível na fileira\n    const chip = d2.$('.chip[aria-current=\"true\"]');\n    const row = d2.$('.chips');\n    if (chip && row) {\n      const l = chip.offsetLeft, r = l + chip.offsetWidth;\n      if (l < row.scrollLeft || r > row.scrollLeft + row.clientWidth) row.scrollLeft = Math.max(0, l - 20);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const desk = layout === 'desktop';\n    const fgMod = refOf(d2.param('faqGroup'));\n    const accMod = refOf(d2.param('accordion'));\n    const iconMod = refOf(d2.param('iconComponent'));\n    const scMod = refOf(d2.param('supportCard'));\n    const btnMod = refOf(d2.param('button'));\n\n    const groups = (list(p.groups).length ? list(p.groups) : DEMO)\n      .map((g, i) => ({ id: String((g && g.id) || 'tema-' + (i + 1)), title: (g && g.title) || '', items: list(g && g.items), open: g && g.open }))\n      .filter((g) => g.title);\n    const labels = {};\n    list(p.topics).forEach((t) => { if (t && t.id) labels[String(t.id)] = t.label; });\n\n    const q = fold(p.query != null && p.query !== '' ? p.query : d2.state.q);\n    const rawQ = String(p.query != null && p.query !== '' ? p.query : (d2.state.q || '')).trim();\n    const hits = (g) => !q || g.items.some((it) => fold(it && it.question).indexOf(q) >= 0 || fold(it && it.answer).indexOf(q) >= 0);\n    const visible = groups.filter(hits);\n\n    const current = d2.state.active && visible.some((g) => g.id === d2.state.active)\n      ? d2.state.active\n      : (p.current && visible.some((g) => g.id === String(p.current)) ? String(p.current) : (visible[0] && visible[0].id));\n\n    const topicBtn = (g, cls) => `<button type=\"button\" class=\"${cls}\" data-topic=\"${esc(g.id)}\" data-key=\"${cls}-${esc(g.id)}\"${g.id === current ? ' aria-current=\"true\"' : ''}>${esc(labels[g.id] || g.title)}</button>`;\n\n    const groupHtml = visible.map((g) => `<div class=\"g\" data-g=\"${esc(g.id)}\" data-key=\"g-${esc(g.id)}\">${fgMod\n      ? d2.render(fgMod, {\n          anchor: g.id, title: g.title, items: g.items, defaultOpen: g.open == null ? [] : g.open, single: truthy(p.single),\n          query: q ? rawQ : '', layout, accordion: accMod, iconComponent: iconMod\n        }, { key: 'fg-' + g.id + (q ? '-q-' + q : ''), style: 'height:auto' })\n      : ''}</div>`).join('');\n\n    const empty = !visible.length ? `<div class=\"empty\" role=\"status\">\n        <p class=\"k\">${esc(p.emptyEyebrow || 'Busca')}</p>\n        <h2>Não achamos nada para “${esc(rawQ)}”</h2>\n        <p>${esc(p.emptyBody || 'Nem a Auto Super achou isso na garagem. Tenta outra palavra ou fala com o SAC, que a gente resolve.')}</p>\n      </div>` : '';\n\n    const showSupport = p.showSupport == null ? true : truthy(p.showSupport);\n    const sup = obj(p.support);\n    const support = showSupport && scMod\n      ? d2.render(scMod, Object.assign({}, sup, { layout, button: btnMod }), {\n          key: 'sup-' + (desk ? 'side' : 'end'), style: 'height:auto',\n          onPrimary: (h) => { try { d2.emit('onSupport', { action: 'primary', href: h }); } catch (err) { /* sem handler */ } },\n          onSecondary: (h) => { try { d2.emit('onSupport', { action: 'secondary', href: h }); } catch (err) { /* sem handler */ } },\n          onFootLink: (h) => { try { d2.emit('onSupport', { action: 'foot', href: h }); } catch (err) { /* sem handler */ } }\n        })\n      : '';\n\n    const navTitle = p.navTitle || 'Temas';\n    const body = desk\n      ? `<aside class=\"side\">\n          <nav class=\"nav\" aria-label=\"${esc(navTitle)}\"><p class=\"nt\">${esc(navTitle)}</p>${visible.map((g) => topicBtn(g, 'topic')).join('')}</nav>\n          ${support}\n        </aside>\n        <div class=\"main\"><div class=\"groups\">${groupHtml}</div>${empty}</div>`\n      : `<div class=\"main\">\n          ${visible.length > 1 ? `<nav class=\"chips\" aria-label=\"${esc(navTitle)}\">${visible.map((g) => topicBtn(g, 'chip')).join('')}</nav>` : ''}\n          <div class=\"groups\">${groupHtml}</div>${empty}\n          ${support ? `<div class=\"help-m\">${support}</div>` : ''}\n        </div>`;\n\n    return `<div class=\"faq l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${body}</div></div>`;\n  }\n};"}},"accordion":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accordion"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"current":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"current"}}}},"emptyBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyBody"}}}},"emptyEyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyEyebrow"}}}},"faqGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"faqGroup"}}}},"groups":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"groups"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"navTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"navTitle"}}}},"onSupport":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSupport"}}}},"onTopic":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onTopic"}}}},"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"query"}}}},"searchTopic":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchTopic"}}}},"showSupport":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showSupport"}}}},"single":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"single"}}}},"support":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"support"}}}},"supportCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"supportCard"}}}},"topics":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"topics"}}}}}}}}}},{"_id":"6ababb354a99aba9ee08d7f7","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"QuantityStepper","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// QuantityStepper — Loja Lucas Fontana\n// Controle − / número / + de 52px de altura (120px de largura; 96px no mobile), borda line-strong.\n// O \"−\" desativa no mínimo e o \"+\" no máximo. Fica à esquerda do \"Adicionar à sacola\" na BuyBox.\n// `size: \"sm\"`: 110 × 44px (botões de 38px), usado em cada item da sacola (CartItem).\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.qty { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-400: #8e9095; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: inline-flex; align-items: center; justify-content: space-between; width: 120px; height: 52px;\n  border: 1px solid var(--line-strong); background: var(--white); }\nbutton { width: 40px; height: 100%; margin: 0; padding: 0; border: 0; background: transparent; color: var(--ink);\n  display: flex; align-items: center; justify-content: center; cursor: pointer; }\nbutton:disabled { color: var(--grey-400); cursor: not-allowed; }\nbutton:focus-visible { outline: 2px solid var(--red-500); outline-offset: -2px; }\ninput { width: 36px; border: 0; padding: 0; margin: 0; text-align: center; font: 700 17px/1 ${FF}; color: var(--ink);\n  background: transparent; -moz-appearance: textfield; appearance: textfield; outline: none; }\ninput::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n.l-mobile { width: 96px; }\n.l-mobile button { width: 32px; }\n.l-mobile input { width: 28px; }\n.qty.sm { width: 110px; height: 44px; }\n.qty.sm button { width: 38px; }\n.qty.sm input { width: 32px; font-size: 16px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const bounds = () => {\n      const p = d2.params || {};\n      const min = p.min == null || p.min === '' ? 1 : Number(p.min);\n      const max = p.max == null || p.max === '' ? 99 : Number(p.max);\n      return { min, max };\n    };\n    const set = (v) => {\n      const { min, max } = bounds();\n      const n = Math.max(min, Math.min(max, Number.isFinite(v) ? v : min));\n      d2.state.v = n;\n      emit(d2, 'onChange', n);\n    };\n    const cur = () => {\n      const p = d2.params || {};\n      return d2.state.v != null ? d2.state.v : Number(p.value || p.defaultValue || bounds().min);\n    };\n    d2.on('.dec', 'click', () => set(cur() - 1));\n    d2.on('.inc', 'click', () => set(cur() + 1));\n    d2.on('input', 'change', (e, el) => set(parseInt(el.value, 10)));\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const min = p.min == null || p.min === '' ? 1 : Number(p.min);\n    const max = p.max == null || p.max === '' ? 99 : Number(p.max);\n    const v = d2.state.v != null ? d2.state.v : Math.max(min, Math.min(max, Number(p.value || p.defaultValue || min)));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const sm = p.size === 'sm';\n    const minus = iconRef ? ic(d2, iconRef, 'minus', sm ? 16 : 18, 'currentColor', 'm') : '−';\n    const plus = iconRef ? ic(d2, iconRef, 'plus', sm ? 16 : 18, 'currentColor', 'p') : '+';\n    return `<div class=\"qty l-${layout}${sm ? ' sm' : ''}\" role=\"group\" aria-label=\"${esc(p.ariaLabel || 'Quantidade')}\">\n      <button type=\"button\" class=\"dec\" aria-label=\"Diminuir quantidade\"${v <= min ? ' disabled' : ''}>${minus}</button>\n      <input type=\"number\" inputmode=\"numeric\" min=\"${min}\" max=\"${max}\" value=\"${v}\" aria-label=\"Quantidade\">\n      <button type=\"button\" class=\"inc\" aria-label=\"Aumentar quantidade\"${v >= max ? ' disabled' : ''}>${plus}</button>\n    </div>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"defaultValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"defaultValue"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"max":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"max"}}}},"min":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"min"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6ababb42f281aa28d4cc4577","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CheckList","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CheckList — Loja Lucas Fontana\n// Benefícios com um check branco num quadrado vermelho de 20px. Texto de 15px, 12px entre as linhas.\n// De três a cinco itens curtos, uma ideia cada. Usado no ProductDetails (aba Descrição) e no PromoCard.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\n// `tone: \"dark\"` deixa o texto branco, para fundo escuro (PromoCard).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = ['[Benefício principal do produto]', '[Segundo benefício — ex.: rendimento, proteção, cheiro]', '[Terceiro benefício — ex.: seguro para a pintura]'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nul { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }\nli { display: flex; gap: 12px; align-items: flex-start; font-size: 15px; line-height: 1.45; color: var(--ink); }\n.tone-dark li { color: var(--white); }\ni { flex: none; width: 20px; height: 20px; margin-top: 1px; background: var(--red-500); color: var(--white);\n  display: flex; align-items: center; justify-content: center; font-style: normal; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const items = list(p.items).length ? list(p.items) : DEMO;\n    const iconRef = refOf(d2.param('iconComponent'));\n    return `<ul class=\"l-${layout}${p.tone === 'dark' ? ' tone-dark' : ''}\">${items.map((t, k) => `<li><i aria-hidden=\"true\">${ic(d2, iconRef, 'check', 13, '#ffffff', 'c' + k)}</i><span>${esc(t)}</span></li>`).join('')}</ul>`;\n  }\n};"}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6ababb53440fbc0d402d13aa","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"BenefitList","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// BenefitList — Loja Lucas Fontana\n// As linhas de garantia abaixo dos botões de compra: ícone, título em negrito e uma linha cinza ou um link,\n// separadas por hairlines. Três linhas, fatos e não slogans.\n// Padrão: \"Enviamos para todo o Brasil\" (truck), \"Pix ou cartão em até [N]x\" (card), \"Troca e devolução\" + link (swap).\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEFAULTS = [\n  { icon: 'truck', title: 'Enviamos para todo o Brasil', text: 'O frete é calculado no fechamento do pedido.' },\n  { icon: 'card', title: 'Pix ou cartão em até [N]x', text: 'Pagamento seguro, com antifraude em todas as compras.' },\n  { icon: 'swap', title: 'Troca e devolução', link: { label: 'Ver política de trocas', href: '/politicas/trocas-e-devolucoes' } }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nul { --red-500: #c8102e; --ink: #0b0b0c; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }\nli { display: flex; gap: 14px; align-items: flex-start; padding: 14px 0; border-bottom: 1px solid var(--line); color: var(--ink); }\n.ic { flex: none; width: 20px; height: 20px; margin-top: 2px; display: flex; }\nb { display: block; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.35; }\nspan.t { display: block; font-size: 14px; line-height: 1.45; color: var(--grey-700); }\na { display: inline-block; font-size: 14px; font-weight: 600; line-height: 1.45; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }\na:hover { color: var(--red-500); }\na:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n`,\n  mounted(d2) {\n    FONT();\n    d2.on('a', 'click', (e, el) => emit(d2, 'onLink', el.getAttribute('href')));\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const items = list(p.items).length ? list(p.items) : DEFAULTS;\n    const iconRef = refOf(d2.param('iconComponent'));\n    return `<ul class=\"l-${layout}\">${items.map((b, k) => `<li>\n      <span class=\"ic\" aria-hidden=\"true\">${ic(d2, iconRef, (b && b.icon) || 'check', 20, '#0b0b0c', 'b' + k + (b && b.icon || ''))}</span>\n      <div><b>${esc(b && b.title)}</b>${b && b.text ? `<span class=\"t\">${esc(b.text)}</span>` : ''}${b && b.link && b.link.label ? `<a href=\"${esc(b.link.href || '#')}\">${esc(b.link.label)}</a>` : ''}</div>\n    </li>`).join('')}</ul>`;\n  }\n};"}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}}}}}}}},{"_id":"6ababb5f80008124dcffddb4","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"QuoteCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// QuoteCard — Loja Lucas Fontana\n// A palavra do Lucas sobre o produto: card preto com xadrez no canto, eyebrow vermelho \"Por que o Lucas recomenda\",\n// a frase em 22px itálico (20px no mobile) e \"— Lucas Fontana\". Use uma fala real do vídeo, com menos de 30 palavras.\n// A frase chega por `quote`, sem aspas (o card coloca).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nfigure { --ink: #0b0b0c; --white: #ffffff; --red-300: #ff4d62; --grey-300: #a7a9ad;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  position: relative; margin: 0; padding: 28px 28px 30px; background: var(--ink); color: var(--white); overflow: hidden; }\n.checker { position: absolute; top: 0; right: 0; width: 96px; height: 48px; pointer-events: none;\n  background: repeating-conic-gradient(rgba(255,255,255,.08) 0 25%, transparent 0 50%) 0 0 / 16px 16px; }\n.eb { position: relative; display: block; margin-bottom: 14px; padding-right: 88px; font-size: 11px; font-weight: 700; letter-spacing: .3em; line-height: 1.4; text-transform: uppercase; color: var(--red-300); }\nblockquote { position: relative; margin: 0; font-size: 22px; font-weight: 800; font-style: italic; font-stretch: 105%; line-height: 1.3; }\nfigcaption { position: relative; margin-top: 16px; font-size: 14px; color: var(--grey-300); }\n.l-mobile { padding: 24px 22px 26px; }\n.l-mobile blockquote { font-size: 20px; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const q = String(p.quote == null || p.quote === '' ? '[Comentário do Lucas sobre o produto — pode vir de uma fala do vídeo.]' : p.quote).replace(/^[\"“”]+|[\"“”]+$/g, '');\n    return `<figure class=\"l-${layout}\">\n      <span class=\"checker\" aria-hidden=\"true\"></span>\n      <span class=\"eb\">${esc(p.eyebrow || 'Por que o Lucas recomenda')}</span>\n      <blockquote>“${esc(q)}”</blockquote>\n      <figcaption>— ${esc(p.author || 'Lucas Fontana')}</figcaption>\n    </figure>`;\n  }\n};"}},"author":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"author"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"quote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quote"}}}}}}}}}},{"_id":"6ababb8130fdedf71ec851a4","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ProductGallery","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ProductGallery — Loja Lucas Fontana\n// Fotos e vídeo do produto.\n// Desktop: coluna de miniaturas de 88px (a selecionada contornada em preto) ao lado da imagem quadrada,\n// com anterior/próximo no canto inferior direito.\n// Tablet: miniaturas em linha embaixo da imagem (72px de altura), imagem em 4:3,2.\n// Mobile: só a imagem, de ponta a ponta, com o contador \"1/5\" e tracinhos vermelhos embaixo; arraste para trocar.\n// Um item de vídeo mostra um quadrado vermelho de play na miniatura. Coloque o vídeo por último.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [{ label: 'Foto 1' }, { label: 'Foto 2' }, { label: 'Foto 3' }, { label: 'Foto 4' }, { video: true, label: 'Vídeo' }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.gal { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-100: #ececea; --grey-500: #6b6e73; --line: #e4e4e2; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: grid; grid-template-columns: 88px minmax(0, 1fr); gap: 16px; align-items: start; width: 100%; }\n.thumbs { display: flex; flex-direction: column; gap: 12px; }\n.thumb { position: relative; width: 88px; height: 88px; margin: 0; padding: 0; border: 2px solid transparent; background: var(--grey-100); cursor: pointer;\n  display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.thumb[aria-current=\"true\"] { border-color: var(--ink); }\n.thumb:focus-visible, .arrows button:focus-visible, .dots button:focus-visible, .play-big:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.ph { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); text-align: center; }\n.thumb .ph { font-size: 10px; }\n.play { position: relative; width: 32px; height: 32px; background: var(--red-500); color: var(--white); display: flex; align-items: center; justify-content: center; }\n.main { position: relative; aspect-ratio: 1 / 1; background: var(--grey-100); display: flex; align-items: center; justify-content: center; overflow: hidden; touch-action: pan-y; user-select: none; }\n.main img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.main .ph { font-size: 12px; }\n.play-big { position: relative; width: 64px; height: 64px; margin: 0; padding: 0; border: 0; background: var(--red-500); color: var(--white); display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.arrows { position: absolute; right: 16px; bottom: 16px; display: flex; gap: 8px; }\n.arrows button { width: 48px; height: 48px; margin: 0; padding: 0; border: 1px solid var(--line); background: var(--white); color: var(--ink); display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.arrows button:hover { border-color: var(--ink); }\n.count { display: none; position: absolute; right: 12px; bottom: 12px; padding: 5px 8px; background: var(--white); font-size: 12px; font-weight: 700; color: var(--ink); }\n.dots { display: none; justify-content: center; gap: 4px; }\n.dots button { width: 38px; height: 32px; margin: 0; padding: 0 4px; border: 0; background: transparent; display: flex; align-items: center; cursor: pointer; }\n.dots span { display: block; width: 100%; height: 3px; background: var(--line-strong); }\n.dots [aria-current=\"true\"] span { background: var(--red-500); }\n\n.l-tablet { grid-template-columns: minmax(0, 1fr); gap: 10px; }\n.l-tablet .thumbs { order: 2; flex-direction: row; gap: 10px; }\n.l-tablet .thumb { flex: 1; width: auto; height: 72px; }\n.l-tablet .main { aspect-ratio: 4 / 3.2; }\n\n.l-mobile { grid-template-columns: minmax(0, 1fr); gap: 8px; }\n.l-mobile .thumbs, .l-mobile .arrows { display: none; }\n.l-mobile .count, .l-mobile .dots { display: flex; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const items = () => { const p = d2.params || {}; return list(p.items).length ? list(p.items) : DEMO; };\n    const go = (k) => {\n      const n = items().length;\n      if (!n) return;\n      const i = ((k % n) + n) % n;\n      if (d2.state.i !== i) { d2.state.i = i; emit(d2, 'onChange', i); }\n    };\n    const cur = () => d2.state.i || 0;\n    d2.on('[data-go]', 'click', (e, el) => go(Number(el.getAttribute('data-go'))));\n    d2.on('.prev', 'click', () => go(cur() - 1));\n    d2.on('.next', 'click', () => go(cur() + 1));\n    d2.on('.play-big', 'click', (e, el) => {\n      const href = el.getAttribute('data-href');\n      emit(d2, 'onVideo', href || '');\n      if (href && typeof window !== 'undefined') window.open(href, '_blank', 'noopener');\n    });\n    // Arrastar para trocar (toque e mouse)\n    let x0 = null;\n    d2.on('.main', 'pointerdown', (e) => { x0 = e.clientX; });\n    d2.on('.main', 'pointerup', (e) => {\n      if (x0 == null) return;\n      const dx = e.clientX - x0; x0 = null;\n      if (Math.abs(dx) > 40) go(cur() + (dx < 0 ? 1 : -1));\n    });\n    d2.on('.gal', 'keydown', (e) => {\n      if (e.key === 'ArrowRight') { e.preventDefault(); go(cur() + 1); }\n      if (e.key === 'ArrowLeft') { e.preventDefault(); go(cur() - 1); }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const items = list(p.items).length ? list(p.items) : DEMO;\n    const n = items.length;\n    const i = Math.min(d2.state.i || 0, Math.max(0, n - 1));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const cur = items[i] || {};\n    const label = (it, k) => (it.video ? 'Vídeo' : 'Foto ' + (k + 1)) + ' de ' + n;\n\n    const thumbs = items.map((it, k) => {\n      const inner = it.video\n        ? `${it.image ? `<img src=\"${esc(it.image)}\" alt=\"\">` : ''}<span class=\"play\">${ic(d2, iconRef, 'play', 14, '#ffffff', 'tp' + k)}</span>`\n        : (it.image ? `<img src=\"${esc(it.image)}\" alt=\"\">` : `<span class=\"ph\">${esc(it.label || 'Foto ' + (k + 1))}</span>`);\n      return `<button type=\"button\" class=\"thumb\" data-go=\"${k}\" aria-current=\"${k === i ? 'true' : 'false'}\" aria-label=\"${esc(label(it, k))}\">${inner}</button>`;\n    }).join('');\n\n    const big = cur.image\n      ? `<img src=\"${esc(cur.image)}\" alt=\"${esc(cur.alt || '')}\" draggable=\"false\">`\n      : (cur.video ? '' : `<span class=\"ph\">Foto ${i + 1} de ${n}</span>`);\n    const videoHref = typeof cur.video === 'string' ? cur.video : (cur.href || '');\n    const play = cur.video\n      ? `<button type=\"button\" class=\"play-big\" data-href=\"${esc(videoHref)}\" aria-label=\"Assistir ao vídeo\">${ic(d2, iconRef, 'play', 26, '#ffffff', 'pb')}</button>`\n      : '';\n\n    const arrows = n > 1 ? `<div class=\"arrows\">\n        <button type=\"button\" class=\"prev\" aria-label=\"Foto anterior\">${ic(d2, iconRef, 'chevron-left', 18, '#0b0b0c', 'al')}</button>\n        <button type=\"button\" class=\"next\" aria-label=\"Próxima foto\">${ic(d2, iconRef, 'chevron-right', 18, '#0b0b0c', 'ar')}</button>\n      </div>` : '';\n    const count = n > 1 ? `<span class=\"count\" aria-live=\"polite\">${i + 1}/${n}</span>` : '';\n    const dots = n > 1 ? `<div class=\"dots\">${items.map((it, k) => `<button type=\"button\" data-go=\"${k}\" aria-label=\"Ir para a foto ${k + 1}\" aria-current=\"${k === i ? 'true' : 'false'}\"><span></span></button>`).join('')}</div>` : '';\n\n    return `<div class=\"gal l-${layout}\" role=\"group\" aria-roledescription=\"galeria\" aria-label=\"${esc(p.ariaLabel || 'Fotos do produto')}\" tabindex=\"-1\">\n      <div class=\"thumbs\">${thumbs}</div>\n      <div class=\"main\">${big}${play}${arrows}${count}</div>\n      ${dots}\n    </div>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onVideo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVideo"}}}}}}}}}},{"_id":"6ababba9f8aee52476044a72","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"BuyBox","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// BuyBox — Loja Lucas Fontana\n// Tudo para decidir e comprar: etiqueta vermelha da categoria, nome do produto (44/32/30px), \"Cód. [SKU]\",\n// selo de parceiro, o Price grande, quantidade + \"Adicionar ao carrinho\" (preto), \"Comprar agora\" (o único vermelho)\n// e a BenefitList.\n// Esgotado (`available: false`): o selo \"Volta já já\" e o \"Me avise quando chegar\" (contorno) no lugar dos botões.\n// O \"Me avise quando chegar\" funciona sozinho (precisa de `slug`): cliente logado → guarda o e-mail da sessão direto;\n// visitante → abre o campo de e-mail (FormField), valida e guarda. Serviços: minhaSessaoContato e leadAvisoSalvar.\n// Estados (d2.state.ns): idle · checking · sending · form · formSending · done. Erro em d2.state.nerr.\n// `partner` (ex.: \"BPower\"): põe o selo \"Parceiro BPower\" e troca a 1ª linha de benefícios para \"Enviado pela BPower\".\n// Não desenha selo, preço, quantidade, botão, campo, aviso nem lista: usa Badge, Price, QuantityStepper, Button, FormField, Notice e BenefitList por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst BENEFITS = [\n  { icon: 'truck', title: 'Enviamos para todo o Brasil', text: 'O frete é calculado no fechamento do pedido.' },\n  { icon: 'card', title: 'Pix ou cartão em até [N]x', text: 'Pagamento seguro, com antifraude em todas as compras.' },\n  { icon: 'swap', title: 'Troca e devolução', link: { label: 'Ver política de trocas', href: '/politicas/trocas-e-devolucoes' } }\n];\n\n// ---- \"Me avise quando chegar\" ----\nconst MAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst NET_ERR = 'Não deu pra salvar agora. Confere sua conexão e tenta de novo.';\nconst MAIL_ERR = 'Confere o e-mail: falta o @ ou o domínio.';\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res) || {};\n// Avisos já pedidos neste navegador (slug → data), por 30 dias; saem quando o produto volta a ter estoque.\n// Mesma chave do ProductCard, para o card e a página do produto concordarem.\nconst LS_KEY = 'lf-avisos';\nconst LS_TTL = 30 * 24 * 3600 * 1000;\nconst readAvisos = () => { try { const o = JSON.parse(localStorage.getItem(LS_KEY) || '{}'); return o && typeof o === 'object' && !Array.isArray(o) ? o : {}; } catch (e) { return {}; } };\nconst hasAviso = (slug) => { if (!slug) return false; const t = Number(readAvisos()[slug]); return !!t && (Date.now() - t) < LS_TTL; };\nconst saveAviso = (slug) => { try { const o = readAvisos(); o[slug] = Date.now(); localStorage.setItem(LS_KEY, JSON.stringify(o)); } catch (e) { /* sem storage */ } };\nconst dropAviso = (slug) => { try { const o = readAvisos(); if (o[slug]) { delete o[slug]; localStorage.setItem(LS_KEY, JSON.stringify(o)); } } catch (e) { /* sem storage */ } };\n\n// Guarda o interesse. `email` vazio = cliente logado (o serviço usa o e-mail da sessão). `fromForm` = veio do campo de e-mail.\nconst sendLead = async (d2, email, fromForm, website) => {\n  const slug = String((d2.params || {}).slug || '').trim();\n  d2.state.ns = fromForm ? 'formSending' : 'sending';\n  d2.state.nerr = '';\n  let out = {};\n  try {\n    const res = await d2.rush('leadAvisoSalvar').invoke({ slug, email: email || '', website: website || '' });\n    out = unwrap(res);\n  } catch (err) { out = { ok: false, error: NET_ERR }; }\n  if (out && out.ok) { saveAviso(slug); d2.state.ns = 'done'; return; }\n  d2.state.nerr = (out && out.error) || NET_ERR;\n  d2.state.ns = fromForm ? 'form' : 'idle';\n};\n\n// 1º clique: logado → envia direto; deslogado → abre o campo de e-mail.\nconst startNotify = async (d2) => {\n  if ((d2.state.ns || 'idle') !== 'idle') return;\n  d2.state.ns = 'checking';\n  d2.state.nerr = '';\n  let s = {};\n  try { s = unwrap(await d2.rush('minhaSessaoContato').invoke({})); } catch (err) { s = {}; }\n  if (s && s.logado) await sendLead(d2, '', false, '');\n  else d2.state.ns = 'form';\n};\n\n// Clique em \"Avisar-me\" com o campo de e-mail aberto.\nconst submitNotify = async (d2) => {\n  if ((d2.state.ns || '') !== 'form') return;\n  const email = String((d2.root && d2.root.__lfNotifyEmail) || '').trim();\n  if (!MAIL_RE.test(email)) { d2.state.nerr = MAIL_ERR; return; }\n  const hp = d2.$ ? d2.$('.hp') : null;\n  await sendLead(d2, email, true, hp ? hp.value : '');\n};\n\n// ---- Adicionar à sacola / Comprar agora: grava na sacola do navegador (mesma chave da página Sacola) ----\nconst bagAdd = (sku, qty) => {\n  try {\n    const raw = JSON.parse(localStorage.getItem('lf-sacola') || '[]');\n    const l = Array.isArray(raw) ? raw : [];\n    const f = l.find((y) => y && y.sku === sku);\n    if (f) f.qty = (Number(f.qty) || 0) + qty; else l.push({ sku, qty });\n    localStorage.setItem('lf-sacola', JSON.stringify(l));\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: l.reduce((s, y) => s + (Number(y.qty) || 0), 0) } }));\n    return true;\n  } catch (e) { return false; }\n};\nlet toastTimer = null;\nconst toast = (msg, ok) => {\n  try {\n    let el = document.getElementById('lf-toast');\n    if (!el) { el = document.createElement('div'); el.id = 'lf-toast'; el.setAttribute('role', 'status'); el.setAttribute('aria-live', 'polite'); document.body.appendChild(el); }\n    const mobile = window.innerWidth < 640;\n    el.style.cssText = `position:fixed;z-index:2147483000;${mobile ? 'left:16px;right:16px;bottom:16px;' : 'top:24px;right:24px;max-width:420px;'}background:#0b0b0c;color:#fff;padding:16px 20px;font-family:Archivo,system-ui,sans-serif;font-size:14px;line-height:1.4;display:flex;gap:14px;align-items:center;box-shadow:0 8px 24px rgba(0,0,0,.25);border-left:4px solid ${ok ? '#2e9e5b' : '#c8102e'}`;\n    el.innerHTML = `<span style=\"flex:1\">${esc(msg)}</span>${ok ? '<a href=\"/sacola\" style=\"color:#fff;font-weight:700;text-decoration:underline;white-space:nowrap\">Ver sacola</a>' : ''}`;\n    clearTimeout(toastTimer);\n    toastTimer = setTimeout(() => { el.style.display = 'none'; }, 4000);\n  } catch (e) { /* sem DOM */ }\n};\nconst addProduct = (d2, go) => {\n  const p = d2.params || {};\n  const sku = String(p.sku || '');\n  if (d2.state.buy === 'going' || (!go && d2.state.add === 'done')) return;\n  if (!sku || sku === '[SKU]') { toast('Não deu pra adicionar agora. Tenta de novo.', false); return; }\n  const qty = Math.max(1, Number((d2.root && d2.root.__lfQty) || 1) || 1);\n  if (!bagAdd(sku, qty)) { toast('Não deu pra adicionar agora. Tenta de novo.', false); return; }\n  if (go) {\n    d2.state.buy = 'going';\n    try { window.location.assign('/sacola'); } catch (e) { d2.state.buy = ''; }\n    return;\n  }\n  d2.state.add = 'done';\n  toast(`${String(p.name || 'Produto')} foi adicionado à sacola.`, true);\n  setTimeout(() => { d2.state.add = ''; }, 2500);\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.buy { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; width: 100%; color: var(--ink); }\np, h1 { margin: 0; }\n.cat { font-size: 12px; font-weight: 700; letter-spacing: .24em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); margin-bottom: 12px; }\n.title { font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: .9; letter-spacing: -.01em; text-transform: uppercase; color: var(--ink); overflow-wrap: break-word; }\n.sku { margin-top: 10px; font-size: 14px; color: var(--grey-500); }\n.badge { margin-top: 12px; height: 24px; }\n.sec { padding: 20px 0 24px; border-bottom: 1px solid var(--line); }\n.actions { display: flex; flex-direction: column; gap: 12px; padding: 24px 0; }\n.row { display: flex; gap: 12px; align-items: stretch; }\n.qty { flex: none; width: 120px; height: 52px; }\n.grow { flex: 1; min-width: 0; height: 52px; }\n.full { width: 100%; height: 52px; }\n.soldout { height: 28px; }\n.act { display: block; width: 100%; height: 100%; }\n.nform { position: relative; display: flex; flex-direction: column; gap: 12px; }\n.nerr { font-size: 13px; line-height: 1.4; color: var(--red-500); }\n.hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }\n\n.l-tablet .title { font-size: 32px; line-height: .95; }\n.l-tablet .cat { font-size: 11px; margin-bottom: 10px; }\n\n.l-mobile .cat { font-size: 11px; margin-bottom: 10px; }\n.l-mobile .title { font-size: 30px; font-stretch: 112%; line-height: .98; letter-spacing: 0; }\n.l-mobile .sku { font-size: 13px; }\n.l-mobile .sec { padding: 20px 0; }\n.l-mobile .actions { padding: 20px 0; gap: 10px; }\n.l-mobile .row { gap: 8px; }\n.l-mobile .qty { width: 96px; }\n.l-mobile .badge { height: 20px; }\n.l-mobile .soldout { height: 22px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    { const mp = d2.params || {}; if (mp.slug && (mp.available == null || truthy(mp.available))) dropAviso(String(mp.slug)); }\n    // Clique delegado: o Button por callback não disparou no app publicado; o span em volta resolve.\n    d2.on('[data-act]', 'click', (e, el) => {\n      const a = el.getAttribute('data-act');\n      if (a === 'add') { addProduct(d2, false); }\n      else if (a === 'buy') { addProduct(d2, true); }\n      else if (a === 'notify') { emit(d2, 'onNotify', 1); startNotify(d2); }\n      else if (a === 'notify-submit') submitNotify(d2);\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const badgeRef = refOf(d2.param('badge'));\n    const priceRef = refOf(d2.param('priceComponent'));\n    const qtyRef = refOf(d2.param('quantityStepper'));\n    const btnRef = refOf(d2.param('button'));\n    const listRef = refOf(d2.param('benefitList'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const available = p.available == null || p.available === '' ? true : truthy(p.available);\n    const partner = p.partner ? String(p.partner) : '';\n    const name = p.name || 'Shampoo automotivo neutro';\n    const category = p.category == null ? 'Lavagem' : p.category;\n    const sku = p.sku == null ? '[SKU]' : p.sku;\n    const getQty = () => (d2.root && d2.root.__lfQty) || 1;\n    const added = d2.state.add === 'done';\n    const going = d2.state.buy === 'going';\n\n    const badgeLabel = p.badgeLabel || (partner ? 'Parceiro ' + partner : '');\n    const badge = badgeLabel && badgeRef\n      ? `<div class=\"badge\">${d2.render(badgeRef, { label: badgeLabel, shortLabel: partner || '', tone: 'red', layout }, { key: 'badge' })}</div>`\n      : '';\n\n    const price = priceRef\n      ? `<div class=\"sec\">${d2.render(priceRef, {\n          price: p.price == null ? 89.9 : p.price, pixPrice: p.pixPrice, pixDiscount: p.pixDiscount,\n          installments: p.installments == null ? 3 : p.installments, oldPrice: p.oldPrice, size: 'buybox', layout\n        }, { key: 'price', style: 'height:auto' })}</div>`\n      : '';\n\n    const btn = (key, cls, label, shortLabel, variant, evt, disabled) => btnRef ? `<div class=\"${cls}\"><span class=\"act\" data-act=\"${evt === 'onBuyNow' ? 'buy' : 'add'}\">${d2.render(btnRef, {\n        label, shortLabel: shortLabel || '', variant, size: 'lg', fullWidth: 'always', layout, isDisabled: !!disabled\n      }, { key, height: 52 })}</span></div>` : '';\n\n    // Botão com clique delegado (data-act), usado no \"Me avise quando chegar\"\n    const act = (key, label, shortLabel, variant, name, disabled) => btnRef ? `<div class=\"full\"><span class=\"act\" data-act=\"${name}\">${d2.render(btnRef, {\n        label, shortLabel: shortLabel || '', variant, size: 'lg', fullWidth: 'always', layout, isDisabled: !!disabled\n      }, { key, height: 52 })}</span></div>` : '';\n\n    let actions;\n    if (available) {\n      const qty = qtyRef\n        ? `<div class=\"qty\">${d2.render(qtyRef, { layout, min: 1, max: p.maxQuantity || 99, value: 1, iconComponent: iconRef }, {\n            key: 'qty', height: 52,\n            onChange: (v) => { if (d2.root) d2.root.__lfQty = v; emit(d2, 'onQuantityChange', v); }\n          })}</div>`\n        : '';\n      actions = `<div class=\"row\">${qty}${btn(added ? 'added' : 'add', 'grow', added ? '✓ Adicionado à sacola' : (p.addLabel || 'Adicionar à sacola'), added ? '✓ Adicionado' : (p.addShortLabel || 'Adicionar'), added ? 'outline' : 'primary', 'onAdd', added)}</div>\n        ${btn(going ? 'going' : 'buy', 'full', going ? 'Indo para a sacola…' : (p.buyNowLabel || 'Comprar agora'), '', 'brand', 'onBuyNow', going)}`;\n    } else {\n      const sold = badgeRef\n        ? `<div class=\"soldout\">${d2.render(badgeRef, { label: p.soldOutLabel || 'Volta já já', isStatus: true, tone: 'ink', layout }, { key: 'sold' })}</div>`\n        : '';\n      const ns = d2.state.ns || (hasAviso(String(p.slug || '')) ? 'done' : 'idle');\n      const nerr = d2.state.nerr ? String(d2.state.nerr) : '';\n      const label = p.notifyLabel || 'Me avise quando chegar';\n      let notify;\n      if (ns === 'done') {\n        const ntRef = refOf(d2.param('notice'));\n        notify = ntRef ? d2.render(ntRef, {\n          tone: 'success', iconName: 'check', live: true, layout, iconComponent: iconRef,\n          body: p.notifyDoneText || '**Combinado!** Quando esse produto voltar ao estoque, a gente avisa por e-mail.'\n        }, { key: 'ndone', style: 'height:auto' }) : `<p class=\"nerr\" role=\"status\">Combinado! Quando esse produto voltar ao estoque, a gente avisa por e-mail.</p>`;\n      } else if (ns === 'form' || ns === 'formSending') {\n        const ffRef = refOf(d2.param('formField'));\n        const sending = ns === 'formSending';\n        // Filhos aninhados não recebem parâmetros novos: a chave muda com o erro e o campo remonta com o valor guardado\n        const field = ffRef ? d2.render(ffRef, {\n          label: 'Seu e-mail', name: 'email', type: 'email', placeholder: 'seuemail@exemplo.com', autoComplete: 'email',\n          value: (d2.root && d2.root.__lfNotifyEmail) || '',\n          hint: nerr ? '' : (p.notifyHint || 'Usamos seu e-mail só para avisar sobre este produto.'),\n          error: nerr, layout, iconComponent: iconRef\n        }, {\n          key: 'nemail-' + (nerr ? 'e' : 'o'), style: 'height:auto',\n          onInput: (v) => { if (d2.root) d2.root.__lfNotifyEmail = v; }\n        }) : '';\n        notify = `<div class=\"nform\">${field}<input class=\"hp\" type=\"text\" name=\"website\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\">${\n          act('nsub-' + ns, sending ? 'Enviando…' : 'Avisar-me', sending ? 'Enviando…' : 'Avisar-me', 'primary', 'notify-submit', sending)}</div>`;\n      } else {\n        const busy = ns === 'checking' || ns === 'sending';\n        notify = `${act('notify-' + ns, busy ? 'Enviando…' : label, busy ? 'Enviando…' : 'Avise-me', 'outline', 'notify', busy)}${nerr ? `<p class=\"nerr\" role=\"alert\">${esc(nerr)}</p>` : ''}`;\n      }\n      actions = `${sold}${notify}`;\n    }\n\n    let items = list(p.benefits);\n    if (!items.length) {\n      items = BENEFITS.slice();\n      if (partner) items[0] = { icon: 'truck', title: 'Enviado pela ' + partner, text: 'O parceiro envia direto para você e informa o código de rastreio.' };\n    }\n    const showBenefits = p.showBenefits == null || p.showBenefits === '' ? true : truthy(p.showBenefits);\n    const benefits = showBenefits && listRef\n      ? d2.render(listRef, { items, layout, iconComponent: iconRef }, {\n          key: 'ben' + (partner ? '-' + partner : ''), style: 'height:auto',\n          onLink: (h) => emit(d2, 'onBenefitLink', h)\n        })\n      : '';\n\n    return `<div class=\"buy l-${layout}\">\n      ${category ? `<p class=\"cat\">${esc(category)}</p>` : ''}\n      <h1 class=\"title\">${esc(name)}</h1>\n      ${sku ? `<p class=\"sku\">Cód. ${esc(sku)}</p>` : ''}\n      ${badge}\n      ${price}\n      <div class=\"actions\">${actions}</div>\n      ${benefits}\n    </div>`;\n  }\n};"}},"addLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addLabel"}}}},"addShortLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addShortLabel"}}}},"available":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"available"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"badgeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badgeLabel"}}}},"benefitList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"benefitList"}}}},"benefits":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"benefits"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"buyNowLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"buyNowLabel"}}}},"category":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"category"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"installments":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"installments"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"maxQuantity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"maxQuantity"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"notifyDoneText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyDoneText"}}}},"notifyHint":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyHint"}}}},"notifyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyLabel"}}}},"oldPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"oldPrice"}}}},"onAdd":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAdd"}}}},"onBenefitLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBenefitLink"}}}},"onBuyNow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBuyNow"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onQuantityChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onQuantityChange"}}}},"partner":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"partner"}}}},"pixDiscount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixDiscount"}}}},"pixPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixPrice"}}}},"price":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"price"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}},"showBenefits":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showBenefits"}}}},"sku":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sku"}}}},"slug":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"slug"}}}},"soldOutLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soldOutLabel"}}}}}}}}}},{"_id":"6ababc85440fbc0d402d16b5","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ProductDetails","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ProductDetails — Loja Lucas Fontana\n// O conteúdo de uma aba do produto: texto em 16px cinza, CheckList de benefícios e/ou a tabela da ficha técnica,\n// com o QuoteCard ao lado no desktop (embaixo no tablet e no mobile).\n// Na aba \"Como usar\", `videoHref` põe o link para o vídeo curto de aplicação.\n// A compatibilidade de peças (\"para qual carro serve\") entra como linhas da ficha técnica (`specs`).\n// O texto chega por `body` (o nome `text` é reservado do Data2).\n// Não desenha lista nem card: usa CheckList e QuoteCard por parâmetro (e repassa o Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst paras = (t) => String(t == null ? '' : t).split(/\\n\\s*\\n/).map((x) => x.trim()).filter(Boolean)\n  .map((x) => `<p class=\"tx\">${esc(x).replace(/\\n/g, '<br>')}</p>`).join('');\n\n// ---- Vídeo em modal: a pessoa assiste sem sair da loja (YouTube, Vimeo, Instagram, TikTok, Facebook e arquivos .mp4/.webm/.mov) ----\nconst videoEmbed = (raw) => {\n  let u;\n  try { u = new URL(String(raw || ''), 'https://x.invalid'); } catch (e) { return null; }\n  const host = u.hostname.replace('www.', '').replace('m.', '');\n  const parts = u.pathname.split('/').filter(Boolean);\n  const last = parts[parts.length - 1] || '';\n  const yt = (id, vertical) => (id ? { kind: 'iframe', vertical: !!vertical, src: 'https://www.youtube-nocookie.com/embed/' + encodeURIComponent(id) + '?autoplay=1&rel=0&playsinline=1&modestbranding=1' } : null);\n  if (host === 'youtu.be') return yt(parts[0]);\n  if (host === 'youtube.com' || host === 'music.youtube.com') {\n    if (parts[0] === 'watch') return yt(u.searchParams.get('v'));\n    if (parts[0] === 'shorts') return yt(parts[1], true);\n    if (parts[0] === 'embed' || parts[0] === 'live' || parts[0] === 'v') return yt(parts[1]);\n  }\n  if (host === 'vimeo.com' && /^[0-9]+$/.test(last)) return { kind: 'iframe', src: 'https://player.vimeo.com/video/' + last + '?autoplay=1&playsinline=1' };\n  if (host === 'instagram.com' && ['reel', 'reels', 'p', 'tv'].indexOf(parts[0]) >= 0 && parts[1]) return { kind: 'iframe', vertical: true, tall: true, src: 'https://www.instagram.com/' + (parts[0] === 'reels' ? 'reel' : parts[0]) + '/' + parts[1] + '/embed' };\n  if (host === 'tiktok.com' && parts.indexOf('video') >= 0 && parts[parts.indexOf('video') + 1]) return { kind: 'iframe', vertical: true, tall: true, src: 'https://www.tiktok.com/embed/v2/' + parts[parts.indexOf('video') + 1] };\n  if (host === 'facebook.com' || host === 'fb.watch') return { kind: 'iframe', src: 'https://www.facebook.com/plugins/video.php?show_text=false&autoplay=true&href=' + encodeURIComponent(String(raw)) };\n  const ext = last.split('.').pop().toLowerCase();\n  if (['mp4', 'webm', 'mov', 'm4v', 'ogv'].indexOf(ext) >= 0) return { kind: 'video', src: String(raw) };\n  return null;\n};\nconst openVideo = (v, opener) => {\n  if (typeof document === 'undefined' || !v) return;\n  const old = document.getElementById('lf-videobox'); if (old) old.remove();\n  const ov = document.createElement('div');\n  ov.id = 'lf-videobox'; ov.setAttribute('role', 'dialog'); ov.setAttribute('aria-modal', 'true'); ov.setAttribute('aria-label', 'Vídeo do produto');\n  ov.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:60px 12px 16px;background:rgba(11,11,12,.92);overflow:hidden';\n  const box = document.createElement('div');\n  box.style.cssText = v.tall ? 'width:min(100%,420px);height:100%;max-height:780px;background:#000'\n    : (v.vertical ? 'height:min(100%,80vh);aspect-ratio:9/16;max-width:100%;background:#000' : 'width:min(100%,1100px);aspect-ratio:16/9;max-height:100%;background:#000');\n  let media;\n  if (v.kind === 'video') {\n    media = document.createElement('video');\n    media.src = v.src; media.controls = true; media.autoplay = true; media.playsInline = true; media.setAttribute('playsinline', '');\n    media.style.cssText = 'width:100%;height:100%;background:#000;display:block';\n  } else {\n    media = document.createElement('iframe');\n    media.src = v.src; media.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin'); media.setAttribute('allow', 'autoplay; fullscreen; picture-in-picture; encrypted-media; clipboard-write'); media.setAttribute('allowfullscreen', ''); media.setAttribute('title', 'Vídeo do produto');\n    media.style.cssText = 'width:100%;height:100%;border:0;display:block;background:#000';\n  }\n  const btn = document.createElement('button');\n  btn.type = 'button'; btn.setAttribute('aria-label', 'Fechar vídeo'); btn.textContent = '×';\n  btn.style.cssText = 'position:absolute;top:max(10px,env(safe-area-inset-top));right:12px;width:44px;height:44px;border:0;border-radius:50%;background:#fff;color:#0b0b0c;font-size:28px;line-height:1;cursor:pointer;z-index:2';\n  const prev = document.body.style.overflow;\n  const onKey = (e) => {\n    if (e.key === 'Escape') { e.stopPropagation(); close(); }\n    else if (e.key === 'Tab') { e.preventDefault(); btn.focus(); }\n  };\n  const close = () => {\n    document.removeEventListener('keydown', onKey, true);\n    ov.remove(); document.body.style.overflow = prev;\n    try { if (opener && opener.focus) opener.focus(); } catch (e) { /* sem foco */ }\n  };\n  ov.addEventListener('click', (e) => { if (e.target === ov) close(); });\n  btn.addEventListener('click', close);\n  box.appendChild(media); ov.appendChild(box); ov.appendChild(btn);\n  if (v.href) {\n    const alt = document.createElement('div');\n    alt.style.cssText = 'position:absolute;left:0;right:0;bottom:max(12px,env(safe-area-inset-bottom));text-align:center;font:13px system-ui,sans-serif;color:rgba(255,255,255,.75)';\n    const a = document.createElement('a');\n    a.href = v.href; a.target = '_blank'; a.rel = 'noopener'; a.textContent = 'Se o vídeo não carregar, abrir no site de origem';\n    a.style.cssText = 'color:rgba(255,255,255,.85);text-decoration:underline';\n    alt.appendChild(a); ov.appendChild(alt);\n  }\n  document.body.appendChild(ov);\n  document.body.style.overflow = 'hidden';\n  document.addEventListener('keydown', onKey, true);\n  btn.focus();\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.det { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 48px; align-items: start; width: 100%; color: var(--ink); }\n.det.solo { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }\n.txt { display: flex; flex-direction: column; gap: 20px; min-width: 0; }\n.tx { margin: 0; font-size: 16px; line-height: 1.75; color: var(--grey-700); max-width: 720px; }\ntable { width: 100%; border-collapse: collapse; font-size: 15px; }\nth, td { text-align: left; padding: 12px 0; border-bottom: 1px solid var(--line); vertical-align: top; line-height: 1.45; }\nth { width: 40%; font-weight: 600; color: var(--grey-700); padding-right: 16px; }\ntd { color: var(--ink); }\n.vid { display: inline-flex; align-items: center; gap: 12px; align-self: flex-start; color: var(--ink); text-decoration: none; font-size: 15px; font-weight: 700; }\n.vid .sq { width: 36px; height: 36px; background: var(--red-500); color: var(--white); display: flex; align-items: center; justify-content: center; flex: none; }\n.vid span.lb { text-decoration: underline; text-underline-offset: 3px; }\n.vid:hover span.lb { color: var(--red-500); }\n.vid:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.det.l-tablet { grid-template-columns: minmax(0, 1fr); gap: 28px; }\n.det.l-mobile { grid-template-columns: minmax(0, 1fr); gap: 20px; }\n.l-mobile .tx { font-size: 15px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.vid', 'click', (e, el) => {\n      const href = el.getAttribute('href');\n      emit(d2, 'onVideo', href);\n      const v = videoEmbed(href);\n      if (v) { v.href = href; try { e.preventDefault(); } catch (err) { /* sem evento */ } openVideo(v, el); }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const clRef = refOf(d2.param('checkList'));\n    const qcRef = refOf(d2.param('quoteCard'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const benefits = list(p.benefits);\n    const specs = list(p.specs).map((r) => Array.isArray(r) ? { label: r[0], value: r[1] } : (r || {})).filter((r) => r.label);\n\n    const check = benefits.length && clRef\n      ? d2.render(clRef, { items: benefits, layout, iconComponent: iconRef }, { key: 'cl', style: 'height:auto' })\n      : '';\n    const table = specs.length\n      ? `<table><tbody>${specs.map((r) => `<tr><th scope=\"row\">${esc(r.label)}</th><td>${esc(r.value)}</td></tr>`).join('')}</tbody></table>`\n      : '';\n    const vid = p.videoHref\n      ? `<a class=\"vid\" href=\"${esc(p.videoHref)}\" target=\"_blank\" rel=\"noopener\"><span class=\"sq\" aria-hidden=\"true\">${ic(d2, iconRef, 'play', 14, '#ffffff', 'vp')}</span><span class=\"lb\">${esc(p.videoLabel || 'Ver o vídeo de aplicação')}</span></a>`\n      : '';\n    const quote = p.quote && qcRef\n      ? `<div class=\"qc\">${d2.render(qcRef, { quote: p.quote, author: p.author, eyebrow: p.eyebrow, layout }, { key: 'qc', style: 'height:auto' })}</div>`\n      : '';\n\n    return `<div class=\"det l-${layout}${quote ? '' : ' solo'}\">\n      <div class=\"txt\">${paras(p.body)}${check}${table}${vid}</div>\n      ${quote}\n    </div>`;\n  }\n};"}},"author":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"author"}}}},"benefits":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"benefits"}}}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onVideo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVideo"}}}},"quote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quote"}}}},"quoteCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quoteCard"}}}},"specs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"specs"}}}},"videoHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"videoHref"}}}},"videoLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"videoLabel"}}}}}}}}}},{"_id":"6ababca769443659fcd00566","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Tabs","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Tabs — Loja Lucas Fontana\n// As informações do produto em abas no desktop e no tablet (\"Descrição\", \"Como usar\", \"Ficha técnica\"):\n// rótulos de 13px em caixa alta, cinza até serem escolhidos; a aba ativa fica preta com sublinhado vermelho de 3px.\n// No mobile as mesmas abas viram sanfona: linhas de 64px com o quadrado de 28px (\"+\" preto fechado, \"−\" vermelho aberto);\n// a primeira começa aberta. As setas do teclado trocam de aba.\n// Cada item é o conteúdo de um ProductDetails: `{label, body, benefits, specs, quote, author, videoHref, videoLabel}`.\n// Já traz o respiro da página (96/40/20px nas laterais e 120/88/64px embaixo); `padded: false` tira.\n// Não desenha o conteúdo: usa ProductDetails por parâmetro (e repassa CheckList, QuoteCard e Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst nums = (v) => {\n  if (Array.isArray(v)) return v.map(Number).filter((n) => !isNaN(n));\n  if (typeof v === 'number') return [v];\n  if (typeof v === 'string' && v.trim()) return v.split(',').map((s) => Number(s.trim())).filter((n) => !isNaN(n));\n  return null;\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst PAD = { desktop: '0 96px 120px', tablet: '0 40px 88px', mobile: '0 20px 64px' };\nconst DEMO = [\n  { label: 'Descrição',\n    body: '[Descrição do produto: o que é, para que serve e por que o Lucas recomenda. Texto escrito para o cliente e para buscas em IA — direto, com as palavras que as pessoas usam para procurar.]',\n    benefits: ['[Benefício principal do produto]', '[Segundo benefício — ex.: rendimento, proteção, cheiro]', '[Terceiro benefício — ex.: seguro para a pintura]'],\n    quote: '[Comentário do Lucas sobre o produto — pode vir de uma fala do vídeo.]' },\n  { label: 'Como usar', body: '[Passo a passo de aplicação — com link para o vídeo curto do Lucas.]', videoHref: '#', videoLabel: 'Ver o vídeo de aplicação' },\n  { label: 'Ficha técnica', specs: [['Diluição', '[1:100]'], ['pH', '[Neutro]'], ['Aplicação', '[Para qual carro/uso serve]'], ['Marca', 'Lucas Fontana']] }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.tabs { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); color: var(--ink); }\n.list { display: flex; gap: 32px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; }\n.list::-webkit-scrollbar { display: none; }\n.tab { position: relative; flex-shrink: 0; height: 52px; margin: 0; padding: 0; border: 0; background: transparent;\n  font: 800 13px/1 ${FF}; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); cursor: pointer; }\n.tab:hover, .tab[aria-selected=\"true\"] { color: var(--ink); }\n.tab[aria-selected=\"true\"]::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 3px; background: var(--red-500); }\n.tab:focus-visible, .head:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.panel { padding-top: 32px; }\n.acc { border-bottom: 1px solid var(--line); }\n.acc:first-child { border-top: 1px solid var(--line); }\n.head { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 12px; min-height: 64px; margin: 0; padding: 0; border: 0; background: transparent;\n  font: 800 14px/1.2 ${FF}; letter-spacing: .14em; text-transform: uppercase; color: var(--ink); cursor: pointer; text-align: left; }\n.head i { flex: none; width: 28px; height: 28px; background: var(--ink); color: var(--white); display: flex; align-items: center; justify-content: center; font-style: normal; }\n.head[aria-expanded=\"true\"] i { background: var(--red-500); }\n.body { padding: 4px 0 24px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const items = () => { const p = d2.params || {}; return list(p.items).length ? list(p.items) : DEMO; };\n    const pick = (k) => {\n      const n = items().length;\n      const i = ((k % n) + n) % n;\n      d2.state.active = i;\n      emit(d2, 'onChange', i);\n      setTimeout(() => { const el = d2.$(`.tab[data-k=\"${i}\"]`); if (el) el.focus(); }, 0);\n    };\n    d2.on('.tab', 'click', (e, el) => { d2.state.active = Number(el.dataset.k); emit(d2, 'onChange', d2.state.active); });\n    d2.on('.list', 'keydown', (e) => {\n      if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;\n      e.preventDefault();\n      pick((d2.state.active || 0) + (e.key === 'ArrowRight' ? 1 : -1));\n    });\n    d2.on('.head', 'click', (e, el) => {\n      const k = Number(el.dataset.k);\n      const p = d2.params || {};\n      const cur = (d2.state.open || nums(p.defaultOpen) || [0]).slice();\n      d2.state.open = cur.indexOf(k) >= 0 ? cur.filter((x) => x !== k) : cur.concat([k]);\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const items = list(p.items).length ? list(p.items) : DEMO;\n    const pdRef = refOf(d2.param('productDetails'));\n    const clRef = refOf(d2.param('checkList'));\n    const qcRef = refOf(d2.param('quoteCard'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const padded = p.padded == null || p.padded === '' ? true : truthy(p.padded);\n    const active = Math.min(d2.state.active || 0, items.length - 1);\n    const open = d2.state.open || nums(p.defaultOpen) || [0];\n\n    const details = (it, k) => pdRef ? d2.render(pdRef, {\n      body: it.body || '', benefits: it.benefits || [], specs: it.specs || [], quote: it.quote || '', author: it.author || '',\n      eyebrow: it.eyebrow || '', videoHref: it.videoHref || '', videoLabel: it.videoLabel || '',\n      layout, checkList: clRef, quoteCard: qcRef, iconComponent: iconRef\n    }, { key: 'pd-' + k + '-' + layout, style: 'height:auto' }) : '';\n\n    let inner;\n    if (layout === 'mobile') {\n      inner = `<div class=\"accs\">${items.map((it, k) => {\n        const isOpen = open.indexOf(k) >= 0;\n        return `<div class=\"acc\">\n          <button type=\"button\" class=\"head\" data-k=\"${k}\" aria-expanded=\"${isOpen ? 'true' : 'false'}\" aria-controls=\"tp-${k}\">\n            <span>${esc(it.label)}</span><i aria-hidden=\"true\">${ic(d2, iconRef, isOpen ? 'minus' : 'plus', 14, '#ffffff', 'h' + k + (isOpen ? 'm' : 'p'))}</i>\n          </button>\n          ${isOpen ? `<div class=\"body\" id=\"tp-${k}\" role=\"region\">${details(it, k)}</div>` : ''}\n        </div>`;\n      }).join('')}</div>`;\n    } else {\n      inner = `<div class=\"list\" role=\"tablist\" aria-label=\"${esc(p.ariaLabel || 'Informações do produto')}\">${items.map((it, k) =>\n        `<button type=\"button\" class=\"tab\" role=\"tab\" id=\"tt-${k}\" data-k=\"${k}\" aria-selected=\"${k === active ? 'true' : 'false'}\" aria-controls=\"tp-${k}\" tabindex=\"${k === active ? 0 : -1}\">${esc(it.label)}</button>`\n      ).join('')}</div>\n      <div class=\"panel\" role=\"tabpanel\" id=\"tp-${active}\" aria-labelledby=\"tt-${active}\">${details(items[active] || {}, active)}</div>`;\n    }\n    return `<section class=\"tabs l-${layout}\" style=\"padding:${padded ? PAD[layout] : '0'}\" aria-label=\"Detalhes do produto\">${inner}</section>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"defaultOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"defaultOpen"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"padded":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"padded"}}}},"productDetails":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productDetails"}}}},"quoteCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quoteCard"}}}}}}}}}},{"_id":"6ababcbfbeb54327cc83c216","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ProductLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ProductLayout — Loja Lucas Fontana\n// O topo da página de produto: breadcrumb e, embaixo, a galeria e a BuyBox lado a lado.\n// Desktop: galeria à esquerda e BuyBox de 471px à direita, que fica presa na tela enquanto a galeria rola,\n// 56px entre as duas; uma hairline fecha o bloco.\n// Tablet: galeria em cima e BuyBox embaixo, na largura toda.\n// Mobile: a galeria vai de ponta a ponta; o breadcrumb rola para o lado em vez de quebrar.\n// As fotos chegam por `images` (itens do ProductGallery) e o produto por `product` (parâmetros da BuyBox).\n// Não desenha nada disso: usa Breadcrumb, ProductGallery e BuyBox por parâmetro (e repassa Badge, Price,\n// QuantityStepper, Button, BenefitList e Icon para eles).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst PAD = { desktop: '32px 96px 64px', tablet: '28px 40px 56px', mobile: '20px 20px 48px' };\nconst CRUMB_H = { desktop: 24, tablet: 24, mobile: 30 };\n// Sacola no navegador (mesma chave da página Sacola): [{sku, qty}]\nconst addToBag = (sku, qty) => {\n  if (!sku) return;\n  try {\n    const bag = JSON.parse(localStorage.getItem('lf-sacola') || '[]');\n    const l = Array.isArray(bag) ? bag : [];\n    const f = l.find((y) => y && y.sku === sku);\n    if (f) f.qty = (Number(f.qty) || 0) + (Number(qty) || 1); else l.push({ sku, qty: Number(qty) || 1 });\n    localStorage.setItem('lf-sacola', JSON.stringify(l));\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: l.reduce((s, y) => s + (Number(y.qty) || 0), 0) } }));\n  } catch (e) { /* sem storage */ }\n};\nconst CRUMBS = [{ label: 'Início', href: '/' }, { label: 'Cuidado automotivo', href: '/categoria/cuidado-automotivo' }, { label: 'Lavagem', href: '/categoria/cuidado-automotivo/lavagem' }, { label: 'Shampoo automotivo neutro' }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.pdp { --line: #e4e4e2; width: 100%; background: #ffffff; }\n.in { display: flex; flex-direction: column; gap: 24px; }\n.crumb { width: 100%; }\n.grid { display: grid; grid-template-columns: minmax(0, 1fr) 471px; gap: 56px; align-items: start; padding-bottom: 56px; border-bottom: 1px solid var(--line); }\n.gal { min-width: 0; }\n.buy { position: sticky; top: 24px; min-width: 0; }\n.l-tablet .grid { grid-template-columns: minmax(0, 1fr); gap: 32px; padding-bottom: 0; border-bottom: 0; }\n.l-tablet .buy { position: static; }\n.l-mobile .in { gap: 16px; }\n.l-mobile .grid { grid-template-columns: minmax(0, 1fr); gap: 24px; padding-bottom: 0; border-bottom: 0; }\n.l-mobile .buy { position: static; }\n.l-mobile .gal { margin: 0 -20px; }\n`,\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const bcRef = refOf(d2.param('breadcrumb'));\n    const galRef = refOf(d2.param('productGallery'));\n    const buyRef = refOf(d2.param('buyBox'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const crumbs = list(p.crumbs).length ? list(p.crumbs) : CRUMBS;\n    const product = obj(p.product);\n\n    const crumb = bcRef\n      ? `<div class=\"crumb\" style=\"height:${CRUMB_H[layout]}px\">${d2.render(bcRef, { items: crumbs, layout, scroll: layout === 'mobile' }, {\n          key: 'bc', onNavigate: (h) => emit(d2, 'onNavigate', h) })}</div>`\n      : '';\n    const gallery = galRef\n      ? d2.render(galRef, { items: list(p.images), layout, iconComponent: iconRef }, { key: 'gal', style: 'height:auto' })\n      : '';\n    const buy = buyRef\n      ? d2.render(buyRef, Object.assign({}, product, {\n          layout,\n          badge: refOf(d2.param('badge')), priceComponent: refOf(d2.param('priceComponent')),\n          quantityStepper: refOf(d2.param('quantityStepper')), button: refOf(d2.param('button')),\n          benefitList: refOf(d2.param('benefitList')), iconComponent: iconRef\n        }), {\n          key: 'buy' + (product.available === false || product.available === 'false' ? '-off' : '') + (product.partner ? '-' + product.partner : ''),\n          style: 'height:auto',\n          onAdd: (q) => { addToBag(product.sku, q); emit(d2, 'onAdd', q); }, onBuyNow: (q) => { addToBag(product.sku, q); emit(d2, 'onBuyNow', q); },\n          onNotify: (q) => emit(d2, 'onNotify', q), onQuantityChange: (q) => emit(d2, 'onQuantityChange', q)\n        })\n      : '';\n\n    return `<div class=\"pdp l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      ${crumb}\n      <div class=\"grid\"><div class=\"gal\">${gallery}</div><div class=\"buy\">${buy}</div></div>\n    </div></div>`;\n  }\n};"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"benefitList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"benefitList"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"buyBox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"buyBox"}}}},"crumbs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"crumbs"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"images":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"images"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onAdd":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAdd"}}}},"onBuyNow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBuyNow"}}}},"onNavigate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNavigate"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onQuantityChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onQuantityChange"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"product":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"product"}}}},"productGallery":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productGallery"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}}}}}}}},{"_id":"6abbd2a25b997bc7f1419f8c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FormField","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FormField — Loja Lucas Fontana\n// O campo de formulário em tamanho cheio: rótulo de 14px em negrito, campo branco de 48px com borda line-strong\n// (fica preta, com anel de 1px, no foco) e, embaixo, dica, erro, contador ou link.\n// `optional` põe \"(opcional)\" sob o rótulo; `hint` é a dica cinza; `error` deixa a borda vermelha e mostra a mensagem;\n// `linkLabel`/`linkHref` põem um link auxiliar sublinhado (\"Não sei meu CEP\").\n// Com `type: \"password\"`, aparece o botão de olho para mostrar/esconder a senha.\n// `multiline: true` vira uma área de texto de 176px (redimensionável); com `maxLength` + `counter: true`\n// mostra \"0 de 1.000 caracteres\" embaixo.\n// `options: [{value, label}]` vira um select com seta (o `placeholder` vira a primeira opção vazia).\n// `mask` formata enquanto a pessoa digita: `cep` (00000-000), `phone` ((00) 00000-0000), `uf` (2 letras maiúsculas),\n// `cpf` (000.000.000-00), `cnpj` (00.000.000/0000-00, aceita letras nas 12 primeiras posições — CNPJ alfanumérico),\n// `digits` (só números). CEP, telefone, CPF e dígitos abrem o teclado numérico; CNPJ abre o teclado comum, em maiúsculas.\n// Placeholder é exemplo (\"seuemail@exemplo.com\"), nunca o rótulo.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst TYPES = ['text', 'email', 'password', 'tel', 'number', 'search', 'url'];\nconst CHEV = '<svg class=\"chev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m6 9 6 6 6-6\"/></svg>';\nconst num = (v) => { const n = parseInt(v, 10); return isFinite(n) && n > 0 ? n : 0; };\nconst fmtN = (n) => String(n).replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.');\n\n// Máscaras: recebem o texto digitado e devolvem o formatado\nconst pattern = (d, pat) => { let out = '', i = 0; for (const c of pat) { if (i >= d.length) break; if (c === '0') out += d[i++]; else out += c; } return out; };\nconst MASKS = {\n  digits: (v) => v.replace(/\\D/g, ''),\n  cep: (v) => pattern(v.replace(/\\D/g, '').slice(0, 8), '00000-000'),\n  cpf: (v) => pattern(v.replace(/\\D/g, '').slice(0, 11), '000.000.000-00'),\n  cnpj: (v) => {\n    // CNPJ alfanumérico (a partir de jul/2026): 12 primeiras posições aceitam A–Z e 0–9; as 2 últimas (DV) só números\n    const s = String(v).toUpperCase().replace(/[^A-Z0-9]/g, '');\n    let out = '';\n    for (const c of s) { if (out.length >= 14) break; if (out.length < 12 || /\\d/.test(c)) out += c; }\n    return pattern(out, '00.000.000/0000-00');\n  },\n  uf: (v) => v.replace(/[^a-zA-Z]/g, '').slice(0, 2).toUpperCase(),\n  phone: (v) => { const d = v.replace(/\\D/g, '').slice(0, 11); return d.length > 10 ? pattern(d, '(00) 00000-0000') : pattern(d, '(00) 0000-0000'); }\n};\nconst NUMERIC = ['digits', 'cep', 'cpf', 'phone'];\nconst CAPS = ['cnpj', 'uf'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ff { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 8px; min-width: 0; width: 100%; }\nlabel { display: flex; flex-direction: column; gap: 2px; font-size: 14px; font-weight: 700; color: var(--ink); }\nlabel small { font-size: 13px; font-weight: 400; color: var(--grey-500); }\n.box { position: relative; display: flex; align-items: center; }\ninput, select, textarea { width: 100%; margin: 0; border: 1px solid var(--line-strong); border-radius: 2px; background: var(--white);\n  font: 400 16px/1.2 ${FF}; color: var(--ink); outline: none; -webkit-appearance: none; appearance: none; }\ninput, select { height: 48px; padding: 0 14px; }\nselect { padding-right: 40px; cursor: pointer; }\nselect.empty { color: var(--grey-500); }\n.chev { position: absolute; right: 12px; width: 16px; height: 16px; color: var(--ink); pointer-events: none; }\n.chev-ic { position: absolute; right: 12px; display: flex; pointer-events: none; }\ntextarea { display: block; height: 176px; min-height: 120px; padding: 12px 14px; line-height: 1.5; resize: vertical; }\ninput::placeholder, textarea::placeholder { color: var(--grey-500); }\ninput:focus, select:focus, textarea:focus { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }\n.has-reveal input { padding-right: 48px; }\n.reveal { position: absolute; right: 4px; width: 40px; height: 40px; margin: 0; padding: 0; border: 0; background: transparent; color: var(--grey-700);\n  display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.reveal:focus-visible { outline: 2px solid var(--red-500); outline-offset: -2px; }\n.error input, .error select, .error textarea { border-color: var(--red-500); box-shadow: 0 0 0 1px var(--red-500); }\np { margin: 0; font-size: 13px; line-height: 1.4; }\n.hint, .count { color: var(--grey-500); }\n.count.full { color: var(--ink); font-weight: 600; }\n.err { font-weight: 600; color: var(--red-500); }\na { align-self: flex-start; font-size: 13px; font-weight: 600; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }\na:hover { color: var(--red-500); }\na:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const keep = (el) => { if (d2.root) d2.root.__lfVal = el ? el.value : ''; };\n    const count = (el) => {\n      const c = d2.$('.count');\n      if (!c || !el) return;\n      const max = num(el.getAttribute('maxlength'));\n      const n = el.value.length;\n      const nEl = c.querySelector('.n');\n      if (nEl) nEl.textContent = fmtN(n);\n      if (max) c.classList.toggle('full', n >= max);\n    };\n    d2.on('.reveal', 'click', () => { keep(d2.$('input')); d2.state.show = !d2.state.show; });\n    d2.on('input', 'input', (e, el) => {\n      const m = MASKS[(d2.params || {}).mask];\n      if (m) { const v = m(el.value); if (v !== el.value) el.value = v; }\n      keep(el); emit(d2, 'onInput', el.value);\n    });\n    d2.on('input', 'change', (e, el) => emit(d2, 'onChange', el.value));\n    d2.on('input, textarea, select', 'focusout', (e, el) => emit(d2, 'onBlur', el.value));\n    d2.on('textarea', 'input', (e, el) => { keep(el); count(el); emit(d2, 'onInput', el.value); });\n    d2.on('textarea', 'change', (e, el) => emit(d2, 'onChange', el.value));\n    d2.on('select', 'change', (e, el) => {\n      keep(el); el.classList.toggle('empty', !el.value);\n      emit(d2, 'onInput', el.value); emit(d2, 'onChange', el.value);\n    });\n    d2.on('a', 'click', (e, el) => emit(d2, 'onLink', el.getAttribute('href')));\n  },\n\n  updated(d2) {\n    // Mantém o que foi digitado quando o campo redesenha (mostrar/esconder senha, erro que aparece ou some)\n    const el = d2.$('input, textarea, select');\n    if (el && d2.root && d2.root.__lfVal != null && el.value !== d2.root.__lfVal) el.value = d2.root.__lfVal;\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const options = arr(p.options).map((o) => (o && typeof o === 'object' ? o : { value: String(o), label: String(o) }));\n    const kind = options.length ? 'select' : (truthy(p.multiline) ? 'textarea' : 'input');\n    const baseType = TYPES.indexOf(p.type) >= 0 ? p.type : 'text';\n    const isPw = kind === 'input' && baseType === 'password';\n    const reveal = isPw && (p.reveal == null || p.reveal === '' ? true : truthy(p.reveal));\n    const show = !!d2.state.show;\n    const type = isPw && show ? 'text' : baseType;\n    const mask = MASKS[p.mask] ? p.mask : '';\n    const inputMode = p.inputMode || (NUMERIC.indexOf(mask) >= 0 ? 'numeric' : '');\n    const max = num(p.maxLength);\n    const withCount = kind === 'textarea' && max > 0 && truthy(p.counter);\n    const id = 'f';\n    const err = p.error ? String(p.error) : '';\n    const hint = p.hint ? String(p.hint) : '';\n    const raw = p.value == null ? '' : String(p.value);\n    const val = mask && kind === 'input' ? MASKS[mask](raw) : raw;\n    const describe = [hint ? id + '-h' : '', err ? id + '-e' : '', withCount ? id + '-c' : ''].filter(Boolean).join(' ');\n    const common = [\n      `id=\"${id}\"`,\n      p.name ? `name=\"${esc(p.name)}\"` : '',\n      p.autoComplete ? `autocomplete=\"${esc(p.autoComplete)}\"` : '',\n      describe ? `aria-describedby=\"${describe}\"` : '',\n      p.ariaLabel && !p.label ? `aria-label=\"${esc(p.ariaLabel)}\"` : '',\n      err ? 'aria-invalid=\"true\"' : '',\n      truthy(p.required) ? 'required' : ''\n    ];\n\n    let control;\n    if (kind === 'select') {\n      const ph = p.placeholder ? `<option value=\"\"${val ? '' : ' selected'}>${esc(p.placeholder)}</option>` : '';\n      const opts = options.map((o) => `<option value=\"${esc(o.value)}\"${String(o.value) === val ? ' selected' : ''}>${esc(o.label != null ? o.label : o.value)}</option>`).join('');\n      const chev = iconRef ? `<span class=\"chev-ic\" aria-hidden=\"true\">${ic(d2, iconRef, 'chevron-down', 16, '#0b0b0c', 'chev')}</span>` : CHEV;\n      control = `<select class=\"${!val && p.placeholder ? 'empty' : ''}\" ${common.filter(Boolean).join(' ')}>${ph}${opts}</select>${chev}`;\n    } else if (kind === 'textarea') {\n      const a = common.concat([\n        p.placeholder ? `placeholder=\"${esc(p.placeholder)}\"` : '',\n        max ? `maxlength=\"${max}\"` : '',\n        p.rows ? `rows=\"${num(p.rows)}\"` : ''\n      ]).filter(Boolean).join(' ');\n      control = `<textarea ${a}>${esc(val)}</textarea>`;\n    } else {\n      const a = common.concat([\n        `type=\"${type}\"`,\n        p.placeholder ? `placeholder=\"${esc(p.placeholder)}\"` : '',\n        inputMode ? `inputmode=\"${esc(inputMode)}\"` : '',\n        CAPS.indexOf(mask) >= 0 ? 'autocapitalize=\"characters\" spellcheck=\"false\"' : '',\n        max ? `maxlength=\"${max}\"` : '',\n        val !== '' ? `value=\"${esc(val)}\"` : ''\n      ]).filter(Boolean).join(' ');\n      const eye = reveal\n        ? `<button type=\"button\" class=\"reveal\" aria-label=\"${show ? 'Esconder senha' : 'Mostrar senha'}\" aria-pressed=\"${show ? 'true' : 'false'}\">${ic(d2, iconRef, show ? 'eye-off' : 'eye', 20, '#45474b', 'eye-' + (show ? 'off' : 'on'))}</button>`\n        : '';\n      control = `<input ${a}>${eye}`;\n    }\n\n    const n = (d2.root && d2.root.__lfVal != null ? d2.root.__lfVal : val).length;\n    const counter = withCount\n      ? `<p class=\"count${n >= max ? ' full' : ''}\" id=\"${id}-c\" aria-live=\"polite\"><span class=\"n\">${fmtN(n)}</span> de ${fmtN(max)} caracteres</p>`\n      : '';\n\n    return `<div class=\"ff l-${layout}${err ? ' error' : ''}${reveal ? ' has-reveal' : ''}\">\n      ${p.label ? `<label for=\"${id}\">${esc(p.label)}${truthy(p.optional) ? '<small>(opcional)</small>' : ''}</label>` : ''}\n      <div class=\"box\">${control}</div>\n      ${err ? `<p class=\"err\" id=\"${id}-e\">${esc(err)}</p>` : ''}\n      ${hint ? `<p class=\"hint\" id=\"${id}-h\">${esc(hint)}</p>` : ''}\n      ${counter}\n      ${p.linkLabel ? `<a href=\"${esc(p.linkHref || '#')}\"${/^https?:\\/\\//.test(String(p.linkHref || '')) ? ' target=\"_blank\" rel=\"noopener\"' : ''}>${esc(p.linkLabel)}</a>` : ''}\n    </div>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"autoComplete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoComplete"}}}},"counter":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"counter"}}}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"hint":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hint"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"inputMode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"inputMode"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"linkHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"linkHref"}}}},"linkLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"linkLabel"}}}},"mask":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"mask"}}}},"maxLength":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"maxLength"}}}},"multiline":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"multiline"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onBlur":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBlur"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}},"optional":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"optional"}}}},"options":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"options"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"required":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"required"}}}},"reveal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"reveal"}}}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}},"type":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"type"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abbd2c37da9f132eb834397","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"LoginForm","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// LoginForm — Loja Lucas Fontana\n// O formulário de entrar, em duas etapas.\n// Etapa 1 (`step: \"login\"`): kicker \"Minha conta\", \"ENTRAR\" (44/36/28px), boas-vindas, e-mail e senha (com olho),\n// \"Esqueci minha senha\", o botão preto \"Entrar\" e a nota \"Você continua conectado por 5 dias neste aparelho.\"\n// Etapa 2 (`step: \"code\"`, verificação em duas etapas): e-mail e senha continuam preenchidos; entram o aviso com barra preta\n// (\"Se suas credenciais estiverem corretas, você recebeu um código de autenticação em seu e-mail.\" — não confirma se a conta\n// existe), o CodeField, o ResendCode e \"Entrar\"; a nota passa a ser \"Por segurança, todo login é confirmado com um código\n// enviado ao seu e-mail.\"\n// Fluxo: \"Entrar\" na etapa 1 confere o básico, emite `onSubmit` {email, password} e avança para o código\n// (`autoAdvance: false` desliga). Na etapa 2, \"Entrar\" confere os 6 números e emite `onVerify` {email, code}.\n// \"Usar outro e-mail\" volta para a etapa 1 (limpa senha e código) e emite `onChangeEmail`; \"Reenviar código\" emite `onResend`.\n// Loja (`autoAdvance: false` + `authStrategy`): antes de entrar pergunta ao `checkService` se o e-mail está bloqueado;\n// entra pela estratégia com o botão em \"Entrando…\"; deu certo → `successService` (zera as tentativas pela sessão) e volta\n// para `?voltar=` (ou `redirectTo`, padrão \"/\"); deu errado → `failService` (soma o erro; no 3º, bloqueia 15 min) e mostra\n// \"E-mail ou senha incorretos.\" ou o aviso de bloqueio. Os serviços chegam por parâmetro (ligados em cada tela).\n// Erros do servidor: `emailError`, `passwordError`, `codeError` (\"Código incorreto. Confere o e-mail e tenta de novo.\").\n// `formError`: aviso único acima dos campos, sem marcar nenhum campo (na loja: \"E-mail ou senha incorretos.\"). Some quando\n// a pessoa volta a digitar.\n// `lockedMinutes`: login travado depois de 3 tentativas erradas — mostra \"Muitas tentativas seguidas.\" com o tempo de espera.\n// `email` já vem preenchido no campo (ex.: quando a pessoa chega pelo aviso de conta existente do cadastro). Sem `email`,\n// usa o `?email=` do endereço da página.\n// Na loja, `autoAdvance: false` desliga a etapa do código (o 2FA fica só para o backoffice).\n// Não desenha campo, aviso, botão nem ícone: usa FormField, CodeField, ResendCode, Notice, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst store = (d2) => (d2.root ? (d2.root.__lf || (d2.root.__lf = { email: '', password: '', code: '' })) : { email: '', password: '', code: '' });\nconst codeLen = (p) => { const n = Number(p.codeLength); return n > 0 && n < 13 ? n : 6; };\nconst qsEmail = () => { try { return new URLSearchParams(window.location.search).get('email') || ''; } catch (e) { return ''; } };\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res) || {};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.lf { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 18px; width: 100%; height: 100%; padding: 56px; margin: 0;\n  border: 1px solid var(--line); border-right: 0; background: var(--white); color: var(--ink); }\np, h1 { margin: 0; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; color: var(--ink); }\n.text { margin: -4px 0 6px; font-size: 16px; line-height: 1.55; color: var(--grey-700); }\n.forgot { align-self: flex-end; font-size: 14px; font-weight: 600; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }\n.forgot:hover { color: var(--red-500); }\n.forgot:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.submit { height: 52px; margin-top: 4px; }\n.note { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 1.45; color: var(--grey-500); }\n\n.l-tablet { padding: 40px; border-right: 1px solid var(--line); border-bottom: 0; }\n.l-tablet .title { font-size: 36px; }\n\n.l-mobile { padding: 0; border: 0; gap: 16px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 28px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .text { font-size: 15px; }\n`,\n\n  mounted(d2, root) {\n    FONT();\n    d2.on('.forgot', 'click', (e, el) => emit(d2, 'onForgot', el.getAttribute('href')));\n    // Quem já está logado e abre /login vai para Minha conta\n    const authRef = d2.param('authStrategy');\n    if (authRef && window.location.pathname === '/login') {\n      setTimeout(() => { try { const u = d2.auth(authRef).user; if (u && u.email) window.location.assign('/perfil/meus-pedidos'); } catch (e) { /* sem sessão */ } }, 300);\n    }\n    // Enter em qualquer campo envia, igual ao botão \"Entrar\"\n    d2.on(root, 'keydown', (e) => {\n      if (e.key !== 'Enter' || e.shiftKey || e.isComposing) return;\n      const go = d2.root && d2.root.__lfSubmit;\n      if (typeof go === 'function') { e.preventDefault(); go(); }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const ffRef = refOf(d2.param('formField'));\n    const cfRef = refOf(d2.param('codeField'));\n    const rsRef = refOf(d2.param('resendCode'));\n    const ntRef = refOf(d2.param('notice'));\n    const btnRef = refOf(d2.param('button'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const s = store(d2);\n    const step = d2.state.step || (p.step === 'code' ? 'code' : 'login');\n    const code = step === 'code';\n    const n = codeLen(p);\n    if (!s.email && p.email) s.email = String(p.email);\n    if (!s.email && !s.__qs) { s.__qs = true; s.email = qsEmail(); }\n    const errE = code ? '' : (d2.state.errE || p.emailError || '');\n    const errP = code ? '' : (d2.state.errP || p.passwordError || '');\n    const errC = d2.state.errC || p.codeError || '';\n    const sending = !!d2.state.sending;\n\n    // ——— Login da loja (sem código): checa bloqueio, entra, registra acerto ou falha\n    const dest = () => {\n      let v = '';\n      try { v = new URLSearchParams(window.location.search).get('voltar') || ''; } catch (e) { v = ''; }\n      return /^\\/(?!\\/)/.test(v) ? v : (p.redirectTo || '/');\n    };\n    const genericError = () => { d2.state.lock = 0; d2.state.fe = 'E-mail ou senha incorretos.'; d2.state.feHidden = false; };\n    const lojaLogin = async (email, password) => {\n      if (d2.state.sending) return;\n      const auth = d2.param('authStrategy');\n      const chk = d2.param('checkService'); const fail = d2.param('failService'); const ok = d2.param('successService');\n      d2.state.sending = true; d2.state.fe = ''; d2.state.lock = 0;\n      if (chk) {\n        let c = {};\n        try { c = unwrap(await d2.rush(chk).invoke({ email })); } catch (e) { c = {}; }\n        if (c.bloqueado) { d2.state.sending = false; d2.state.lock = Number(c.minutos) || 15; d2.state.feHidden = false; return; }\n      }\n      let entrou = false;\n      try { await d2.auth(auth).login({ credentials: { email: email.toLowerCase(), senha: password } }); entrou = true; } catch (e) { entrou = false; }\n      if (entrou) {\n        if (ok) { try { await d2.rush(ok).invoke({}); } catch (e) { /* zera depois */ } }\n        // Login no lugar de uma rota protegida (router): fica na página pedida; na página /login: volta para a origem\n        const here = window.location.pathname || '/';\n        const slot = here !== '/login' && here.indexOf('/preview/') < 0;\n        window.location.assign(slot ? here + window.location.search : dest());\n        return;\n      }\n      let f = {};\n      if (fail) { try { f = unwrap(await d2.rush(fail).invoke({ email })); } catch (e) { f = {}; } }\n      d2.state.sending = false;\n      store(d2).password = ''; d2.state.passGen = (d2.state.passGen || 0) + 1;\n      if (f.bloqueado) { d2.state.fe = ''; d2.state.lock = Number(f.minutos) || 15; d2.state.feHidden = false; return; }\n      genericError();\n    };\n\n    const submit = () => {\n      const st = store(d2);\n      if ((d2.state.step || (p.step === 'code' ? 'code' : 'login')) === 'code') {\n        const c = String(st.code || '');\n        const eC = c.length < n ? `Digite os ${n} números do código.` : '';\n        d2.state.errC = eC;\n        if (!eC) emit(d2, 'onVerify', { email: String(st.email || '').trim(), code: c });\n        return;\n      }\n      const email = String(st.email || '').trim();\n      const password = String(st.password || '');\n      const eE = !email ? 'Informe seu e-mail.' : (!EMAIL.test(email) ? 'Confira o e-mail: falta o @ ou o domínio.' : '');\n      const eP = !password ? 'Informe sua senha.' : '';\n      d2.state.errE = eE; d2.state.errP = eP;\n      if (!eE && !eP) {\n        emit(d2, 'onSubmit', { email, password });\n        const advance = p.autoAdvance == null || p.autoAdvance === '' || truthy(p.autoAdvance);\n        if (!advance && d2.param('authStrategy')) { lojaLogin(email, password); return; }\n        if (advance) { st.code = ''; d2.state.errC = ''; d2.state.step = 'code'; }\n      }\n    };\n    if (d2.root) d2.root.__lfSubmit = submit;\n    const backToLogin = () => {\n      const st = store(d2);\n      st.password = ''; st.code = '';\n      d2.state.errC = ''; d2.state.errE = ''; d2.state.errP = '';\n      d2.state.step = 'login';\n      emit(d2, 'onChangeEmail', st.email);\n    };\n\n    const field = (key, params, err, onInput, extraKey) => ffRef ? d2.render(ffRef, Object.assign({ layout, iconComponent: iconRef, error: err }, params), {\n      key: key + '-' + step + (extraKey || '') + (err ? '-err-' + err.length : ''), style: 'height:auto', onInput\n    }) : '';\n    const hideAlert = () => { if (!d2.state.feHidden && (p.formError || p.lockedMinutes || d2.state.fe || d2.state.lock)) d2.state.feHidden = true; };\n\n    const email = field('email', {\n      label: 'E-mail', type: 'email', name: 'email', autoComplete: 'email', inputMode: 'email',\n      placeholder: 'seuemail@exemplo.com', value: s.email\n    }, errE, (v) => { store(d2).email = v; hideAlert(); });\n    const pass = field('senha', {\n      label: 'Senha', type: 'password', name: 'senha', autoComplete: 'current-password', placeholder: 'Sua senha',\n      value: s.password || (code ? (p.password || '••••••••••••') : '')\n    }, errP, (v) => { store(d2).password = v; hideAlert(); }, '-' + (d2.state.passGen || 0));\n\n    // Aviso único (erro genérico ou bloqueio), acima dos campos\n    const lockMin = Number(d2.state.lock) > 0 ? Number(d2.state.lock) : (Number(p.lockedMinutes) > 0 ? Number(p.lockedMinutes) : 0);\n    const feMsg = d2.state.fe || p.formError || '';\n    const feText = lockMin\n      ? '**Muitas tentativas seguidas.** Por segurança, espera ' + lockMin + ' minutos para tentar de novo ou use **Esqueci minha senha**, logo abaixo.'\n      : (feMsg ? '**' + String(feMsg) + '**' : '');\n    const formAlert = !code && feText && !d2.state.feHidden && ntRef ? d2.render(ntRef, {\n      tone: 'error', iconName: lockMin ? 'lock' : 'close', live: true, layout, iconComponent: iconRef, body: feText\n    }, { key: 'fe-' + (lockMin ? 'l' + lockMin : 'e') + '-' + feText.length + '-' + (d2.state.passGen || 0), style: 'height:auto' }) : '';\n\n    const notice = code && ntRef ? d2.render(ntRef, {\n      tone: 'accent', iconName: 'mail', live: true, layout, iconComponent: iconRef,\n      body: p.noticeText || 'Se suas credenciais estiverem corretas, você recebeu um **código de autenticação** em seu e-mail.'\n    }, { key: 'notice', style: 'height:auto' }) : '';\n    const codeField = code && cfRef ? d2.render(cfRef, {\n      layout, length: n, validity: p.codeValidity || '', error: errC, autoFocus: true, value: s.code\n    }, {\n      key: 'code' + (errC ? '-err-' + errC.length : ''), style: 'height:auto',\n      onInput: (v) => { store(d2).code = v; }\n    }) : '';\n    const resend = code && rsRef ? d2.render(rsRef, {\n      layout, seconds: p.resendSeconds == null || p.resendSeconds === '' ? 59 : p.resendSeconds, showChange: true\n    }, {\n      key: 'resend', style: 'height:auto',\n      onResend: () => emit(d2, 'onResend', store(d2).email), onChangeEmail: backToLogin\n    }) : '';\n\n    const btn = btnRef ? `<div class=\"submit\">${d2.render(btnRef, {\n      label: sending ? 'Entrando…' : (p.submitLabel || 'Entrar'), variant: 'primary', size: 'lg', fullWidth: 'always', isDisabled: sending, layout\n    }, { key: 'submit-' + (sending ? 's' : 'i'), height: 52, onClick: submit })}</div>` : '';\n\n    const defNote = code ? 'Por segurança, todo login é confirmado com um código enviado ao seu e-mail.' : 'Você continua conectado por 5 dias neste aparelho.';\n    const note = p.note === '' ? '' : (p.note || defNote);\n    const forgotHref = (p.forgotHref || '/esqueci-a-senha') + (s.email ? '?email=' + encodeURIComponent(String(s.email).trim()) : '');\n\n    return `<div class=\"lf l-${layout} step-${step}\" role=\"form\" aria-label=\"${code ? 'Confirmar o login com o código' : 'Entrar na sua conta'}\">\n      <p class=\"kicker\">${esc(p.eyebrow || 'Minha conta')}</p>\n      <h1 class=\"title\">${esc(p.title || 'Entrar')}</h1>\n      <p class=\"text\">${esc(p.description || 'Que bom te ver de novo. Seu carrinho continua salvo quando você entrar.')}</p>\n      ${formAlert}\n      ${email}\n      ${pass}\n      <a class=\"forgot\" href=\"${esc(forgotHref)}\">${esc(p.forgotLabel || 'Esqueci minha senha')}</a>\n      ${notice}\n      ${codeField}\n      ${resend}\n      ${btn}\n      ${note ? `<div class=\"note\">${ic(d2, iconRef, 'lock', 14, '#6b6e73', 'lock')}<span>${esc(note)}</span></div>` : ''}\n    </div>`;\n  }\n};"}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"autoAdvance":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoAdvance"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkService"}}}},"codeError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeError"}}}},"codeField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeField"}}}},"codeLength":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeLength"}}}},"codeValidity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeValidity"}}}},"description":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"description"}}}},"email":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"email"}}}},"emailError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emailError"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"failService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"failService"}}}},"forgotHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"forgotHref"}}}},"forgotLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"forgotLabel"}}}},"formError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formError"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"lockedMinutes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"lockedMinutes"}}}},"note":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"note"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"noticeText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"noticeText"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onForgot":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onForgot"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onVerify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVerify"}}}},"password":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"password"}}}},"passwordError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"passwordError"}}}},"redirectTo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"redirectTo"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"resendSeconds":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendSeconds"}}}},"step":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"step"}}}},"submitLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"submitLabel"}}}},"successService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"successService"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abbd2dc7da9f132eb8343de","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"PromoCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// PromoCard — Loja Lucas Fontana\n// Card preto que vende uma ação: xadrez no canto, kicker vermelho claro, título largo em caixa alta,\n// CheckList branca, botão vermelho opcional e régua vermelha de 6px embaixo (5px no mobile).\n// Login: \"Primeira vez aqui? / Crie sua conta em 1 minuto\" + \"Criar conta\".\n// `compact` é a versão estreita para a lateral do cadastro: sem régua, xadrez menor e uma nota com cadeado (`foot`).\n// Não desenha lista, botão nem ícone: usa CheckList, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst ITEMS = ['Acompanhe seus pedidos e o código de rastreio', 'Compre mais rápido com seus dados já preenchidos', 'Seja avisado quando o produto do vídeo voltar ao estoque'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.pc { --red-500: #c8102e; --red-300: #ff4d62; --ink: #0b0b0c; --white: #ffffff; --grey-200: #c9cacd; --graphite-700: #34363a;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  position: relative; display: flex; flex-direction: column; justify-content: center; gap: 20px; width: 100%; height: 100%;\n  padding: 56px; background: var(--ink); color: var(--white); overflow: hidden; border-bottom: 6px solid var(--red-500); }\np, h2 { margin: 0; position: relative; }\n.checker { position: absolute; top: 0; right: 0; width: 150px; height: 90px; pointer-events: none;\n  background: repeating-conic-gradient(rgba(255,255,255,.08) 0 25%, transparent 0 50%) 0 0 / 30px 30px; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-300); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-300); flex: none; }\n.title { font-size: 34px; font-weight: 800; font-stretch: 115%; line-height: 1; text-transform: uppercase; }\n.list, .cta { position: relative; }\n.cta { height: 52px; margin-top: 4px; align-self: flex-start; min-width: 200px; }\n.foot { position: relative; display: flex; gap: 12px; align-items: flex-start; padding-top: 20px; border-top: 1px solid var(--graphite-700);\n  font-size: 14px; line-height: 1.5; color: var(--grey-200); }\n.foot .fi { flex: none; margin-top: 2px; }\n.foot b { color: var(--white); }\n\n.compact { padding: 32px 28px; gap: 16px; border-bottom: 0; justify-content: flex-start; }\n.compact .checker { width: 72px; height: 48px; background-size: 24px 24px; }\n.compact .title { font-size: 24px; }\n\n.l-tablet { padding: 40px; }\n.l-tablet .title { font-size: 30px; }\n.l-mobile { padding: 32px 24px; border-bottom-width: 5px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 24px; font-stretch: 112%; }\n.l-mobile .cta { align-self: stretch; }\n.l-mobile.compact { padding: 28px 22px; }\n`,\n\n  mounted(d2) { FONT(); },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const compact = truthy(p.compact);\n    const clRef = refOf(d2.param('checkList'));\n    const btnRef = refOf(d2.param('button'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const items = list(p.items).length ? list(p.items) : (compact ? [] : ITEMS);\n    const eyebrow = p.eyebrow == null ? (compact ? 'Por que criar conta' : 'Primeira vez aqui?') : p.eyebrow;\n    const title = p.title == null ? (compact ? '' : 'Crie sua conta em 1 minuto') : p.title;\n    const ctaLabel = p.ctaLabel == null ? (compact ? '' : 'Criar conta') : p.ctaLabel;\n    const ctaHref = p.ctaHref || '/cadastro';\n\n    const listHtml = items.length && clRef\n      ? `<div class=\"list\">${d2.render(clRef, { items, tone: 'dark', layout, iconComponent: iconRef }, { key: 'cl', style: 'height:auto' })}</div>`\n      : '';\n    const cta = ctaLabel && btnRef\n      ? `<div class=\"cta\">${d2.render(btnRef, { label: ctaLabel, variant: 'brand', size: 'lg', fullWidth: 'always', href: ctaHref, layout }, {\n          key: 'cta', height: 52, onClick: () => emit(d2, 'onCta', ctaHref) })}</div>`\n      : '';\n    const foot = p.foot\n      ? `<div class=\"foot\"><span class=\"fi\">${ic(d2, iconRef, 'lock', 18, '#ffffff', 'lock')}</span><div>${esc(p.foot).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>')}</div></div>`\n      : '';\n\n    return `<aside class=\"pc l-${layout}${compact ? ' compact' : ''}\">\n      <span class=\"checker\" aria-hidden=\"true\"></span>\n      ${eyebrow ? `<p class=\"kicker\">${esc(eyebrow)}</p>` : ''}\n      ${title ? `<h2 class=\"title\">${esc(title)}</h2>` : ''}\n      ${listHtml}\n      ${cta}\n      ${foot}\n    </aside>`;\n  }\n};"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"compact":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"compact"}}}},"ctaHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaHref"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"foot":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"foot"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abbd2ee0c5e4f969dbbc838","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AuthLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AuthLayout — Loja Lucas Fontana\n// O corpo das páginas de conta: um cartão de até 1040px centralizado.\n// `mode: \"login\"` (padrão): a metade branca do formulário (LoginForm) e a metade escura do PromoCard lado a lado, na mesma altura.\n// Tablet: as metades empilham numa coluna de 560px. Mobile: o formulário perde a borda e o PromoCard vem 32px abaixo.\n// `mode: \"forgot\"`: cartão único de 560px centralizado com o ForgotPasswordForm (recuperar senha); sem borda no mobile.\n// Traz o respiro da página: 72/48/32px em cima e 120/88/64px embaixo.\n// Os textos chegam por `form` (parâmetros do LoginForm), `promo` (do PromoCard) e `forgot` (do ForgotPasswordForm).\n// Login da loja: `authStrategy` (lojaAuth), `checkService`, `failService` e `successService` (tentativas de login) são\n// repassados ao LoginForm — ligue os quatro em cada tela de Login.\n// Componentes do projeto usados dentro (LoginForm, PromoCard, ForgotPasswordForm, FormField, Button, Icon, CodeField,\n// ResendCode, Notice, CheckList, StepIndicator) são parâmetros do tipo `component`, ligados em cada tela com @Nome —\n// só assim entram no pacote do app publicado.\n// Para abrir o login direto na etapa do código: `form: { step: \"code\", email: \"…\" }`; a recuperação na etapa 2: `forgot: { step: \"sent\" }`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst PAD = { desktop: '72px 96px 120px', tablet: '48px 40px 88px', mobile: '32px 20px 64px' };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.auth { width: 100%; background: #ffffff; }\n.in { display: flex; justify-content: center; }\n.split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); width: 100%; max-width: 1040px; align-items: stretch; }\n.half { min-width: 0; }\n.single .split { grid-template-columns: minmax(0, 1fr); max-width: 560px; }\n.l-tablet .split { grid-template-columns: minmax(0, 1fr); max-width: 560px; }\n.l-mobile .split { grid-template-columns: minmax(0, 1fr); gap: 32px; }\n`,\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const r = (n) => refOf(d2.param(n));\n    const icon = r('iconComponent'); const btn = r('button');\n    const forgot = p.mode === 'forgot';\n    const style = layout === 'desktop' && !forgot ? 'height:100%' : 'height:auto';\n\n    let inner;\n    if (forgot) {\n      const f = r('forgotForm') ? d2.render(r('forgotForm'), Object.assign({}, obj(p.forgot), {\n        layout, stepIndicator: r('stepIndicator'), formField: r('formField'), notice: r('notice'), resendCode: r('resendCode'), button: btn, iconComponent: icon\n      }), {\n        key: 'forgot', style,\n        onSubmit: (v) => emit(d2, 'onSubmit', v), onResend: (v) => emit(d2, 'onResend', v),\n        onChangeEmail: (v) => emit(d2, 'onChangeEmail', v), onBack: (h) => emit(d2, 'onBack', h)\n      }) : '';\n      inner = `<div class=\"half\">${f}</div>`;\n    } else {\n      const form = r('loginForm') ? d2.render(r('loginForm'), Object.assign({}, obj(p.form), {\n        layout, formField: r('formField'), button: btn, iconComponent: icon, codeField: r('codeField'), resendCode: r('resendCode'), notice: r('notice'),\n        authStrategy: d2.param('authStrategy'), checkService: d2.param('checkService'), failService: d2.param('failService'), successService: d2.param('successService')\n      }), {\n        key: 'form', style,\n        onSubmit: (v) => emit(d2, 'onSubmit', v), onForgot: (h) => emit(d2, 'onForgot', h),\n        onVerify: (v) => emit(d2, 'onVerify', v), onResend: (v) => emit(d2, 'onResend', v), onChangeEmail: (v) => emit(d2, 'onChangeEmail', v)\n      }) : '';\n      const promo = r('promoCard') ? d2.render(r('promoCard'), Object.assign({}, obj(p.promo), {\n        layout, checkList: r('checkList'), button: btn, iconComponent: icon\n      }), { key: 'promo', style, onCta: (h) => emit(d2, 'onCta', h) }) : '';\n      inner = `<div class=\"half\">${form}</div><div class=\"half\">${promo}</div>`;\n    }\n\n    return `<div class=\"auth l-${layout}${forgot ? ' single' : ''}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      <div class=\"split\">${inner}</div>\n    </div></div>`;\n  }\n};"}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"checkService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkService"}}}},"codeField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeField"}}}},"failService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"failService"}}}},"forgot":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"forgot"}}}},"forgotForm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"forgotForm"}}}},"form":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"form"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"loginForm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loginForm"}}}},"mode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"mode"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onBack":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBack"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"onForgot":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onForgot"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onVerify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVerify"}}}},"promo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"promo"}}}},"promoCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"promoCard"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"stepIndicator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"stepIndicator"}}}},"successService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"successService"}}}}}}}}}},{"_id":"6abbec1eeebb96eb174b6b5d","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Notice","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Notice — Loja Lucas Fontana\n// Caixa de aviso discreta, fundo cinza-claro, com ícone: por que pedimos um dado ou o que não guardamos\n// (\"**Não guardamos dados de cartão** — o pagamento é feito com segurança pelo Mercado Pago.\").\n// `tone: \"accent\"` põe uma barra preta de 4px à esquerda, para a mensagem que responde ao que a pessoa acabou de fazer\n// (o aviso do código de autenticação no login, com o ícone `mail`).\n// O ícone chega por `iconName` (padrão `lock`) — `icon` é nome reservado do bloco base do Data2.\n// O texto chega por `body`; trechos entre **asteriscos duplos** saem em negrito. (O nome `text` também é reservado.)\n// `live: true` anuncia a mensagem para leitor de tela quando ela aparece.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent` (tipo component, ligado com @Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.nt { --ink: #0b0b0c; --grey-50: #f5f5f3; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; gap: 14px; align-items: flex-start; width: 100%; padding: 16px 18px; background: var(--grey-50);\n  font-size: 14px; line-height: 1.5; color: var(--grey-700); }\n.nt b { color: var(--ink); }\n.ic { flex: none; margin-top: 2px; display: flex; }\n.accent { border-left: 4px solid var(--ink); padding: 16px 18px 16px 20px; align-items: center; }\n.accent .ic { margin-top: 0; }\n.success { background: #e3f4e8; border-left: 4px solid #1e8e4a; padding: 16px 18px 16px 20px; align-items: center; color: #14532d; font-weight: 600; }\n.success b { color: #0f3d22; }\n.error { background: #fbe9ec; border-left: 4px solid #c8102e; padding: 16px 18px 16px 20px; align-items: center; color: #7a0b1c; font-weight: 600; }\n.error b { color: #5c0815; }\n.success .ic, .error .ic { margin-top: 0; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const accent = p.tone === 'accent';\n    const success = p.tone === 'success';\n    const err = p.tone === 'error';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const name = (typeof p.iconName === 'string' && p.iconName) || (typeof p.icon === 'string' && p.icon) || (success ? 'check' : err ? 'close' : 'lock');\n    const size = accent || success || err ? 20 : 18;\n    const icon = iconRef ? d2.render(iconRef, { name, size, iconColor: success ? '#1e8e4a' : err ? '#c8102e' : '#0b0b0c' }, { key: 'ic-' + name, width: size, height: size }) : '';\n    const body = p.body == null ? '**Não guardamos dados de cartão** — o pagamento é feito com segurança pelo Mercado Pago.' : p.body;\n    return `<div class=\"nt l-${layout}${accent ? ' accent' : ''}${success ? ' success' : ''}${err ? ' error' : ''}\" role=\"${truthy(p.live) ? 'status' : 'note'}\">\n      <span class=\"ic\" aria-hidden=\"true\">${icon}</span><div>${rich(body)}</div>\n    </div>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"iconName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconName"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"live":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"live"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6abbec3396e03407355ee24f","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CodeField","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CodeField — Loja Lucas Fontana\n// O campo do código de autenticação (login em duas etapas): 56px de altura (52px no mobile), números centralizados,\n// grandes (24px, 800) e espaçados, \"000000\" de exemplo e a dica\n// \"Digite os 6 números enviados para o seu e-mail. O código vale por [X] minutos.\"\n// É UM campo só, não seis caixinhas: aceita colar, abre o teclado numérico (`inputmode=\"numeric\"`),\n// o celular sugere o código que chegou (`autocomplete=\"one-time-code\"`) e tudo que não é número é descartado.\n// `error` mostra o código errado ou vencido (\"Código incorreto. Confere o e-mail e tenta de novo.\").\n// Emite `onInput` a cada tecla e `onComplete` quando os 6 números estão preenchidos.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst len = (p) => { const n = Number(p.length); return n > 0 && n < 13 ? n : 6; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ff { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-400: #8e9095; --grey-500: #6b6e73; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 8px; width: 100%; min-width: 0; }\nlabel { font-size: 14px; font-weight: 700; color: var(--ink); }\ninput { width: 100%; height: 56px; margin: 0; padding: 0 0 0 .5em; border: 1px solid var(--line-strong); border-radius: 2px; background: var(--white);\n  font: 800 24px/1 ${FF}; letter-spacing: .5em; text-align: center; font-variant-numeric: tabular-nums; color: var(--ink);\n  outline: none; -webkit-appearance: none; appearance: none; }\ninput::placeholder { color: var(--grey-400); letter-spacing: .5em; }\ninput:focus { border-color: var(--ink); box-shadow: 0 0 0 1px var(--ink); }\n.error input { border-color: var(--red-500); box-shadow: 0 0 0 1px var(--red-500); }\np { margin: 0; font-size: 13px; line-height: 1.4; }\n.hint { color: var(--grey-500); }\n.err { font-weight: 600; color: var(--red-500); }\n.l-mobile input { height: 52px; font-size: 22px; letter-spacing: .4em; }\n.l-mobile input::placeholder { letter-spacing: .4em; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('input', 'input', (e, el) => {\n      const n = len(d2.params || {});\n      const clean = String(el.value || '').replace(/\\D/g, '').slice(0, n);\n      if (el.value !== clean) el.value = clean;\n      if (d2.root) d2.root.__lfVal = clean;\n      emit(d2, 'onInput', clean);\n      if (clean.length === n) emit(d2, 'onComplete', clean);\n    });\n    if (truthy((d2.params || {}).autoFocus)) setTimeout(() => { const el = d2.$('input'); if (el) el.focus({ preventScroll: true }); }, 60);\n  },\n\n  updated(d2) {\n    const el = d2.$('input');\n    if (el && d2.root && d2.root.__lfVal != null && el.value !== d2.root.__lfVal) el.value = d2.root.__lfVal;\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const n = len(p);\n    const err = p.error ? String(p.error) : '';\n    const hint = p.hint == null ? `Digite os ${n} números enviados para o seu e-mail. O código vale por ${p.validity || '[X]'} minutos.` : String(p.hint);\n    const describe = [err ? 'c-e' : '', hint ? 'c-h' : ''].filter(Boolean).join(' ');\n    const value = p.value ? String(p.value).replace(/\\D/g, '').slice(0, n) : '';\n    return `<div class=\"ff l-${layout}${err ? ' error' : ''}\">\n      <label for=\"c\">${esc(p.label || 'Código de autenticação')}</label>\n      <input id=\"c\" name=\"${esc(p.name || 'codigo')}\" type=\"text\" inputmode=\"numeric\" autocomplete=\"one-time-code\" pattern=\"[0-9]*\"\n        maxlength=\"${n}\" placeholder=\"${'0'.repeat(n)}\"${value ? ` value=\"${value}\"` : ''}${describe ? ` aria-describedby=\"${describe}\"` : ''}${err ? ' aria-invalid=\"true\"' : ''}>\n      ${err ? `<p class=\"err\" id=\"c-e\">${esc(err)}</p>` : ''}\n      ${hint ? `<p class=\"hint\" id=\"c-h\">${esc(hint)}</p>` : ''}\n    </div>`;\n  }\n};"}},"autoFocus":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoFocus"}}}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"hint":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hint"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"length":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"length"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onComplete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onComplete"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"validity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"validity"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abbec4338f2579fe8c01c50","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ResendCode","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ResendCode — Loja Lucas Fontana\n// A linha embaixo do campo do código: \"Não chegou? Reenviar em **0:59**\" contando de verdade.\n// Quando chega a zero, vira o link \"Reenviar código\"; ao reenviar, o contador recomeça.\n// \"Usar outro e-mail\" fica à direita (`showChange: false` esconde).\n// `align: \"center\"` empilha e centraliza as duas partes (tela \"Confira seu e-mail\" da recuperação de senha).\n// Mantenha o contador em 60 segundos ou mais (`seconds`, padrão 59 = 0:59).\n// Emite `onResend` e `onChangeEmail`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst start = (p) => (p.seconds == null || p.seconds === '' ? 59 : Math.max(0, Number(p.seconds) || 0));\nconst mmss = (s) => Math.floor(s / 60) + ':' + ('0' + (s % 60)).slice(-2);\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.rs { --red-500: #c8102e; --ink: #0b0b0c; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px 16px; min-height: 44px; width: 100%;\n  font-size: 15px; color: var(--grey-700); }\n.rs.center { flex-direction: column; justify-content: center; gap: 10px; min-height: 0; text-align: center; }\nb { color: var(--ink); font-weight: 800; font-variant-numeric: tabular-nums; }\nbutton { margin: 0; padding: 0; border: 0; background: none; font: 700 15px/1.2 ${FF}; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\nbutton:hover { color: var(--red-500); }\nbutton:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.l-mobile { font-size: 14px; }\n.l-mobile button { font-size: 14px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const tick = () => {\n      const p = d2.params || {};\n      if (truthy(p.paused)) return;\n      const left = d2.state.left == null ? start(p) : d2.state.left;\n      if (left > 0) d2.state.left = left - 1;\n    };\n    const t = setInterval(tick, 1000);\n    d2.onUnmount(() => clearInterval(t));\n    d2.on('.resend', 'click', () => { d2.state.left = start(d2.params || {}); emit(d2, 'onResend', true); });\n    d2.on('.change', 'click', () => emit(d2, 'onChangeEmail', true));\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const left = d2.state.left == null ? start(p) : d2.state.left;\n    const showChange = p.showChange == null || p.showChange === '' ? true : truthy(p.showChange);\n    const lead = esc(p.leadLabel || 'Não chegou?');\n    const status = left > 0\n      ? `<span aria-live=\"off\">${lead} Reenviar em <b>${mmss(left)}</b></span>`\n      : `<span>${lead} <button type=\"button\" class=\"resend\">${esc(p.resendLabel || 'Reenviar código')}</button></span>`;\n    return `<div class=\"rs l-${layout}${p.align === 'center' ? ' center' : ''}\">\n      ${status}\n      ${showChange ? `<button type=\"button\" class=\"change\">${esc(p.changeLabel || 'Usar outro e-mail')}</button>` : ''}\n    </div>`;\n  }\n};"}},"align":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"align"}}}},"changeLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"changeLabel"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"leadLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"leadLabel"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"paused":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"paused"}}}},"resendLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendLabel"}}}},"seconds":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"seconds"}}}},"showChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showChange"}}}}}}}}}},{"_id":"6abc284feebb96eb174c178e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FormRow","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FormRow — Loja Lucas Fontana\n// Dois campos lado a lado (16px entre eles) no desktop e no tablet; empilhados no mobile.\n// `keep: true` mantém as duas colunas no mobile para pares curtos (Número + Complemento), com 12px entre eles.\n// Os campos chegam por `fields`: uma lista de parâmetros do FormField. Um item vazio (null ou {}) deixa a vaga em branco,\n// para um campo curto ficar com meia largura (CEP, Estado, WhatsApp).\n// Repassa o que é digitado como { name, value } em `onInput` e `onChange`.\n// Não desenha campo: usa o FormField pelo parâmetro `formField` (e repassa o Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; width: 100%; }\n.l-mobile .row { grid-template-columns: minmax(0, 1fr); }\n.l-mobile .row.keep { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }\n.l-mobile .row:not(.keep) .gap { display: none; }\n.cell { min-width: 0; }\n`,\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const ffRef = refOf(d2.param('formField'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const fields = arr(p.fields).slice(0, 2);\n    while (fields.length < 2) fields.push(null);\n\n    const cells = fields.map((f, i) => {\n      const fp = obj(f);\n      if (!Object.keys(fp).length || !ffRef) return '<div class=\"cell gap\" aria-hidden=\"true\"></div>';\n      const name = fp.name || 'campo' + (i + 1);\n      const field = d2.render(ffRef, Object.assign({}, fp, { layout, iconComponent: iconRef }), {\n        key: 'f-' + name,\n        onInput: (v) => emit(d2, 'onInput', { name, value: v }),\n        onChange: (v) => emit(d2, 'onChange', { name, value: v }),\n        onLink: (h) => emit(d2, 'onLink', { name, href: h })\n      });\n      return `<div class=\"cell\">${field}</div>`;\n    }).join('');\n\n    return `<div class=\"l-${layout}\"><div class=\"row${truthy(p.keep) ? ' keep' : ''}\">${cells}</div></div>`;\n  }\n};"}},"fields":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"fields"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"keep":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"keep"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}}}}}}}},{"_id":"6abc284f869c478810fbd9eb","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ChipGroup","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ChipGroup — Loja Lucas Fontana\n// Uma escolha única em chips de 44px: borda line-strong, texto 14px em negrito; o escolhido fica preenchido de preto.\n// Feito sobre radio: as setas do teclado trocam a escolha e o grupo tem legenda para leitor de tela.\n// Usado em \"Sobre o que você quer falar?\" (Elogio · Sugestão de vídeo · Ideia de produto · Quero contratar o Lucas · Outro assunto).\n// `options: [{value, label}]`, `value` para a escolha inicial, `error` para a mensagem de validação.\n// Avisa a escolha em `onChange` (o valor da opção).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nfieldset { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  border: 0; margin: 0; padding: 0; min-width: 0; display: flex; flex-direction: column; gap: 10px; }\nlegend { padding: 0; margin-bottom: 10px; font-size: 14px; font-weight: 700; color: var(--ink); }\n.list { display: flex; flex-wrap: wrap; gap: 8px; }\n.item { position: relative; display: inline-flex; }\n.item input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }\n.item span { display: inline-flex; align-items: center; height: 44px; padding: 0 14px; border: 1px solid var(--line-strong); border-radius: 0;\n  background: var(--white); font-size: 14px; font-weight: 700; color: var(--ink); cursor: pointer; white-space: nowrap;\n  transition: background-color .15s ease, border-color .15s ease, color .15s ease; }\n.item span:hover { border-color: var(--ink); }\n.item input:checked + span { background: var(--ink); border-color: var(--ink); color: var(--white); }\n.item input:focus-visible + span { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.l-mobile .item span { white-space: normal; min-height: 44px; height: auto; padding: 10px 14px; line-height: 1.25; }\n.error .item span { border-color: var(--red-500); }\n.error .item input:checked + span { border-color: var(--ink); }\n.err { margin: 0; font-size: 13px; line-height: 1.4; font-weight: 600; color: var(--red-500); }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('input[type=\"radio\"]', 'change', (e, el) => {\n      if (!el.checked) return;\n      d2.state.v = el.value;\n      emit(d2, 'onChange', el.value);\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const opts = arr(p.options).map((o) => (o && typeof o === 'object' ? o : { value: String(o), label: String(o) }));\n    const name = p.name ? String(p.name) : 'assunto';\n    const cur = d2.state.v !== undefined ? d2.state.v : (p.value == null ? '' : String(p.value));\n    const err = p.error ? String(p.error) : '';\n    const items = opts.map((o) => `<label class=\"item\" data-key=\"${esc(o.value)}\">\n        <input type=\"radio\" name=\"${esc(name)}\" value=\"${esc(o.value)}\"${String(o.value) === cur ? ' checked' : ''}>\n        <span>${esc(o.label != null ? o.label : o.value)}</span></label>`).join('');\n    return `<fieldset class=\"l-${layout}${err ? ' error' : ''}\"${err ? ' aria-invalid=\"true\" aria-describedby=\"cg-e\"' : ''}>\n      ${p.label ? `<legend>${esc(p.label)}</legend>` : ''}\n      <div class=\"list\">${items}</div>\n      ${err ? `<p class=\"err\" id=\"cg-e\">${esc(err)}</p>` : ''}\n    </fieldset>`;\n  }\n};"}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"options":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"options"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abc284f3857467fb2c20c97","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Callout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Callout — Loja Lucas Fontana\n// Bloco cinza-claro (grey-50) com régua vermelha de 4px à esquerda, para campos ou notas que aparecem por causa de uma\n// escolha anterior: \"**Parcerias, mídia e imprensa.** Conta um pouco da proposta…\" com empresa e tipo de proposta.\n// O texto chega por `body` (trechos entre **asteriscos duplos** saem em negrito).\n// Os campos chegam por `rows`: uma lista de linhas, cada linha com os parâmetros de um FormRow ({ fields: [...], keep }).\n// Repassa o que é digitado como { name, value } em `onInput` e `onChange`.\n// Não desenha campo: usa FormRow e FormField por parâmetro (e repassa o Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.co { --red-500: #c8102e; --ink: #0b0b0c; --grey-50: #f5f5f3; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 16px; width: 100%; padding: 20px 20px 22px; background: var(--grey-50); border-left: 4px solid var(--red-500); }\n.co > p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--grey-700); }\n.co > p b { color: var(--ink); }\n.l-mobile.co { padding: 18px 16px 20px; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const frRef = refOf(d2.param('formRow'));\n    const ffRef = refOf(d2.param('formField'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const rows = frRef ? arr(p.rows).map((r, i) => {\n      const rp = Array.isArray(r) ? { fields: r } : obj(r);\n      return d2.render(frRef, Object.assign({}, rp, { layout, formField: ffRef, iconComponent: iconRef }), {\n        key: 'row-' + i,\n        onInput: (v) => emit(d2, 'onInput', v),\n        onChange: (v) => emit(d2, 'onChange', v)\n      });\n    }).join('') : '';\n    return `<div class=\"co l-${layout}\">${p.body ? `<p>${rich(p.body)}</p>` : ''}${rows}</div>`;\n  }\n};"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formRow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formRow"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}}}}}}}},{"_id":"6abc2890351c900cbaf4da2e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"HelpCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// HelpCard — Loja Lucas Fontana\n// \"Precisa de ajuda com este pedido?\": título, uma linha apontando para o SAC e um botão de largura cheia \"Falar com o SAC\".\n// Padrão: fundo branco com borda `line` e botão com contorno.\n// `tone: \"grey\"` é a versão grey-50 sem borda, título largo em caixa alta e botão preto — usada como\n// \"Dúvida sobre pedido?\" na página Fale com a Auto Super.\n// `linkLabel`/`linkHref` põem um link sublinhado embaixo (\"Ver perguntas frequentes\").\n// O destino do SAC chega por `ctaHref` (WhatsApp ou e-mail do SAC — a definir). Sem link, o botão avisa em `onCta`.\n// Não desenha botão: usa o Button pelo parâmetro `button`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.hc { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 12px; width: 100%; padding: 24px; border: 1px solid var(--line); background: var(--white); }\nh2 { margin: 0; font-size: 17px; font-weight: 700; line-height: 1.3; color: var(--ink); }\np { margin: 0 0 4px; font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.grey { background: var(--grey-50); border-color: var(--grey-50); }\n.grey h2 { font-size: 18px; font-weight: 800; font-stretch: 112%; letter-spacing: .01em; text-transform: uppercase; }\n.link { align-self: center; margin-top: 6px; font-size: 14px; font-weight: 700; color: var(--ink); text-decoration: underline; text-underline-offset: 3px;\n  min-height: 44px; display: inline-flex; align-items: center; }\n.link:hover { color: var(--red-500); }\n.link:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.l-mobile.hc { padding: 20px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.link', 'click', (e, el) => emit(d2, 'onLink', el.getAttribute('href')));\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const grey = p.tone === 'grey';\n    const btnRef = refOf(d2.param('button'));\n    const title = p.title || 'Precisa de ajuda com este pedido?';\n    const body = p.body || 'Fala com a gente pelo SAC informando o número do pedido.';\n    const cta = p.ctaLabel || 'Falar com o SAC';\n    const btn = btnRef ? d2.render(btnRef, {\n      label: cta, variant: grey ? 'primary' : 'outline', fullWidth: 'always', layout,\n      href: p.ctaHref || '', newTab: !!p.ctaHref\n    }, { key: 'cta', onClick: () => emit(d2, 'onCta', p.ctaHref || '') }) : '';\n    return `<section class=\"hc l-${layout}${grey ? ' grey' : ''}\">\n      <h2>${esc(title)}</h2>\n      <p>${rich(body)}</p>\n      ${btn}\n      ${p.linkLabel ? `<a class=\"link\" href=\"${esc(p.linkHref || '#')}\">${esc(p.linkLabel)}</a>` : ''}\n    </section>`;\n  }\n};\n"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"ctaHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaHref"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"linkHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"linkHref"}}}},"linkLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"linkLabel"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"tone":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tone"}}}}}}}}}},{"_id":"6abc289138ce7ce429d5b7c7","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ChannelCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ChannelCard — Auto Super Shop\n// \"Siga o canal\": uma foto da Auto Super sobre a régua vermelha de 6px (5px abaixo de 1200px), depois um painel preto com o\n// kicker em red-300, uma linha de texto e os botões quadrados de 44px do YouTube e do Instagram.\n// Sem `image`, mostra o espaço reservado da foto com o texto de `placeholder` (\"Foto da Auto Super\").\n// `youtubeHref` / `instagramHref`: sem link, o botão aparece marcado como \"link a definir\" e não navega.\n// `showYoutube: false` / `showInstagram: false` escondem um deles.\n// Não desenha ícone: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst ic = (d2, ref, name, size, color, key) => ref\n  ? d2.render(ref, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' })\n  : '';\nconst IMG = '<svg viewBox=\"0 0 24 24\" width=\"28\" height=\"28\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"1\"/><circle cx=\"9\" cy=\"10\" r=\"2\"/><path d=\"m21 17-5-5-9 8\"/></svg>';\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ch { --red-500: #c8102e; --red-300: #ff4d62; --ink: #0b0b0c; --white: #ffffff; --graphite-700: #34363a; --graphite-900: #1c1d1f;\n  --grey-200: #c9cacd; --grey-300: #a7a9ad;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; width: 100%; background: var(--ink); color: var(--white); }\n.media { position: relative; height: 256px; background: var(--graphite-900); border-bottom: 6px solid var(--red-500);\n  display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.l-tablet .media, .l-mobile .media { border-bottom-width: 5px; }\n.l-tablet .media { height: 220px; }\n.l-mobile .media { height: 240px; }\n.media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }\n.ph { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 0 16px; text-align: center;\n  font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-300); }\n.ph small { font-size: 10px; font-weight: 600; letter-spacing: .1em; opacity: .8; }\n.body { display: flex; flex-direction: column; gap: 12px; padding: 22px 24px 26px; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3;\n  text-transform: uppercase; color: var(--red-300); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.body p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--grey-200); }\n.social { display: flex; gap: 10px; margin-top: 4px; }\n.social a { width: 44px; height: 44px; border: 1px solid var(--graphite-700); color: var(--grey-200); display: flex; align-items: center; justify-content: center;\n  transition: border-color .15s ease, color .15s ease; }\n.social a:hover { border-color: var(--white); color: var(--white); }\n.social a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.social a.pending { border-style: dashed; cursor: default; }\n.l-mobile .body { padding: 20px 20px 24px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.social a', 'click', (e, el) => {\n      if (el.classList.contains('pending')) e.preventDefault();\n      emit(d2, 'onSocial', { network: el.getAttribute('data-net'), href: el.classList.contains('pending') ? '' : el.getAttribute('href') });\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const eyebrow = p.eyebrow || 'Siga o canal';\n    const body = p.body || 'Vídeo novo toda semana. Muita coisa que chega por aqui vira pauta.';\n    const media = p.image\n      ? `<img src=\"${esc(p.image)}\" alt=\"${esc(p.imageAlt || 'Auto Super')}\">`\n      : `<span class=\"ph\">${IMG}${esc(p.placeholder || 'Foto da Auto Super')}<small>Imagem a definir</small></span>`;\n    const link = (net, label, href, show) => {\n      if (show === false || show === 'false') return '';\n      const pending = !href;\n      return `<a data-net=\"${net}\" href=\"${esc(href || '#')}\"${pending ? ' class=\"pending\" aria-disabled=\"true\" title=\"Link a definir\"' : ' target=\"_blank\" rel=\"noopener\"'}\n        aria-label=\"${esc(label)}${pending ? ' (link a definir)' : ''}\">${ic(d2, iconRef, net, 20, 'currentColor', 'ic-' + net)}</a>`;\n    };\n    return `<section class=\"ch l-${layout}\">\n      <div class=\"media\">${media}</div>\n      <div class=\"body\">\n        <span class=\"kicker\">${esc(eyebrow)}</span>\n        <p>${esc(body)}</p>\n        <div class=\"social\">${link('youtube', 'YouTube da Auto Super', p.youtubeHref, p.showYoutube)}${link('instagram', 'Instagram da Auto Super', p.instagramHref, p.showInstagram)}</div>\n      </div>\n    </section>`;\n  }\n};\n"}},"body":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"body"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"imageAlt":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imageAlt"}}}},"instagramHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"instagramHref"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onSocial":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSocial"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"showInstagram":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showInstagram"}}}},"showYoutube":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showYoutube"}}}},"youtubeHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"youtubeHref"}}}}}}}}}},{"_id":"6abc28c33857467fb2c20ea0","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ContactLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ContactLayout — Auto Super Shop\n// O corpo da página \"Fale com a Auto Super\": o formulário (até 800px) ao lado de um aside fixo (sticky) de 380px com o\n// ChannelCard e o HelpCard cinza \"Dúvida sobre pedido?\". Tablet: aside de 300px. Mobile: uma coluna, o card do SAC sobe\n// para cima do formulário e o card do canal fecha a página.\n// Ordem do formulário: ChipGroup de assuntos → nome + e-mail → WhatsApp (opcional) → Callout quando o assunto pede\n// (padrão: \"Quero contratar a Auto Super\", com empresa e tipo de proposta) → mensagem com contador de 1.000 caracteres\n// → linha de privacidade → botão preto \"Enviar mensagem →\".\n// Valida no envio: assunto, nome, e-mail válido e mensagem (e empresa, quando o Callout aparece); o erro some quando a\n// pessoa corrige o campo. Com tudo certo, avisa `onSubmit` com\n// { subject, subjectLabel, name, email, whatsapp, company, proposalType, message } e mostra o estado de sucesso:\n// um Notice de destaque (ícone check) + \"Mandar outro recado\", que limpa o formulário.\n// `status`: \"sent\" força o sucesso; \"error\" mostra o aviso de falha acima do botão; \"sending\" trava o botão.\n// `values` preenche o formulário ao abrir (útil para pré-visualizar, ex.: { subject: \"contratar\" }).\n// `channel` e `help` levam os parâmetros do ChannelCard e do HelpCard (foto, links e destino do SAC a definir).\n// Não desenha nada: usa ChipGroup, FormRow, FormField, Callout, Button, Notice, ChannelCard e HelpCard por parâmetro\n// (e repassa o Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nconst SUBJECTS = [\n  { value: 'elogio', label: 'Elogio' },\n  { value: 'video', label: 'Sugestão de vídeo' },\n  { value: 'produto', label: 'Ideia de produto' },\n  { value: 'contratar', label: 'Quero contratar a Auto Super' },\n  { value: 'outro', label: 'Outro assunto' }\n];\nconst PROPOSALS = [\n  { value: 'publi', label: 'Publi / conteúdo patrocinado' },\n  { value: 'evento', label: 'Evento' },\n  { value: 'entrevista', label: 'Entrevista / imprensa' },\n  { value: 'outro', label: 'Outro' }\n];\nconst MSG = {\n  subject: 'Escolhe um assunto pra gente saber pra quem mandar.',\n  name: 'Conta pra gente como te chamar.',\n  email: 'Confere o e-mail: falta o @ ou o domínio.',\n  emailEmpty: 'Deixa um e-mail pra gente conseguir responder.',\n  message: 'Escreve sua mensagem antes de enviar.',\n  company: 'Conta o nome da marca, agência ou veículo.'\n};\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\nconst PAD = { desktop: '40px 96px 120px', tablet: '32px 40px 88px', mobile: '24px 20px 64px' };\nconst box = (d2) => { const r = d2.root || d2; if (!r.__lfv) r.__lfv = {}; return r; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ct { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); }\n.in { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 56px; align-items: start; }\n.l-tablet .in { grid-template-columns: minmax(0, 1fr) 300px; gap: 32px; }\n.l-mobile .in { grid-template-columns: minmax(0, 1fr); gap: 32px; }\n.top { display: none; }\n.l-mobile .top { display: block; }\n.form { display: flex; flex-direction: column; gap: 20px; max-width: 800px; min-width: 0; }\n.legal { margin: 0; font-size: 14px; line-height: 1.5; color: var(--grey-700); }\n.legal a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }\n.legal a:hover { color: var(--red-500); }\n.legal a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.submit { align-self: flex-start; margin-top: 4px; }\n.l-mobile .submit { align-self: stretch; }\n.aside { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 24px; min-width: 0; }\n.l-mobile .aside { position: static; }\n.l-mobile .aside .help { display: none; }\n.done { display: flex; flex-direction: column; gap: 20px; align-items: flex-start; }\n.l-mobile .done { align-items: stretch; }\n`,\n\n  mounted(d2) {\n    FONT();\n    // Campo-isca contra robôs: pessoas não enxergam, robôs preenchem\n    d2.on('input[data-hp]', 'input', (e, el) => { box(d2).__lfv.website = el.value; });\n    // Cliente logado: já vem com nome e e-mail do cadastro\n    d2.rush('minhaSessaoContato').invoke({}).then((res) => {\n      const out = (res && res.data !== undefined ? res.data : res) || {};\n      if (!out.logado) return;\n      const v = box(d2).__lfv;\n      let changed = false;\n      if (!v.name && out.nome) { v.name = out.nome; changed = true; }\n      if (!v.email && out.email) { v.email = out.email; changed = true; }\n      if (changed) d2.state.gen = (d2.state.gen || 0) + 1;\n    }).catch(() => { /* segue sem pré-preencher */ });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      chip: refOf(d2.param('chipGroup')), row: refOf(d2.param('formRow')), ff: refOf(d2.param('formField')),\n      callout: refOf(d2.param('callout')), btn: refOf(d2.param('button')), notice: refOf(d2.param('notice')),\n      chan: refOf(d2.param('channelCard')), help: refOf(d2.param('helpCard')), icon: refOf(d2.param('iconComponent'))\n    };\n    const b = box(d2);\n    if (!b.__lfSeeded) { b.__lfSeeded = true; Object.assign(b.__lfv, obj(p.values)); }\n    const v = b.__lfv;\n    const gen = d2.state.gen || 0;\n    const errors = d2.state.errors || {};\n    const subjects = arr(p.subjects).length ? arr(p.subjects) : SUBJECTS;\n    const hireValue = p.hireValue || 'contratar';\n    const subject = d2.state.subject !== undefined ? d2.state.subject : (v.subject || '');\n    const hire = subject === hireValue;\n    const status = String(p.status || '');\n    const sent = status === 'sent' || !!d2.state.sent;\n    const sending = status === 'sending' || !!d2.state.busy;\n    // No tablet a coluna do formulário tem ~356px: as linhas de dois campos empilham para não cortar os placeholders.\n    const rowLayout = layout === 'tablet' ? 'mobile' : layout;\n\n    const clearErr = (name) => {\n      if (!errors[name]) return;\n      const next = Object.assign({}, errors); delete next[name]; d2.state.errors = next;\n    };\n    const onField = (e) => { if (!e || !e.name) return; v[e.name] = e.value; clearErr(e.name); };\n\n    const validate = () => {\n      const e = {};\n      if (!v.subject) e.subject = MSG.subject;\n      if (!String(v.name || '').trim()) e.name = MSG.name;\n      const mail = String(v.email || '').trim();\n      if (!mail) e.email = MSG.emailEmpty; else if (!EMAIL.test(mail)) e.email = MSG.email;\n      if (!String(v.message || '').trim()) e.message = MSG.message;\n      if (v.subject === hireValue && !String(v.company || '').trim()) e.company = MSG.company;\n      return e;\n    };\n    const submit = async () => {\n      if (sending) return;\n      const e = validate();\n      d2.state.errors = e;\n      if (Object.keys(e).length) return;\n      const s = subjects.find((o) => String(o.value) === String(v.subject)) || {};\n      const isHire = v.subject === hireValue;\n      const payload = {\n        subject: v.subject, subjectLabel: s.label || v.subject,\n        name: String(v.name || '').trim(), email: String(v.email || '').trim(), whatsapp: String(v.whatsapp || '').trim(),\n        company: isHire ? String(v.company || '').trim() : '', proposalType: isHire ? (v.proposalType || '') : '',\n        message: String(v.message || '').trim()\n      };\n      emit(d2, 'onSubmit', payload);\n      d2.state.busy = true; d2.state.failMsg = ''; d2.state.failNet = false;\n      try {\n        const res = await d2.rush('mensagemEnviar').invoke(Object.assign({}, payload, { website: String(v.website || '') }));\n        const out = (res && res.data !== undefined ? res.data : res) || {};\n        if (out && out.ok) d2.state.sent = true;\n        else d2.state.failMsg = (out && out.error) || '';\n        if (!(out && out.ok) && !d2.state.failMsg) d2.state.failNet = true;\n      } catch (err) { d2.state.failNet = true; }\n      d2.state.busy = false;\n    };\n    const reset = () => {\n      b.__lfv = {}; d2.state.errors = {}; d2.state.subject = ''; d2.state.sent = false; d2.state.gen = gen + 1;\n      emit(d2, 'onReset');\n    };\n\n    const help = (key) => R.help ? d2.render(R.help, Object.assign({\n      tone: 'grey', title: 'Dúvida sobre pedido?',\n      body: 'Troca, entrega ou pagamento é com o SAC — lá a resposta é mais rápida.',\n      linkLabel: 'Ver perguntas frequentes', linkHref: '/faq'\n    }, obj(p.help), { layout, button: R.btn }), {\n      key, onCta: (h) => emit(d2, 'onSac', h), onLink: (h) => emit(d2, 'onFaq', h)\n    }) : '';\n    const channel = R.chan ? d2.render(R.chan, Object.assign({}, obj(p.channel), { layout, iconComponent: R.icon }), {\n      key: 'chan', onSocial: (s) => emit(d2, 'onSocial', s)\n    }) : '';\n\n    let form;\n    if (sent) {\n      const notice = R.notice ? d2.render(R.notice, {\n        tone: 'success', iconName: 'check', live: true, layout, iconComponent: R.icon,\n        body: p.successBody || '**Recado entregue.** Sua mensagem já chegou no time da Auto Super. Se precisar, a gente responde no e-mail que você deixou.'\n      }, { key: 'ok' }) : '';\n      const again = R.btn ? d2.render(R.btn, {\n        label: p.againLabel || 'Mandar outro recado', variant: 'outline', fullWidth: 'mobile', layout\n      }, { key: 'again', onClick: reset }) : '';\n      form = `<div class=\"done\">${notice}${again}</div>`;\n    } else {\n      // Filhos aninhados não recebem parâmetros novos depois de montados: a chave muda quando o erro ou o assunto mudam,\n      // e o campo remonta com o valor guardado (nada do que foi digitado se perde).\n      const sig = (names) => names.map((n) => (errors[n] ? '1' : '0')).join('');\n      const k = (n, names) => n + '-' + gen + (names ? '-' + sig(names) : '') + (hire ? '-h' : '');\n      const chips = R.chip ? d2.render(R.chip, {\n        label: p.subjectLabel || 'Sobre o que você quer falar?', options: subjects, value: subject, name: 'assunto',\n        error: errors.subject || '', layout\n      }, { key: k('subject', ['subject']), onChange: (val) => { v.subject = val; d2.state.subject = val; clearErr('subject'); } }) : '';\n      const row = (key, fields) => R.row ? d2.render(R.row, { fields, layout: rowLayout, formField: R.ff, iconComponent: R.icon }, { key: k(key, fields.filter(Boolean).map((f) => f.name)), onInput: onField }) : '';\n      const r1 = row('r1', [\n        { label: 'Seu nome', name: 'name', placeholder: 'Como você quer ser chamado', autoComplete: 'name', value: v.name || '', error: errors.name || '' },\n        { label: 'E-mail', name: 'email', type: 'email', placeholder: 'seuemail@exemplo.com', autoComplete: 'email', hint: 'Só para responder você, se precisar.', value: v.email || '', error: errors.email || '' }\n      ]);\n      const r2 = row('r2', [\n        { label: 'WhatsApp', name: 'whatsapp', type: 'tel', optional: true, placeholder: '(00) 00000-0000', autoComplete: 'tel', inputMode: 'tel', value: v.whatsapp || '' },\n        null\n      ]);\n      const callout = hire && R.callout ? d2.render(R.callout, {\n        layout: rowLayout, formRow: R.row, formField: R.ff, iconComponent: R.icon,\n        body: p.calloutBody || '**Parcerias, mídia e imprensa.** Conta um pouco da proposta que o time da Auto Super retorna pelo e-mail informado.',\n        rows: [{ fields: [\n          { label: 'Empresa ou veículo', name: 'company', placeholder: 'Nome da marca, agência ou veículo', autoComplete: 'organization', value: v.company || '', error: errors.company || '' },\n          { label: 'Tipo de proposta', name: 'proposalType', placeholder: 'Escolha uma opção', options: arr(p.proposalTypes).length ? arr(p.proposalTypes) : PROPOSALS, value: v.proposalType || '' }\n        ] }]\n      }, { key: k('callout', ['company']), onInput: onField, onChange: onField }) : '';\n      const msg = R.ff ? d2.render(R.ff, {\n        label: 'Mensagem', name: 'message', multiline: true, maxLength: 1000, counter: true, layout, iconComponent: R.icon,\n        placeholder: hire ? 'Conta sobre a marca, a ideia, o formato (publi, evento, entrevista) e as datas.' : 'Conta pra gente o que você curtiu, o que quer ver no canal ou o que dá pra melhorar.',\n        value: v.message || '', error: errors.message || ''\n      }, { key: k('message', ['message']), onInput: (val) => onField({ name: 'message', value: val }) }) : '';\n      const failText = d2.state.failMsg || '';\n      const fail = (status === 'error' || d2.state.failNet || failText) && R.notice ? d2.render(R.notice, {\n        tone: 'error', iconName: 'close', live: true, layout, iconComponent: R.icon,\n        body: failText ? '**Não deu pra enviar.** ' + failText : (p.errorBody || '**Não deu pra enviar agora.** Confere sua conexão e tenta de novo em instantes — o que você escreveu continua aqui.')\n      }, { key: 'fail-' + failText.length + (d2.state.failNet ? 'n' : '') }) : '';\n      const btn = R.btn ? d2.render(R.btn, {\n        label: sending ? 'Enviando…' : (p.submitLabel || 'Enviar mensagem'), variant: 'primary', size: 'lg', arrow: !sending,\n        isDisabled: sending, fullWidth: 'mobile', layout\n      }, { key: 'submit-' + (sending ? 's' : 'i'), onClick: submit }) : '';\n      form = `${chips}${r1}${r2}${callout}${msg}<input data-hp name=\"website\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\" style=\"position:absolute;left:-9999px;width:1px;height:1px;opacity:0\">\n        <p class=\"legal\">Ao enviar, você concorda com a nossa <a href=\"${esc(p.privacyHref || '/politica-de-privacidade')}\">Política de Privacidade</a>.</p>\n        ${fail}<div class=\"submit\">${btn}</div>`;\n    }\n\n    return `<div class=\"ct l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      <div class=\"top\">${layout === 'mobile' ? help('help-top') : ''}</div>\n      <div class=\"form\">${form}</div>\n      <aside class=\"aside\">${channel}<div class=\"help\">${layout === 'mobile' ? '' : help('help')}</div></aside>\n    </div></div>`;\n  }\n};"}},"againLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"againLabel"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"callout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"callout"}}}},"calloutBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"calloutBody"}}}},"channel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"channel"}}}},"channelCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"channelCard"}}}},"chipGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chipGroup"}}}},"confirmOnSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"confirmOnSubmit"}}}},"errorBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"errorBody"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formRow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formRow"}}}},"help":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"help"}}}},"helpCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"helpCard"}}}},"hireValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hireValue"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onFaq":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onFaq"}}}},"onReset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onReset"}}}},"onSac":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSac"}}}},"onSocial":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSocial"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"privacyHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"privacyHref"}}}},"proposalTypes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"proposalTypes"}}}},"status":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"status"}}}},"subjectLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subjectLabel"}}}},"subjects":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subjects"}}}},"submitLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"submitLabel"}}}},"successBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"successBody"}}}},"values":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"values"}}}}}}}}}},{"_id":"6abc3713eebb96eb174c3d34","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Checkbox","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Checkbox — Loja Lucas Fontana\n// Quadrado de 18px com borda de 1,5px grey-500; marcado fica vermelho com check branco.\n// A linha inteira (42px) é a área de toque. Rótulo de 15px.\n// No rótulo: `[texto](link)` vira link sublinhado; `((texto))` sai em cinza (ex.: \"(opcional — dá pra mudar quando quiser)\").\n// `top: true` alinha o quadrado com a primeira linha, para rótulos de várias linhas (consentimentos).\n// `count` mostra um número cinza à direita (filtros: \"Lavagem 12\"). `error` mostra a mensagem em vermelho embaixo.\n// Emite `onChange` com true/false. Não desenha o check: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst rich = (t) => esc(t)\n  .replace(/\\[([^\\]]+)\\]\\(\\^([^)\\s]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noopener noreferrer\">$1</a>')\n  .replace(/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g, '<a href=\"$2\">$1</a>')\n  .replace(/\\(\\((.+?)\\)\\)/g, '<span class=\"muted\">$1</span>')\n  .replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.cb { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; }\nlabel { display: flex; align-items: center; gap: 12px; min-height: 42px; padding: 4px 0; cursor: pointer; font-size: 15px; line-height: 1.45; color: var(--ink); }\n.top label { align-items: flex-start; }\ninput { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }\n.box { flex: none; width: 18px; height: 18px; border: 1.5px solid var(--grey-500); border-radius: 2px; background: var(--white);\n  display: flex; align-items: center; justify-content: center; color: var(--white); }\n.top .box { margin-top: 2px; }\n.box > * { opacity: 0; }\ninput:checked + .box { background: var(--red-500); border-color: var(--red-500); }\ninput:checked + .box > * { opacity: 1; }\ninput:focus-visible + .box { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.dis label { cursor: not-allowed; opacity: .5; }\n.txt { flex: 1; min-width: 0; }\n.txt a { color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; }\n.txt a:hover { color: var(--red-500); }\n.muted { color: var(--grey-500); }\n.count { flex: none; font-size: 13px; color: var(--grey-500); }\n.err { margin: 0 0 0 30px; font-size: 13px; font-weight: 600; color: var(--red-500); }\n.error .box { border-color: var(--red-500); }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('input', 'change', (e, el) => { d2.state.checked = el.checked; emit(d2, 'onChange', el.checked); });\n    d2.on('.txt a', 'click', (e, el) => { e.stopPropagation(); emit(d2, 'onLink', el.getAttribute('href')); });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const checked = d2.state.checked != null ? d2.state.checked : truthy(p.checked);\n    const dis = truthy(p.disabled);\n    const err = p.error ? String(p.error) : '';\n    const check = iconRef ? d2.render(iconRef, { name: 'check', size: 13, iconColor: '#ffffff' }, { key: 'ck', width: 13, height: 13 }) : '✓';\n    return `<div class=\"cb l-${layout}${truthy(p.top) ? ' top' : ''}${dis ? ' dis' : ''}${err ? ' error' : ''}\">\n      <label>\n        <input type=\"checkbox\"${p.name ? ` name=\"${esc(p.name)}\"` : ''}${p.value ? ` value=\"${esc(p.value)}\"` : ''}${checked ? ' checked' : ''}${dis ? ' disabled' : ''}${err ? ' aria-invalid=\"true\" aria-describedby=\"cb-e\"' : ''}>\n        <span class=\"box\" aria-hidden=\"true\">${check}</span>\n        <span class=\"txt\">${rich(p.label || '')}</span>\n        ${p.count != null && p.count !== '' ? `<span class=\"count\">${esc(p.count)}</span>` : ''}\n      </label>\n      ${err ? `<p class=\"err\" id=\"cb-e\">${esc(err)}</p>` : ''}\n    </div>`;\n  }\n};"}},"checked":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checked"}}}},"count":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"count"}}}},"disabled":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"disabled"}}}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}},"top":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"top"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abc37270c5e4f969dbce7d2","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"Radio","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Radio — Loja Lucas Fontana\n// Grupo de opções redondas (escolha uma): círculo de 18px com borda de 1,5px grey-500; o escolhido fica com\n// borda vermelha e o miolo vermelho. Cada opção é uma linha de 42px clicável, rótulo de 15px.\n// No rótulo: `**texto**` sai em negrito e `((texto))` em cinza — \"**CPF** ((pessoa física))\".\n// As opções ficam lado a lado; no mobile empilham (`inline: false` empilha sempre).\n// Usado no TaxIdField (CPF/CNPJ) e, depois, na forma de pagamento. Emite `onChange` com o `value` escolhido.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>').replace(/\\(\\((.+?)\\)\\)/g, '<span class=\"muted\">$1</span>');\nconst DEMO = [{ value: 'cpf', label: '**CPF** ((pessoa física))' }, { value: 'cnpj', label: '**CNPJ** ((pessoa jurídica))' }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.rg { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-wrap: wrap; gap: 0 28px; width: 100%; margin: 0; padding: 0; border: 0; }\n.stack { flex-direction: column; }\nlabel { display: flex; align-items: center; gap: 10px; min-height: 42px; cursor: pointer; font-size: 15px; color: var(--ink); }\ninput { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }\n.dot { flex: none; width: 18px; height: 18px; border: 1.5px solid var(--grey-500); border-radius: 50%; background: var(--white);\n  display: flex; align-items: center; justify-content: center; }\n.dot::after { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--red-500); opacity: 0; }\ninput:checked + .dot { border-color: var(--red-500); }\ninput:checked + .dot::after { opacity: 1; }\ninput:focus-visible + .dot { outline: 2px solid var(--red-500); outline-offset: 2px; }\nb { font-weight: 700; }\n.muted { color: var(--grey-500); }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('input', 'change', (e, el) => { d2.state.value = el.value; emit(d2, 'onChange', el.value); });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const opts = list(p.options).length ? list(p.options) : DEMO;\n    const value = d2.state.value != null ? d2.state.value : (p.value != null && p.value !== '' ? String(p.value) : String((opts[0] || {}).value || ''));\n    const inline = p.inline == null || p.inline === '' ? true : truthy(p.inline);\n    const stack = !inline || layout === 'mobile';\n    const name = esc(p.name || 'opcao');\n    return `<div class=\"rg l-${layout}${stack ? ' stack' : ''}\" role=\"radiogroup\"${p.ariaLabel ? ` aria-label=\"${esc(p.ariaLabel)}\"` : ''}>\n      ${opts.map((o) => `<label><input type=\"radio\" name=\"${name}\" value=\"${esc(o.value)}\"${String(o.value) === value ? ' checked' : ''}><span class=\"dot\" aria-hidden=\"true\"></span><span>${rich(o.label || o.value)}</span></label>`).join('')}\n    </div>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"inline":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"inline"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"options":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"options"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abc3741b953c62a975bfee6","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"TaxIdField","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// TaxIdField — Loja Lucas Fontana\n// Um campo só para CPF ou CNPJ: título \"CPF ou CNPJ\", duas opções (\"**CPF** (pessoa física)\" / \"**CNPJ** (pessoa jurídica)\")\n// que escolhem o documento, o campo com a máscara e o exemplo certos (000.000.000-00 / 00.000.000/0000-00)\n// e a dica \"Um campo só: a gente confere se o número é válido e usa na nota fiscal.\"\n// Confere os dígitos verificadores (não só a quantidade de números) assim que o último número é digitado e de novo\n// quando a pessoa sai do campo; mostra \"CPF inválido. Confere os números.\" (ou CNPJ) e apaga o erro quando fica certo.\n// Recusa também sequências repetidas (111.111.111-11, 00.000.000/0000-00).\n// `error` mostra um erro vindo de fora (tem prioridade). As opções ficam lado a lado; no mobile empilham.\n// Emite `onInput` com {kind, value, digits, valid} e `onKindChange` com \"cpf\" ou \"cnpj\".\n// Não desenha opção nem campo: usa Radio e FormField por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n// Dígitos verificadores\nconst validCPF = (d) => {\n  if (!/^\\d{11}$/.test(d) || /^(\\d)\\1+$/.test(d)) return false;\n  const calc = (n) => { let s = 0; for (let i = 0; i < n; i++) s += Number(d[i]) * (n + 1 - i); const r = (s * 10) % 11; return r === 10 ? 0 : r; };\n  return calc(9) === Number(d[9]) && calc(10) === Number(d[10]);\n};\n// CNPJ alfanumérico (Receita/Serpro, a partir de jul/2026): 12 primeiras posições A–Z/0–9, 2 últimas numéricas;\n// cada caractere vale o código ASCII − 48 (0–9 continuam 0–9; A=17 … Z=42), mesmo módulo 11 e mesmos pesos.\n// Os CNPJs só numéricos de hoje continuam válidos pela mesma conta. Exemplo oficial: 12.ABC.345/01DE-35.\nconst validCNPJ = (raw) => {\n  const d = String(raw || '').toUpperCase();\n  if (!/^[A-Z0-9]{12}\\d{2}$/.test(d) || /^(.)\\1+$/.test(d)) return false;\n  const val = (c) => c.charCodeAt(0) - 48;\n  const calc = (n) => { const w = n === 12 ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; let s = 0; for (let i = 0; i < n; i++) s += val(d[i]) * w[i]; const r = s % 11; return r < 2 ? 0 : 11 - r; };\n  return calc(12) === Number(d[12]) && calc(13) === Number(d[13]);\n};\nconst norm = (kind, v) => (kind === 'cnpj' ? String(v || '').toUpperCase().replace(/[^A-Z0-9]/g, '') : String(v || '').replace(/\\D/g, ''));\nconst check = (kind, v) => { const d = norm(kind, v); return kind === 'cnpj' ? validCNPJ(d) : validCPF(d); };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nfieldset { --ink: #0b0b0c; font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 4px; width: 100%; margin: 0; padding: 0; border: 0; min-width: 0; }\nlegend { padding: 0; margin-bottom: 2px; font-size: 14px; font-weight: 700; color: var(--ink); }\n.field { margin-top: 4px; }\n.live { margin: 4px 0 0; font-size: 13px; font-weight: 600; line-height: 1.4; color: #c8102e; }\n`,\n\n  mounted(d2) { FONT(); },\n\n  updated(d2) {\n    // Devolve o cursor ao campo quando o erro aparece ou some enquanto a pessoa digita\n    if (!d2.root || !d2.root.__refocus) return;\n    d2.root.__refocus = false;\n    setTimeout(() => {\n      const host = d2.$('.field > *');\n      const inp = host && host.shadowRoot ? host.shadowRoot.querySelector('input') : null;\n      if (inp) { inp.focus({ preventScroll: true }); const n = inp.value.length; try { inp.setSelectionRange(n, n); } catch (e) { /* tipo sem seleção */ } }\n    }, 30);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const radioRef = refOf(d2.param('radio'));\n    const ffRef = refOf(d2.param('formField'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const kind = d2.state.kind || (p.kind === 'cnpj' ? 'cnpj' : 'cpf');\n    const st = d2.root ? (d2.root.__lf || (d2.root.__lf = { value: p.value || '' })) : { value: p.value || '' };\n    const own = d2.state.err || '';\n    const err = p.error || own;\n    const live = !err && d2.state.live ? d2.state.live : '';\n    const label = kind === 'cnpj' ? 'CNPJ' : 'CPF';\n\n    const radio = radioRef ? d2.render(radioRef, {\n      name: p.name ? p.name + '-tipo' : 'documento-tipo', value: kind, layout, ariaLabel: 'Tipo de documento',\n      options: [{ value: 'cpf', label: '**CPF** ((pessoa física))' }, { value: 'cnpj', label: '**CNPJ** ((pessoa jurídica))' }]\n    }, {\n      key: 'radio', style: 'height:auto',\n      onChange: (v) => {\n        const k = v === 'cnpj' ? 'cnpj' : 'cpf';\n        const s = d2.root ? d2.root.__lf : null; if (s) s.value = '';\n        d2.state.err = ''; d2.state.kind = k;\n        emit(d2, 'onKindChange', k);\n        emit(d2, 'onInput', { kind: k, value: '', digits: '', valid: false });\n      }\n    }) : '';\n\n    const hint = p.hint == null\n      ? (kind === 'cnpj' ? 'Aceita o CNPJ novo, com letras. A gente confere se é válido e usa na nota fiscal.' : 'Um campo só: a gente confere se o número é válido e usa na nota fiscal.')\n      : String(p.hint);\n    const field = ffRef ? d2.render(ffRef, {\n      ariaLabel: label, name: p.name || 'documento', mask: kind, inputMode: kind === 'cnpj' ? 'text' : 'numeric',\n      placeholder: kind === 'cnpj' ? '00.000.000/0000-00' : '000.000.000-00',\n      hint, error: err, value: st.value, layout, iconComponent: iconRef\n    }, {\n      key: 'doc-' + kind + (err ? '-err-' + err.length : ''), style: 'height:auto',\n      onInput: (v) => {\n        const s = d2.root ? d2.root.__lf : null; if (s) s.value = v;\n        const d = norm(kind, v);\n        const full = d.length === (kind === 'cnpj' ? 14 : 11);\n        const ok = check(kind, v);\n        // Valida na hora: com todos os números digitados mostra o erro (ou apaga, se estiver certo);\n        // enquanto a pessoa ainda digita, apaga um erro antigo para não ficar vermelho à toa.\n        const e = full && !ok ? `${label} inválido. Confere os números.` : '';\n        // Durante a digitação o aviso aparece embaixo do campo sem redesenhá-lo (o cursor continua no lugar).\n        if (e !== (d2.state.live || '')) d2.state.live = e;\n        // Número completo e certo: apaga o erro de borda vermelha que tinha vindo ao sair do campo.\n        if (full && ok && d2.state.err) d2.state.err = '';\n        emit(d2, 'onInput', { kind, value: v, digits: d, valid: ok });\n      },\n      onBlur: (v) => {\n        const d = norm(kind, v);\n        const e = d && !check(kind, v) ? `${label} inválido. Confere os números.` : '';\n        if (e !== (d2.state.err || '')) { d2.state.err = e; d2.state.live = ''; }\n      }\n    }) : '';\n\n    return `<fieldset class=\"l-${layout}\"><legend>${esc(p.label || 'CPF ou CNPJ')}</legend>${radio}<div class=\"field\">${field}</div>${live ? `<p class=\"live\" role=\"alert\">${esc(live)}</p>` : ''}</fieldset>`;\n  }\n};"}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"hint":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hint"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"kind":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kind"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"onKindChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onKindChange"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abc375f3857467fb2c232e5","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FormSection","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FormSection — Loja Lucas Fontana\n// Um grupo de campos com título numerado: o número (`number`, ex.: \"01\") em vermelho itálico largo e o nome em caixa alta espaçada\n// (\"**01** SEUS DADOS\"). (`index` também funciona por dentro de outro componente, mas é nome reservado do bloco.) `plain: true` dá um título sem número, no estilo display (\"COMUNICAÇÕES\").\n// Os campos chegam por `rows`: cada linha tem 1 ou 2 campos. Duas na mesma linha ficam lado a lado (16px) no desktop\n// e no tablet e empilham no mobile — menos a linha com `keep: true` (Número + Complemento), que mantém as duas colunas.\n// Um campo `{kind: \"spacer\"}` guarda meia largura para um campo curto sozinho (CEP, Estado).\n// Cada campo: `{key, kind, ...}` — `kind` \"field\" (padrão, parâmetros do FormField), \"taxid\" (TaxIdField)\n// ou \"checkbox\" (parâmetros do Checkbox: `label`, `checked`). Checkboxes seguidos na mesma linha ficam empilhados, sem espaço\n// entre eles (cada um já tem 42px de área de toque), como em \"Comunicações\".\n// `note` põe uma linha cinza no fim (\"Avisos sobre os seus pedidos… continuam chegando normalmente.\").\n// `divider: true` põe uma linha fina acima da seção, com 24px de respiro.\n// Valores e erros chegam por `values` e `errors` (objetos por `key`); emite `onInput` e `onBlur` com {key, value}\n// (no checkbox, `value` é true/false).\n// 20px entre os campos. Não desenha campo: usa FormField, TaxIdField, Radio, Checkbox e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [\n  { fields: [{ key: 'documento', kind: 'taxid' }] },\n  { fields: [{ key: 'nome', label: 'Nome completo', placeholder: 'Como está no seu documento', autoComplete: 'name' }] },\n  { fields: [{ key: 'email', label: 'E-mail', type: 'email', placeholder: 'seuemail@exemplo.com', autoComplete: 'email' },\n    { key: 'whatsapp', label: 'WhatsApp', type: 'tel', mask: 'phone', placeholder: '(00) 00000-0000', hint: 'Para avisos do pedido.', autoComplete: 'tel' }] }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.fs { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; gap: 20px; width: 100%; margin: 0; }\n.fs.div { padding-top: 24px; border-top: 1px solid var(--line); }\nh2 { display: flex; align-items: baseline; gap: 12px; margin: 0; font-size: 13px; font-weight: 800; letter-spacing: .2em; line-height: 1.2; text-transform: uppercase; color: var(--ink); }\nh2 b { font-size: 22px; font-weight: 800; font-style: italic; font-stretch: 125%; letter-spacing: 0; color: var(--red-500); }\nh2.plain { display: block; font-size: 20px; font-weight: 800; font-stretch: 115%; letter-spacing: 0; line-height: 1.05; }\n.row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; align-items: start; }\n.row.one { grid-template-columns: minmax(0, 1fr); }\n.row.checks { display: flex; flex-direction: column; gap: 0; }\n.l-mobile .row { grid-template-columns: minmax(0, 1fr); gap: 20px; }\n.l-mobile .row.checks { gap: 0; }\n.l-mobile .row.keep { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }\n.l-mobile .row .spacer { display: none; }\n.note { margin: -12px 0 0; font-size: 13px; line-height: 1.4; color: var(--grey-500); }\n`,\n\n  mounted() { FONT(); },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const ffRef = refOf(d2.param('formField'));\n    const txRef = refOf(d2.param('taxIdField'));\n    const rdRef = refOf(d2.param('radio'));\n    const cbRef = refOf(d2.param('checkbox'));\n    const iconRef = refOf(d2.param('iconComponent'));\n    const values = obj(p.values);\n    const errors = obj(p.errors);\n    const ver = p.version == null ? '' : String(p.version);\n    const rows = (list(p.rows).length ? list(p.rows) : DEMO).map((r) => (Array.isArray(r) ? { fields: r } : (r || { fields: [] })));\n\n    const renderField = (f) => {\n      if (!f || f.kind === 'spacer') return '<span class=\"spacer\" aria-hidden=\"true\"></span>';\n      const key = String(f.key || f.name || '');\n      const err = errors[key] ? String(errors[key]) : '';\n      const k = key + '-' + ver + (err ? '-e' + err.length : '');\n      if (f.kind === 'checkbox') {\n        const on = values[key] == null ? truthy(f.checked) : truthy(values[key]);\n        return cbRef ? d2.render(cbRef, {\n          label: f.label || '', name: f.name || key, checked: on, top: f.top !== false, error: err, layout, iconComponent: iconRef\n        }, { key: 'cb-' + k, style: 'height:auto', onChange: (val) => emit(d2, 'onInput', { key, value: !!val }), onLink: (h) => emit(d2, 'onLink', { key, href: h }) }) : '';\n      }\n      if (f.kind === 'taxid') {\n        const v = values[key] && typeof values[key] === 'object' ? values[key] : { kind: values[key + 'Kind'] || 'cpf', value: values[key] || '' };\n        return txRef ? d2.render(txRef, {\n          name: f.name || key, kind: v.kind || 'cpf', value: v.value || '', error: err, hint: f.hint, label: f.label,\n          layout, radio: rdRef, formField: ffRef, iconComponent: iconRef\n        }, { key: 'tx-' + k, style: 'height:auto', onInput: (val) => emit(d2, 'onInput', { key, value: val }) }) : '';\n      }\n      const params = Object.assign({}, f, { name: f.name || key, value: values[key] == null ? (f.value || '') : values[key], error: err, layout, iconComponent: iconRef });\n      delete params.key; delete params.kind;\n      return ffRef ? d2.render(ffRef, params, {\n        key: 'ff-' + k, style: 'height:auto',\n        onInput: (val) => emit(d2, 'onInput', { key, value: val }),\n        onBlur: (val) => emit(d2, 'onBlur', { key, value: val }),\n        onLink: (h) => emit(d2, 'onLink', { key, href: h })\n      }) : '';\n    };\n\n    const title = p.title\n      ? `<h2 class=\"${truthy(p.plain) ? 'plain' : ''}\">${!truthy(p.plain) && (p.number || p.index) ? `<b>${esc(p.number || p.index)}</b>` : ''}${esc(p.title)}</h2>`\n      : '';\n    const body = rows.map((r) => {\n      const fs = list(r.fields);\n      const checks = fs.length > 0 && fs.every((f) => f && f.kind === 'checkbox');\n      if (checks) return `<div class=\"row checks\">${fs.map(renderField).join('')}</div>`;\n      return `<div class=\"row${fs.length < 2 ? ' one' : ''}${truthy(r.keep) ? ' keep' : ''}\">${fs.map(renderField).join('')}</div>`;\n    }).join('');\n    const note = p.note ? `<p class=\"note\">${esc(p.note)}</p>` : '';\n    return `<section class=\"fs l-${layout}${truthy(p.divider) ? ' div' : ''}\">${title}${body}${note}</section>`;\n  }\n};\n"}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"divider":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"divider"}}}},"errors":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"errors"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"index":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"index"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"note":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"note"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onBlur":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBlur"}}}},"onInput":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onInput"}}}},"onLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLink"}}}},"plain":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"plain"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}},"taxIdField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"taxIdField"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"values":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"values"}}}},"version":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"version"}}}}}}}}}},{"_id":"6abc37b3eebb96eb174c3eef","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"SignupForm","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// SignupForm — Loja Lucas Fontana\n// O formulário de criar conta, em duas etapas.\n// Etapa 1 (`step: \"form\"`): 01 SEUS DADOS (CPF/CNPJ, nome, e-mail, WhatsApp), 02 ENDEREÇO DE ENTREGA (CEP com\n// \"Não sei meu CEP\", rua, número + complemento, bairro + cidade, estado), 03 SENHA (senha + confirmação + regras),\n// os dois consentimentos (aceite obrigatório da Política de Privacidade e dos Termos; novidades por e-mail/WhatsApp\n// opcional, nenhum marcado ao abrir), o botão preto \"Criar minha conta\" e \"Já tem conta? Entrar\".\n// CEP: com os 8 números, consulta o ViaCEP e preenche rua, bairro, cidade e estado (a pessoa pode corrigir).\n// Antes de enviar confere: obrigatórios, nome e sobrenome, CPF/CNPJ válido, e-mail, WhatsApp (DDD + 9 números), CEP, UF,\n// a política de senha, a confirmação igual e o aceite. Mostra o erro em cada campo. Tudo certo: emite `onSubmit` com os\n// dados e passa para a etapa 2 (`verifyEmail: false` desliga — na V1 da loja fica desligado).\n// Loja (`verifyEmail: false`): envia os dados ao serviço `signupService` (cadastrarCliente), mostra \"Criando sua conta…\",\n// e com sucesso entra pela estratégia `authStrategy` (lojaAuth) e volta para `?voltar=` (ou `redirectTo`, padrão \"/\").\n// Conta existente → aviso com \"Entrar\"/\"Esqueci minha senha\"; dados inválidos → erro em cada campo; falha de conexão →\n// aviso no fim, com tudo preenchido. Os dois serviços chegam por parâmetro (ligados em cada tela).\n// Etapa 2 (`step: \"code\"`, confirmação de e-mail): aviso com barra preta (\"Enviamos um código de confirmação para …\"),\n// CodeField, ResendCode (\"Corrigir o e-mail\" volta para a etapa 1 com tudo preenchido) e \"Confirmar e criar conta\".\n// Emite `onVerify` {email, code}, `onResend` e `onChangeEmail`. Erro do servidor: `codeError`, `emailError`, `documentError`.\n// `accountExists: true` (resposta do servidor quando o e-mail ou o documento já têm conta): aviso único no topo, sem dizer\n// qual dos dois — \"Os dados informados já possuem um cadastro vinculado.\" — com \"Entrar\" e \"Esqueci minha senha\", que\n// levam o e-mail já preenchido (`loginHref`, `forgotHref`). A página rola até o aviso; o formulário continua preenchido.\n// Senha segura: 8+ caracteres, letra maiúscula e minúscula, número, símbolo, diferente do e-mail e fora da lista de\n// senhas comuns. As regras aparecem embaixo da senha e ganham check enquanto a pessoa digita.\n// Não desenha campo, aviso, caixa nem botão: usa FormSection, FormField, TaxIdField, Radio, Checkbox, Notice,\n// CodeField, ResendCode, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst UFS = 'AC AL AP AM BA CE DF ES GO MA MT MS MG PA PB PR PE PI RJ RN RS RO RR SC SP SE TO'.split(' ');\nconst onlyD = (v) => String(v || '').replace(/\\D/g, '');\nconst validCPF = (d) => {\n  if (!/^\\d{11}$/.test(d) || /^(\\d)\\1+$/.test(d)) return false;\n  const calc = (n) => { let s = 0; for (let i = 0; i < n; i++) s += Number(d[i]) * (n + 1 - i); const r = (s * 10) % 11; return r === 10 ? 0 : r; };\n  return calc(9) === Number(d[9]) && calc(10) === Number(d[10]);\n};\n// CNPJ alfanumérico (jul/2026): 12 primeiras posições A–Z/0–9, DV numérico; cada caractere vale ASCII − 48.\nconst validCNPJ = (raw) => {\n  const d = String(raw || '').toUpperCase();\n  if (!/^[A-Z0-9]{12}\\d{2}$/.test(d) || /^(.)\\1+$/.test(d)) return false;\n  const val = (c) => c.charCodeAt(0) - 48;\n  const calc = (n) => { const w = n === 12 ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; let s = 0; for (let i = 0; i < n; i++) s += val(d[i]) * w[i]; const r = s % 11; return r < 2 ? 0 : 11 - r; };\n  return calc(12) === Number(d[12]) && calc(13) === Number(d[13]);\n};\nconst docNorm = (kind, v) => (kind === 'cnpj' ? String(v || '').toUpperCase().replace(/[^A-Z0-9]/g, '') : onlyD(v));\n// Política de senha segura (a mesma conferência é refeita no servidor)\nconst COMMON = ['12345678', '123456789', '1234567890', 'senha123', 'senha@123', 'senha@1234', 'abcd@1234', 'abc@1234', 'qwerty123', 'qwerty@123',\n  'password1', 'password@1', 'admin@123', 'mudar@123', 'brasil@123', 'brasil2026', 'lucas@123', 'fontana@123', 'carro@123', 'teste@123'];\nconst pwRules = (pw, email) => {\n  const s = String(pw || ''); const low = s.toLowerCase();\n  const mail = String(email || '').trim().toLowerCase(); const user = mail.split('@')[0] || '';\n  return [\n    { id: 'len', label: '8 caracteres ou mais', ok: s.length >= 8 },\n    { id: 'case', label: 'Letra maiúscula e minúscula', ok: /[a-z]/.test(s) && /[A-Z]/.test(s) },\n    { id: 'num', label: 'Um número', ok: /\\d/.test(s) },\n    { id: 'sym', label: 'Um símbolo (ex.: ! @ # $ % & *)', ok: /[^A-Za-z0-9\\s]/.test(s) },\n    { id: 'mail', label: 'Diferente do seu e-mail', ok: !!s && (!mail || (low !== mail && !(user.length >= 3 && low.indexOf(user) >= 0))) },\n    { id: 'common', label: 'Não pode ser uma senha comum', ok: !!s && COMMON.indexOf(low) < 0 && !/^(.)\\1+$/.test(s) }\n  ];\n};\nconst store = (d2) => {\n  const blank = { v: { documento: { kind: 'cpf', value: '' } }, privacy: false, news: false, code: '', lastCep: '' };\n  return d2.root ? (d2.root.__lf || (d2.root.__lf = blank)) : blank;\n};\nconst SECTIONS = (cepHint) => [\n  { id: 's1', index: '01', title: 'Seus dados', rows: [\n    { fields: [{ key: 'documento', kind: 'taxid' }] },\n    { fields: [{ key: 'nome', label: 'Nome completo', placeholder: 'Como está no seu documento', autoComplete: 'name' }] },\n    { fields: [{ key: 'email', label: 'E-mail', type: 'email', placeholder: 'seuemail@exemplo.com', autoComplete: 'email' },\n      { key: 'whatsapp', label: 'WhatsApp', type: 'tel', mask: 'phone', placeholder: '(00) 00000-0000', hint: 'Para avisos do pedido.', autoComplete: 'tel' }] }] },\n  { id: 's2', index: '02', title: 'Endereço de entrega', rows: [\n    { fields: [{ key: 'cep', label: 'CEP', mask: 'cep', placeholder: '00000-000', autoComplete: 'postal-code', hint: cepHint,\n      linkLabel: 'Não sei meu CEP', linkHref: 'https://buscacepinter.correios.com.br/app/endereco/index.php' }, { kind: 'spacer' }] },\n    { fields: [{ key: 'rua', label: 'Rua / avenida', autoComplete: 'address-line1' }] },\n    { keep: true, fields: [{ key: 'numero', label: 'Número', placeholder: 'Ou S/N' }, { key: 'complemento', label: 'Complemento', optional: true, placeholder: 'Apto, bloco, referência', autoComplete: 'address-line2' }] },\n    { fields: [{ key: 'bairro', label: 'Bairro' }, { key: 'cidade', label: 'Cidade', autoComplete: 'address-level2' }] },\n    { fields: [{ key: 'uf', label: 'Estado', mask: 'uf', placeholder: 'UF', maxLength: 2, autoComplete: 'address-level1' }, { kind: 'spacer' }] }] },\n  { id: 's3', index: '03', title: 'Senha', rows: [\n    { fields: [{ key: 'senha', label: 'Senha', type: 'password', placeholder: 'Crie uma senha', autoComplete: 'new-password' },\n      { key: 'confirma', label: 'Confirmar senha', type: 'password', placeholder: 'Repita a senha', autoComplete: 'new-password' }] }] }\n];\nconst SEC_OF = { documento: 's1', nome: 's1', email: 's1', whatsapp: 's1', cep: 's2', rua: 's2', numero: 's2', complemento: 's2', bairro: 's2', cidade: 's2', uf: 's2', senha: 's3', confirma: 's3' };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.sf { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; gap: 32px; width: 100%; color: var(--ink); }\n.consents { display: flex; flex-direction: column; gap: 4px; padding-top: 24px; border-top: 1px solid var(--line); }\n.foot { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }\n.submit { width: 280px; height: 52px; }\n.have { margin: 0; font-size: 15px; color: var(--grey-700); }\n.have a { color: var(--ink); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }\n.have a:hover { color: var(--red-500); }\n.have a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.summary { margin: 0; font-size: 14px; font-weight: 600; color: var(--red-500); }\n.code { display: flex; flex-direction: column; gap: 18px; max-width: 520px; }\n.full { width: 100%; height: 52px; }\n.exists { display: flex; flex-direction: column; gap: 16px; }\n.exists .acts { display: flex; flex-wrap: wrap; gap: 12px; }\n.exists .acts > * { flex: 0 0 auto; width: auto !important; }\n.rules { list-style: none; margin: -12px 0 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px 16px; }\n.rules li { display: flex; align-items: center; gap: 8px; font-size: 13px; line-height: 1.35; color: var(--grey-500); }\n.rules .dot { width: 16px; height: 16px; flex: none; border: 1.5px solid #d9d9d6; display: flex; align-items: center; justify-content: center; }\n.rules .ok { color: var(--ink); font-weight: 600; }\n.rules .ok .dot { background: var(--ink); border-color: var(--ink); }\n.rules.err li:not(.ok) { color: var(--red-500); }\n.rules.err li:not(.ok) .dot { border-color: var(--red-500); }\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.l-mobile .rules { grid-template-columns: minmax(0, 1fr); }\n.l-mobile .exists .acts { flex-direction: column; }\n.l-mobile .exists .acts > * { width: 100% !important; }\n.l-mobile { gap: 28px; }\n.l-mobile .foot { flex-direction: column; align-items: stretch; gap: 16px; }\n.l-mobile .submit { width: 100%; }\n.l-mobile .have { text-align: center; }\n`,\n\n  mounted(d2, root) {\n    FONT();\n    d2.on('.have a', 'click', (e, el) => emit(d2, 'onLogin', el.getAttribute('href')));\n    // Enter em qualquer campo envia, igual ao botão \"Criar minha conta\"\n    d2.on(root, 'keydown', (e) => {\n      if (e.key !== 'Enter' || e.shiftKey || e.isComposing) return;\n      const go = d2.root && d2.root.__sfSubmit;\n      if (typeof go === 'function') { e.preventDefault(); go(); }\n    });\n    // Quem já está logado e abre /cadastro vai para Minha conta\n    const authRef = d2.param('authStrategy');\n    if (authRef && window.location.pathname === '/cadastro') {\n      setTimeout(() => { try { const u = d2.auth(authRef).user; if (u && u.email) window.location.assign('/perfil/meus-pedidos'); } catch (e) { /* sem sessão */ } }, 300);\n    }\n  },\n\n  updated(d2) {\n    // Conta existente: rola até o aviso uma vez\n    const p = d2.params || {};\n    if (!truthy(p.accountExists) && !d2.state.exists) { if (d2.root) d2.root.__lfExists = false; return; }\n    if (d2.root && d2.root.__lfExists) return;\n    if (d2.root) d2.root.__lfExists = true;\n    setTimeout(() => { const el = d2.$('.exists'); if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 80);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      fs: refOf(d2.param('formSection')), ff: refOf(d2.param('formField')), tx: refOf(d2.param('taxIdField')),\n      rd: refOf(d2.param('radio')), cb: refOf(d2.param('checkbox')), nt: refOf(d2.param('notice')),\n      cf: refOf(d2.param('codeField')), rs: refOf(d2.param('resendCode')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent'))\n    };\n    const s = store(d2);\n    const step = d2.state.step || (p.step === 'code' ? 'code' : 'form');\n    const errs = Object.assign({}, d2.state.errs || {});\n    if (p.emailError && !errs.email) errs.email = p.emailError;\n    if (p.documentError && !errs.documento) errs.documento = p.documentError;\n    const ver = d2.state.ver || { s1: 0, s2: 0, s3: 0, c: 0 };\n    const bump = (ids) => { const v = Object.assign({ s1: 0, s2: 0, s3: 0, c: 0 }, d2.state.ver || {}); ids.forEach((i) => { v[i] = (v[i] || 0) + 1; }); d2.state.ver = v; };\n\n    // ——— CEP → ViaCEP\n    const lookupCep = (digits) => {\n      const st = store(d2);\n      if (digits.length !== 8 || digits === st.lastCep) return;\n      st.lastCep = digits;\n      d2.state.cepStatus = 'loading';\n      fetch('https://viacep.com.br/ws/' + digits + '/json/').then((r) => r.json()).then((j) => {\n        const e = Object.assign({}, d2.state.errs || {});\n        if (!j || j.erro) {\n          e.cep = 'CEP não encontrado. Confere os números ou preencha o endereço.';\n        } else {\n          delete e.cep;\n          const v = store(d2).v;\n          v.rua = j.logradouro || v.rua || ''; v.bairro = j.bairro || v.bairro || '';\n          v.cidade = j.localidade || v.cidade || ''; v.uf = j.uf || v.uf || '';\n          ['rua', 'bairro', 'cidade', 'uf'].forEach((k) => { if (v[k]) delete e[k]; });\n        }\n        d2.state.errs = e; d2.state.cepStatus = j && !j.erro ? 'ok' : 'fail'; bump(['s2']);\n        emit(d2, 'onCep', j && !j.erro ? j : null);\n      }).catch(() => { d2.state.cepStatus = 'fail'; });\n    };\n\n    // ——— Conferência\n    const validate = () => {\n      const v = store(d2).v; const e = {};\n      const doc = v.documento || {}; const dd = docNorm(doc.kind, doc.value);\n      if (!dd) e.documento = 'Informe seu ' + (doc.kind === 'cnpj' ? 'CNPJ' : 'CPF') + '.';\n      else if (!(doc.kind === 'cnpj' ? validCNPJ(dd) : validCPF(dd))) e.documento = (doc.kind === 'cnpj' ? 'CNPJ' : 'CPF') + ' inválido. Confere os números.';\n      const nome = String(v.nome || '').trim();\n      if (!nome) e.nome = 'Informe seu nome completo.'; else if (nome.split(/\\s+/).length < 2) e.nome = 'Coloque nome e sobrenome.';\n      const email = String(v.email || '').trim();\n      if (!email) e.email = 'Informe seu e-mail.'; else if (!EMAIL.test(email)) e.email = 'Confira o e-mail: falta o @ ou o domínio.';\n      const w = onlyD(v.whatsapp); if (!w) e.whatsapp = 'Informe seu WhatsApp.'; else if (w.length !== 11 || w[2] !== '9') e.whatsapp = 'Confira o WhatsApp: DDD + 9 números (celular).';\n      const c = onlyD(v.cep); if (!c) e.cep = 'Informe o CEP.'; else if (c.length !== 8) e.cep = 'O CEP tem 8 números.';\n      else if ((d2.state.errs || {}).cep) e.cep = d2.state.errs.cep;\n      if (!String(v.rua || '').trim()) e.rua = 'Informe a rua ou avenida.';\n      if (!String(v.numero || '').trim()) e.numero = 'Informe o número (ou \"s/n\").';\n      if (!String(v.bairro || '').trim()) e.bairro = 'Informe o bairro.';\n      if (!String(v.cidade || '').trim()) e.cidade = 'Informe a cidade.';\n      const uf = String(v.uf || '').toUpperCase(); if (!uf) e.uf = 'Informe o estado.'; else if (UFS.indexOf(uf) < 0) e.uf = 'Use a sigla do estado (ex.: SP).';\n      const pw = String(v.senha || '');\n      if (!pw) e.senha = 'Crie uma senha.'; else if (pwRules(pw, v.email).some((r) => !r.ok)) e.senha = 'A senha ainda não cumpre todas as regras abaixo.';\n      if (!v.confirma) e.confirma = 'Repita a senha.'; else if (v.confirma !== pw) e.confirma = 'As senhas não são iguais.';\n      if (!store(d2).privacy) e.privacy = 'Para criar a conta, aceite a Política de Privacidade e os Termos de Uso.';\n      return e;\n    };\n\n    // ——— Envio para o servidor (loja)\n    const dest = () => {\n      let v = '';\n      try { v = new URLSearchParams(window.location.search).get('voltar') || ''; } catch (e) { v = ''; }\n      return /^\\/(?!\\/)/.test(v) ? v : (p.redirectTo || '/');\n    };\n    const sendSignup = async (data) => {\n      if (d2.state.sending) return;\n      d2.state.sending = true; d2.state.srvErr = ''; d2.state.exists = false;\n      let r = null;\n      const svc = d2.param('signupService');\n      const auth = d2.param('authStrategy');\n      if (!svc || !auth) { d2.state.sending = false; d2.state.srvErr = 'Não deu pra criar a conta agora. Tenta de novo em instantes.'; return; }\n      try {\n        const res = await d2.rush(svc).invoke(data);\n        r = res && res.data !== undefined ? res.data : res;\n      } catch (e) { r = null; }\n      if (r && r.ok) {\n        try {\n          await d2.auth(auth).login({ credentials: { email: r.email || data.email, senha: data.password } });\n          window.location.assign(dest());\n        } catch (e) {\n          window.location.assign((p.loginHref || '/login') + '?email=' + encodeURIComponent(r.email || data.email));\n        }\n        return;\n      }\n      d2.state.sending = false;\n      if (r && r.motivo === 'conta_existente') { d2.state.exists = true; return; }\n      if (r && r.erros && typeof r.erros === 'object') {\n        const e = Object.assign({}, r.erros);\n        d2.state.errs = e; bump(['s1', 's2', 's3', 'c']);\n        setTimeout(() => { const el = d2.$('.sf'); if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 50);\n        return;\n      }\n      d2.state.srvErr = 'Não deu pra criar a conta agora. Confere sua conexão e tenta de novo — o que você preencheu continua aqui.';\n    };\n\n    const submit = () => {\n      const e = validate();\n      const prev = d2.state.errs || {};\n      const changed = new Set();\n      Object.keys(Object.assign({}, prev, e)).forEach((k) => { if (prev[k] !== e[k] && SEC_OF[k]) changed.add(SEC_OF[k]); });\n      if (prev.privacy !== e.privacy) changed.add('c');\n      d2.state.errs = e; bump(Array.from(changed));\n      if (Object.keys(e).length) {\n        setTimeout(() => { const el = d2.$('.sf'); if (el && el.scrollIntoView) el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, 50);\n        return;\n      }\n      const st = store(d2); const v = st.v;\n      const data = {\n        documentKind: (v.documento || {}).kind || 'cpf', document: docNorm((v.documento || {}).kind, (v.documento || {}).value), name: String(v.nome || '').trim(),\n        email: String(v.email || '').trim(), whatsapp: onlyD(v.whatsapp),\n        address: { cep: onlyD(v.cep), street: v.rua || '', number: v.numero || '', complement: v.complemento || '', district: v.bairro || '', city: v.cidade || '', state: String(v.uf || '').toUpperCase() },\n        password: v.senha, acceptedPrivacy: true, marketingOptIn: !!st.news\n      };\n      emit(d2, 'onSubmit', data);\n      // Loja (sem código): cria a conta pelo `signupService`, entra pelo `authStrategy` e volta para a origem\n      if (p.verifyEmail === false || p.verifyEmail === 'false') { sendSignup(data); return; }\n      if (p.verifyEmail == null || p.verifyEmail === '' || truthy(p.verifyEmail)) { st.code = ''; d2.state.errC = ''; d2.state.step = 'code'; }\n    };\n    if (d2.root) d2.root.__sfSubmit = submit;\n\n    const onField = (ev) => {\n      if (!ev || !ev.key) return;\n      const st = store(d2);\n      st.v[ev.key] = ev.value;\n      if (ev.key === 'cep') lookupCep(onlyD(ev.value));\n      if (ev.key === 'senha' || ev.key === 'email') d2.state['pw_' + ev.key] = String(ev.value || '');\n    };\n\n    // ——— Etapa 2: código\n    if (step === 'code') {\n      const email = String(store(d2).v.email || p.email || '[seuemail@exemplo.com]');\n      const errC = d2.state.errC || p.codeError || '';\n      const verify = () => {\n        const c = String(store(d2).code || '');\n        const eC = c.length < 6 ? 'Digite os 6 números do código.' : '';\n        d2.state.errC = eC;\n        if (!eC) emit(d2, 'onVerify', { email, code: c });\n      };\n      const back = () => { store(d2).code = ''; d2.state.errC = ''; d2.state.step = 'form'; bump(['s1', 's2', 's3', 'c']); emit(d2, 'onChangeEmail', email); };\n      const notice = R.nt ? d2.render(R.nt, {\n        tone: 'accent', iconName: 'mail', live: true, layout, iconComponent: R.icon,\n        body: 'Enviamos um **código de confirmação** para **' + email + '**. Digite abaixo para ativar sua conta.'\n      }, { key: 'notice', style: 'height:auto' }) : '';\n      const code = R.cf ? d2.render(R.cf, {\n        layout, length: 6, validity: p.codeValidity || '', error: errC, autoFocus: true, value: store(d2).code, label: 'Código de confirmação'\n      }, { key: 'code' + (errC ? '-e' + errC.length : ''), style: 'height:auto', onInput: (v) => { store(d2).code = v; } }) : '';\n      const resend = R.rs ? d2.render(R.rs, { layout, seconds: p.resendSeconds == null || p.resendSeconds === '' ? 59 : p.resendSeconds, changeLabel: 'Corrigir o e-mail' }, {\n        key: 'resend', style: 'height:auto', onResend: () => emit(d2, 'onResend', email), onChangeEmail: back\n      }) : '';\n      const btn = R.btn ? `<div class=\"full\">${d2.render(R.btn, { label: p.verifyLabel || 'Confirmar e criar conta', variant: 'primary', size: 'lg', fullWidth: 'always', layout }, { key: 'verify', height: 52, onClick: verify })}</div>` : '';\n      return `<div class=\"sf l-${layout} step-code\" role=\"form\" aria-label=\"Confirmar o e-mail\"><div class=\"code\">${notice}${code}${resend}${btn}</div></div>`;\n    }\n\n    // ——— Etapa 1: formulário\n    const cepHint = d2.state.cepStatus === 'loading' ? 'Buscando o endereço…' : (d2.state.cepStatus === 'ok' ? 'Endereço preenchido pelo CEP. Confere e completa o que faltar.' : '');\n    const sections = SECTIONS(cepHint).map((sec) => R.fs ? d2.render(R.fs, {\n      index: sec.index, title: sec.title, rows: sec.rows, values: store(d2).v, errors: errs, version: String(ver[sec.id] || 0) + (sec.id === 's2' ? '-' + (d2.state.cepStatus || '') : ''),\n      layout, formField: R.ff, taxIdField: R.tx, radio: R.rd, iconComponent: R.icon\n    }, { key: sec.id + '-' + (ver[sec.id] || 0) + (sec.id === 's2' ? '-' + (d2.state.cepStatus || '') : ''), style: 'height:auto', onInput: onField }) : '');\n\n    // Regras da senha, logo abaixo da seção 03 — atualizam enquanto a pessoa digita\n    const rules = pwRules(store(d2).v.senha, store(d2).v.email);\n    const rulesHtml = `<ul class=\"rules${errs.senha && store(d2).v.senha ? ' err' : ''}\" aria-label=\"Regras da senha\">${rules.map((r) => `<li class=\"${r.ok ? 'ok' : ''}\">\n        <span class=\"dot\" aria-hidden=\"true\">${r.ok && R.icon ? d2.render(R.icon, { name: 'check', size: 12, iconColor: '#ffffff' }, { key: 'rk-' + r.id, width: 12, height: 12, style: 'flex:none' }) : ''}</span>\n        <span>${esc(r.label)}<span class=\"sr\">${r.ok ? ' — ok' : ' — falta'}</span></span></li>`).join('')}</ul>`;\n    sections.splice(3, 0, rulesHtml);\n\n    // Conta existente (resposta do servidor)\n    const mail = String(store(d2).v.email || '').trim();\n    const q = mail ? '?email=' + encodeURIComponent(mail) : '';\n    const exists = (truthy(p.accountExists) || d2.state.exists) ? `<div class=\"exists\">${R.nt ? d2.render(R.nt, {\n      tone: 'error', iconName: 'close', live: true, layout, iconComponent: R.icon,\n      body: p.accountExistsText || '**Os dados informados já possuem um cadastro vinculado.** Entre com a sua senha ou recupere o acesso.'\n    }, { key: 'exists', style: 'height:auto' }) : ''}<div class=\"acts\">${R.btn ? d2.render(R.btn, { label: 'Entrar', variant: 'primary', href: (p.loginHref || '/login') + q, layout }, { key: 'ex-login' }) : ''}${R.btn ? d2.render(R.btn, { label: 'Esqueci minha senha', variant: 'outline', href: (p.forgotHref || '/esqueci-a-senha') + q, layout }, { key: 'ex-forgot' }) : ''}</div></div>` : '';\n\n    const cons = (key, label, top, err) => R.cb ? d2.render(R.cb, { name: key, label, top, checked: !!store(d2)[key], error: err || '', layout, iconComponent: R.icon }, {\n      key: 'cb-' + key + '-' + (ver.c || 0), style: 'height:auto', onChange: (v) => { store(d2)[key] = !!v; }\n    }) : '';\n    const consents = `<div class=\"consents\">\n      ${cons('privacy', 'Li e aceito a [Política de Privacidade](' + (p.privacyHref || '/politicas/privacidade') + ') e os [Termos de Uso](' + (p.termsHref || '/politicas/termos-de-venda') + ').', true, errs.privacy)}\n      ${cons('news', 'Quero receber novidades, lançamentos e ofertas por e-mail e WhatsApp. ((opcional — dá pra mudar quando quiser))', true, '')}\n    </div>`;\n    const n = Object.keys(errs).length;\n    const summary = n ? `<p class=\"summary\" role=\"alert\">Faltou ${n === 1 ? '1 coisa' : n + ' coisas'} — confere os campos marcados em vermelho.</p>` : (d2.state.srvErr ? `<p class=\"summary\" role=\"alert\">${esc(d2.state.srvErr)}</p>` : '');\n    const sending = !!d2.state.sending;\n    const btn = R.btn ? `<div class=\"submit\">${d2.render(R.btn, { label: sending ? 'Criando sua conta…' : (p.submitLabel || 'Criar minha conta'), variant: 'primary', size: 'lg', fullWidth: 'always', isDisabled: sending, layout }, { key: 'submit-' + (sending ? 's' : 'i'), height: 52, onClick: submit })}</div>` : '';\n    return `<div class=\"sf l-${layout}\" role=\"form\" aria-label=\"Criar conta\">\n      ${exists}\n      ${sections.join('')}\n      ${consents}\n      ${summary}\n      <div class=\"foot\">${btn}<p class=\"have\">Já tem conta? <a href=\"${esc(p.loginHref || '/login')}\">Entrar</a></p></div>\n    </div>`;\n  }\n};"}},"accountExists":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accountExists"}}}},"accountExistsText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accountExistsText"}}}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"codeError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeError"}}}},"codeField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeField"}}}},"codeValidity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeValidity"}}}},"documentError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"documentError"}}}},"email":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"email"}}}},"emailError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emailError"}}}},"forgotHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"forgotHref"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formSection":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formSection"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"loginHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loginHref"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onCep":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCep"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onLogin":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLogin"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onVerify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVerify"}}}},"privacyHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"privacyHref"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"redirectTo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"redirectTo"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"resendSeconds":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendSeconds"}}}},"signupService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"signupService"}}}},"step":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"step"}}}},"submitLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"submitLabel"}}}},"taxIdField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"taxIdField"}}}},"termsHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"termsHref"}}}},"verifyEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"verifyEmail"}}}},"verifyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"verifyLabel"}}}}}}}}}},{"_id":"6abc37d14516435d442e6467","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"SignupLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// SignupLayout — Loja Lucas Fontana\n// O corpo da página de cadastro: kicker \"Criar conta\", título \"BORA CRIAR SUA CONTA\" (44/36/28px) e a frase\n// \"Leva um minutinho…\", depois o formulário (SignupForm, até 824px) e, no desktop, a lateral fixa de 360px com o\n// PromoCard compact (\"Por que criar conta\"), que acompanha a rolagem. No tablet e no mobile a lateral some.\n// Traz o respiro da página: 72/48/32px em cima e 120/88/64px embaixo.\n// Os textos do formulário chegam por `form` (parâmetros do SignupForm) e os da lateral por `aside` (do PromoCard).\n// `signupService` (serviço cadastrarCliente) e `authStrategy` (estratégia lojaAuth) são repassados ao SignupForm, que\n// cria a conta, entra e volta para a origem — ligue os dois em cada tela.\n// Componentes do projeto usados dentro (SignupForm, PromoCard, FormSection, FormField, TaxIdField, Checkbox, Notice,\n// CodeField, ResendCode, CheckList, Button, Icon) são parâmetros do tipo `component`, ligados em cada tela com @Nome —\n// só assim entram no pacote do app publicado. `radio` usa o Radio nativo do Data2 (texto).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst PAD = { desktop: '72px 96px 120px', tablet: '48px 40px 88px', mobile: '32px 20px 64px' };\nconst WHY = ['Acompanhe seus pedidos e o código de rastreio', 'Compre mais rápido com seus dados já preenchidos', 'Seja avisado quando o produto do vídeo voltar ao estoque'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.su { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); color: var(--ink); }\n.in { display: grid; grid-template-columns: minmax(0, 824px) 360px; justify-content: space-between; gap: 64px; align-items: start; max-width: 1248px; margin: 0 auto; }\n.main { min-width: 0; display: flex; flex-direction: column; gap: 40px; }\n.head { display: flex; flex-direction: column; gap: 14px; max-width: 640px; }\np, h1 { margin: 0; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.text { font-size: 16px; line-height: 1.55; color: var(--grey-700); }\n.aside { position: sticky; top: 24px; min-width: 0; }\n.l-tablet .in, .l-mobile .in { grid-template-columns: minmax(0, 1fr); }\n.l-tablet .aside, .l-mobile .aside { display: none; }\n.l-tablet .title { font-size: 36px; }\n.l-mobile .main { gap: 32px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 28px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .text { font-size: 15px; }\n`,\n\n  mounted() { FONT(); },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const r = (n) => refOf(d2.param(n));\n    const icon = r('iconComponent'); const btn = r('button');\n    const form = r('signupForm') ? d2.render(r('signupForm'), Object.assign({}, obj(p.form), {\n      layout, formSection: r('formSection'), formField: r('formField'), taxIdField: r('taxIdField'), radio: r('radio'),\n      checkbox: r('checkbox'), notice: r('notice'), codeField: r('codeField'), resendCode: r('resendCode'), button: btn, iconComponent: icon,\n      signupService: d2.param('signupService'), authStrategy: d2.param('authStrategy')\n    }), {\n      key: 'form', style: 'height:auto',\n      onSubmit: (v) => emit(d2, 'onSubmit', v), onVerify: (v) => emit(d2, 'onVerify', v), onResend: (v) => emit(d2, 'onResend', v),\n      onChangeEmail: (v) => emit(d2, 'onChangeEmail', v), onLogin: (h) => emit(d2, 'onLogin', h), onCep: (v) => emit(d2, 'onCep', v)\n    }) : '';\n    const showAside = layout === 'desktop' && (p.showAside == null || p.showAside === '' || truthy(p.showAside));\n    const aside = showAside && r('promoCard') ? d2.render(r('promoCard'), Object.assign({\n      compact: true, eyebrow: 'Por que criar conta', items: WHY,\n      foot: '**Não guardamos dados de cartão.** O pagamento é feito com segurança pelo Mercado Pago.'\n    }, obj(p.aside), { layout, checkList: r('checkList'), button: btn, iconComponent: icon }), { key: 'aside', style: 'height:auto' }) : '';\n    return `<div class=\"su l-${layout}\" style=\"padding:${PAD[layout]}\"><div class=\"in\">\n      <div class=\"main\">\n        <div class=\"head\">\n          <p class=\"kicker\">${esc(p.eyebrow || 'Criar conta')}</p>\n          <h1 class=\"title\">${esc(p.title || 'Bora criar sua conta')}</h1>\n          <p class=\"text\">${esc(p.description || 'Leva um minutinho. Com esses dados a gente emite sua nota fiscal e entrega seu pedido sem enrolação.')}</p>\n        </div>\n        ${form}\n      </div>\n      ${showAside ? `<aside class=\"aside\">${aside}</aside>` : ''}\n    </div></div>`;\n  }\n};"}},"aside":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"aside"}}}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"codeField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeField"}}}},"description":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"description"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"form":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"form"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formSection":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formSection"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onCep":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCep"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onLogin":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLogin"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onVerify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onVerify"}}}},"promoCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"promoCard"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"showAside":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showAside"}}}},"signupForm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"signupForm"}}}},"signupService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"signupService"}}}},"taxIdField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"taxIdField"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abcf7cf2e8b2579d186175e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"StepIndicator","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// StepIndicator — Loja Lucas Fontana\n// Barra de etapas para fluxos curtos: uma barra de 4px por etapa, com o rótulo numerado em caixa alta embaixo\n// (\"1. Pedir link · 2. Confira o e-mail · 3. Nova senha · 4. Pronto\"). Etapas feitas e a atual ficam com a barra\n// vermelha e o rótulo preto; as próximas ficam cinza. A etapa atual leva `aria-current=\"step\"`.\n// De 3 a 5 etapas com rótulos de duas ou três palavras; no mobile os rótulos quebram embaixo da barra.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst list = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst RECOVERY = ['Pedir link', 'Confira o e-mail', 'Nova senha', 'Pronto'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nol { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  list-style: none; margin: 0; padding: 0; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; width: 100%; }\nli { display: flex; flex-direction: column; gap: 8px; font-size: 12px; font-weight: 800; letter-spacing: .1em; line-height: 1.25; text-transform: uppercase; color: var(--grey-500); }\nli::before { content: ''; height: 4px; background: var(--line); }\n.is-done, .is-current { color: var(--ink); }\n.is-done::before, .is-current::before { background: var(--red-500); }\n.l-mobile { gap: 6px; }\n.l-mobile li { font-size: 10px; letter-spacing: .08em; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const steps = list(p.steps).length ? list(p.steps) : RECOVERY;\n    const cur = Math.max(1, Number(p.current) || 1);\n    return `<ol class=\"l-${layout}\" aria-label=\"${esc(p.ariaLabel || 'Etapas')}\">${steps.map((t, i) => {\n      const n = i + 1; const st = n < cur ? 'is-done' : (n === cur ? 'is-current' : 'is-pending');\n      return `<li class=\"${st}\"${n === cur ? ' aria-current=\"step\"' : ''}>${n}. ${esc(t)}</li>`;\n    }).join('')}</ol>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"current":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"current"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"steps":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"steps"}}}}}}}}}},{"_id":"6abcf7f22e8b2579d18617af","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ForgotPasswordForm","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ForgotPasswordForm — Loja Lucas Fontana\n// O cartão de recuperar senha, aberto pelo \"Esqueci minha senha\" do login. Fica no AuthLayout `mode: \"forgot\"`\n// (cartão único de 560px centralizado; sem borda no mobile). Duas etapas no MESMO componente:\n// Etapa 1 (`step: \"request\"`): StepIndicator em 1, kicker \"Recuperar senha\", \"ESQUECEU A SENHA?\", \"Acontece nas melhores\n// garagens…\", o campo de e-mail, o botão preto \"Enviar link\" e \"Voltar para o login\".\n// Etapa 2 (`step: \"sent\"`): StepIndicator em 2, o quadrado preto com envelope, kicker \"Quase lá\", \"CONFIRA SEU E-MAIL\",\n// \"Se existir uma conta com **[e-mail]**, você vai receber um link… O link vale por [X] minutos.\", o aviso com barra preta\n// sobre spam/promoções, o botão preto \"Voltar para o login\" e, centralizado, \"Não chegou? Reenviar em 0:59\" / \"Usar outro e-mail\".\n// Nunca diz se o e-mail tem cadastro: o texto da etapa 2 é o mesmo em qualquer caso.\n// Fluxo: \"Enviar link\" confere o e-mail, emite `onSubmit` {email} e passa sozinho para a etapa 2 (`autoAdvance: false` desliga).\n// \"Usar outro e-mail\" volta para a etapa 1 com o e-mail preenchido (`onChangeEmail`); \"Reenviar\" emite `onResend`.\n// As etapas 3 (Nova senha) e 4 (Pronto) ainda não estão no design system.\n// Não desenha etapas, campo, aviso, botão nem ícone: usa StepIndicator, FormField, Notice, ResendCode, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\nconst store = (d2) => (d2.root ? (d2.root.__lf || (d2.root.__lf = { email: '' })) : { email: '' });\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.fp { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 18px; width: 100%; height: 100%; padding: 56px; border: 1px solid var(--line); background: var(--white); color: var(--ink); }\np, h1 { margin: 0; }\n.steps { margin-bottom: 12px; }\n.mark { width: 56px; height: 56px; background: var(--ink); color: var(--white); display: flex; align-items: center; justify-content: center; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; line-height: 1.3; text-transform: uppercase; color: var(--red-500); }\n.kicker::before { content: ''; width: 22px; height: 3px; background: var(--red-500); flex: none; }\n.title { font-size: 40px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.text { margin: -4px 0 6px; font-size: 16px; line-height: 1.55; color: var(--grey-700); }\n.text b { color: var(--ink); }\n.btn { height: 52px; margin-top: 4px; }\n.back { align-self: center; min-height: 36px; display: inline-flex; align-items: center; font-size: 15px; font-weight: 700; color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }\n.back:hover { color: var(--red-500); }\n.back:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.l-tablet { padding: 40px; }\n.l-tablet .title { font-size: 34px; }\n.l-mobile { padding: 0; border: 0; gap: 16px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile .title { font-size: 30px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .text { font-size: 15px; }\n`,\n\n  mounted(d2, root) {\n    FONT();\n    d2.on('.back', 'click', (e, el) => emit(d2, 'onBack', el.getAttribute('href')));\n    // Enter no campo envia, igual ao botão \"Enviar link\"\n    d2.on(root, 'keydown', (e) => {\n      if (e.key !== 'Enter' || e.shiftKey || e.isComposing) return;\n      const go = d2.root && d2.root.__fpSubmit;\n      if (typeof go === 'function') { e.preventDefault(); go(); }\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { st: refOf(d2.param('stepIndicator')), ff: refOf(d2.param('formField')), nt: refOf(d2.param('notice')),\n      rs: refOf(d2.param('resendCode')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const s = store(d2);\n    if (!s.email && p.email) s.email = String(p.email);\n    if (!s.email && !s.__qs) { s.__qs = true; try { s.email = new URLSearchParams(window.location.search).get('email') || ''; } catch (e) { s.email = ''; } }\n    const step = d2.state.step || (p.step === 'sent' ? 'sent' : 'request');\n    const sent = step === 'sent';\n    const loginHref = p.loginHref || '/login';\n    const err = sent ? '' : (d2.state.err || p.emailError || '');\n\n    const submit = () => {\n      const email = String(store(d2).email || '').trim();\n      const e = !email ? 'Informe o e-mail da sua conta.' : (!EMAIL.test(email) ? 'Confira o e-mail: falta o @ ou o domínio.' : '');\n      d2.state.err = e;\n      if (e) return;\n      emit(d2, 'onSubmit', { email });\n      if (p.autoAdvance == null || p.autoAdvance === '' || truthy(p.autoAdvance)) d2.state.step = 'sent';\n    };\n    const back = () => { d2.state.err = ''; d2.state.step = 'request'; emit(d2, 'onChangeEmail', store(d2).email); };\n    if (d2.root) d2.root.__fpSubmit = sent ? null : submit;\n\n    const steps = R.st ? `<div class=\"steps\">${d2.render(R.st, { current: sent ? 2 : 1, ariaLabel: 'Etapas para recuperar a senha', layout }, { key: 'st-' + step, style: 'height:auto' })}</div>` : '';\n    const mark = sent && R.icon ? `<span class=\"mark\" aria-hidden=\"true\">${d2.render(R.icon, { name: 'mail', size: 24, iconColor: '#ffffff' }, { key: 'mail', width: 24, height: 24 })}</span>` : '';\n    const btn = (key, label, href, onClick) => R.btn ? `<div class=\"btn\">${d2.render(R.btn, { label, variant: 'primary', size: 'lg', fullWidth: 'always', href: href || '', layout }, { key, height: 52, onClick })}</div>` : '';\n\n    let body;\n    if (sent) {\n      const email = esc(store(d2).email || '[e-mail informado]');\n      const notice = R.nt ? d2.render(R.nt, { tone: 'accent', iconName: 'mail', layout, iconComponent: R.icon, body: p.noticeText || 'Não achou? Dá uma olhada no spam ou na aba de promoções.' }, { key: 'notice', style: 'height:auto' }) : '';\n      const resend = R.rs ? d2.render(R.rs, { layout, align: 'center', seconds: p.resendSeconds == null || p.resendSeconds === '' ? 59 : p.resendSeconds, resendLabel: 'Reenviar link' }, {\n        key: 'resend', style: 'height:auto', onResend: () => emit(d2, 'onResend', store(d2).email), onChangeEmail: back\n      }) : '';\n      body = `${steps}${mark}\n        <p class=\"kicker\">Quase lá</p>\n        <h1 class=\"title\">Confira seu e‑mail</h1>\n        <p class=\"text\" role=\"status\">Se existir uma conta com <b>${email}</b>, você vai receber um link para criar uma senha nova. O link vale por ${esc(p.linkValidity || '[X]')} minutos.</p>\n        ${notice}\n        ${btn('back', 'Voltar para o login', loginHref, () => emit(d2, 'onBack', loginHref))}\n        ${resend}`;\n    } else {\n      const field = R.ff ? d2.render(R.ff, {\n        label: 'E-mail', type: 'email', name: 'email', autoComplete: 'email', inputMode: 'email', placeholder: 'seuemail@exemplo.com',\n        value: store(d2).email, error: err, layout, iconComponent: R.icon\n      }, { key: 'email' + (err ? '-e' + err.length : ''), style: 'height:auto', onInput: (v) => { store(d2).email = v; } }) : '';\n      body = `${steps}\n        <p class=\"kicker\">Recuperar senha</p>\n        <h1 class=\"title\">Esqueceu a senha?</h1>\n        <p class=\"text\">${esc(p.description || 'Acontece nas melhores garagens. Digite o e-mail da sua conta que a gente manda um link para você criar uma senha nova.')}</p>\n        ${field}\n        ${btn('submit', p.submitLabel || 'Enviar link', '', submit)}\n        <a class=\"back\" href=\"${esc(loginHref)}\">Voltar para o login</a>`;\n    }\n    return `<div class=\"fp l-${layout} step-${step}\" role=\"form\" aria-label=\"Recuperar senha\">${body}</div>`;\n  }\n};"}},"autoAdvance":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"autoAdvance"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"description":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"description"}}}},"email":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"email"}}}},"emailError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emailError"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"linkValidity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"linkValidity"}}}},"loginHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loginHref"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"noticeText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"noticeText"}}}},"onBack":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBack"}}}},"onChangeEmail":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChangeEmail"}}}},"onResend":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onResend"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"resendSeconds":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendSeconds"}}}},"step":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"step"}}}},"stepIndicator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"stepIndicator"}}}},"submitLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"submitLabel"}}}}}}}}}},{"_id":"6abd1fc47e17d43be42a5943","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"StatusBadge","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// StatusBadge — Loja Lucas Fontana\n// O status do pedido, diferenciado por preenchimento e borda além da cor (quem não enxerga cor também distingue):\n// `shipped` \"Enviado\" — preenchido de preto.\n// `pending` \"Aguardando pagamento\" — contorno e texto vermelhos (é o que pede ação).\n// `paid` \"Pagamento aprovado\" — contorno preto.\n// `delivered` \"Entregue\" — fundo grey-100.\n// `canceled` \"Cancelado\" — contorno cinza tracejado.\n// `refused` \"Recusado ou expirado\" e `refunded` \"Estornado\" — os dois status de pagamento do documento de requisitos (5.2),\n// com o mesmo visual tracejado do cancelado.\n// 28px de altura, 11px em caixa alta. `label` troca o texto padrão.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nconst STATUS = {\n  shipped: 'Enviado', pending: 'Aguardando pagamento', paid: 'Pagamento aprovado', delivered: 'Entregue',\n  canceled: 'Cancelado', refused: 'Recusado ou expirado', refunded: 'Estornado'\n};\n\nexport default {\n  css: `\n:host { display: inline-block; }\n* { box-sizing: border-box; }\n.st { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-100: #ececea; --grey-400: #8e9095; --grey-500: #6b6e73;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: inline-flex; align-items: center; height: 28px; padding: 0 10px; font-size: 11px; font-weight: 800; letter-spacing: .12em; line-height: 1;\n  text-transform: uppercase; white-space: nowrap; border: 1.5px solid transparent; }\n.s-shipped { background: var(--ink); color: var(--white); }\n.s-pending { background: var(--white); color: var(--red-500); border-color: var(--red-500); }\n.s-paid { background: var(--white); color: var(--ink); border-color: var(--ink); }\n.s-delivered { background: var(--grey-100); color: var(--ink); }\n.s-canceled, .s-refused, .s-refunded { background: var(--white); color: var(--grey-500); border: 1.5px dashed var(--grey-400); }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const st = STATUS[p.status] ? p.status : 'paid';\n    return `<span class=\"st s-${st}\">${esc(p.label || STATUS[st])}</span>`;\n  }\n};\n"}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"status":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"status"}}}}}}}}}},{"_id":"6abd1fc4e6552f4472d7dacf","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"PanelHeader","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// PanelHeader — Loja Lucas Fontana\n// O título de um painel da conta: largo em caixa alta, 28px (24px no mobile), com uma linha cinza embaixo\n// (\"Clique em um pedido para ver os itens, o pagamento e o rastreio.\").\n// `title`, `text`, `layout`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ph { --ink: #0b0b0c; --grey-500: #6b6e73; font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 6px; width: 100%; }\nh2 { margin: 0; font-size: 28px; font-weight: 800; font-stretch: 115%; line-height: 1.05; text-transform: uppercase; color: var(--ink); overflow-wrap: break-word; }\np { margin: 0; font-size: 15px; line-height: 1.45; color: var(--grey-500); }\n.l-mobile h2 { font-size: 24px; font-stretch: 112%; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    return `<div class=\"ph l-${layout}\"><h2>${esc(p.title || 'Meus pedidos')}</h2>${p.text ? `<p>${esc(p.text)}</p>` : ''}</div>`;\n  }\n};\n"}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"text":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"text"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abd1fc4edeff59159dbcd8e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OrderList","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// OrderList — Loja Lucas Fontana\n// Os pedidos da pessoa, do mais novo para o mais antigo.\n// Desktop: tabela (Pedido · Data · Itens · Total · Status) com régua preta no cabeçalho, linhas de 80px e um botão com\n// contorno \"Ver pedido\" por linha. Tablet: cards em duas colunas. Mobile: uma coluna.\n// Pedido aguardando pagamento ganha o link vermelho \"Pagar com Pix\" (dentro do card no tablet e no mobile).\n// `orders: [{ number, date, items, total, status, statusLabel, href, payHref }]` — `status` segue o StatusBadge.\n// Sem pedidos, mostra o estado vazio (\"Nenhum pedido por aqui ainda\" + \"Ver produtos\"); textos em `empty`.\n// Avisa `onView` e `onPay` com { number, href } quando o pedido não tem link próprio.\n// Não desenha status, botão, link nem ícone: usa StatusBadge, Button, TextLink e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nconst byNumberDesc = (a, b) => String(b.number).localeCompare(String(a.number), 'pt-BR', { numeric: true });\nconst itemsTxt = (n) => { const k = Number(n) || 0; return k + (k === 1 ? ' item' : ' itens'); };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ol { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; padding: 0 16px 12px; border-bottom: 2px solid var(--ink); font-size: 12px; font-weight: 800; letter-spacing: .14em;\n  text-transform: uppercase; color: var(--ink); white-space: nowrap; }\ntd { height: 80px; padding: 0 16px; border-bottom: 1px solid var(--line); font-size: 15px; color: var(--grey-700); white-space: nowrap; vertical-align: middle; }\nth:first-child, td:first-child { padding-left: 0; }\nth:last-child, td:last-child { padding-right: 0; }\ntd.num { font-size: 16px; font-weight: 800; color: var(--ink); }\ntd.total { font-weight: 700; color: var(--ink); }\ntd.act { text-align: right; }\n.bw { display: flex; align-items: center; height: 28px; overflow: hidden; flex: none; }\n.actions { display: inline-flex; align-items: center; justify-content: flex-end; gap: 16px; }\n.pay { font-size: 14px; font-weight: 700; color: var(--red-500); text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }\n.pay:hover { color: var(--ink); }\n.pay:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }\n.l-mobile .cards { grid-template-columns: minmax(0, 1fr); gap: 12px; }\n.card { display: flex; flex-direction: column; gap: 14px; padding: 20px; border: 1px solid var(--line); background: var(--white); min-width: 0; }\n.top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }\n.top h3 { margin: 0; font-size: 18px; font-weight: 800; line-height: 1.2; color: var(--ink); }\n.row { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; color: var(--grey-700); }\n.row b { color: var(--ink); }\n.foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n.foot .view { margin-left: auto; }\n.empty { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; padding: 40px; border: 1px solid var(--line); background: var(--white); }\n.l-mobile .empty { padding: 28px 20px; align-items: stretch; }\n.empty .sq { width: 56px; height: 56px; background: var(--ink); color: var(--white); display: flex; align-items: center; justify-content: center; }\n.empty h3 { margin: 4px 0 0; font-size: 24px; font-weight: 800; font-stretch: 112%; line-height: 1.1; text-transform: uppercase; color: var(--ink); }\n.empty p { margin: 0; max-width: 520px; font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.empty .cta { margin-top: 8px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.pay', 'click', (e, el) => {\n      const number = el.getAttribute('data-num');\n      if (!el.getAttribute('data-href')) e.preventDefault();\n      emit(d2, 'onPay', { number, href: el.getAttribute('data-href') || '' });\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { st: refOf(d2.param('statusBadge')), btn: refOf(d2.param('button')), link: refOf(d2.param('textLink')), icon: refOf(d2.param('iconComponent')) };\n    const orders = arr(p.orders).map(obj).filter((o) => o.number != null).sort(byNumberDesc);\n\n    if (!orders.length) {\n      const e = obj(p.empty);\n      const sq = R.icon ? d2.render(R.icon, { name: 'bag', size: 26, iconColor: '#ffffff' }, { key: 'e-ic', width: 26, height: 26, style: 'flex:none' }) : '';\n      const cta = R.btn ? d2.render(R.btn, {\n        label: e.ctaLabel || 'Ver produtos', href: e.ctaHref || '/produtos', variant: 'primary', arrow: true, fullWidth: 'mobile', layout\n      }, { key: 'e-cta' }) : '';\n      return `<div class=\"ol l-${layout}\"><div class=\"empty\">\n        <span class=\"sq\" aria-hidden=\"true\">${sq}</span>\n        <h3>${esc(e.title || 'Nenhum pedido por aqui ainda')}</h3>\n        <p>${esc(e.text || 'Quando você fizer a primeira compra, é aqui que acompanha o pagamento, o envio e o código de rastreio.')}</p>\n        <div class=\"cta\">${cta}</div>\n      </div></div>`;\n    }\n\n    const badge = (o, where) => R.st ? '<div class=\"bw\">' + d2.render(R.st, { status: o.status, label: o.statusLabel || '' },\n      { key: 'st-' + where + '-' + o.number + '-' + o.status + '-' + (o.statusLabel || ''), style: 'display:inline-flex;vertical-align:middle;flex:none' }) + '</div>' : '';\n    const pay = (o) => (o.status === 'pending' && o.payHref !== null)\n      ? `<a class=\"pay\" href=\"${esc(o.payHref || '#')}\" data-num=\"${esc(o.number)}\" data-href=\"${esc(o.payHref || '')}\">Pagar com Pix</a>` : '';\n    const view = (o) => emit(d2, 'onView', { number: String(o.number), href: o.href || '' });\n\n    if (layout === 'desktop') {\n      const rows = orders.map((o) => {\n        const btn = R.btn ? d2.render(R.btn, { label: 'Ver pedido', variant: 'outline', size: 'sm', href: o.href || '', layout },\n          { key: 'v-' + o.number + (o.href ? '-l' : ''), onClick: () => view(o) }) : '';\n        return `<tr data-key=\"${esc(o.number)}\">\n          <td class=\"num\">#${esc(o.number)}</td><td>${esc(o.date)}</td><td>${esc(itemsTxt(o.items))}</td><td class=\"total\">${esc(o.total)}</td>\n          <td>${badge(o, 't')}</td>\n          <td class=\"act\"><span class=\"actions\">${pay(o)}${btn}</span></td>\n        </tr>`;\n      }).join('');\n      return `<div class=\"ol l-${layout}\"><table>\n        <thead><tr><th scope=\"col\">Pedido</th><th scope=\"col\">Data</th><th scope=\"col\">Itens</th><th scope=\"col\">Total</th><th scope=\"col\">Status</th><th><span class=\"sr\">Ações</span></th></tr></thead>\n        <tbody>${rows}</tbody></table></div>`;\n    }\n\n    const cards = orders.map((o) => {\n      const link = R.link ? d2.render(R.link, { label: 'Ver pedido', arrow: true, href: o.href || '', layout },\n        { key: 'l-' + o.number + (o.href ? '-l' : ''), onClick: () => view(o) }) : '';\n      return `<article class=\"card\" data-key=\"${esc(o.number)}\">\n        <div class=\"top\"><h3>Pedido #${esc(o.number)}</h3>${badge(o, 'c')}</div>\n        <div class=\"row\"><span>${esc(o.date)} · ${esc(itemsTxt(o.items))}</span><b>${esc(o.total)}</b></div>\n        <div class=\"foot\">${pay(o)}<span class=\"view\">${link}</span></div>\n      </article>`;\n    }).join('');\n    return `<div class=\"ol l-${layout}\"><div class=\"cards\">${cards}</div></div>`;\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"empty":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"empty"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onPay":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPay"}}}},"onView":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onView"}}}},"orders":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orders"}}}},"statusBadge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusBadge"}}}},"textLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"textLink"}}}}}}}}}},{"_id":"6abd1fc4b32542bffaceb415","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AccountLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AccountLayout — Loja Lucas Fontana\n// A moldura de \"Minha conta\": breadcrumb, \"Olá, [Nome]!\" (44/36/28px) e o subtítulo, depois a navegação da conta e o painel.\n// Desktop: menu lateral de 264px (Meus pedidos · Meus dados · Endereços, uma linha fina e Sair), com o item atual\n// preenchido de preto. Tablet e mobile: o menu vira uma barra de abas embaixo da saudação (rola para o lado no mobile);\n// no mobile o \"Sair\" sai da barra e vai para o fim da página.\n// O painel começa com o PanelHeader (`panelTitle`, `panelText`) e segue com o componente do conteúdo, que chega por\n// `content` com os parâmetros em `contentParams` (Meus pedidos: OrderList + { orders }). Assim a mesma moldura\n// serve para Meus dados e Endereços.\n// Endereços do router da loja: /perfil/meus-pedidos · /perfil/meus-dados · /perfil/meus-enderecos.\n// Loja: com `sessionService` (minhaSessao) ligado, a saudação usa o primeiro nome de quem está logado; com `authStrategy`\n// (lojaAuth) ligado, \"Sair\" encerra a sessão e leva para a Home. Sem eles (previews), vale `name` e \"Sair\" só avisa `onLogout`.\n// Serviços da página: `listService`, `saveService`, `deleteService` e `mainService` (e a `authStrategy`) são repassados ao\n// conteúdo com os mesmos nomes — Meus dados usa `listService` (ler) e `saveService`; Endereços usa os quatro.\n// `name`, `title`, `subtitle`, `crumbs`, `current` (pedidos | dados | enderecos), `nav` (troca os itens).\n// Avisa `onNavigate` (item), `onLogout`, e repassa `onView`, `onPay`, `onChange` e `onSubmit` do conteúdo.\n// Não desenha breadcrumb, título do painel nem ícone: usa Breadcrumb, PanelHeader e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res) || {};\n\nconst NAV = [\n  { key: 'pedidos', label: 'Meus pedidos', icon: 'bag', href: '/perfil/meus-pedidos' },\n  { key: 'dados', label: 'Meus dados', icon: 'user', href: '/perfil/meus-dados' },\n  { key: 'enderecos', label: 'Endereços', icon: 'pin', href: '/perfil/meus-enderecos' }\n];\n// Filhos aninhados não recebem parâmetros novos depois de montados: a chave muda quando o conteúdo muda.\nconst hash = (v) => { const t = JSON.stringify(v) || ''; let h = 0; for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };\nconst PAD = { desktop: '32px 96px 120px', tablet: '28px 40px 88px', mobile: '20px 20px 64px' };\nconst CRUMB_H = { desktop: 24, tablet: 24, mobile: 30 };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ac { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); color: var(--ink); }\n.in { display: flex; flex-direction: column; }\n.head { display: flex; flex-direction: column; gap: 12px; padding-bottom: 32px; border-bottom: 1px solid var(--line); }\n.hello { margin: 4px 0 0; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; overflow-wrap: break-word; }\n.sub { margin: 0; font-size: 16px; line-height: 1.45; color: var(--grey-700); }\n.body { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 56px; padding-top: 40px; align-items: start; }\n.side { display: flex; flex-direction: column; }\n.side a { display: flex; align-items: center; gap: 14px; height: 52px; padding: 0 16px; font-size: 16px; font-weight: 700; line-height: 1;\n  color: var(--ink); text-decoration: none; cursor: pointer; }\n.side a:hover { background: var(--grey-50); }\n.side a[aria-current=\"page\"], .side a[aria-current=\"page\"]:hover { background: var(--ink); color: var(--white); }\n.side a:focus-visible, .tabs a:focus-visible, .out:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.side hr { width: 100%; height: 1px; border: 0; background: var(--line); margin: 12px 0; }\n.main { display: flex; flex-direction: column; gap: 24px; min-width: 0; }\n.tabs { display: none; gap: 32px; border-bottom: 1px solid var(--line); overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch; }\n.tabs::-webkit-scrollbar { display: none; }\n.tabs a { position: relative; flex-shrink: 0; display: inline-flex; align-items: center; height: 52px; font-size: 13px; font-weight: 800; line-height: 1;\n  letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); text-decoration: none; white-space: nowrap; }\n.tabs a:hover, .tabs a[aria-current=\"page\"] { color: var(--ink); }\n.tabs a[aria-current=\"page\"]::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 3px; background: var(--red-500); }\n.out { display: none; align-items: center; gap: 12px; align-self: flex-start; min-height: 44px; margin-top: 8px; font-size: 15px; font-weight: 700;\n  color: var(--ink); text-decoration: none; }\n.out:hover { color: var(--red-500); }\n\n.l-tablet .head { padding-bottom: 24px; border-bottom: 0; }\n.l-tablet .hello { font-size: 36px; }\n.l-tablet .body, .l-mobile .body { grid-template-columns: minmax(0, 1fr); gap: 32px; padding-top: 0; }\n.l-tablet .side, .l-mobile .side { display: none; }\n.l-tablet .tabs, .l-mobile .tabs { display: flex; }\n.l-mobile .head { padding-bottom: 24px; border-bottom: 0; }\n.l-mobile .hello { font-size: 28px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .sub { font-size: 15px; }\n.l-mobile .body { gap: 24px; }\n.l-mobile .tabs { gap: 28px; margin: 0 -20px; padding: 0 20px; }\n.l-mobile .tabs .t-out { display: none; }\n.l-mobile .out { display: inline-flex; }\n`,\n\n  mounted(d2) {\n    FONT();\n    // No tablet e no mobile, a barra de abas rola sozinha até a aba atual (ex.: \"Endereços\" não fica cortado na borda)\n    const showCurrent = () => { const t = d2.$('.tabs [aria-current=\"page\"]'); if (t && t.parentNode) t.parentNode.scrollLeft = Math.max(0, t.offsetLeft - 20); };\n    setTimeout(showCurrent, 0);\n    d2.root && (d2.root.__lfShowTab = showCurrent);\n    d2.on('[data-nav]', 'click', (e, el) => emit(d2, 'onNavigate', { key: el.getAttribute('data-nav'), href: el.getAttribute('href') }));\n    d2.on('[data-out]', 'click', async (e, el) => {\n      emit(d2, 'onLogout', { href: el.getAttribute('href') });\n      const auth = d2.param('authStrategy');\n      if (!auth) return;\n      e.preventDefault();\n      try { await d2.auth(auth).logout(); } catch (err) { /* a sessão pode já ter expirado */ }\n      try { sessionStorage.removeItem('lf-sessao'); } catch (err) { /* sem storage */ }\n      window.location.assign('/');\n    });\n    // Nome de quem está logado (só com o serviço de sessão ligado)\n    const sess = d2.param('sessionService');\n    if (sess) {\n      d2.rush(sess).invoke({}).then((res) => { const r = unwrap(res); if (r.logado && r.nome) d2.state.nome = String(r.nome); }).catch(() => { /* fica o padrão */ });\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { bc: refOf(d2.param('breadcrumb')), ph: refOf(d2.param('panelHeader')), icon: refOf(d2.param('iconComponent')), content: refOf(d2.param('content')) };\n    const nav = arr(p.nav).length ? arr(p.nav).map(obj) : NAV;\n    const current = p.current || 'pedidos';\n    const logoutHref = '/';\n    const crumbs = arr(p.crumbs).length ? arr(p.crumbs) : [{ label: 'Início', href: '/' }, { label: 'Minha conta' }];\n    const sessOn = !!d2.param('sessionService');\n    const nome = sessOn ? (d2.state.nome || '') : (p.name || '[Nome]');\n    const title = p.title || (nome ? 'Olá, ' + nome + '!' : 'Olá!');\n    const sub = p.subtitle || 'Aqui você acompanha seus pedidos e mantém seus dados em dia.';\n    const ic = (name, key, color) => R.icon\n      ? d2.render(R.icon, { name, size: 20, iconColor: color }, { key, width: 20, height: 20, style: 'flex:none' }) : '';\n\n    const crumb = R.bc ? `<div style=\"height:${CRUMB_H[layout]}px\">${d2.render(R.bc, { items: crumbs, layout },\n      { key: 'bc', onNavigate: (h) => emit(d2, 'onNavigate', { key: 'breadcrumb', href: h }) })}</div>` : '';\n\n    const side = layout === 'desktop' ? `<nav class=\"side\" aria-label=\"Minha conta\">\n      ${nav.map((n) => {\n        const on = n.key === current;\n        return `<a href=\"${esc(n.href || '#')}\" data-nav=\"${esc(n.key)}\"${on ? ' aria-current=\"page\"' : ''}>${ic(n.icon || 'bag', 'n-' + n.key + (on ? '-on' : ''), on ? '#ffffff' : '#0b0b0c')}${esc(n.label)}</a>`;\n      }).join('')}\n      <hr>\n      <a href=\"${esc(logoutHref)}\" data-out=\"1\">${ic('logout', 'n-out', '#0b0b0c')}Sair</a>\n    </nav>` : '';\n\n    const tabs = layout === 'desktop' ? '' : `<nav class=\"tabs\" aria-label=\"Minha conta\">\n      ${nav.map((n) => `<a href=\"${esc(n.href || '#')}\" data-nav=\"${esc(n.key)}\"${n.key === current ? ' aria-current=\"page\"' : ''}>${esc(n.label)}</a>`).join('')}\n      <a class=\"t-out\" href=\"${esc(logoutHref)}\" data-out=\"1\">Sair</a>\n    </nav>`;\n\n    const panel = R.ph ? d2.render(R.ph, { title: p.panelTitle || 'Meus pedidos', text: p.panelText == null ? 'Clique em um pedido para ver os itens, o pagamento e o rastreio.' : p.panelText, layout },\n      { key: 'ph-' + layout + '-' + (p.panelTitle || '') }) : '';\n\n    // Conteúdo do painel: recebe os parâmetros da tela + os serviços da página (com os mesmos nomes)\n    const cp = obj(p.contentParams);\n    const svc = {};\n    ['authStrategy', 'listService', 'saveService', 'deleteService', 'mainService'].forEach((k) => { const v = d2.param(k); if (v) svc[k] = v; });\n    const content = R.content ? d2.render(R.content, Object.assign({}, cp, svc, { layout }), {\n      key: 'ct-' + layout + '-' + hash(cp),\n      onView: (v) => emit(d2, 'onView', v), onPay: (v) => emit(d2, 'onPay', v),\n      onChange: (v) => emit(d2, 'onChange', v), onSubmit: (v) => emit(d2, 'onSubmit', v)\n    }) : '';\n\n    const out = layout === 'mobile' ? `<a class=\"out\" href=\"${esc(logoutHref)}\" data-out=\"1\">${ic('logout', 'm-out', '#0b0b0c')}Sair da conta</a>` : '';\n\n    return `<div class=\"ac l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      <div class=\"head\">${crumb}<h1 class=\"hello\">${esc(title)}</h1><p class=\"sub\">${esc(sub)}</p></div>\n      <div class=\"body\">${side}<div class=\"main\">${tabs}${panel}${content}${out}</div></div>\n    </div></div>`;\n  }\n};"}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"content":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"content"}}}},"contentParams":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"contentParams"}}}},"crumbs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"crumbs"}}}},"current":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"current"}}}},"deleteService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"deleteService"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"listService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"listService"}}}},"mainService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"mainService"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"nav":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"nav"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onLogout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onLogout"}}}},"onNavigate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNavigate"}}}},"onPay":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPay"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onView":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onView"}}}},"panelHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panelHeader"}}}},"panelText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panelText"}}}},"panelTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panelTitle"}}}},"saveService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"saveService"}}}},"sessionService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sessionService"}}}},"subtitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subtitle"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abd39a2edeff59159dc0d8a","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AccountDataForm","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AccountDataForm — Loja Lucas Fontana\n// O conteúdo do painel \"Meus dados\" (entra no AccountLayout por `content`), na ordem do design system:\n// Notice cinza com cadeado (\"Não guardamos dados de cartão…\") → CPF ou CNPJ → Nome completo → E-mail + WhatsApp →\n// \"Comunicações\" (linha fina acima, consentimento por e-mail e por WhatsApp e a linha cinza sobre os avisos de pedido)\n// → botão preto \"Salvar alterações\" → rodapé da conta (\"Quer trocar a senha?…\" e o link \"Quero excluir minha conta\").\n// Salvar valida documento (dígitos conferidos), nome, e-mail e WhatsApp; cada erro some quando a pessoa corrige o campo.\n// Com tudo certo, avisa `onSubmit` com { docKind, doc, name, email, whatsapp, optEmail, optWhatsapp } e mostra o Notice\n// de destaque \"Dados salvos.\" acima do botão (some quando a pessoa mexe de novo).\n// \"Quero excluir minha conta\" abre a confirmação ali mesmo (régua vermelha, o que é apagado, \"Sim, excluir minha conta\" /\n// \"Cancelar\"); só o \"Sim\" avisa `onDelete` e troca a confirmação pelo aviso de pedido recebido.\n// `values`: { docKind, doc, name, email, whatsapp, optEmail, optWhatsapp } — o que vem do cadastro.\n// `status`: \"saving\" trava o botão; \"error\" mostra o aviso de falha acima dele; \"saved\" força o aviso de salvo.\n// Não desenha nada: usa FormSection, FormField, TaxIdField, Radio, Checkbox, Button, Notice e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\n// Mesma conferência de dígitos do TaxIdField (CPF e CNPJ, inclusive o alfanumérico)\nconst validCPF = (d) => {\n  if (!/^\\d{11}$/.test(d) || /^(\\d)\\1+$/.test(d)) return false;\n  const calc = (n) => { let s = 0; for (let i = 0; i < n; i++) s += Number(d[i]) * (n + 1 - i); const r = (s * 10) % 11; return r === 10 ? 0 : r; };\n  return calc(9) === Number(d[9]) && calc(10) === Number(d[10]);\n};\nconst validCNPJ = (raw) => {\n  const d = String(raw || '').toUpperCase();\n  if (!/^[A-Z0-9]{12}\\d{2}$/.test(d) || /^(.)\\1+$/.test(d)) return false;\n  const val = (c) => c.charCodeAt(0) - 48;\n  const calc = (n) => { const w = n === 12 ? [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] : [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]; let s = 0; for (let i = 0; i < n; i++) s += val(d[i]) * w[i]; const r = s % 11; return r < 2 ? 0 : 11 - r; };\n  return calc(12) === Number(d[12]) && calc(13) === Number(d[13]);\n};\nconst docDigits = (kind, v) => (kind === 'cnpj' ? String(v || '').toUpperCase().replace(/[^A-Z0-9]/g, '') : String(v || '').replace(/\\D/g, ''));\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/;\n// Filhos aninhados não recebem parâmetros novos depois de montados: a chave muda quando o que eles mostram muda.\nconst hash = (v) => { const t = JSON.stringify(v) || ''; let h = 0; for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };\nconst box = (d2) => { const r = d2.root || d2; if (!r.__lfv) r.__lfv = {}; return r; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ad { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; gap: 24px; width: 100%; background: var(--white); }\n.save { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }\n.l-mobile .save { align-items: stretch; }\n.foot { display: flex; flex-direction: column; gap: 20px; padding-top: 24px; border-top: 1px solid var(--line); font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.foot p { margin: 0; }\n.foot b { color: var(--ink); }\n.danger { align-self: flex-start; min-height: 44px; margin: -12px 0 0; padding: 0; border: 0; background: none; font: 600 14px ${FF}; color: var(--grey-700);\n  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\n.danger:hover { color: var(--red-500); }\n.danger:focus-visible, .confirm button:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.confirm { display: flex; flex-direction: column; gap: 12px; padding: 20px 20px 22px; background: var(--grey-50); border-left: 4px solid var(--red-500); }\n.confirm h3 { margin: 0; font-size: 18px; font-weight: 800; font-stretch: 112%; line-height: 1.2; text-transform: uppercase; color: var(--ink); }\n.confirm p { margin: 0; font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.confirm .acts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; }\n.confirm .acts > * { flex: 0 0 auto; width: auto !important; }\n.l-mobile .confirm .acts > * { width: 100% !important; }\n.l-mobile .confirm { padding: 18px 16px 20px; }\n.l-mobile .confirm .acts { flex-direction: column; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.danger', 'click', () => { d2.state.confirm = true; d2.state.deleted = false; });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      fs: refOf(d2.param('formSection')), ff: refOf(d2.param('formField')), tx: refOf(d2.param('taxIdField')), rd: refOf(d2.param('radio')),\n      cb: refOf(d2.param('checkbox')), btn: refOf(d2.param('button')), notice: refOf(d2.param('notice')), icon: refOf(d2.param('iconComponent'))\n    };\n    const b = box(d2);\n    if (!b.__lfSeeded) {\n      b.__lfSeeded = true;\n      const iv = obj(p.values);\n      Object.assign(b.__lfv, {\n        doc: { kind: iv.docKind === 'cnpj' ? 'cnpj' : 'cpf', value: iv.doc || '' },\n        name: iv.name || '', email: iv.email || '', whatsapp: iv.whatsapp || '',\n        optEmail: iv.optEmail == null ? true : truthy(iv.optEmail), optWhatsapp: truthy(iv.optWhatsapp)\n      });\n    }\n    const v = b.__lfv;\n    const gen = d2.state.gen || 0;\n    const errors = d2.state.errors || {};\n    const status = String(p.status || '');\n    const saving = status === 'saving';\n    const saved = status === 'saved' || !!d2.state.saved;\n\n    const clearErr = (key) => {\n      if (!errors[key]) return;\n      const next = Object.assign({}, errors); delete next[key]; d2.state.errors = next;\n    };\n    const onInput = (e) => {\n      if (!e || !e.key) return;\n      if (e.key === 'doc') v.doc = { kind: (e.value && e.value.kind) || v.doc.kind, value: (e.value && e.value.value) || '' };\n      else v[e.key] = e.value;\n      if (d2.state.saved) d2.state.saved = false;\n      clearErr(e.key);\n    };\n\n    const validate = () => {\n      const e = {};\n      const d = docDigits(v.doc.kind, v.doc.value);\n      const label = v.doc.kind === 'cnpj' ? 'CNPJ' : 'CPF';\n      if (!d) e.doc = 'Informe seu ' + label + ': ele vai na nota fiscal.';\n      else if (!(v.doc.kind === 'cnpj' ? validCNPJ(d) : validCPF(d))) e.doc = label + ' inválido. Confere os números.';\n      if (!String(v.name || '').trim()) e.name = 'Informe seu nome completo, como está no documento.';\n      const mail = String(v.email || '').trim();\n      if (!mail) e.email = 'Deixa um e-mail: é por ele que você entra na conta.';\n      else if (!EMAIL.test(mail)) e.email = 'Confere o e-mail: falta o @ ou o domínio.';\n      const w = String(v.whatsapp || '').replace(/\\D/g, '');\n      if (!w) e.whatsapp = 'Informe o WhatsApp para os avisos do pedido.';\n      else if (w.length < 10) e.whatsapp = 'Confere o número: faltam dígitos (com DDD).';\n      return e;\n    };\n    const submit = () => {\n      if (saving) return;\n      const e = validate();\n      d2.state.errors = e;\n      if (Object.keys(e).length) { d2.state.saved = false; return; }\n      emit(d2, 'onSubmit', {\n        docKind: v.doc.kind, doc: v.doc.value, name: String(v.name).trim(), email: String(v.email).trim(), whatsapp: v.whatsapp,\n        optEmail: !!v.optEmail, optWhatsapp: !!v.optWhatsapp\n      });\n      d2.state.saved = true;\n    };\n\n    const notice = R.notice ? d2.render(R.notice, {\n      iconName: 'lock', layout, iconComponent: R.icon,\n      body: p.noticeBody || 'Esses dados são usados para emitir sua nota fiscal. **Não guardamos dados de cartão** — o pagamento é feito com segurança pelo Mercado Pago.'\n    }, { key: 'info' }) : '';\n\n    const dataErr = { doc: errors.doc, name: errors.name, email: errors.email, whatsapp: errors.whatsapp };\n    const data = R.fs ? d2.render(R.fs, {\n      layout, formField: R.ff, taxIdField: R.tx, radio: R.rd, iconComponent: R.icon, version: String(gen),\n      values: { doc: v.doc, name: v.name, email: v.email, whatsapp: v.whatsapp }, errors: dataErr,\n      rows: [\n        { fields: [{ key: 'doc', kind: 'taxid', name: 'documento' }] },\n        { fields: [{ key: 'name', label: 'Nome completo', placeholder: 'Como está no seu documento', autoComplete: 'name' }] },\n        { fields: [\n          { key: 'email', label: 'E-mail', type: 'email', placeholder: 'seuemail@exemplo.com', autoComplete: 'email' },\n          { key: 'whatsapp', label: 'WhatsApp', type: 'tel', mask: 'phone', placeholder: '(00) 00000-0000', hint: 'Para avisos do pedido.', autoComplete: 'tel' }\n        ] }\n      ]\n    }, { key: 'data-' + gen + '-' + hash(dataErr), onInput, onBlur: onInput }) : '';\n\n    const comms = R.fs ? d2.render(R.fs, {\n      layout, checkbox: R.cb, iconComponent: R.icon, title: 'Comunicações', plain: true, divider: true, version: String(gen),\n      values: { optEmail: v.optEmail, optWhatsapp: v.optWhatsapp },\n      note: 'Avisos sobre os seus pedidos (confirmação, pagamento e envio) continuam chegando normalmente.',\n      rows: [{ fields: [\n        { key: 'optEmail', kind: 'checkbox', label: 'Quero receber novidades, lançamentos e ofertas por **e-mail**.' },\n        { key: 'optWhatsapp', kind: 'checkbox', label: 'Quero receber novidades e ofertas por **WhatsApp**.' }\n      ] }]\n    }, { key: 'comms-' + gen, onInput }) : '';\n\n    const okNote = saved && R.notice ? d2.render(R.notice, {\n      tone: 'success', iconName: 'check', live: true, layout, iconComponent: R.icon,\n      body: p.savedBody || '**Dados salvos.** As alterações já valem para os próximos pedidos e notas fiscais.'\n    }, { key: 'saved' }) : '';\n    const failNote = status === 'error' && R.notice ? d2.render(R.notice, {\n      tone: 'error', iconName: 'close', live: true, layout, iconComponent: R.icon,\n      body: p.errorBody || '**Não deu pra salvar agora.** Confere sua conexão e tenta de novo — o que você mudou continua aqui.'\n    }, { key: 'fail' }) : '';\n    const btn = R.btn ? d2.render(R.btn, {\n      label: saving ? 'Salvando…' : 'Salvar alterações', variant: 'primary', isDisabled: saving, fullWidth: 'mobile', layout\n    }, { key: 'save-' + (saving ? 's' : 'i'), onClick: submit }) : '';\n\n    let del = '<button type=\"button\" class=\"danger\">Quero excluir minha conta</button>';\n    if (d2.state.deleted) {\n      del = R.notice ? d2.render(R.notice, {\n        tone: 'success', iconName: 'check', live: true, layout, iconComponent: R.icon,\n        body: p.deletedBody || '**Pedido de exclusão recebido.** A confirmação chega no e-mail cadastrado, e a gente encerra a conta em seguida.'\n      }, { key: 'deleted' }) : '';\n    } else if (d2.state.confirm) {\n      const yes = R.btn ? d2.render(R.btn, { label: 'Sim, excluir minha conta', variant: 'primary', fullWidth: 'mobile', layout },\n        { key: 'del-yes', onClick: () => { d2.state.confirm = false; d2.state.deleted = true; emit(d2, 'onDelete', { email: String(v.email || '').trim() }); } }) : '';\n      const no = R.btn ? d2.render(R.btn, { label: 'Cancelar', variant: 'outline', fullWidth: 'mobile', layout },\n        { key: 'del-no', onClick: () => { d2.state.confirm = false; } }) : '';\n      del = `<div class=\"confirm\" role=\"alertdialog\" aria-labelledby=\"del-t\" aria-describedby=\"del-d\">\n        <h3 id=\"del-t\">Excluir sua conta?</h3>\n        <p id=\"del-d\">${rich(p.deleteBody || 'Isso apaga seu cadastro, seus endereços e o histórico de pedidos da conta, e **não dá pra desfazer**. As notas fiscais já emitidas continuam guardadas, como a lei exige.')}</p>\n        <div class=\"acts\">${yes}${no}</div>\n      </div>`;\n    }\n\n    return `<div class=\"ad l-${layout}\">\n      ${notice}${data}${comms}\n      <div class=\"save\">${okNote}${failNote}${btn}</div>\n      <div class=\"foot\">\n        <p>${rich(p.passwordText || 'Quer trocar a senha? Saia da conta e use **“Esqueci minha senha”** na tela de login.')}</p>\n        ${del}\n      </div>\n    </div>`;\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"deleteBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"deleteBody"}}}},"deletedBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"deletedBody"}}}},"errorBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"errorBody"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formSection":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formSection"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"noticeBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"noticeBody"}}}},"onDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDelete"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"passwordText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"passwordText"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"savedBody":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"savedBody"}}}},"status":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"status"}}}},"taxIdField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"taxIdField"}}}},"values":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"values"}}}}}}}}}},{"_id":"6abd4565edeff59159dc322b","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AddressCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AddressCard — Loja Lucas Fontana\n// Um endereço salvo: pino + nome (\"Casa\"), quem recebe em negrito, as linhas da rua e o CEP, e as ações separadas por\n// uma linha fina: \"Editar\" · \"Excluir\" (cinza) · \"Definir como principal\".\n// `main: true` é o endereço principal: borda preta de 2px, selo vermelho \"Principal\" e sem \"Definir como principal\".\n// `confirming: true` troca as ações pela confirmação \"Excluir este endereço?\" com \"Excluir\" e \"Cancelar\".\n// `note` mostra uma linha acima das ações (ex.: \"Esse é o principal. Defina outro como principal antes de excluir.\").\n// `lines` (lista de textos) ou os campos soltos `street`, `number`, `complement`, `district`, `city`, `uf`.\n// `addressId` identifica o endereço nos avisos (`id` é nome reservado do bloco).\n// Avisa `onEdit`, `onDelete`, `onMakeMain`, `onConfirmDelete` e `onCancelDelete` com o `addressId`.\n// Não desenha selo, botão nem ícone: usa Badge, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.ac { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: flex; flex-direction: column; gap: 14px; height: 100%; padding: 22px; border: 1px solid var(--line); background: var(--white); }\n.ac.main { border: 2px solid var(--ink); padding: 21px; }\n.top { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 24px; }\nh3 { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 17px; font-weight: 800; line-height: 1.2; color: var(--ink); overflow-wrap: anywhere; }\naddress { margin: 0; font-style: normal; font-size: 15px; line-height: 1.6; color: var(--grey-700); overflow-wrap: anywhere; }\naddress b { color: var(--ink); }\n.foot { display: flex; flex-direction: column; gap: 12px; margin-top: auto; padding-top: 14px; border-top: 1px solid var(--line); }\n.note { margin: 0; font-size: 14px; line-height: 1.45; color: var(--ink); }\n.acts { display: flex; flex-wrap: wrap; gap: 8px 24px; }\n.acts button { padding: 0; min-height: 28px; border: 0; background: none; font: 700 14px/1 ${FF}; color: var(--ink);\n  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\n.acts .del { color: var(--grey-500); font-weight: 600; }\n.acts button:hover { color: var(--red-500); }\n.acts button:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.confirm { display: flex; flex-direction: column; gap: 12px; }\n.confirm p { margin: 0; font-size: 15px; font-weight: 700; color: var(--ink); }\n.confirm .btns { display: flex; flex-wrap: wrap; gap: 10px; }\n.confirm .btns > * { flex: 0 0 auto; width: auto !important; }\n.l-mobile .acts { gap: 8px 20px; }\n.l-mobile .acts button { min-height: 36px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('[data-act]', 'click', (e, el) => {\n      const a = el.getAttribute('data-act');\n      const id = el.getAttribute('data-id') || (d2.params || {}).addressId;\n      if (a === 'edit') emit(d2, 'onEdit', id);\n      if (a === 'del') emit(d2, 'onDelete', id);\n      if (a === 'main') emit(d2, 'onMakeMain', id);\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const main = truthy(p.main);\n    const id = p.addressId == null ? '' : String(p.addressId);\n    const R = { badge: refOf(d2.param('badge')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const lines = arr(p.lines).length ? arr(p.lines) : [\n      [p.street, p.number].filter(Boolean).join(', ') + (p.complement ? ' — ' + p.complement : ''),\n      [p.district, [p.city, p.uf].filter(Boolean).join('/')].filter(Boolean).join(' — ')\n    ].filter((l) => l && l.trim());\n    const pin = R.icon ? d2.render(R.icon, { name: 'pin', size: 20, iconColor: '#0b0b0c' }, { key: 'pin', width: 20, height: 20, style: 'flex:none' }) : '';\n    const badge = main && R.badge ? d2.render(R.badge, { label: 'Principal', tone: 'red', layout: 'desktop' }, { key: 'badge', height: 24, style: 'flex:none;width:auto' }) : '';\n\n    let foot;\n    if (truthy(p.confirming)) {\n      const yes = R.btn ? d2.render(R.btn, { label: 'Excluir', variant: 'primary', size: 'sm', layout }, { key: 'yes', onClick: () => emit(d2, 'onConfirmDelete', id) }) : '';\n      const no = R.btn ? d2.render(R.btn, { label: 'Cancelar', variant: 'outline', size: 'sm', layout }, { key: 'no', onClick: () => emit(d2, 'onCancelDelete', id) }) : '';\n      foot = `<div class=\"confirm\" role=\"alertdialog\" aria-label=\"Excluir este endereço?\"><p>Excluir este endereço?</p><div class=\"btns\">${yes}${no}</div></div>`;\n    } else {\n      foot = `${p.note ? `<p class=\"note\" role=\"status\">${rich(p.note)}</p>` : ''}\n        <div class=\"acts\">\n          <button type=\"button\" data-act=\"edit\" data-id=\"${esc(id)}\">Editar</button>\n          <button type=\"button\" class=\"del\" data-act=\"del\" data-id=\"${esc(id)}\">Excluir</button>\n          ${main ? '' : `<button type=\"button\" data-act=\"main\" data-id=\"${esc(id)}\">Definir como principal</button>`}\n        </div>`;\n    }\n\n    return `<article class=\"ac l-${layout}${main ? ' main' : ''}\">\n      <div class=\"top\"><h3>${pin}${esc(p.label || 'Endereço')}</h3>${badge}</div>\n      <address>${p.recipient ? `<b>${esc(p.recipient)}</b>` : ''}${lines.map((l) => '<br>' + esc(l)).join('')}${p.cep ? '<br>CEP ' + esc(p.cep) : ''}</address>\n      <div class=\"foot\">${foot}</div>\n    </article>`;\n  }\n};"}},"addressId":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addressId"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cep":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cep"}}}},"city":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"city"}}}},"complement":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"complement"}}}},"confirming":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"confirming"}}}},"district":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"district"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"lines":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"lines"}}}},"main":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"main"}}}},"note":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"note"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onCancelDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCancelDelete"}}}},"onConfirmDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onConfirmDelete"}}}},"onDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDelete"}}}},"onEdit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onEdit"}}}},"onMakeMain":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onMakeMain"}}}},"recipient":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"recipient"}}}},"street":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"street"}}}},"uf":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"uf"}}}}}}}}}},{"_id":"6abd45657e17d43be42abc07","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AddAddressCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AddAddressCard — Loja Lucas Fontana\n// O espaço tracejado que adiciona um endereço novo: um quadrado preto de 44px com \"+\" sobre \"Adicionar endereço\".\n// 180px de altura mínima (120px no mobile); a borda fica preta no hover. `label` troca o texto.\n// Avisa `onClick`. Não desenha o \"+\": usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\nbutton { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-400: #8e9095;\n  -webkit-font-smoothing: antialiased; width: 100%; height: 100%; margin: 0;\n  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; min-height: 180px; padding: 24px;\n  border: 1.5px dashed var(--grey-400); background: var(--white); font: 800 13px/1 ${FF}; letter-spacing: .12em; text-transform: uppercase;\n  color: var(--ink); cursor: pointer; }\nbutton:hover { border-color: var(--ink); }\nbutton:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\ni { width: 44px; height: 44px; background: var(--ink); color: var(--white); display: flex; align-items: center; justify-content: center; }\n.l-mobile { min-height: 120px; }\n`,\n  mounted(d2) { FONT(); d2.on('button', 'click', () => emit(d2, 'onClick')); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const plus = iconRef ? d2.render(iconRef, { name: 'plus', size: 18, iconColor: '#ffffff' }, { key: 'plus', width: 18, height: 18, style: 'flex:none' }) : '+';\n    return `<button type=\"button\" class=\"l-${layout}\"><i aria-hidden=\"true\">${plus}</i>${esc(p.label || 'Adicionar endereço')}</button>`;\n  }\n};\n"}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onClick":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClick"}}}}}}}}}},{"_id":"6abd4566b32542bffacf1a2b","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"AddressBook","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// AddressBook — Loja Lucas Fontana\n// O conteúdo do painel \"Endereços\" (entra no AccountLayout por `content`).\n// Lista: os AddressCards em duas colunas no desktop e no tablet (16px) e uma no mobile (12px), o principal primeiro,\n// e o AddAddressCard no fim. Sem endereços: uma linha explicando que o primeiro vira o principal + o AddAddressCard.\n// Formulário (ao adicionar ou editar, no lugar da lista): \"Novo endereço\" / \"Editar endereço\" → nome do endereço em chips\n// (Casa · Trabalho · Outro; \"Outro\" abre um campo curto) → quem recebe → CEP (com \"Não sei meu CEP\") → rua → número +\n// complemento → bairro + cidade → estado → \"Usar como endereço principal\" → \"Salvar endereço\" / \"Cancelar\".\n// Busca de CEP: com os 8 números digitados, consulta o ViaCEP e preenche rua, bairro, cidade e estado; o cursor vai para\n// o número. CEP não encontrado, sem rua (CEP geral da cidade) ou busca fora do ar avisa embaixo do campo do CEP, e dá para\n// preencher tudo à mão.\n// Regras: o primeiro endereço já é o principal; o principal não pode ser excluído enquanto houver outros (\"Defina outro\n// como principal antes de excluir\"); excluir pede confirmação no próprio card.\n// Depois de salvar, trocar o principal ou excluir, mostra um Notice verde acima da lista.\n// `addresses: [{ id, label, recipient, cep, street, number, complement, district, city, uf, main }]`.\n// Avisa `onSave`, `onDelete` e `onMakeMain` com { address, addresses }, e `onChange` com a lista a cada mudança.\n// `startMode: \"new\" | \"edit\"` (+ `startEditId`) abre direto no formulário, para pré-visualizar. `cepLookup: false` desliga a busca.\n// Não desenha nada: usa AddressCard, AddAddressCard, FormSection, FormField, ChipGroup, Checkbox, Button, Notice, Badge e Icon.\n// Contas reais: com `listService`, `saveService`, `deleteService` e `mainService` (serviços protegidos pela sessão da loja)\n// e `authStrategy` ligados, a lista vem da conta de quem está logado e cada ação grava no servidor; sem eles (previews), vale\n// `addresses` e tudo roda só na tela. O servidor refaz todas as conferências e devolve a lista atualizada.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nconst LABELS = [{ value: 'Casa', label: 'Casa' }, { value: 'Trabalho', label: 'Trabalho' }, { value: 'outro', label: 'Outro' }];\n// As 27 siglas, a mesma conferência do cadastro (SignupForm)\nconst UFS = 'AC AL AP AM BA CE DF ES GO MA MT MS MG PA PB PR PE PI RJ RN RS RO RR SC SP SE TO'.split(' ');\nconst hash = (v) => { const t = JSON.stringify(v) || ''; let h = 0; for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };\nconst box = (d2) => d2.root || d2;\nconst cepDigits = (v) => String(v || '').replace(/\\D/g, '');\nconst sortMain = (list) => list.slice().sort((a, b) => (b.main ? 1 : 0) - (a.main ? 1 : 0));\n// Procura dentro dos shadow roots aninhados (AddressBook → FormSection → FormField → input)\nconst deepFind = (root, sel) => {\n  if (!root) return null;\n  const hit = root.querySelector ? root.querySelector(sel) : null;\n  if (hit) return hit;\n  const all = root.querySelectorAll ? root.querySelectorAll('*') : [];\n  for (const el of all) { if (el.shadowRoot) { const r = deepFind(el.shadowRoot, sel); if (r) return r; } }\n  return null;\n};\nconst EMPTY = { labelChoice: 'Casa', labelOther: '', recipient: '', cep: '', street: '', number: '', complement: '', district: '', city: '', uf: '', main: false };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ab { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; gap: 24px; width: 100%; background: var(--white); }\n.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: stretch; }\n.l-mobile .grid { grid-template-columns: minmax(0, 1fr); gap: 12px; }\n.grid > * { min-width: 0; }\n.empty { margin: 0; font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.form { display: flex; flex-direction: column; gap: 20px; }\n.form h3 { margin: 0; font-size: 20px; font-weight: 800; font-stretch: 115%; line-height: 1.05; text-transform: uppercase; color: var(--ink); }\n.btns { display: flex; flex-wrap: wrap; gap: 12px; padding-top: 4px; }\n.srv { margin: 0; font-size: 14px; font-weight: 600; color: var(--red-500); }\n.btns > * { flex: 0 0 auto; width: auto !important; }\n.l-mobile .btns { flex-direction: column; }\n.l-mobile .btns > * { width: 100% !important; }\n`,\n\n  mounted(d2) { FONT(); },\n\n  updated(d2) {\n    const b = box(d2);\n    // Endereços da conta: carrega quando há sessão da loja (e de novo se quem está logado mudar)\n    const ls = d2.param('listService'); const au = d2.param('authStrategy');\n    if (ls && au) {\n      let u = null; try { u = d2.auth(au).user; } catch (e) { u = null; }\n      const who = u && u.email ? String(u.email) : '';\n      if (who && b.__loadedFor !== who) {\n        b.__loadedFor = who; d2.state.loading = true;\n        d2.rush(ls).invoke({}).then((res) => {\n          const r = (res && res.data !== undefined ? res.data : res) || {};\n          if (r.ok) { b.__list = sortMain(arr(r.addresses).map(obj).map((x) => Object.assign({}, x, { main: truthy(x.main) }))); b.__loaded = true; d2.state.listErr = ''; }\n          else d2.state.listErr = r.motivo === 'sem_sessao' ? 'Entre na sua conta para ver seus endereços.' : 'Não deu pra carregar seus endereços agora.';\n          d2.state.loading = false;\n        }).catch(() => { d2.state.listErr = 'Não deu pra carregar seus endereços agora.'; d2.state.loading = false; });\n      }\n    }\n    if (!b.__focus) return;\n    const sel = b.__focus; b.__focus = '';\n    setTimeout(() => { const el = deepFind(d2.root && d2.root.shadowRoot ? d2.root.shadowRoot : d2.root, sel) || deepFind(document, sel); if (el) try { el.focus({ preventScroll: false }); } catch (e) { /* sem foco */ } }, 60);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      card: refOf(d2.param('addressCard')), add: refOf(d2.param('addAddressCard')), fs: refOf(d2.param('formSection')), ff: refOf(d2.param('formField')),\n      chip: refOf(d2.param('chipGroup')), cb: refOf(d2.param('checkbox')), btn: refOf(d2.param('button')), notice: refOf(d2.param('notice')),\n      badge: refOf(d2.param('badge')), icon: refOf(d2.param('iconComponent'))\n    };\n    const b = box(d2);\n    if (!b.__lfSeeded) {\n      b.__lfSeeded = true;\n      b.__list = sortMain((d2.param('listService') ? [] : arr(p.addresses)).map(obj).map((a, i) => Object.assign({}, a, { id: String(a.id || 'end-' + (i + 1)), main: truthy(a.main) })));\n      if (b.__list.length && !b.__list.some((a) => a.main)) b.__list[0].main = true;\n      b.__seq = b.__list.length;\n      b.__form = Object.assign({}, EMPTY);\n      if (p.startMode === 'new' || p.startMode === 'edit') {\n        const target = p.startMode === 'edit' ? (b.__list.find((a) => a.id === String(p.startEditId)) || b.__list[0]) : null;\n        b.__startMode = target ? 'edit' : 'new';\n        b.__startId = target ? target.id : '';\n      }\n    }\n    const list = b.__list;\n    const mode = d2.state.mode || b.__startMode || 'list';\n    const editId = d2.state.editId !== undefined ? d2.state.editId : (b.__startId || '');\n    if (mode !== 'list' && !b.__formFor) {\n      // abre o formulário com os dados do endereço (ou vazio)\n      const a = mode === 'edit' ? list.find((x) => x.id === editId) : null;\n      const isStd = a && (a.label === 'Casa' || a.label === 'Trabalho');\n      b.__form = a ? Object.assign({}, EMPTY, a, { labelChoice: isStd ? a.label : 'outro', labelOther: isStd ? '' : (a.label || '') }) : Object.assign({}, EMPTY, { main: list.length === 0 });\n      b.__formFor = mode + ':' + (editId || 'new');\n    }\n    const f = b.__form;\n    const gen = d2.state.gen || 0;\n    const errors = d2.state.errors || {};\n\n    // Endereços reais da conta (serviços protegidos pela sessão da loja)\n    const remote = !!d2.param('listService');\n    const authRef = d2.param('authStrategy');\n    if (remote && authRef) { try { void d2.auth(authRef).user; } catch (e) { /* sem sessão */ } }\n    const call = async (name, payload) => {\n      try { const r = await d2.rush(d2.param(name)).invoke(payload || {}); return (r && r.data !== undefined ? r.data : r) || {}; } catch (e) { return { ok: false, motivo: 'rede' }; }\n    };\n    const apply = (r) => { b.__list = sortMain(arr(r.addresses).map(obj).map((x) => Object.assign({}, x, { main: truthy(x.main) }))); b.__loaded = true; };\n\n    const changed = (action, address) => {\n      const addresses = b.__list.map((a) => Object.assign({}, a));\n      if (action) emit(d2, action, { address, addresses });\n      emit(d2, 'onChange', addresses);\n    };\n    const toList = (flash) => {\n      b.__formFor = ''; b.__startMode = ''; b.__startId = '';\n      d2.state.errors = {}; d2.state.cep = ''; d2.state.confirmId = ''; d2.state.noteId = '';\n      d2.state.flash = flash || ''; d2.state.editId = ''; d2.state.mode = 'list'; d2.state.srvErr = '';\n    };\n    const open = (m, id) => {\n      b.__formFor = ''; d2.state.flash = ''; d2.state.confirmId = ''; d2.state.noteId = ''; d2.state.errors = {}; d2.state.cep = '';\n      d2.state.editId = id || ''; d2.state.mode = m; d2.state.gen = gen + 1;\n    };\n\n    // ---------- busca de CEP\n    const lookup = (digits) => {\n      if (p.cepLookup === false || p.cepLookup === 'false' || typeof fetch !== 'function') return;\n      if (b.__lastCep === digits) return;\n      b.__lastCep = digits;\n      d2.state.cep = 'loading';\n      fetch('https://viacep.com.br/ws/' + digits + '/json/')\n        .then((r) => (r.ok ? r.json() : Promise.reject(new Error('http'))))\n        .then((j) => {\n          if (cepDigits(b.__form.cep) !== digits) return;\n          const e = Object.assign({}, d2.state.errors || {});\n          if (!j || j.erro) {\n            e.cep = 'CEP não encontrado. Confere o número ou preenche o endereço à mão.';\n            b.__focus = 'input[name=\"cep\"]';\n            d2.state.errors = e; d2.state.cep = 'notfound'; d2.state.gen = (d2.state.gen || 0) + 1;\n            return;\n          }\n          Object.assign(b.__form, { street: j.logradouro || b.__form.street, district: j.bairro || b.__form.district, city: j.localidade || b.__form.city, uf: j.uf || b.__form.uf });\n          delete e.cep;\n          ['street', 'district', 'city', 'uf'].forEach((x) => { if (b.__form[x]) delete e[x]; });\n          b.__focus = j.logradouro ? 'input[name=\"number\"]' : 'input[name=\"street\"]';\n          d2.state.errors = e;\n          d2.state.cep = j.logradouro ? 'found' : 'city';\n          d2.state.gen = (d2.state.gen || 0) + 1;\n        })\n        .catch(() => {\n          if (cepDigits(b.__form.cep) !== digits) return;\n          b.__focus = 'input[name=\"street\"]';\n          d2.state.cep = 'error'; d2.state.gen = (d2.state.gen || 0) + 1;\n        });\n    };\n\n    const onField = (e) => {\n      if (!e || !e.key) return;\n      f[e.key] = e.value;\n      if (e.key === 'cep') {\n        const d = cepDigits(e.value);\n        if (d.length === 8) lookup(d);\n        else { b.__lastCep = ''; }\n      }\n      if (errors[e.key]) { const n = Object.assign({}, errors); delete n[e.key]; d2.state.errors = n; }\n    };\n\n    const validate = () => {\n      const e = {};\n      if (f.labelChoice === 'outro' && !String(f.labelOther || '').trim()) e.labelOther = 'Dá um nome pra esse endereço (ex.: Casa da mãe).';\n      if (!String(f.recipient || '').trim()) e.recipient = 'Informe o nome de quem vai receber.';\n      if (cepDigits(f.cep).length !== 8) e.cep = 'Confere o CEP: são 8 números.';\n      if (!String(f.street || '').trim()) e.street = 'Informe a rua ou avenida.';\n      if (!String(f.number || '').trim()) e.number = 'Informe o número (ou S/N).';\n      if (!String(f.district || '').trim()) e.district = 'Informe o bairro.';\n      if (!String(f.city || '').trim()) e.city = 'Informe a cidade.';\n      const uf = String(f.uf || '').trim().toUpperCase();\n      if (!uf) e.uf = 'Informe o estado.'; else if (UFS.indexOf(uf) < 0) e.uf = 'Use a sigla do estado (ex.: SP).';\n      return e;\n    };\n    const save = async () => {\n      const e = validate();\n      d2.state.errors = e;\n      if (Object.keys(e).length) return;\n      const label = f.labelChoice === 'outro' ? String(f.labelOther).trim() : f.labelChoice;\n      const clean = {\n        label, recipient: String(f.recipient).trim(), cep: String(f.cep).trim(), street: String(f.street).trim(), number: String(f.number).trim(),\n        complement: String(f.complement || '').trim(), district: String(f.district).trim(), city: String(f.city).trim(), uf: String(f.uf).trim().toUpperCase()\n      };\n      if (remote) {\n        if (d2.state.busy) return;\n        d2.state.busy = true; d2.state.srvErr = '';\n        const r = await call('saveService', { id: mode === 'edit' ? editId : '', main: truthy(f.main), address: clean });\n        d2.state.busy = false;\n        if (r.ok) { apply(r); d2.state.listErr = ''; changed('onSave', { label: clean.label }); toList('saved'); return; }\n        if (r.erros && typeof r.erros === 'object') { d2.state.errors = Object.assign({}, r.erros); return; }\n        d2.state.srvErr = r.motivo === 'sem_sessao' ? 'Sua sessão expirou. Entre de novo para salvar.' : 'Não deu pra salvar agora. Confere sua conexão e tenta de novo.';\n        return;\n      }\n      let saved;\n      if (mode === 'edit') {\n        const cur = b.__list.find((a) => a.id === editId);\n        saved = Object.assign(cur || {}, clean);\n        if (truthy(f.main) && !saved.main) { b.__list.forEach((a) => { a.main = false; }); saved.main = true; }\n      } else {\n        b.__seq = (b.__seq || 0) + 1;\n        saved = Object.assign({ id: 'end-' + b.__seq + '-' + Date.now().toString(36) }, clean, { main: b.__list.length === 0 || truthy(f.main) });\n        if (saved.main) b.__list.forEach((a) => { a.main = false; });\n        b.__list.push(saved);\n      }\n      b.__list = sortMain(b.__list);\n      changed('onSave', Object.assign({}, saved));\n      toList('saved');\n    };\n\n    // ---------- notice acima da lista\n    const FLASH = {\n      saved: '**Endereço salvo.** Ele já aparece na hora de finalizar a compra.',\n      main: '**Endereço principal atualizado.** É ele que vem preenchido no checkout.',\n      deleted: '**Endereço excluído.**'\n    };\n    const flash = d2.state.flash && R.notice ? d2.render(R.notice, {\n      tone: 'success', iconName: 'check', live: true, layout, iconComponent: R.icon, body: FLASH[d2.state.flash] || ''\n    }, { key: 'flash-' + d2.state.flash + '-' + gen }) : '';\n\n    if (mode === 'list') {\n      const confirmId = d2.state.confirmId || '';\n      const noteId = d2.state.noteId || '';\n      const cards = b.__list.map((a) => {\n        const cp = {\n          addressId: a.id, label: a.label, main: !!a.main, recipient: a.recipient, cep: a.cep, street: a.street, number: a.number, complement: a.complement,\n          district: a.district, city: a.city, uf: a.uf, lines: arr(a.lines), confirming: confirmId === a.id,\n          note: noteId === a.id ? 'Esse é o seu **principal**. Defina outro como principal antes de excluir.' : '', layout\n        };\n        return R.card ? d2.render(R.card, Object.assign({ badge: R.badge, button: R.btn, iconComponent: R.icon }, cp), {\n          key: 'card-' + hash(cp), style: 'height:auto;min-width:0',\n          onEdit: (id) => open('edit', id),\n          onDelete: (id) => {\n            const t = b.__list.find((x) => x.id === id);\n            d2.state.flash = '';\n            if (t && t.main && b.__list.length > 1) { d2.state.confirmId = ''; d2.state.noteId = id; return; }\n            d2.state.noteId = ''; d2.state.confirmId = id;\n          },\n          onCancelDelete: () => { d2.state.confirmId = ''; },\n          onConfirmDelete: async (id) => {\n            const t = b.__list.find((x) => x.id === id);\n            if (remote) {\n              const r = await call('deleteService', { id });\n              d2.state.confirmId = '';\n              if (!r.ok) { d2.state.listErr = r.motivo === 'principal' ? 'Defina outro endereço como principal antes de excluir.' : 'Não deu pra excluir agora. Tenta de novo.'; return; }\n              apply(r); d2.state.listErr = ''; changed('onDelete', t ? Object.assign({}, t) : { id }); toList('deleted'); return;\n            }\n            b.__list = b.__list.filter((x) => x.id !== id);\n            changed('onDelete', t ? Object.assign({}, t) : { id });\n            toList('deleted');\n          },\n          onMakeMain: async (id) => {\n            if (remote) {\n              const r = await call('mainService', { id });\n              if (!r.ok) { d2.state.listErr = 'Não deu pra trocar o principal agora. Tenta de novo.'; return; }\n              apply(r); d2.state.listErr = ''; const t0 = b.__list.find((x) => x.id === id); changed('onMakeMain', t0 ? Object.assign({}, t0) : { id }); toList('main'); return;\n            }\n            b.__list.forEach((x) => { x.main = x.id === id; });\n            b.__list = sortMain(b.__list);\n            const t = b.__list.find((x) => x.id === id);\n            changed('onMakeMain', t ? Object.assign({}, t) : { id });\n            toList('main');\n          }\n        }) : '';\n      }).join('');\n      const add = R.add ? d2.render(R.add, { layout, iconComponent: R.icon, label: 'Adicionar endereço' }, { key: 'add', style: 'height:auto;min-width:0', onClick: () => open('new') }) : '';\n      const notReady = remote && !b.__loaded;\n      const status = notReady ? `<p class=\"empty\" role=\"status\">${esc(d2.state.listErr || 'Carregando seus endereços…')}</p>` : (d2.state.listErr ? `<p class=\"srv\" role=\"alert\">${esc(d2.state.listErr)}</p>` : '');\n      const empty = (b.__list.length || notReady) ? '' : `<p class=\"empty\">${rich(p.emptyText || 'Você ainda não salvou nenhum endereço. O primeiro que você cadastrar vira o **principal** e já vem preenchido no checkout.')}</p>`;\n      return `<div class=\"ab l-${layout}\">${flash}${status}${empty}${notReady ? '' : `<div class=\"grid\">${cards}${add}</div>`}</div>`;\n    }\n\n    // ---------- formulário\n    const k = (name, keys) => name + '-' + gen + '-' + hash((keys || []).map((x) => errors[x] || ''));\n    const chips = R.chip ? d2.render(R.chip, {\n      label: 'Nome do endereço', options: LABELS, value: f.labelChoice, name: 'apelido', layout\n    }, { key: 'chips-' + gen, onChange: (v) => { f.labelChoice = v; if (errors.labelOther) { const n = Object.assign({}, errors); delete n.labelOther; d2.state.errors = n; } d2.state.labelChoice = v; } }) : '';\n    const other = (d2.state.labelChoice || f.labelChoice) === 'outro' && R.ff ? d2.render(R.ff, {\n      label: 'Como você quer chamar esse endereço?', name: 'labelOther', placeholder: 'Ex.: Casa da mãe, Oficina', maxLength: 30,\n      value: f.labelOther, error: errors.labelOther || '', layout, iconComponent: R.icon\n    }, { key: k('other', ['labelOther']), onInput: (v) => onField({ key: 'labelOther', value: v }) }) : '';\n\n    const cepState = d2.state.cep || '';\n    const cepHint = cepState === 'found' ? 'Endereço encontrado. Confere e completa com o número.'\n      : cepState === 'city' ? 'Esse CEP vale para a cidade toda: preenche a rua e o bairro.'\n      : cepState === 'error' ? 'Não deu pra buscar o CEP agora. Preenche o endereço à mão.' : '';\n    const fieldsKeys = ['recipient', 'cep', 'street', 'number', 'complement', 'district', 'city', 'uf'];\n    const fs = R.fs ? d2.render(R.fs, {\n      layout, formField: R.ff, iconComponent: R.icon, version: String(gen),\n      values: fieldsKeys.reduce((o, x) => { o[x] = f[x] || ''; return o; }, {}),\n      errors: fieldsKeys.reduce((o, x) => { if (errors[x]) o[x] = errors[x]; return o; }, {}),\n      rows: [\n        { fields: [{ key: 'recipient', label: 'Nome de quem recebe', placeholder: 'Quem vai estar lá pra receber', autoComplete: 'name' }] },\n        { fields: [{ key: 'cep', label: 'CEP', mask: 'cep', placeholder: '00000-000', autoComplete: 'postal-code', hint: cepHint,\n          linkLabel: 'Não sei meu CEP', linkHref: 'https://buscacepinter.correios.com.br/app/endereco/index.php' }, { kind: 'spacer' }] },\n        { fields: [{ key: 'street', label: 'Rua / avenida', autoComplete: 'address-line1' }] },\n        { keep: true, fields: [{ key: 'number', label: 'Número', placeholder: 'Ou S/N' }, { key: 'complement', label: 'Complemento', optional: true, placeholder: 'Apto, bloco, referência', autoComplete: 'address-line2' }] },\n        { fields: [{ key: 'district', label: 'Bairro' }, { key: 'city', label: 'Cidade', autoComplete: 'address-level2' }] },\n        { fields: [{ key: 'uf', label: 'Estado', mask: 'uf', placeholder: 'UF', maxLength: 2, autoComplete: 'address-level1' }, { kind: 'spacer' }] }\n      ]\n    }, { key: 'fs-' + gen + '-' + hash(errors), onInput: onField, onBlur: onField }) : '';\n    const editingMain = mode === 'edit' && (list.find((a) => a.id === editId) || {}).main;\n    const first = mode === 'new' && list.length === 0;\n    const mainCb = editingMain || !R.cb ? '' : d2.render(R.cb, {\n      name: 'principal', checked: first || truthy(f.main), disabled: first, top: true, layout, iconComponent: R.icon,\n      label: first ? 'Usar como endereço principal ((o primeiro endereço já vira o principal))' : 'Usar como endereço principal'\n    }, { key: 'main-' + gen + (first ? '-f' : ''), onChange: (v) => { f.main = !!v; } });\n\n    const saveBtn = R.btn ? d2.render(R.btn, { label: d2.state.busy ? 'Salvando…' : 'Salvar endereço', variant: 'primary', isDisabled: !!d2.state.busy, layout }, { key: 'save-' + (d2.state.busy ? 'b' : 'i'), onClick: save }) : '';\n    const cancel = R.btn ? d2.render(R.btn, { label: 'Cancelar', variant: 'outline', layout }, { key: 'cancel', onClick: () => toList('') }) : '';\n\n    return `<div class=\"ab l-${layout}\"><div class=\"form\">\n      <h3>${mode === 'edit' ? 'Editar endereço' : 'Novo endereço'}</h3>\n      ${chips}${other}${fs}${mainCb}${d2.state.srvErr ? `<p class=\"srv\" role=\"alert\">${esc(d2.state.srvErr)}</p>` : ''}\n      <div class=\"btns\">${saveBtn}${cancel}</div>\n    </div></div>`;\n  }\n};"}},"addAddressCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addAddressCard"}}}},"addressCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addressCard"}}}},"addresses":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"addresses"}}}},"authStrategy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"authStrategy"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cepLookup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cepLookup"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"chipGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chipGroup"}}}},"deleteService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"deleteService"}}}},"emptyText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyText"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formSection":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formSection"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"listService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"listService"}}}},"mainService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"mainService"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onDelete":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onDelete"}}}},"onMakeMain":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onMakeMain"}}}},"onSave":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSave"}}}},"saveService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"saveService"}}}},"startEditId":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"startEditId"}}}},"startMode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"startMode"}}}}}}}}}},{"_id":"6abd5edc8dad80a86684540b","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"BackLink","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// BackLink — Loja Lucas Fontana\n// O link \"← Voltar para …\" em caixa alta, acima do título de uma página de detalhe (\"← Voltar para meus pedidos\").\n// 13px em negrito espaçado, seta de 16px, 36px de área de toque; fica vermelho no hover.\n// `label`, `href`. Sem `href`, avisa `onClick`. Não desenha a seta: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\na { --red-500: #c8102e; --ink: #0b0b0c; font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  display: inline-flex; align-items: center; gap: 10px; min-height: 36px; font-size: 13px; font-weight: 800; letter-spacing: .12em;\n  line-height: 1; text-transform: uppercase; color: var(--ink); text-decoration: none; cursor: pointer; }\na:hover { color: var(--red-500); }\na:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.ar { display: flex; flex: none; }\n`,\n  mounted(d2) {\n    FONT();\n    d2.on('a', 'click', (e, el) => { if (!el.getAttribute('data-href')) e.preventDefault(); emit(d2, 'onClick', el.getAttribute('data-href') || ''); });\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const iconRef = refOf(d2.param('iconComponent'));\n    const ar = iconRef ? d2.render(iconRef, { name: 'arrow-left', size: 16, iconColor: 'currentColor' }, { key: 'ar', width: 16, height: 16, style: 'flex:none' }) : '←';\n    return `<a href=\"${esc(p.href || '#')}\" data-href=\"${esc(p.href || '')}\"><span class=\"ar\" aria-hidden=\"true\">${ar}</span>${esc(p.label || 'Voltar')}</a>`;\n  }\n};\n"}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onClick":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClick"}}}}}}}}}},{"_id":"6abd5edd5d6e585d083bb06b","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OrderProgress","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// OrderProgress — Loja Lucas Fontana\n// A linha do tempo do pedido: Pedido confirmado → Pagamento aprovado → Enviado → Entregue.\n// Etapa feita (`done`): quadrado preto de 32px com check, ligado à próxima por uma linha preta.\n// Etapa atual (`current`): quadrado vermelho sólido. Etapa que falta (`pending`): quadrado vazio\n// com borda cinza e texto cinza. Na horizontal no desktop e no tablet; na vertical no mobile.\n// `steps: [{ label, date, state }]`. Sem `steps`, mostra as 4 etapas do pedido como pendentes.\n// Não desenha o check: usa o Icon pelo parâmetro `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nconst DEFAULT = [{ label: 'Pedido confirmado' }, { label: 'Pagamento aprovado' }, { label: 'Enviado' }, { label: 'Entregue' }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nol { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased;\n  list-style: none; margin: 0; padding: 32px 24px 28px; border: 1px solid var(--line); background: var(--white);\n  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); }\nli { position: relative; display: flex; flex-direction: column; gap: 12px; min-width: 0; padding-right: 12px; }\n.mark { position: relative; z-index: 1; width: 32px; height: 32px; border: 2px solid var(--line-strong); background: var(--white); color: var(--white);\n  display: flex; align-items: center; justify-content: center; flex: none; }\n.done .mark { background: var(--ink); border-color: var(--ink); }\n.current .mark { background: var(--red-500); border-color: var(--red-500); }\nli::before { content: ''; position: absolute; top: 15px; left: 44px; right: 12px; height: 2px; background: var(--line); }\nli:last-child::before { display: none; }\n.done::before { background: var(--ink); }\nb { display: block; font-size: 15px; font-weight: 700; line-height: 1.3; color: var(--ink); }\nspan.d { display: block; margin-top: 2px; font-size: 13px; line-height: 1.4; color: var(--grey-700); }\n.pending b, .pending span.d { color: var(--grey-500); }\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\n.l-mobile { grid-auto-flow: row; padding: 20px; }\n.l-mobile li { flex-direction: row; gap: 14px; padding: 0 0 20px; }\n.l-mobile li:last-child { padding-bottom: 0; }\n.l-mobile li::before { top: 32px; bottom: 0; left: 15px; right: auto; width: 2px; height: auto; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const steps = arr(p.steps).length ? arr(p.steps).map(obj) : DEFAULT;\n    const NAME = { done: 'feito', current: 'etapa atual', pending: 'ainda não' };\n    const items = steps.map((s, i) => {\n      const st = ['done', 'current', 'pending'].indexOf(s.state) >= 0 ? s.state : 'pending';\n      const ck = st === 'done' && iconRef ? d2.render(iconRef, { name: 'check', size: 16, iconColor: '#ffffff' }, { key: 'ck-' + i, width: 16, height: 16, style: 'flex:none' }) : '';\n      return `<li class=\"${st}\"${st === 'current' ? ' aria-current=\"step\"' : ''}>\n        <span class=\"mark\" aria-hidden=\"true\">${ck}</span>\n        <div><b>${esc(s.label)}<span class=\"sr\"> — ${NAME[st]}</span></b>${s.date ? `<span class=\"d\">${esc(s.date)}</span>` : ''}</div>\n      </li>`;\n    }).join('');\n    return `<ol class=\"l-${layout}\" aria-label=\"${esc(p.label || 'Andamento do pedido')}\">${items}</ol>`;\n  }\n};\n"}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"steps":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"steps"}}}}}}}}}},{"_id":"6abd5edd5d6e585d083bb070","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OrderSummary","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// OrderSummary — Loja Lucas Fontana\n// O resumo do pedido em grey-50: \"RESUMO DO PEDIDO\", as linhas (subtotal, frete), o total em 18px negrito com linha acima,\n// e blocos com título (Pagamento, Endereço de entrega, Forma de envio), cada um separado por uma linha.\n// `rows: [[rótulo, valor]]`, `total`, `sections: [{ title, lines, mutedFrom }]` — `mutedFrom` deixa em cinza as linhas a\n// partir daquela posição (ex.: \"Processado pelo Mercado Pago\"). Trechos entre **asteriscos duplos** saem em negrito.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nsection { --ink: #0b0b0c; --grey-50: #f5f5f3; --grey-700: #45474b; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; width: 100%; padding: 24px; background: var(--grey-50); }\nh2, h3 { margin: 0; font-size: 13px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase; color: var(--ink); }\nh2 { margin-bottom: 16px; }\n.row { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: 15px; line-height: 1.45; color: var(--grey-700); }\n.row span:last-child { white-space: nowrap; }\n.total { margin-top: 10px; padding: 14px 0 20px; border-top: 1px solid var(--line-strong); font-size: 18px; font-weight: 800; color: var(--ink); }\n.sec { display: flex; flex-direction: column; gap: 8px; padding: 18px 0; border-top: 1px solid var(--line-strong); }\n.sec:last-child { padding-bottom: 0; }\n.sec h3 { font-size: 11px; margin-bottom: 4px; }\n.sec p { margin: 0; font-size: 15px; line-height: 1.55; color: var(--ink); overflow-wrap: anywhere; }\n.sec p.muted { color: var(--grey-700); }\n.l-mobile, .l-tablet { padding: 20px; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const rows = arr(p.rows).map((r) => (Array.isArray(r) ? r : [obj(r).label, obj(r).value]));\n    const secs = arr(p.sections).map(obj);\n    return `<section class=\"l-${layout}\" aria-label=\"${esc(p.title || 'Resumo do pedido')}\">\n      <h2>${esc(p.title || 'Resumo do pedido')}</h2>\n      ${rows.map((r) => `<div class=\"row\"><span>${esc(r[0])}</span><span>${esc(r[1])}</span></div>`).join('')}\n      ${p.total ? `<div class=\"row total\"><span>Total</span><span>${esc(p.total)}</span></div>` : ''}\n      ${secs.map((s) => {\n        const from = s.mutedFrom == null || s.mutedFrom === '' ? -1 : Number(s.mutedFrom);\n        return `<div class=\"sec\"><h3>${esc(s.title)}</h3>${arr(s.lines).map((l, k) => `<p${from >= 0 && k >= from ? ' class=\"muted\"' : ''}>${rich(l)}</p>`).join('')}</div>`;\n      }).join('')}\n    </section>`;\n  }\n};\n"}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}},"sections":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sections"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"total":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"total"}}}}}}}}}},{"_id":"6abd5f228b5295df285a9df2","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ShipmentCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ShipmentCard — Loja Lucas Fontana\n// Um pacote do pedido: kicker vermelho (\"Envio 1 de 2\", com o selo vermelho \"Parceiro\" nos envios da BPower), quem envia\n// (\"Enviado pela Loja Lucas Fontana\" / \"Enviado pelo parceiro BPower Motorsport\"), o status, a caixa cinza de rastreio\n// (transportadora, código, botão de copiar e o botão preto \"Rastrear →\") e os itens (foto de 88px — 72px no mobile —,\n// nome, variação, quantidade e preço).\n// Sem código de rastreio, a caixa mostra \"O código de rastreio aparece aqui quando o pedido for enviado.\" (troca em\n// `trackingPendingText`) e não mostra o \"Rastrear\".\n// Copiar: o botão vira um check por 2 segundos e avisa \"Código copiado\" para leitor de tela.\n// `title`, `partner` (true ou o texto do selo), `from`, `status`, `statusLabel`, `tracking: { carrier, code, href }`,\n// `items: [{ name, meta: [..], qty, price, image }]`. Avisa `onCopy` e, quando o rastreio não tem link, `onTrack`.\n// Não desenha selo, botão nem ícone: usa StatusBadge, Badge, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nsection { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-100: #ececea; --grey-500: #6b6e73; --grey-700: #45474b;\n  --line: #e4e4e2; --line-strong: #d9d9d6;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; display: flex; flex-direction: column; gap: 20px; width: 100%; padding: 24px;\n  border: 1px solid var(--line); background: var(--white); }\n.head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }\n.kicker { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 12px; font-weight: 800; letter-spacing: .24em; line-height: 1.3;\n  text-transform: uppercase; color: var(--red-500); }\n.from { margin: 0; font-size: 17px; font-weight: 700; line-height: 1.3; color: var(--ink); }\n.bw { display: flex; align-items: center; height: 28px; flex: none; }\n.track { display: flex; align-items: center; gap: 12px; padding: 16px 16px 16px 20px; background: var(--grey-50); }\n.code { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }\n.code small { font-size: 13px; color: var(--grey-500); }\n.code b { font-size: 17px; font-weight: 800; letter-spacing: .06em; color: var(--ink); overflow-wrap: anywhere; }\n.copy { width: 44px; height: 44px; flex: none; margin: 0; padding: 0; border: 1px solid var(--line-strong); background: var(--white); color: var(--ink);\n  display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.copy:hover { border-color: var(--ink); }\n.copy:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.copy.ok { background: var(--ink); border-color: var(--ink); }\n.trk { flex: none; }\n.pending { display: flex; align-items: center; gap: 12px; padding: 16px 20px; background: var(--grey-50); font-size: 15px; line-height: 1.45; color: var(--grey-700); }\n.pending i { display: flex; flex: none; color: var(--ink); }\n.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }\nul { list-style: none; margin: 0; padding: 0; }\nli { display: grid; grid-template-columns: 88px minmax(0, 1fr) auto; gap: 16px; align-items: start; padding: 20px 0; border-top: 1px solid var(--line); }\nli:last-child { padding-bottom: 0; }\n.img { width: 88px; height: 88px; background: var(--grey-100); display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.img img { width: 100%; height: 100%; object-fit: cover; }\n.ph { font-size: 9px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); }\n.name { margin: 0 0 6px; font-size: 16px; font-weight: 700; line-height: 1.3; color: var(--ink); }\n.meta { margin: 0; font-size: 14px; line-height: 1.5; color: var(--grey-700); }\n.price { font-size: 16px; font-weight: 800; color: var(--ink); white-space: nowrap; }\n.l-mobile { padding: 20px; gap: 16px; }\n.l-mobile .head { flex-direction: column; gap: 12px; }\n.l-mobile .track { flex-wrap: wrap; padding: 16px; }\n.l-mobile .code { flex-basis: 100%; }\n.l-mobile .trk { flex: 1; }\n.l-mobile li { grid-template-columns: 72px minmax(0, 1fr) auto; gap: 12px; }\n.l-mobile .img { width: 72px; height: 72px; }\n.l-mobile .name { font-size: 15px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('.copy', 'click', (e, el) => {\n      const code = el.getAttribute('data-code') || '';\n      try { if (navigator.clipboard) navigator.clipboard.writeText(code); } catch (err) { /* sem área de transferência */ }\n      emit(d2, 'onCopy', code);\n      d2.state.copied = true;\n      clearTimeout(d2.root && d2.root.__lfT);\n      const t = setTimeout(() => { d2.state.copied = false; }, 2000);\n      if (d2.root) d2.root.__lfT = t;\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { st: refOf(d2.param('statusBadge')), badge: refOf(d2.param('badge')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const t = obj(p.tracking);\n    const copied = !!d2.state.copied;\n    const partner = p.partner === true || p.partner === 'true' ? 'Parceiro' : (typeof p.partner === 'string' && p.partner && p.partner !== 'false' ? p.partner : '');\n    const ic = (name, size, color, key) => R.icon ? d2.render(R.icon, { name, size, iconColor: color }, { key, width: size, height: size, style: 'flex:none' }) : '';\n    const badge = partner && R.badge ? d2.render(R.badge, { label: partner, tone: 'red', layout: 'desktop' }, { key: 'pb', height: 22, style: 'flex:none;width:auto' }) : '';\n    const status = p.status && R.st ? `<div class=\"bw\">${d2.render(R.st, { status: p.status, label: p.statusLabel || '' }, { key: 'st-' + p.status + (p.statusLabel || ''), style: 'display:inline-flex;flex:none' })}</div>` : '';\n\n    let track = '';\n    if (t.code) {\n      const btn = R.btn ? `<span class=\"trk\">${d2.render(R.btn, { label: 'Rastrear', variant: 'primary', size: 'sm', arrow: true, href: t.href || '', newTab: !!t.href, fullWidth: layout === 'mobile' ? 'always' : '', layout },\n        { key: 'trk-' + (t.href ? 'l' : 'b'), onClick: () => emit(d2, 'onTrack', t.code) })}</span>` : '';\n      track = `<div class=\"track\">\n        <div class=\"code\"><small>Código de rastreio${t.carrier ? ' · ' + esc(t.carrier) : ''}</small><b>${esc(t.code)}</b></div>\n        <button type=\"button\" class=\"copy${copied ? ' ok' : ''}\" data-code=\"${esc(t.code)}\" aria-label=\"Copiar código de rastreio\">${copied ? ic('check', 18, '#ffffff', 'ok') : ic('copy', 18, '#0b0b0c', 'cp')}</button>\n        <span class=\"sr\" aria-live=\"polite\">${copied ? 'Código copiado' : ''}</span>\n        ${btn}\n      </div>`;\n    } else if (p.tracking !== false && p.tracking !== 'false') {\n      track = `<div class=\"pending\"><i aria-hidden=\"true\">${ic('truck', 20, '#0b0b0c', 'tr')}</i><span>${esc(p.trackingPendingText || 'O código de rastreio aparece aqui quando o pedido for enviado.')}</span></div>`;\n    }\n\n    const items = arr(p.items).map(obj);\n    const list = items.length ? `<ul>${items.map((it) => `<li>\n        <span class=\"img\">${it.image ? `<img src=\"${esc(it.image)}\" alt=\"\">` : '<span class=\"ph\">Foto</span>'}</span>\n        <div><p class=\"name\">${esc(it.name)}</p>${arr(it.meta).map((m) => `<p class=\"meta\">${esc(m)}</p>`).join('')}${it.qty != null && it.qty !== '' ? `<p class=\"meta\">Qtd.: ${esc(it.qty)}</p>` : ''}</div>\n        <span class=\"price\">${esc(it.price)}</span>\n      </li>`).join('')}</ul>` : '';\n\n    return `<section class=\"l-${layout}\" aria-label=\"${esc(p.title || 'Envio')}\">\n      <div class=\"head\"><div>${p.title || badge ? `<span class=\"kicker\">${esc(p.title || '')}${badge}</span>` : ''}${p.from ? `<p class=\"from\">${esc(p.from)}</p>` : ''}</div>${status}</div>\n      ${track}${list}\n    </section>`;\n  }\n};\n"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"from":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"from"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onCopy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCopy"}}}},"onTrack":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onTrack"}}}},"partner":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"partner"}}}},"status":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"status"}}}},"statusBadge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusBadge"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"tracking":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tracking"}}}},"trackingPendingText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"trackingPendingText"}}}}}}}}}},{"_id":"6abd5f247830b24ef504f6ae","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OrderLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// OrderLayout — Loja Lucas Fontana\n// O corpo da página de um pedido: breadcrumb, \"← Voltar para meus pedidos\", \"Pedido #1045\" (44/36/28px) com a data e o\n// selo de status, o andamento (OrderProgress) e, embaixo, os envios (um ShipmentCard por remetente) ao lado de uma coluna\n// fixa (sticky) de 380px com o resumo (OrderSummary) e o HelpCard. No tablet e no mobile a coluna desce para baixo dos envios.\n// Avisos por status, logo abaixo do título:\n// `pending` → \"Falta pagar.\" com o botão \"Pagar com Pix\" (`payHref`; sem link, avisa `onPay`), prazo em `payUntil`.\n// `canceled`, `refused`, `refunded` → o aviso do que aconteceu, e o andamento some (não faz sentido nesses casos).\n// `alert: { body, iconName }` troca o texto do aviso; `showProgress: true` mantém o andamento mesmo cancelado.\n// `number`, `date`, `status`, `statusLabel`, `crumbs`, `back: { label, href }`, `steps` (do OrderProgress),\n// `shipments` (parâmetros de cada ShipmentCard), `summary` (parâmetros do OrderSummary), `help` (parâmetros do HelpCard).\n// Repassa `onBack`, `onCopy`, `onTrack`, `onSac` e `onPay`.\n// Não desenha nada: usa Breadcrumb, BackLink, StatusBadge, OrderProgress, ShipmentCard, OrderSummary, HelpCard, Notice,\n// Button, Badge e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst hash = (v) => { const t = JSON.stringify(v) || ''; let h = 0; for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };\n\nconst PAD = { desktop: '32px 96px 120px', tablet: '28px 40px 88px', mobile: '20px 20px 64px' };\nconst CRUMB_H = { desktop: 24, tablet: 24, mobile: 30 };\nconst ALERTS = {\n  canceled: { tone: 'error', iconName: 'close', body: '**Pedido cancelado.** Se já tinha pago, o valor volta pelo mesmo meio de pagamento. Qualquer dúvida, fala com o SAC informando o número do pedido.' },\n  refused: { tone: 'error', iconName: 'close', body: '**Pagamento recusado ou expirado.** O pedido não foi confirmado e nada foi cobrado. Se ainda quiser os produtos, é só fazer um pedido novo.' },\n  refunded: { tone: 'accent', iconName: 'swap', body: '**Pagamento estornado.** O valor volta pelo mesmo meio de pagamento: no Pix, na hora; no cartão, no prazo do seu banco.' }\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ol { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-700: #45474b;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; width: 100%; background: var(--white); color: var(--ink); }\n.in { display: flex; flex-direction: column; gap: 24px; }\n.top { display: flex; flex-direction: column; gap: 12px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }\nh1 { margin: 0 0 8px; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.date { margin: 0; font-size: 15px; color: var(--grey-700); }\n.bw { display: flex; align-items: center; height: 28px; flex: none; margin-bottom: 2px; }\n.alert { display: flex; flex-direction: column; gap: 16px; align-items: flex-start; }\n.grid { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 32px; align-items: start; }\n.main { display: flex; flex-direction: column; gap: 20px; min-width: 0; }\n.aside { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 24px; min-width: 0; }\n.l-tablet h1 { font-size: 36px; }\n.l-tablet .grid, .l-mobile .grid { grid-template-columns: minmax(0, 1fr); gap: 20px; }\n.l-tablet .aside, .l-mobile .aside { position: static; }\n.l-mobile .in { gap: 16px; }\n.l-mobile h1 { font-size: 28px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .date { font-size: 14px; }\n.l-mobile .alert { align-items: stretch; }\n`,\n\n  mounted() { FONT(); },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      bc: refOf(d2.param('breadcrumb')), back: refOf(d2.param('backLink')), st: refOf(d2.param('statusBadge')), prog: refOf(d2.param('orderProgress')),\n      ship: refOf(d2.param('shipmentCard')), sum: refOf(d2.param('orderSummary')), help: refOf(d2.param('helpCard')), notice: refOf(d2.param('notice')),\n      btn: refOf(d2.param('button')), badge: refOf(d2.param('badge')), icon: refOf(d2.param('iconComponent'))\n    };\n    const number = p.number || '0000';\n    const status = p.status || '';\n    const crumbs = arr(p.crumbs).length ? arr(p.crumbs) : [{ label: 'Início', href: '/' }, { label: 'Minha conta', href: '/perfil/meus-pedidos' }, { label: 'Meus pedidos', href: '/perfil/meus-pedidos' }, { label: 'Pedido #' + number }];\n    const back = Object.assign({ label: 'Voltar para meus pedidos', href: '/perfil/meus-pedidos' }, obj(p.back));\n\n    const crumb = R.bc ? `<div style=\"height:${CRUMB_H[layout]}px\">${d2.render(R.bc, { items: crumbs, layout }, { key: 'bc' })}</div>` : '';\n    const backLink = R.back ? d2.render(R.back, { label: back.label, href: back.href, iconComponent: R.icon }, { key: 'back', height: 36, onClick: (h) => emit(d2, 'onBack', h) }) : '';\n    const badge = status && R.st ? `<div class=\"bw\">${d2.render(R.st, { status, label: p.statusLabel || '' }, { key: 'st-' + status + (p.statusLabel || ''), style: 'display:inline-flex;flex:none' })}</div>` : '';\n\n    // aviso por status\n    const al = obj(p.alert);\n    let alert = '';\n    if (status === 'pending' && R.notice) {\n      const note = d2.render(R.notice, {\n        tone: 'accent', iconName: al.iconName || 'card', live: true, layout, iconComponent: R.icon,\n        body: al.body || ('**Falta pagar.** O pedido fica reservado até ' + (p.payUntil || '[prazo]') + '. Assim que o pagamento for confirmado, a gente começa a separar.')\n      }, { key: 'al-pending-' + hash(al) + (p.payUntil || ''), style: 'height:auto;width:100%' });\n      const pay = R.btn ? d2.render(R.btn, { label: 'Pagar com Pix', variant: 'primary', arrow: true, href: p.payHref || '', fullWidth: 'mobile', layout },\n        { key: 'pay-' + (p.payHref ? 'l' : 'b'), onClick: () => emit(d2, 'onPay', number) }) : '';\n      alert = `<div class=\"alert\">${note}${pay}</div>`;\n    } else if (ALERTS[status] && R.notice) {\n      const a = ALERTS[status];\n      alert = `<div class=\"alert\">${d2.render(R.notice, { tone: a.tone, iconName: al.iconName || a.iconName, live: true, layout, iconComponent: R.icon, body: al.body || a.body },\n        { key: 'al-' + status + '-' + hash(al), style: 'height:auto;width:100%' })}</div>`;\n    }\n    const hideProg = !!ALERTS[status] && !(p.showProgress === true || p.showProgress === 'true');\n    const prog = !hideProg && R.prog ? d2.render(R.prog, { steps: arr(p.steps), layout, iconComponent: R.icon }, { key: 'prog-' + hash(arr(p.steps)), style: 'height:auto' }) : '';\n\n    const ships = arr(p.shipments).map(obj).map((s, i) => R.ship ? d2.render(R.ship, Object.assign({}, s, {\n      layout, statusBadge: R.st, badge: R.badge, button: R.btn, iconComponent: R.icon\n    }), { key: 'ship-' + i + '-' + hash(s), style: 'height:auto', onCopy: (c) => emit(d2, 'onCopy', c), onTrack: (c) => emit(d2, 'onTrack', c) }) : '').join('');\n    const sum = R.sum ? d2.render(R.sum, Object.assign({}, obj(p.summary), { layout }), { key: 'sum-' + hash(obj(p.summary)), style: 'height:auto' }) : '';\n    const help = R.help ? d2.render(R.help, Object.assign({}, obj(p.help), { layout, button: R.btn }), { key: 'help-' + hash(obj(p.help)), style: 'height:auto', onCta: (h) => emit(d2, 'onSac', h) }) : '';\n\n    return `<div class=\"ol l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">\n      <div class=\"top\">${crumb}${backLink}</div>\n      <div class=\"head\"><div><h1>Pedido #${esc(number)}</h1>${p.date ? `<p class=\"date\">${esc(p.date)}</p>` : ''}</div>${badge}</div>\n      ${alert}${prog}\n      <div class=\"grid\"><div class=\"main\">${ships}</div><aside class=\"aside\">${sum}${help}</aside></div>\n    </div></div>`;\n  }\n};\n"}},"alert":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"alert"}}}},"back":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"back"}}}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"crumbs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"crumbs"}}}},"date":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"date"}}}},"help":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"help"}}}},"helpCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"helpCard"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"number":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"number"}}}},"onBack":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onBack"}}}},"onCopy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCopy"}}}},"onPay":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPay"}}}},"onSac":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSac"}}}},"onTrack":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onTrack"}}}},"orderProgress":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderProgress"}}}},"orderSummary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"orderSummary"}}}},"payHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"payHref"}}}},"payUntil":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"payUntil"}}}},"shipmentCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shipmentCard"}}}},"shipments":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shipments"}}}},"showProgress":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showProgress"}}}},"status":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"status"}}}},"statusBadge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusBadge"}}}},"statusLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusLabel"}}}},"steps":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"steps"}}}},"summary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"summary"}}}}}}}}}},{"_id":"6abd7b31650a71c6b67049e5","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FilterChip","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FilterChip — Loja Lucas Fontana\n// Um filtro aplicado: chip preto de 36px com o nome do filtro e um ×. Clicar remove o filtro.\n// Um por filtro ativo, seguido do link \"Limpar filtros\" (na CatalogToolbar). Nome acessível: \"Remover filtro …\".\n// Emite `onRemove` com o `value`. Não desenha o ×: usa o Icon pelo parâmetro `iconComponent`.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: inline-block; }\nbutton { display: inline-flex; align-items: center; gap: 12px; height: 36px; margin: 0; padding: 0 10px 0 14px; border: 0; background: #0b0b0c; color: #ffffff;\n  font: 600 14px/1 ${FF}; -webkit-font-smoothing: antialiased; cursor: pointer; white-space: nowrap; }\nbutton:hover { background: #1c1d1f; }\nbutton:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n`,\n  mounted(d2) { d2.on('button', 'click', () => emit(d2, 'onRemove', (d2.params || {}).value || (d2.params || {}).label)); },\n  render(d2) {\n    const p = d2.params || {};\n    const iconRef = refOf(d2.param('iconComponent'));\n    const x = iconRef ? d2.render(iconRef, { name: 'close', size: 16, iconColor: '#ffffff' }, { key: 'x', width: 16, height: 16 }) : '×';\n    return `<button type=\"button\" aria-label=\"Remover filtro ${esc(p.label)}\"><span>${esc(p.label || 'Filtro')}</span>${x}</button>`;\n  }\n};"}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"onRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRemove"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abd7b437bb89d4f862ec861","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FilterGroup","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FilterGroup — Loja Lucas Fontana\n// Uma seção de filtro: título em caixa alta de 12px (800, .24em) seguido de uma hairline até a borda, e as opções.\n// `type: \"checkbox\"` (padrão) usa o Checkbox, com a contagem cinza à direita (\"Lavagem 12\"); `type: \"radio\"` usa o Radio\n// empilhado (ex.: \"Ordenar por\" na FilterSheet). As opções chegam por `options` [{value, label, count, checked}].\n// Ordem na loja: Categoria, Marca, Preço, Disponibilidade (a FilterSheet põe \"Ordenar por\" antes).\n// Emite `onChange`: {value, checked} no checkbox; {value} no radio. Usa Checkbox, Radio e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [{ value: 'Cuidado automotivo', label: 'Cuidado automotivo', count: 9, checked: true }, { value: 'Performance', label: 'Performance', count: 2 }, { value: 'Bonés', label: 'Bonés', count: 1 }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nfieldset { border: 0; margin: 0; padding: 0; min-width: 0; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\nlegend { display: flex; align-items: center; gap: 12px; width: 100%; padding: 0; margin: 0 0 12px; font-size: 12px; font-weight: 800; letter-spacing: .24em; line-height: 1.3; text-transform: uppercase; color: #0b0b0c; }\nlegend::after { content: ''; flex: 1; height: 1px; background: #e4e4e2; }\n.body { display: flex; flex-direction: column; }\n`,\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const cbRef = refOf(d2.param('checkbox')); const rdRef = refOf(d2.param('radio')); const icon = refOf(d2.param('iconComponent'));\n    const opts = list(p.options).length ? list(p.options) : DEMO;\n    const name = p.name || 'filtro';\n    let body;\n    if (p.type === 'radio') {\n      const val = p.value != null && p.value !== '' ? String(p.value) : String((opts.find((o) => truthy(o.checked)) || opts[0] || {}).value || '');\n      body = rdRef ? d2.render(rdRef, { name, value: val, inline: false, layout, options: opts.map((o) => ({ value: o.value, label: o.label })) },\n        { key: 'rd-' + val, style: 'height:auto', onChange: (v) => emit(d2, 'onChange', { value: v }) }) : '';\n    } else {\n      body = opts.map((o, i) => cbRef ? d2.render(cbRef, {\n        name, value: String(o.value), label: o.label, count: o.count == null ? '' : String(o.count), checked: truthy(o.checked), layout, iconComponent: icon\n      }, { key: 'cb-' + i + '-' + (truthy(o.checked) ? 1 : 0), style: 'height:auto', onChange: (c) => emit(d2, 'onChange', { value: o.value, checked: !!c }) }) : '').join('');\n    }\n    return `<fieldset class=\"l-${layout}\"><legend>${esc(p.title || 'Categoria')}</legend><div class=\"body\">${body}</div></fieldset>`;\n  }\n};"}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"options":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"options"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"type":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"type"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abd7b56edeff59159dcd070","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"PriceFilter","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// PriceFilter — Loja Lucas Fontana\n// O grupo de preço: título \"PREÇO\" com hairline, campos \"Mínimo\" e \"Máximo\" (só números) com um botão preto de seta para\n// aplicar, e as faixas prontas como Radio: Até R$ 100 · R$ 100 a R$ 300 · R$ 300 a R$ 1.000 · Acima de R$ 1.000.\n// Emite `onApply` {min, max} (números ou null) e `onRange` com o `value` da faixa (\"0-100\", \"100-300\", \"300-1000\", \"1000-\").\n// Usa FormField, Radio e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst RANGES = [{ value: '0-100', label: 'Até R$ 100' }, { value: '100-300', label: 'R$ 100 a R$ 300' }, { value: '300-1000', label: 'R$ 300 a R$ 1.000' }, { value: '1000-', label: 'Acima de R$ 1.000' }];\nconst numOrNull = (v) => { const n = parseInt(String(v == null ? '' : v).replace(/\\D/g, ''), 10); return isFinite(n) ? n : null; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nfieldset { border: 0; margin: 0; padding: 0; min-width: 0; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\nlegend { display: flex; align-items: center; gap: 12px; width: 100%; padding: 0; margin: 0 0 12px; font-size: 12px; font-weight: 800; letter-spacing: .24em; line-height: 1.3; text-transform: uppercase; color: #0b0b0c; }\nlegend::after { content: ''; flex: 1; height: 1px; background: #e4e4e2; }\n.inputs { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 44px; gap: 8px; align-items: end; margin-bottom: 6px; }\n.go { width: 44px; height: 48px; margin: 0; padding: 0; border: 0; border-radius: 2px; background: #0b0b0c; color: #ffffff; display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.go:hover { background: #1c1d1f; }\n.go:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n`,\n  mounted(d2) {\n    d2.on('.go', 'click', () => { const s = (d2.root && d2.root.__lf) || {}; emit(d2, 'onApply', { min: numOrNull(s.min), max: numOrNull(s.max) }); });\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const ffRef = refOf(d2.param('formField')); const rdRef = refOf(d2.param('radio')); const icon = refOf(d2.param('iconComponent'));\n    const s = d2.root ? (d2.root.__lf || (d2.root.__lf = { min: p.min == null ? '' : String(p.min), max: p.max == null ? '' : String(p.max) })) : { min: '', max: '' };\n    const name = p.name || 'preco';\n    const ranges = list(p.ranges).length ? list(p.ranges) : RANGES;\n    const field = (k, label, ph) => ffRef ? d2.render(ffRef, { label, name: name + '-' + k, mask: 'digits', inputMode: 'numeric', placeholder: ph, value: s[k] || '', layout, iconComponent: icon },\n      { key: k, style: 'height:auto', onInput: (v) => { if (d2.root && d2.root.__lf) d2.root.__lf[k] = v; } }) : '';\n    const go = icon ? d2.render(icon, { name: 'arrow-right', size: 18, iconColor: '#ffffff' }, { key: 'go', width: 18, height: 18 }) : '→';\n    const val = p.value == null ? '' : String(p.value);\n    const radios = rdRef ? d2.render(rdRef, { name, value: val || '__none__', inline: false, layout, options: ranges.map((r) => ({ value: r.value, label: r.label })) },\n      { key: 'r-' + (val || 'none'), style: 'height:auto', onChange: (v) => emit(d2, 'onRange', v) }) : '';\n    return `<fieldset class=\"l-${layout}\"><legend>${esc(p.title || 'Preço')}</legend>\n      <div class=\"inputs\">${field('min', 'Mínimo', 'R$ 0')}${field('max', 'Máximo', 'R$ 5.000')}<button type=\"button\" class=\"go\" aria-label=\"Aplicar faixa de preço\">${go}</button></div>\n      ${radios}\n    </fieldset>`;\n  }\n};"}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"max":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"max"}}}},"min":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"min"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"onApply":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onApply"}}}},"onRange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRange"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"ranges":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ranges"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abd7b63e43d37dad5dd5e38","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"LoadMore","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// LoadMore — Loja Lucas Fontana\n// Paginação das listagens: botão de contorno \"Carregar mais produtos\" (52px; largura toda no mobile) e, embaixo,\n// \"Mostrando 9 de [N] produtos\" (anunciado por leitor de tela quando muda).\n// `done: true` esconde o botão (acabaram os produtos). `loading: true` mostra \"Carregando…\".\n// Emite `onClick`. Usa o Button pelo parâmetro `button`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.more { display: flex; flex-direction: column; align-items: center; gap: 16px; padding-top: 24px; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.btn { width: 300px; height: 52px; }\np { margin: 0; font-size: 14px; color: #6b6e73; }\n.l-mobile .btn { width: 100%; }\n`,\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const btnRef = refOf(d2.param('button'));\n    const loading = truthy(p.loading);\n    const btn = !truthy(p.done) && btnRef ? `<div class=\"btn\">${d2.render(btnRef, {\n      label: loading ? 'Carregando…' : (p.label || 'Carregar mais produtos'), variant: 'outline', size: 'lg', fullWidth: 'always', isDisabled: loading, layout\n    }, { key: 'b-' + (loading ? 'l' : 'n'), height: 52, onClick: () => emit(d2, 'onClick', true) })}</div>` : '';\n    return `<div class=\"more l-${layout}\">${btn}${p.countLabel ? `<p aria-live=\"polite\">${esc(p.countLabel)}</p>` : ''}</div>`;\n  }\n};"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"countLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"countLabel"}}}},"done":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"done"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"loading":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loading"}}}},"onClick":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClick"}}}}}}}}}},{"_id":"6abd7b7a070cf08086c28d5c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FilterPanel","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FilterPanel — Loja Lucas Fontana\n// A lateral de filtros do desktop (264px, na CatalogLayout): título \"FILTRAR\" com o ícone de filtro e o link \"Limpar tudo\",\n// depois os grupos, 28px entre eles. Abaixo de 1200px os mesmos grupos vão para a FilterSheet.\n// Os grupos chegam por `groups`: [{id, kind: \"checkbox\" | \"radio\" | \"price\", title, options, value, min, max, ranges}].\n// Emite `onChange` {group, value, checked}, `onPrice` {group, min, max}, `onRange` {group, value} e `onClear`.\n// `showClear: false` esconde \"Limpar tudo\". Usa FilterGroup, PriceFilter, Checkbox, Radio, FormField e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [\n  { id: 'categoria', title: 'Categoria', options: [{ value: 'Cuidado automotivo', label: 'Cuidado automotivo', count: 9, checked: true }, { value: 'Performance', label: 'Performance', count: 2 }, { value: 'Bonés', label: 'Bonés', count: 1 }] },\n  { id: 'marca', title: 'Marca', options: [{ value: 'Lucas Fontana', label: 'Lucas Fontana', count: 10 }, { value: 'BPower Motorsport', label: 'BPower Motorsport', count: 2 }] },\n  { id: 'preco', kind: 'price', title: 'Preço' },\n  { id: 'estoque', title: 'Disponibilidade', options: [{ value: 'em-estoque', label: 'Mostrar só produtos em estoque' }] }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.fp { display: flex; flex-direction: column; gap: 28px; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }\nh2 { display: flex; align-items: center; gap: 12px; margin: 0; font-size: 15px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: #0b0b0c; }\n.clear { margin: 0; padding: 0; border: 0; background: none; font: 600 14px/1.2 ${FF}; color: #0b0b0c; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\n.clear:hover { color: #c8102e; }\n.clear:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n`,\n  mounted(d2) { d2.on('.clear', 'click', () => emit(d2, 'onClear', true)); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { fg: refOf(d2.param('filterGroup')), pf: refOf(d2.param('priceFilter')), cb: refOf(d2.param('checkbox')), rd: refOf(d2.param('radio')), ff: refOf(d2.param('formField')), icon: refOf(d2.param('iconComponent')) };\n    const groups = list(p.groups).length ? list(p.groups) : DEMO;\n    const ver = p.version == null ? '0' : String(p.version);\n    const title = R.icon ? d2.render(R.icon, { name: 'filter', size: 20, iconColor: '#0b0b0c' }, { key: 'fi', width: 20, height: 20 }) : '';\n    const body = groups.map((g) => {\n      if (g.kind === 'price') {\n        return R.pf ? d2.render(R.pf, { title: g.title || 'Preço', name: g.id, min: g.min == null ? '' : String(g.min), max: g.max == null ? '' : String(g.max), value: g.value || '', ranges: g.ranges || [], layout, formField: R.ff, radio: R.rd, iconComponent: R.icon }, {\n          key: 'p-' + g.id + '-' + ver, style: 'height:auto',\n          onApply: (v) => emit(d2, 'onPrice', Object.assign({ group: g.id }, v || {})), onRange: (v) => emit(d2, 'onRange', { group: g.id, value: v })\n        }) : '';\n      }\n      return R.fg ? d2.render(R.fg, { title: g.title, name: g.id, type: g.kind === 'radio' ? 'radio' : 'checkbox', options: g.options || [], value: g.value || '', layout, checkbox: R.cb, radio: R.rd, iconComponent: R.icon }, {\n        key: 'g-' + g.id + '-' + ver, style: 'height:auto',\n        onChange: (v) => emit(d2, 'onChange', Object.assign({ group: g.id }, v || {}))\n      }) : '';\n    }).join('');\n    const showClear = p.showClear == null || p.showClear === '' ? true : truthy(p.showClear);\n    return `<div class=\"fp l-${layout}\">\n      ${truthy(p.hideHead) ? '' : `<div class=\"head\"><h2>${title}${esc(p.title || 'Filtrar')}</h2>${showClear ? `<button type=\"button\" class=\"clear\">${esc(p.clearLabel || 'Limpar tudo')}</button>` : ''}</div>`}\n      ${body}\n    </div>`;\n  }\n};"}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"clearLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"clearLabel"}}}},"filterGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterGroup"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"groups":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"groups"}}}},"hideHead":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hideHead"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onClear":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClear"}}}},"onPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPrice"}}}},"onRange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRange"}}}},"priceFilter":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceFilter"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"showClear":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showClear"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"version":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"version"}}}}}}}}}},{"_id":"6abd7b998b5295df285aea5c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"FilterSheet","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// FilterSheet — Loja Lucas Fontana\n// A gaveta de filtrar e ordenar do tablet e do mobile: cabeçalho \"FILTRAR E ORDENAR\" com o botão de fechar, os grupos\n// rolando (começa por \"Ordenar por\", depois os mesmos grupos da FilterPanel) e o rodapé fixo com \"Limpar\" (contorno)\n// e \"Ver resultados\" (preto). Borda vermelha de 3px no topo.\n// Mobile: sobe por cima de um fundo escuro, deixando 48px da página à mostra no topo. Tablet: painel de 420px pela direita.\n// Fecha no Esc, no fundo escuro e no ×; trava a rolagem da página enquanto aberta.\n// `open` mostra; `static: true` desenha no lugar (para documentação). Os grupos chegam por `groups` (como na FilterPanel)\n// e a ordenação por `sortOptions`/`sortValue`. Emite `onClose`, `onClear`, `onApply`, `onSort` e os eventos dos grupos.\n// Usa FilterPanel, FilterGroup, PriceFilter, Checkbox, Radio, FormField, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst SORTS = [{ value: 'relevancia', label: 'Relevância' }, { value: 'menor-preco', label: 'Menor preço' }, { value: 'maior-preco', label: 'Maior preço' }, { value: 'mais-vendidos', label: 'Mais vendidos' }];\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.sheet { position: fixed; inset: 0; z-index: 1000; display: flex; justify-content: flex-end; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.static { position: relative; inset: auto; height: 844px; }\n.scrim { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: rgba(11, 11, 12, .6); cursor: pointer; }\n.panel { position: relative; display: flex; flex-direction: column; width: 420px; max-width: 100%; height: 100%; background: #ffffff; border-top: 3px solid #c8102e; outline: none; }\n.head { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 64px; padding: 0 12px 0 20px; border-bottom: 1px solid #e4e4e2; }\nh2 { display: flex; align-items: center; gap: 12px; margin: 0; font-size: 18px; font-weight: 800; font-stretch: 112%; text-transform: uppercase; color: #0b0b0c; }\n.close { width: 44px; height: 44px; margin: 0; padding: 0; border: 0; background: transparent; color: #0b0b0c; display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.close:focus-visible { outline: 2px solid #c8102e; outline-offset: -2px; }\n.body { flex: 1; overflow-y: auto; padding: 16px 20px 24px; display: flex; flex-direction: column; gap: 24px; }\n.foot { flex: none; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; padding: 16px 20px; border-top: 1px solid #e4e4e2; background: #ffffff; }\n.fb { height: 52px; }\n.l-mobile .panel { width: 100%; height: calc(100% - 48px); margin-top: 48px; }\n`,\n  mounted(d2) {\n    d2.on('.scrim, .close', 'click', () => emit(d2, 'onClose', true));\n    const onKey = (e) => { const p = d2.params || {}; if (e.key === 'Escape' && truthy(p.open) && !truthy(p.static)) emit(d2, 'onClose', true); };\n    document.addEventListener('keydown', onKey);\n    d2.onUnmount(() => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; });\n  },\n  updated(d2) {\n    const p = d2.params || {};\n    const on = truthy(p.open) && !truthy(p.static);\n    document.body.style.overflow = on ? 'hidden' : '';\n    if (on) { const el = d2.$('.panel'); if (el && d2.root && !d2.root.__focused) { el.focus(); d2.root.__focused = true; } }\n    else if (d2.root) d2.root.__focused = false;\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'mobile';\n    const open = truthy(p.open) || truthy(p.static);\n    if (!open) return '';\n    const R = { fp: refOf(d2.param('filterPanel')), fg: refOf(d2.param('filterGroup')), pf: refOf(d2.param('priceFilter')), cb: refOf(d2.param('checkbox')), rd: refOf(d2.param('radio')), ff: refOf(d2.param('formField')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const ver = p.version == null ? '0' : String(p.version);\n    const sorts = list(p.sortOptions).length ? list(p.sortOptions) : SORTS;\n    const sortVal = p.sortValue || 'relevancia';\n    const sort = R.fg ? d2.render(R.fg, { title: 'Ordenar por', name: 'ordenar', type: 'radio', options: sorts, value: sortVal, layout, radio: R.rd, iconComponent: R.icon },\n      { key: 's-' + sortVal, style: 'height:auto', onChange: (v) => emit(d2, 'onSort', v && v.value) }) : '';\n    const groups = R.fp ? d2.render(R.fp, { groups: list(p.groups), hideHead: true, version: ver, layout, filterGroup: R.fg, priceFilter: R.pf, checkbox: R.cb, radio: R.rd, formField: R.ff, iconComponent: R.icon }, {\n      key: 'fp-' + ver, style: 'height:auto',\n      onChange: (v) => emit(d2, 'onChange', v), onPrice: (v) => emit(d2, 'onPrice', v), onRange: (v) => emit(d2, 'onRange', v)\n    }) : '';\n    const fi = R.icon ? d2.render(R.icon, { name: 'filter', size: 20, iconColor: '#0b0b0c' }, { key: 'fi', width: 20, height: 20 }) : '';\n    const x = R.icon ? d2.render(R.icon, { name: 'close', size: 22, iconColor: '#0b0b0c' }, { key: 'x', width: 22, height: 22 }) : '×';\n    const fb = (k, label, variant, evt) => R.btn ? `<div class=\"fb\">${d2.render(R.btn, { label, variant, size: 'lg', fullWidth: 'always', layout }, { key: k, height: 52, onClick: () => emit(d2, evt, true) })}</div>` : '';\n    return `<div class=\"sheet l-${layout}${truthy(p.static) ? ' static' : ''}\">\n      <button type=\"button\" class=\"scrim\" aria-label=\"Fechar filtros\" tabindex=\"-1\"></button>\n      <div class=\"panel\" role=\"dialog\"${truthy(p.static) ? '' : ' aria-modal=\"true\"'} aria-label=\"${esc(p.title || 'Filtrar e ordenar')}\" tabindex=\"-1\">\n        <div class=\"head\"><h2>${fi}${esc(p.title || 'Filtrar e ordenar')}</h2><button type=\"button\" class=\"close\" aria-label=\"Fechar\">${x}</button></div>\n        <div class=\"body\">${sort}${groups}</div>\n        <div class=\"foot\">${fb('clr', p.clearLabel || 'Limpar', 'outline', 'onClear')}${fb('apply', p.applyLabel || 'Ver resultados', 'primary', 'onApply')}</div>\n      </div>\n    </div>`;\n  }\n};"}},"applyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"applyLabel"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"clearLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"clearLabel"}}}},"filterGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterGroup"}}}},"filterPanel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterPanel"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"groups":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"groups"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onApply":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onApply"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onClear":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClear"}}}},"onClose":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClose"}}}},"onPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onPrice"}}}},"onRange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRange"}}}},"onSort":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSort"}}}},"open":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"open"}}}},"priceFilter":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceFilter"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"sortOptions":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sortOptions"}}}},"sortValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sortValue"}}}},"static":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"static"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"version":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"version"}}}}}}}}}},{"_id":"6abd7bb3e43d37dad5dd5f05","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CatalogToolbar","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CatalogToolbar — Loja Lucas Fontana\n// A linha acima dos resultados: filtros ativos, contagem e ordenação.\n// Desktop: FilterChips ativos + \"Limpar filtros\" à esquerda, \"Ordenar por\" à direita (a contagem fica no PageHeader).\n// Tablet: botão de contorno \"Filtrar (n)\", a contagem e \"Ordenar por\"; os chips numa segunda linha.\n// Mobile: \"Filtrar (n)\" e o seletor de ordenação dividem a largura; depois os chips e, embaixo, a contagem.\n// Chips chegam por `chips` [{id, label}]; `filterCount` (padrão: quantidade de chips); `countLabel` (\"Mostrando 9 de 42 produtos\");\n// ordenação por `sortOptions` [{value, label}] e `sortValue`.\n// Emite `onRemove` (id do chip), `onClear`, `onOpenFilters` e `onSort` (value). Usa FilterChip, FormField e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst SORTS = [{ value: 'relevancia', label: 'Relevância' }, { value: 'menor-preco', label: 'Menor preço' }, { value: 'maior-preco', label: 'Maior preço' }, { value: 'mais-vendidos', label: 'Mais vendidos' }];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.tb { display: flex; flex-direction: column; gap: 16px; font-family: ${FF}; -webkit-font-smoothing: antialiased; color: #0b0b0c; }\n.row { display: flex; align-items: center; gap: 16px; min-height: 48px; }\n.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }\n.clear { margin: 0; padding: 0; border: 0; background: none; font: 600 14px/1.2 ${FF}; color: #0b0b0c; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }\n.clear:hover { color: #c8102e; }\n.count { font-size: 15px; color: #6b6e73; }\n.sort { margin-left: auto; display: flex; align-items: center; gap: 12px; }\n.sort > span { font-size: 14px; font-weight: 700; white-space: nowrap; }\n.sel { width: 200px; }\n.filter { display: inline-flex; align-items: center; justify-content: center; gap: 10px; height: 48px; margin: 0; padding: 0 20px; border: 1px solid #0b0b0c; background: #ffffff; color: #0b0b0c;\n  font: 800 13px/1 ${FF}; letter-spacing: .12em; text-transform: uppercase; cursor: pointer; }\n.filter:hover { background: #0b0b0c; color: #ffffff; }\n.filter:focus-visible, .clear:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.l-mobile { gap: 12px; }\n.l-mobile .row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }\n.l-mobile .sort { margin-left: 0; }\n.l-mobile .sort > span { display: none; }\n.l-mobile .sel { width: 100%; }\n.l-mobile .filter { width: 100%; padding: 0 12px; }\n`,\n  mounted(d2) {\n    d2.on('.clear', 'click', () => emit(d2, 'onClear', true));\n    d2.on('.filter', 'click', () => emit(d2, 'onOpenFilters', true));\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { fc: refOf(d2.param('filterChip')), ff: refOf(d2.param('formField')), icon: refOf(d2.param('iconComponent')) };\n    const chips = list(p.chips);\n    const n = p.filterCount == null || p.filterCount === '' ? chips.length : Number(p.filterCount) || 0;\n    const sorts = list(p.sortOptions).length ? list(p.sortOptions) : SORTS;\n    const sortVal = p.sortValue || 'relevancia';\n    const chipHtml = chips.length ? `<div class=\"chips\">${chips.map((c, i) => R.fc ? d2.render(R.fc, { label: c.label, value: c.id, iconComponent: R.icon }, { key: 'c-' + (c.id || i), width: 'auto', height: 36, onRemove: () => emit(d2, 'onRemove', c.id) }) : '').join('')}<button type=\"button\" class=\"clear\">Limpar filtros</button></div>` : '';\n    const sel = R.ff ? `<div class=\"sel\">${d2.render(R.ff, { ariaLabel: 'Ordenar por', name: 'ordenar', options: sorts, value: sortVal, layout, iconComponent: R.icon }, { key: 'sort-' + sortVal, style: 'height:auto', onChange: (v) => emit(d2, 'onSort', v) })}</div>` : '';\n    const sort = `<div class=\"sort\"><span>Ordenar por</span>${sel}</div>`;\n    const fi = R.icon ? d2.render(R.icon, { name: 'filter', size: 20, iconColor: 'currentColor' }, { key: 'fi', width: 20, height: 20 }) : '';\n    const filterBtn = `<button type=\"button\" class=\"filter\" aria-haspopup=\"dialog\">${fi}Filtrar${n ? ' (' + n + ')' : ''}</button>`;\n    const count = p.countLabel ? `<span class=\"count\">${esc(p.countLabel)}</span>` : '';\n    let html;\n    if (layout === 'desktop') html = `<div class=\"row\">${chipHtml || '<span></span>'}${sort}</div>`;\n    else if (layout === 'tablet') html = `<div class=\"row\">${filterBtn}${count}${sort}</div>${chipHtml}`;\n    else html = `<div class=\"row\">${filterBtn}${sort}</div>${chipHtml}${count}`;\n    return `<div class=\"tb l-${layout}\">${html}</div>`;\n  }\n};"}},"chips":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chips"}}}},"countLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"countLabel"}}}},"filterChip":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterChip"}}}},"filterCount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterCount"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onClear":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onClear"}}}},"onOpenFilters":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onOpenFilters"}}}},"onRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRemove"}}}},"onSort":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSort"}}}},"sortOptions":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sortOptions"}}}},"sortValue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sortValue"}}}}}}}}}},{"_id":"6abd7bcc8b5295df285aeadc","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"EmptyState","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// EmptyState — Auto Super Shop\n// O que a busca sem resultado mostra, centralizado: quadrado preto com o ícone de busca riscada, kicker vermelho\n// \"Nenhum resultado\", título citando a busca (44/36/28px), uma linha no tom da Auto Super, a busca grande (56px, borda preta,\n// mantém o botão \"Buscar\" em qualquer largura), chips de sugestão (\"Tente buscar por: shampoo · cera · pretinho · BPower · boné\")\n// e o botão vermelho \"Ver todos os produtos\". Na página, vem seguido da faixa cinza \"Os mais vendidos da loja\".\n// `query` monta o título padrão (\"Nada encontrado para “…”\"). Emite `onSearch` (texto) e `onSuggestion` (sugestão clicada).\n// Usa SearchBar, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst PAD = { desktop: '80px 96px 96px', tablet: '64px 40px 80px', mobile: '48px 20px 64px' };\nconst SUGG = ['shampoo', 'cera', 'pretinho', 'BPower', 'boné'];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.es { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 20px; width: 100%; background: #ffffff; color: #0b0b0c; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\np, h1 { margin: 0; }\n.mark { width: 56px; height: 56px; margin-bottom: 8px; background: #0b0b0c; color: #ffffff; display: flex; align-items: center; justify-content: center; }\n.kicker { display: flex; align-items: center; gap: 12px; font-size: 12px; font-weight: 700; letter-spacing: .3em; text-transform: uppercase; color: #c8102e; }\n.kicker::before { content: ''; width: 22px; height: 3px; background: #c8102e; }\nh1 { max-width: 960px; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; overflow-wrap: break-word; }\n.text { max-width: 540px; font-size: 17px; line-height: 1.6; color: #45474b; }\n.search { width: 100%; max-width: 584px; height: 56px; margin-top: 8px; }\n.sugg { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 12px; }\n.sugg > span { font-size: 15px; color: #6b6e73; margin-right: 4px; }\n.chip { display: inline-flex; align-items: center; height: 40px; margin: 0; padding: 0 16px; border: 1px solid #d9d9d6; background: #ffffff; color: #0b0b0c; font: 600 15px/1 ${FF}; cursor: pointer; }\n.chip:hover { border-color: #0b0b0c; }\n.chip:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.cta { height: 52px; margin-top: 12px; min-width: 280px; }\n.l-tablet h1 { font-size: 36px; }\n.l-mobile { gap: 16px; }\n.l-mobile .kicker { font-size: 11px; letter-spacing: .26em; }\n.l-mobile h1 { font-size: 28px; font-stretch: 112%; line-height: 1.02; letter-spacing: 0; }\n.l-mobile .text { font-size: 15px; }\n.l-mobile .search { height: 52px; }\n.l-mobile .sugg { gap: 8px; }\n.l-mobile .sugg > span { flex-basis: 100%; margin: 0 0 4px; }\n.l-mobile .chip { height: 44px; padding: 0 14px; font-size: 14px; }\n.l-mobile .cta { width: 100%; min-width: 0; }\n`,\n  mounted(d2) { d2.on('.chip', 'click', (e, el) => emit(d2, 'onSuggestion', el.getAttribute('data-q'))); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { sb: refOf(d2.param('searchBar')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const q = String(p.query || '').trim();\n    const title = p.title || (q ? `Nada encontrado para “${q}”` : 'Nenhum produto por aqui');\n    const sugg = list(p.suggestions).length ? list(p.suggestions) : SUGG;\n    const mark = R.icon ? d2.render(R.icon, { name: p.iconName || 'search-x', size: 26, iconColor: '#ffffff' }, { key: 'm', width: 26, height: 26 }) : '';\n    const search = R.sb ? `<div class=\"search\">${d2.render(R.sb, { size: 'lg', showButton: true, layout, placeholder: 'Busque por produto, categoria ou marca', shortPlaceholder: 'Buscar produto ou marca', button: R.btn, searchIcon: R.icon }, { key: 'sb', onSearch: (v) => emit(d2, 'onSearch', v) })}</div>` : '';\n    const cta = R.btn && p.ctaLabel !== '' ? `<div class=\"cta\">${d2.render(R.btn, { label: p.ctaLabel || 'Ver todos os produtos', variant: 'brand', size: 'lg', fullWidth: 'always', arrow: true, href: p.ctaHref || '/lista-de-produtos/q', layout }, { key: 'cta', height: 52 })}</div>` : '';\n    return `<div class=\"es l-${layout}\" style=\"padding:${PAD[layout]}\" role=\"status\">\n      <span class=\"mark\" aria-hidden=\"true\">${mark}</span>\n      <p class=\"kicker\">${esc(p.eyebrow || 'Nenhum resultado')}</p>\n      <h1>${esc(title)}</h1>\n      <p class=\"text\">${esc(p.description || 'Nem a Auto Super achou isso na garagem. Confira se escreveu certinho ou tente um termo mais simples.')}</p>\n      ${search}\n      <div class=\"sugg\"><span>${esc(p.suggestionsLabel || 'Tente buscar por:')}</span>${sugg.map((t) => `<button type=\"button\" class=\"chip\" data-q=\"${esc(t)}\">${esc(t)}</button>`).join('')}</div>\n      ${cta}\n    </div>`;\n  }\n};"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"ctaHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaHref"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"description":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"description"}}}},"eyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"eyebrow"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"iconName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconName"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSearch"}}}},"onSuggestion":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSuggestion"}}}},"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"query"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"suggestions":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"suggestions"}}}},"suggestionsLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"suggestionsLabel"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6abd7c5d7bb89d4f862ecb61","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CatalogLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CatalogLayout — Loja Lucas Fontana\n// O corpo da página de LISTA DE PRODUTOS — serve para categoria e para resultado de busca (a mesma página):\n// PageHeader (título, breadcrumb e \"Mostrando 9 de N produtos\"), e embaixo:\n// Desktop: a FilterPanel numa coluna de 264px presa na rolagem, 48px dos resultados.\n// Tablet e mobile: a lateral some; o botão \"Filtrar (n)\" da CatalogToolbar abre a FilterSheet.\n// Resultados: CatalogToolbar, grade de ProductCards com contorno (3 colunas no desktop e tablet, 2 no mobile) e LoadMore.\n// Sem resultado: o EmptyState e a faixa cinza \"Os mais vendidos da loja\".\n// Dados: `dataset` recebe o DataSet da tabela Produtos (`@produtosDataSet`) — o componente carrega todas as páginas.\n// `products` (lista) serve de alternativa. Produtos com `ativo: false` não aparecem.\n// A página filtra, ordena e pagina aqui mesmo: busca (`query` ou `?q=` na URL), Categoria, Marca, Preço (faixas ou mínimo/máximo),\n// \"Só em estoque\", e ordenação Relevância · Menor preço · Maior preço · Mais vendidos. `category` abre já filtrado. Os filtros também ficam na URL (`?q=`, `?categoria=slug`, `?marca=slug`, `?preco=100-300`, `?estoque=1`, `?ordem=`): a página lê ao abrir e atualiza a cada mudança.\n// Não desenha nada disso: usa PageHeader, Breadcrumb, FilterPanel, FilterSheet, FilterGroup, PriceFilter, CatalogToolbar,\n// FilterChip, ProductCard, LoadMore, EmptyState, SectionHeader, SearchBar, Checkbox, Radio, FormField, Button, Badge, Price e Icon.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst obj = (v) => { if (v && typeof v === 'object' && !Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } } return {}; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst fold = (s) => String(s == null ? '' : s).normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase().trim();\nconst brl = (n) => 'R$ ' + Number(n).toLocaleString('pt-BR', { minimumFractionDigits: 0, maximumFractionDigits: 0 });\nconst PAD = { desktop: '40px 96px 120px', tablet: '32px 40px 88px', mobile: '24px 20px 64px' };\nconst CARD_H = { desktop: 500, tablet: 470, mobile: 384 };\nconst SORTS = [{ value: 'relevancia', label: 'Relevância' }, { value: 'menor-preco', label: 'Menor preço' }, { value: 'maior-preco', label: 'Maior preço' }, { value: 'mais-vendidos', label: 'Mais vendidos' }];\nconst RANGES = [{ value: '0-100', label: 'Até R$ 100' }, { value: '100-300', label: 'R$ 100 a R$ 300' }, { value: '300-1000', label: 'R$ 300 a R$ 1.000' }, { value: '1000-', label: 'Acima de R$ 1.000' }];\nconst DESCR = { 'Cuidado automotivo': 'Tudo que o Lucas usa pra deixar o carro brilhando — e mais um pouco.', Performance: 'As peças de performance dos parceiros, testadas no canal.', 'Bonés': 'Pra quem é da garagem até no visual.', 'Vestuário': 'Pra quem é da garagem até no visual.', 'Acessórios': 'Bonés, chaveiros e adesivos pra quem é da garagem.' };\nconst blankSel = () => ({ categoria: [], subcategoria: [], marca: [], estoque: false, range: '', min: null, max: null });\n// Categorias ativas (tabela Categorias); Produtos.categoria é um lookup, então o nome vem daqui\nlet CATS = null; let CATS_LOADING = null;\n// Produtos ativos da loja (serviço público produtosLoja); usado quando não há DataSet legível\nlet PRODS = null; let PRODS_LOADING = null;\n// Sacola no navegador (mesma chave da página Sacola): [{sku, qty}]\nconst addToBag = (sku, qty) => {\n  if (!sku) return;\n  try {\n    const bag = JSON.parse(localStorage.getItem('lf-sacola') || '[]');\n    const list2 = Array.isArray(bag) ? bag : [];\n    const f = list2.find((y) => y && y.sku === sku);\n    if (f) f.qty = (Number(f.qty) || 0) + (Number(qty) || 1); else list2.push({ sku, qty: Number(qty) || 1 });\n    localStorage.setItem('lf-sacola', JSON.stringify(list2));\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: list2.reduce((s, y) => s + (Number(y.qty) || 0), 0) } }));\n  } catch (e) { /* sem storage */ }\n};\n// ---- Busca: ignora acento e caixa, plural e palavras de ligação; pesa nome > SKU > marca/parceiro > categoria/subcategoria ----\nconst STOP = ['de', 'da', 'do', 'das', 'dos', 'para', 'pra', 'com', 'e', 'o', 'a', 'os', 'as', 'um', 'uma', 'em', 'no', 'na', 'nos', 'nas', 'por', 'ao'];\nconst stem = (w) => {\n  if (w.length > 4 && /ns$/.test(w)) return w.slice(0, -2) + 'm';\n  if (w.length > 5 && /res$/.test(w)) return w.slice(0, -2);\n  if (w.length > 3 && /s$/.test(w)) return w.slice(0, -1);\n  return w;\n};\nconst wordsOf = (s) => fold(s).split(/[^a-z0-9]+/).filter(Boolean);\nconst SEARCH_FIELDS = [['nome', 10], ['sku', 8], ['marca', 6], ['parceiroNome', 6], ['categoria', 5], ['subcategoria', 5]];\n// distância de edição até 1 (uma letra a mais, a menos, trocada ou duas vizinhas invertidas)\nconst near = (a, b) => {\n  if (a === b) return true;\n  const la = a.length; const lb = b.length;\n  if (Math.abs(la - lb) > 1) return false;\n  let i = 0; while (i < la && i < lb && a[i] === b[i]) i++;\n  if (la === lb) return a.slice(i + 1) === b.slice(i + 1) || (a[i] === b[i + 1] && a[i + 1] === b[i] && a.slice(i + 2) === b.slice(i + 2));\n  return la > lb ? a.slice(i + 1) === b.slice(i) : b.slice(i + 1) === a.slice(i);\n};\n// 1º passo: todas as palavras (exato). 2º: tolera um erro de digitação. 3º: qualquer uma das palavras.\nconst searchProducts = (all, q) => {\n  const tokens = wordsOf(q).filter((w) => STOP.indexOf(w) < 0).map(stem);\n  if (!tokens.length) return { list: all.map((x) => ({ x, score: 0 })), mode: 'all' };\n  const phrase = wordsOf(q).join(' ');\n  const idx = all.map((x) => ({ x, f: SEARCH_FIELDS.map((fd) => ({ w: fd[1], words: wordsOf(x[fd[0]]).map(stem) })), first: (wordsOf(x.nome).map(stem)[0] || ''), full: wordsOf(x.nome).join(' ') }));\n  const scoreTok = (e, t, fuzzy) => {\n    let best = 0;\n    e.f.forEach((fl) => fl.words.forEach((w) => {\n      let s = 0;\n      if (w === t) s = fl.w * 3;\n      else if (w.indexOf(t) === 0) s = fl.w * 2;\n      else if (t.length >= 3 && w.indexOf(t) > 0) s = fl.w;\n      else if (fuzzy && t.length >= 5 && near(w, t)) s = fl.w * 0.8;\n      if (s > best) best = s;\n    }));\n    if (best && e.first.indexOf(t) === 0) best += 3;\n    return best;\n  };\n  const run = (fuzzy, needAll) => {\n    const out = [];\n    idx.forEach((e) => {\n      const sc = tokens.map((t) => scoreTok(e, t, fuzzy));\n      const hit = sc.filter((s) => s > 0).length;\n      if (needAll ? hit === tokens.length : hit > 0) out.push({ x: e.x, score: sc.reduce((a, b) => a + b, 0) + hit * 5 + (e.full.indexOf(phrase) >= 0 ? 20 : 0) });\n    });\n    return out;\n  };\n  let list = run(false, true); let mode = 'exact';\n  if (!list.length) { list = run(true, true); mode = 'fuzzy'; }\n  if (!list.length && tokens.length > 1) { list = run(true, false); mode = 'partial'; }\n  return { list, mode };\n};\n// Página não encontrada: o router mostra esta tela em qualquer endereço que não existe; o endereço vira o termo \"buscado\"\nconst notFoundPath = () => {\n  try {\n    const path = String(location.pathname || '');\n    if (!path || path === '/' || path.indexOf('/preview') === 0 || path.indexOf('/lista-de-produtos') === 0) return '';\n    return path;\n  } catch (e) { return ''; }\n};\nconst nfText = (path) => {\n  let t = String(path || '');\n  try { t = decodeURIComponent(t); } catch (e) { /* mantém */ }\n  return t.split('/').filter(Boolean).join(' ').split('-').join(' ').split('_').join(' ').trim();\n};\n// Resultado de busca não entra no Google: noindex enquanto houver ?q=\nconst robots = (q) => {\n  try {\n    if (typeof document === 'undefined' || !document.head) return;\n    let m = document.head.querySelector('meta[name=\"robots\"]');\n    if (q) {\n      if (!m) { m = document.createElement('meta'); m.setAttribute('name', 'robots'); document.head.appendChild(m); }\n      m.setAttribute('content', 'noindex,follow');\n    } else if (m && m.getAttribute('content') === 'noindex,follow' && m.parentNode) m.parentNode.removeChild(m);\n  } catch (e) { /* sem head */ }\n};\n// Mantém a posição da rolagem quando os filtros mudam e a lista é redesenhada\nconst holdScroll = (d2) => {\n  try {\n    if (typeof window === 'undefined' || typeof document === 'undefined') return () => {};\n    let sc = null;\n    let n = d2.root;\n    while (n && !sc) {\n      n = n.parentNode || (n.getRootNode && n.getRootNode().host) || null;\n      if (n && n.nodeType === 1) {\n        const cs = window.getComputedStyle(n);\n        if (/(auto|scroll)/.test(cs.overflowY) && n.scrollHeight > n.clientHeight) sc = n;\n      }\n    }\n    const el = sc || document.scrollingElement || document.documentElement;\n    const y = el.scrollTop;\n    const restore = () => { try { if (Math.abs(el.scrollTop - y) > 2) el.scrollTop = y; } catch (e) { /* sem rolagem */ } };\n    return () => {\n      if (typeof requestAnimationFrame === 'function') requestAnimationFrame(() => { restore(); requestAnimationFrame(restore); });\n      [120, 300, 600].forEach((ms) => setTimeout(restore, ms));\n    };\n  } catch (e) { return () => {}; }\n};\nconst slugify = (s) => fold(s).replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');\nconst csv = (v) => String(v || '').split(',').map((x) => x.trim()).filter(Boolean);\n// Filtros na URL: ?q= &categoria=slug,slug &marca=slug &preco=100-300 &estoque=1 &ordem=menor-preco\nconst readUrl = () => {\n  try {\n    const u = new URLSearchParams((typeof location !== 'undefined' && location.search) || '');\n    const out = {};\n    const q = u.get('q'); if (q) out.q = q;\n    const s = blankSel(); let has = false;\n    const c = csv(u.get('categoria')); if (c.length) { s.categoria = c; has = true; }\n    const m = csv(u.get('marca')); if (m.length) { s.marca = m; has = true; }\n    const sc = csv(u.get('subcategoria')); if (sc.length) { s.subcategoria = sc; has = true; }\n    const pr = u.get('preco');\n    if (pr && /^\\d*-\\d*$/.test(pr) && pr !== '-') {\n      if (RANGES.some((x) => x.value === pr)) s.range = pr; else { const b = rangeBounds(pr); s.min = b[0]; s.max = b[1]; }\n      has = true;\n    }\n    if (u.get('estoque') === '1') { s.estoque = true; has = true; }\n    if (has) out.sel = s;\n    const o = u.get('ordem'); if (o && SORTS.some((x) => x.value === o)) out.sort = o;\n    return out;\n  } catch (e) { return {}; }\n};\nconst writeUrl = (st, cats) => {\n  try {\n    if (typeof history === 'undefined' || !history.replaceState || typeof location === 'undefined') return;\n    const slugByName = {}; (cats || []).forEach((c) => { slugByName[c.nome] = c.slug; });\n    const u = new URLSearchParams();\n    const sel = st.sel || blankSel();\n    if (st.q) u.set('q', st.q);\n    if (sel.categoria.length) u.set('categoria', sel.categoria.map((n) => slugByName[n] || slugify(n)).join(','));\n    if (sel.subcategoria && sel.subcategoria.length) u.set('subcategoria', sel.subcategoria.map(slugify).join(','));\n    if (sel.marca.length) u.set('marca', sel.marca.map(slugify).join(','));\n    if (sel.range) u.set('preco', sel.range);\n    else if (sel.min != null || sel.max != null) u.set('preco', (sel.min == null ? '' : sel.min) + '-' + (sel.max == null ? '' : sel.max));\n    if (sel.estoque) u.set('estoque', '1');\n    if (st.sort && st.sort !== 'relevancia') u.set('ordem', st.sort);\n    const qs = u.toString().replace(/%2C/gi, ',');\n    history.replaceState(history.state, '', location.pathname + (qs ? '?' + qs : '') + (location.hash || ''));\n  } catch (e) { /* sem history */ }\n};\nconst rangeBounds = (r) => { if (!r) return [null, null]; const [a, b] = r.split('-'); return [a === '' ? null : Number(a), b === '' || b == null ? null : Number(b)]; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.cat { width: 100%; background: #ffffff; font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.in { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 48px; align-items: start; }\n.side { position: sticky; top: 24px; min-width: 0; }\n.main { display: flex; flex-direction: column; gap: 24px; min-width: 0; }\n.grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px; }\n.l-tablet .in, .l-mobile .in { grid-template-columns: minmax(0, 1fr); }\n.l-tablet .main { gap: 20px; }\n.l-tablet .grid { gap: 16px; }\n.l-mobile .main { gap: 16px; }\n.l-mobile .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }\n.best { background: #f5f5f3; }\n.best .bin { display: flex; flex-direction: column; gap: 40px; }\n.best .grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }\n.l-tablet .best .grid, .l-mobile .best .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.l-mobile .best .bin { gap: 24px; }\n`,\n\n  mounted(d2) {\n    // Traz todos os produtos da tabela (todas as páginas do DataSet)\n    try { const dh = d2.param('dataset') && d2.data ? d2.data('dataset') : null; if (dh && typeof dh.loadAll === 'function') dh.loadAll(); } catch (e) { /* sem DataSet */ }\n    if (!CATS) {\n      if (!CATS_LOADING) {\n        CATS_LOADING = d2.rush('categoriasLoja').invoke({}).then((res) => { CATS = (res && res.data !== undefined ? res.data : res) || []; }).catch(() => { CATS_LOADING = null; });\n      }\n      const pr = CATS_LOADING;\n      if (pr) pr.then(() => { d2.state.catsTick = Date.now(); });\n    }\n    if (!PRODS) {\n      if (!PRODS_LOADING) {\n        PRODS_LOADING = d2.rush('produtosLoja').invoke({}).then((res) => { PRODS = (res && res.data !== undefined ? res.data : res) || []; }).catch(() => { PRODS_LOADING = null; });\n      }\n      const pr2 = PRODS_LOADING;\n      if (pr2) pr2.then(() => { d2.state.catsTick = Date.now(); });\n    }\n    // Lê o filtro inicial da URL (?q= e ?categoria=), se houver\n    try {\n      const url = readUrl();\n      if (url.q) d2.state.q = url.q;\n      if (url.sel) d2.state.sel = url.sel;\n      if (url.sort) d2.state.sort = url.sort;\n    } catch (e) { /* sem URL */ }\n    // Busca do Header (SearchBar com busTopic \"loja:search\"): filtra a lista sem recarregar a página\n    if (d2.bus && typeof d2.bus.on === 'function') {\n      d2.bus.on('loja:search', (v) => {\n        const nq = String(v == null ? '' : v).trim();\n        const cur = d2.state.q != null ? String(d2.state.q) : '';\n        const f = d2.root && d2.root.__lfSet;\n        if (f && nq !== cur && !notFoundPath()) f({ q: nq });\n      });\n    }\n  },\n\n  updated(d2) {\n    if (notFoundPath()) { robots(true); try { document.title = 'Página não encontrada | Lucas Fontana'; } catch (e) { /* sem document */ } return; }\n    robots(d2.state.q != null ? String(d2.state.q).trim() : String((d2.params || {}).query || '').trim());\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    void d2.state.catsTick;\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const desk = layout === 'desktop';\n    const r = (n) => refOf(d2.param(n));\n    const R = {\n      ph: r('pageHeader'), bc: r('breadcrumb'), fp: r('filterPanel'), fs: r('filterSheet'), fg: r('filterGroup'), pf: r('priceFilter'),\n      tb: r('catalogToolbar'), fc: r('filterChip'), pc: r('productCard'), lm: r('loadMore'), es: r('emptyState'), sh: r('sectionHeader'),\n      sb: r('searchBar'), cb: r('checkbox'), rd: r('radio'), ff: r('formField'), btn: r('button'), badge: r('badge'), price: r('priceComponent'), icon: r('iconComponent')\n    };\n    const pageSize = Number(p.pageSize) > 0 ? Number(p.pageSize) : 9;\n    const descr = Object.assign({}, DESCR, obj(p.categoryDescriptions));\n\n    // ——— Estado (com valores iniciais vindos dos parâmetros)\n    // Na rota da loja (/lista-de-produtos) quem manda é a URL: `category` e `query` do bloco só valem nas telas de design (builder)\n    const onRoute = typeof location !== 'undefined' && String(location.pathname || '').indexOf('/lista-de-produtos') >= 0;\n    const nfPath = notFoundPath();\n    const notFound = !!nfPath;\n    const searchHere = (v) => {\n      if (notFound) { const t = String(v || '').trim(); if (typeof window !== 'undefined') window.location.assign('/lista-de-produtos/q' + (t ? '?q=' + encodeURIComponent(t) : '')); return; }\n      set({ q: String(v || ''), sel: blankSel() });\n    };\n    const selRaw = d2.state.sel || (() => { const s = blankSel(); if (p.category && !onRoute) s.categoria = [String(p.category)]; return s; })();\n    const q = notFound ? nfText(nfPath) : (d2.state.q != null ? String(d2.state.q) : (onRoute ? '' : String(p.query || '')));\n    const sort = d2.state.sort || p.sort || 'relevancia';\n    const pages = d2.state.pages || 1;\n    const ver = String(d2.state.ver || 0);\n    const set = (patch) => {\n      const keep = holdScroll(d2);\n      const base = { sel: d2.state.sel || sel, q: d2.state.q != null ? d2.state.q : q, sort: d2.state.sort || sort, pages: d2.state.pages || pages };\n      const next = Object.assign(base, patch);\n      if (patch.sel || patch.q != null || patch.sort) next.pages = patch.pages || 1;\n      d2.state.sel = next.sel; d2.state.q = next.q; d2.state.sort = next.sort; d2.state.pages = next.pages;\n      d2.state.ver = (d2.state.ver || 0) + 1;\n      writeUrl({ q: next.q, sel: next.sel, sort: next.sort }, CATS);\n      emit(d2, 'onFiltersChange', { query: next.q, sort: next.sort, filters: next.sel });\n      try { if (d2.bus && typeof d2.bus.emit === 'function') d2.bus.emit('loja:q', next.q); } catch (err) { /* sem bus */ }\n      keep();\n    };\n    if (d2.root) d2.root.__lfSet = set;\n    const toggle = (group, value, on) => {\n      const s = JSON.parse(JSON.stringify(sel));\n      if (group === 'estoque') s.estoque = !!on;\n      else { const arr = s[group] || []; const i = arr.indexOf(value); if (on && i < 0) arr.push(value); if (!on && i >= 0) arr.splice(i, 1); s[group] = arr;\n        if (group === 'categoria' && arr.length && s.subcategoria && s.subcategoria.length) { const okSub = all.filter((x) => arr.indexOf(x.categoria) >= 0).map((x) => x.subcategoria); s.subcategoria = s.subcategoria.filter((v) => okSub.indexOf(v) >= 0); }\n      }\n      set({ sel: s });\n    };\n\n    // ——— Dados\n    const dsRef = d2.param('dataset');\n    const dh = dsRef && d2.data ? d2.data('dataset') : null;\n    const rows = dh && typeof dh.records === 'function' ? dh.records() : null;\n    const rawRows = (rows && rows.length ? rows : (PRODS && PRODS.length ? PRODS : list(p.products)));\n    const catById = {}; (CATS || []).forEach((c) => { catById[String(c._id)] = c.nome; });\n    const catOf = (v) => { if (!CATS) return ''; const id = v && typeof v === 'object' ? (v._id || '') : v; return catById[String(id)] !== undefined ? catById[String(id)] : ''; };\n    const all = rawRows.filter((x) => x && x.ativo !== false && x.ativo !== 'false').map((x) => Object.assign({}, x, { categoria: catOf(x.categoria) })).filter((x) => !CATS || x.categoria);\n    // Valores que vêm da URL (slug) viram o nome da categoria/marca; o que não existe é ignorado\n    const catBySlug = {}; (CATS || []).forEach((c) => { catBySlug[slugify(c.nome)] = c.nome; if (c.slug) catBySlug[slugify(c.slug)] = c.nome; });\n    const catNames = (CATS || []).map((c) => c.nome);\n    const marcaBySlug = {}; all.forEach((x) => { if (x.marca) marcaBySlug[slugify(x.marca)] = x.marca; });\n    const subBySlug = {}; all.forEach((x) => { if (x.subcategoria) subBySlug[slugify(x.subcategoria)] = x.subcategoria; });\n    const sel = Object.assign({}, selRaw, {\n      categoria: selRaw.categoria.map((v) => catBySlug[slugify(v)] || v).filter((v) => !CATS || catNames.indexOf(v) >= 0),\n      marca: selRaw.marca.map((v) => marcaBySlug[slugify(v)] || v).filter((v) => !all.length || all.some((x) => x.marca === v)),\n      subcategoria: (selRaw.subcategoria || []).map((v) => subBySlug[slugify(v)] || v).filter((v) => !all.length || all.some((x) => x.subcategoria === v))\n    });\n    const srch = searchProducts(all, q);\n    const scoreOf = new Map(srch.list.map((r) => [r.x, r.score]));\n    const base = srch.list.map((r) => r.x);\n    const sugg = Array.from(new Set([].concat((CATS || []).map((c) => c.nome), all.map((x) => x.marca).filter(Boolean), all.map((x) => x.nome)))).slice(0, 6);\n    const [rMin, rMax] = sel.range ? rangeBounds(sel.range) : [sel.min, sel.max];\n    const pass = (x, skip) => {\n      if (skip !== 'categoria' && sel.categoria.length && sel.categoria.indexOf(x.categoria) < 0) return false;\n      if (skip !== 'subcategoria' && sel.subcategoria.length && sel.subcategoria.indexOf(x.subcategoria) < 0) return false;\n      if (skip !== 'marca' && sel.marca.length && sel.marca.indexOf(x.marca) < 0) return false;\n      if (sel.estoque && !(Number(x.estoque) > 0)) return false;\n      const pr = Number(x.preco) || 0;\n      if (rMin != null && pr < rMin) return false;\n      if (rMax != null && pr > rMax) return false;\n      return true;\n    };\n    const results = notFound ? [] : base.filter((x) => pass(x));\n    const sorters = {\n      'menor-preco': (a, b) => (a.preco || 0) - (b.preco || 0),\n      'maior-preco': (a, b) => (b.preco || 0) - (a.preco || 0),\n      'mais-vendidos': (a, b) => (b.vendas || 0) - (a.vendas || 0),\n      relevancia: (a, b) => (scoreOf.get(b) || 0) - (scoreOf.get(a) || 0) || (b.destaque ? 1 : 0) - (a.destaque ? 1 : 0) || (b.vendas || 0) - (a.vendas || 0) || String(a.nome).localeCompare(String(b.nome), 'pt-BR')\n    };\n    results.sort(sorters[sort] || sorters.relevancia);\n    const total = results.length;\n    const shown = Math.min(total, pageSize * pages);\n    const countLabel = total ? `Mostrando ${shown} de ${total} ${total === 1 ? 'produto' : 'produtos'}` : '';\n    const loading = (dh && dh.loading && !all.length) || (!!CATS_LOADING && !CATS) || (!rawRows.length && !!PRODS_LOADING && !PRODS);\n\n    // ——— Opções dos filtros (contagem sobre a busca, sem o próprio grupo)\n    const facet = (key) => {\n      const counts = {};\n      base.filter((x) => pass(x, key)).forEach((x) => { const v = x[key]; if (v) counts[v] = (counts[v] || 0) + 1; });\n      sel[key].forEach((v) => { if (!(v in counts)) counts[v] = 0; });\n      return Object.keys(counts).sort((a, b) => a.localeCompare(b, 'pt-BR')).map((v) => ({ value: v, label: v, count: counts[v], checked: sel[key].indexOf(v) >= 0 }));\n    };\n    const groups = [\n      { id: 'categoria', title: 'Categoria', options: facet('categoria') },\n      ...(facet('subcategoria').length ? [{ id: 'subcategoria', title: 'Subcategoria', options: facet('subcategoria') }] : []),\n      { id: 'marca', title: 'Marca', options: facet('marca') },\n      { id: 'preco', kind: 'price', title: 'Preço', ranges: RANGES, value: sel.range || '', min: sel.range ? '' : (sel.min == null ? '' : sel.min), max: sel.range ? '' : (sel.max == null ? '' : sel.max) },\n      { id: 'estoque', title: 'Disponibilidade', options: [{ value: 'em-estoque', label: 'Mostrar só produtos em estoque', checked: sel.estoque }] }\n    ];\n    const onGroup = (v) => { if (!v) return; toggle(v.group, v.value, v.checked); };\n    const onPrice = (v) => { const s = JSON.parse(JSON.stringify(sel)); s.range = ''; s.min = v && v.min != null ? v.min : null; s.max = v && v.max != null ? v.max : null; set({ sel: s }); };\n    const onRange = (v) => { const s = JSON.parse(JSON.stringify(sel)); s.range = (v && v.value) || ''; s.min = null; s.max = null; set({ sel: s }); };\n    const clearAll = () => set({ sel: blankSel() });\n\n    // ——— Chips ativos\n    const chips = [];\n    sel.categoria.forEach((v) => chips.push({ id: 'categoria|' + v, label: v }));\n    sel.subcategoria.forEach((v) => chips.push({ id: 'subcategoria|' + v, label: v }));\n    sel.marca.forEach((v) => chips.push({ id: 'marca|' + v, label: v }));\n    if (sel.range) chips.push({ id: 'preco|', label: (RANGES.find((x) => x.value === sel.range) || {}).label || 'Preço' });\n    else if (sel.min != null || sel.max != null) chips.push({ id: 'preco|', label: sel.min != null && sel.max != null ? `${brl(sel.min)} a ${brl(sel.max)}` : (sel.min != null ? `A partir de ${brl(sel.min)}` : `Até ${brl(sel.max)}`) });\n    if (sel.estoque) chips.push({ id: 'estoque|', label: 'Só em estoque' });\n    const removeChip = (id) => {\n      const [g, v] = String(id || '').split('|');\n      const s = JSON.parse(JSON.stringify(sel));\n      if (g === 'preco') { s.range = ''; s.min = null; s.max = null; } else if (g === 'estoque') s.estoque = false; else s[g] = (s[g] || []).filter((x) => x !== v);\n      set({ sel: s });\n    };\n\n    // ——— Cabeçalho (categoria ou busca)\n    const singleCat = sel.categoria.length === 1 ? sel.categoria[0] : '';\n    const head = notFound ? { eyebrow: 'Página não encontrada', title: 'Não encontramos essa página', description: 'O endereço pode estar errado ou a página saiu do ar.', crumbs: [{ label: 'Início', href: '/' }, { label: 'Página não encontrada' }] } : q\n      ? { eyebrow: 'Busca', title: `Resultados para “${q}”`, description: total ? `${total} ${total === 1 ? 'produto encontrado' : 'produtos encontrados'}.${srch.mode === 'fuzzy' ? ' Resultados aproximados: não achamos o termo exato.' : srch.mode === 'partial' ? ' Nenhum produto tem todas as palavras; mostrando os que têm alguma delas.' : ''}` : '', crumbs: [{ label: 'Início', href: '/' }, { label: 'Busca' }] }\n      : singleCat\n        ? { eyebrow: 'Categoria', title: singleCat, description: descr[singleCat] || '', crumbs: [{ label: 'Início', href: '/' }, { label: singleCat }] }\n        : { eyebrow: 'Loja', title: p.allTitle || 'Todos os produtos', description: p.allDescription || 'Os produtos que aparecem no canal, com a curadoria de quem testa de verdade.', crumbs: [{ label: 'Início', href: '/' }, { label: 'Todos os produtos' }] };\n    const header = R.ph ? d2.render(R.ph, {\n      eyebrow: head.eyebrow, title: head.title, description: head.description, crumbs: head.crumbs, meta: countLabel, showSearch: false, layout, breadcrumb: R.bc\n    }, { key: 'ph-' + ver + '-' + fold(head.title) + '-' + total, style: 'height:auto' }) : '';\n\n    // ——— Card de produto (no tablet a grade tem 3 colunas estreitas: o card usa a versão compacta, com rótulos curtos)\n    const cardLayout = layout === 'desktop' ? 'desktop' : 'mobile';\n    const card = (x, i, prefix) => R.pc ? `<div class=\"cell\" style=\"height:${CARD_H[layout]}px\">${d2.render(R.pc, {\n      name: x.nome, brand: x.subcategoria || x.categoria || '', price: Number(x.preco) || 0, oldPrice: x.precoAntigo ? Number(x.precoAntigo) : undefined,\n      installments: Number(x.parcelas) || 1, href: '/produto/' + (x.slug || ''), sku: x.sku || '', image: x.imagem || '', outOfStock: !(Number(x.estoque) > 0),\n      partner: x.origem === 'parceiro' ? (String(x.marca || '').split(' ')[0] || 'Parceiro') : '', variant: 'outlined', layout: cardLayout,\n      priceComponent: R.price, button: R.btn, badge: R.badge, iconComponent: R.icon\n    }, { key: prefix + (x.sku || i) + '-' + layout, onAdd: (s) => { addToBag(x.sku, 1); emit(d2, 'onAdd', s); }, onNotify: (s) => emit(d2, 'onNotify', s), onOpen: (s) => emit(d2, 'onOpen', s) })}</div>` : '';\n\n    if (loading) return `<div class=\"cat l-${layout}\">${header}<div class=\"in\" style=\"padding:${PAD[layout]}\"></div></div>`;\n\n    // ——— Sem resultado: EmptyState + mais vendidos\n    if (!total) {\n      const empty = R.es ? d2.render(R.es, {\n        query: q, suggestions: sugg, ctaHref: '/lista-de-produtos/q', title: q ? '' : 'Nenhum produto com esses filtros', description: notFound ? 'Nem o Lucas achou essa página na garagem. Confira o endereço ou busque por um produto.' : (q ? '' : 'Tira um filtro ou dois que a gente acha alguma coisa pra você.'),\n        layout, searchBar: R.sb, button: R.btn, iconComponent: R.icon\n      }, {\n        key: 'es-' + ver, style: 'height:auto',\n        onSearch: searchHere, onSuggestion: searchHere\n      }) : '';\n      const best = all.slice().sort(sorters['mais-vendidos']).slice(0, 4);\n      const bestHead = R.sh ? d2.render(R.sh, { eyebrow: 'Enquanto isso', title: 'Os mais vendidos da loja', layout }, { key: 'bh', style: 'height:auto' }) : '';\n      const bestHtml = best.length ? `<section class=\"best\"><div class=\"bin\" style=\"padding:${PAD[layout].replace(/^\\S+/, layout === 'mobile' ? '64px' : layout === 'tablet' ? '88px' : '96px')}\">${bestHead}<div class=\"grid\">${best.map((x, i) => card(x, i, 'b-')).join('')}</div></div></section>` : '';\n      return `<div class=\"cat l-${layout}\">${header}${empty}${bestHtml}</div>`;\n    }\n\n    // ——— Resultados\n    const panel = desk && R.fp ? `<aside class=\"side\" aria-label=\"Filtros\">${d2.render(R.fp, {\n      groups, version: ver + '-' + all.length, layout, filterGroup: R.fg, priceFilter: R.pf, checkbox: R.cb, radio: R.rd, formField: R.ff, iconComponent: R.icon\n    }, { key: 'fp-' + ver + '-' + all.length, style: 'height:auto', onChange: onGroup, onPrice, onRange, onClear: clearAll })}</aside>` : '';\n    const toolbar = R.tb ? d2.render(R.tb, {\n      chips, filterCount: chips.length, countLabel, sortOptions: SORTS, sortValue: sort, layout, filterChip: R.fc, formField: R.ff, iconComponent: R.icon\n    }, {\n      key: 'tb-' + ver + '-' + total, style: 'height:auto',\n      onRemove: removeChip, onClear: clearAll, onSort: (v) => set({ sort: v || 'relevancia' }),\n      onOpenFilters: () => { d2.state.sheet = true; d2.state.ver = (d2.state.ver || 0) + 1; }\n    }) : '';\n    const grid = `<div class=\"grid\">${results.slice(0, shown).map((x, i) => card(x, i, 'r-')).join('')}</div>`;\n    const more = R.lm ? d2.render(R.lm, { countLabel, done: shown >= total, layout, button: R.btn }, { key: 'lm-' + shown + '-' + total, style: 'height:auto', onClick: () => set({ pages: pages + 1 }) }) : '';\n    const closeSheet = () => { d2.state.sheet = false; d2.state.ver = (d2.state.ver || 0) + 1; };\n    const sheet = !desk && d2.state.sheet && R.fs ? d2.render(R.fs, {\n      open: true, groups, sortOptions: SORTS, sortValue: sort, version: ver, layout,\n      filterPanel: R.fp, filterGroup: R.fg, priceFilter: R.pf, checkbox: R.cb, radio: R.rd, formField: R.ff, button: R.btn, iconComponent: R.icon\n    }, {\n      key: 'fs-' + ver, style: 'height:0',\n      onClose: closeSheet, onApply: closeSheet, onClear: clearAll, onChange: onGroup, onPrice, onRange, onSort: (v) => set({ sort: v || 'relevancia' })\n    }) : '';\n\n    return `<div class=\"cat l-${layout}\">${header}\n      <div class=\"in\" style=\"padding:${PAD[layout]}\">${panel}<div class=\"main\">${toolbar}${grid}${more}</div></div>\n      ${sheet}\n    </div>`;\n  }\n};"}},"allDescription":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"allDescription"}}}},"allTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"allTitle"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"catalogToolbar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"catalogToolbar"}}}},"category":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"category"}}}},"categoryDescriptions":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"categoryDescriptions"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"filterChip":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterChip"}}}},"filterGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterGroup"}}}},"filterPanel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterPanel"}}}},"filterSheet":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"filterSheet"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"loadMore":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loadMore"}}}},"onAdd":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAdd"}}}},"onFiltersChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onFiltersChange"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onOpen":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onOpen"}}}},"pageHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pageHeader"}}}},"pageSize":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pageSize"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"priceFilter":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceFilter"}}}},"productCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productCard"}}}},"products":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"products"}}}},"query":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"query"}}}},"radio":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"radio"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"sectionHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sectionHeader"}}}},"sort":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sort"}}}}}}}}}},{"_id":"6abeb166ff933b0688424a3e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CartItem","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CartItem — Loja Lucas Fontana\n// Uma linha da sacola: foto (112/104/84px), categoria em vermelho, nome (link), opção/variação, a quantidade (QuantityStepper\n// pequeno), o total da linha e o botão de lixeira (44px).\n// Desktop: o preço unitário vai na linha da opção (\"· R$ 89,90 cada\"); quantidade e total em colunas próprias.\n// Tablet e mobile: quantidade embaixo do texto e o total à direita, com \"R$ 89,90 cada\" embaixo.\n// `lowStock` mostra o aviso em vermelho (\"Última unidade!\"); `max` limita a quantidade. `partner` adiciona a linha com caminhão\n// (\"Enviado pelo parceiro BPower Motorsport\").\n// `soldOut`: foto apagada com \"Volta já já\", sem quantidade, um traço cinza no lugar do total e o aviso com barra preta\n// \"Esgotou enquanto estava na sua sacola. Esse item não entra no total. **Me avise quando chegar**\". O item continua na lista.\n// Emite `onQuantityChange` (número), `onRemove` e `onNotify`. Usa QuantityStepper, Badge e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ci { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-50: #f5f5f3; --grey-100: #ececea; --grey-400: #8e9095; --grey-500: #6b6e73; --grey-700: #45474b; --grey-900: #1c1d1f; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; color: var(--ink);\n  display: grid; grid-template-columns: 112px minmax(0, 1fr) 110px 140px 44px; column-gap: 20px; grid-template-areas: \"img info qty total del\";\n  padding: 24px 0; border-bottom: 1px solid var(--line); align-items: start; }\np { margin: 0; }\n.img { grid-area: img; position: relative; width: 112px; aspect-ratio: 1 / 1; background: var(--grey-100); display: flex; align-items: center; justify-content: center; overflow: hidden; }\n.img img { width: 100%; height: 100%; object-fit: cover; }\n.ph { font-size: 9px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--grey-500); }\n.badge { position: absolute; top: 6px; left: 6px; height: 18px; }\n.so .img > img, .so .img > .ph { opacity: .55; }\n.info { grid-area: info; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }\n.cat { font-size: 10px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase; color: var(--red-500); }\n.name { font-size: 16px; font-weight: 700; line-height: 1.3; }\n.name a { color: inherit; text-decoration: none; }\n.name a:hover { color: var(--red-500); }\n.name a:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.meta { font-size: 13px; line-height: 1.4; color: var(--grey-700); }\n.low { font-size: 13px; font-weight: 700; color: var(--red-500); }\n.partner { margin-top: 2px; display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--grey-700); }\n.notice { margin-top: 8px; align-self: stretch; display: flex; flex-direction: column; gap: 4px; padding: 13px 16px 13px 18px; border-left: 4px solid var(--ink); background: var(--grey-50); font-size: 13px; line-height: 1.45; color: var(--grey-900); }\n.notice a { color: var(--ink); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }\n.notice a:hover { color: var(--red-500); }\n.so .info { grid-column: 2 / 4; }\n.qtyc { grid-area: qty; display: flex; justify-content: center; }\n.qtyc > * { width: 110px; height: 44px; }\n.total { grid-area: total; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-top: 4px; text-align: right; }\n.total b { font-size: 18px; font-weight: 800; font-stretch: 110%; line-height: 1.1; white-space: nowrap; }\n.unit { display: none; font-size: 12px; color: var(--grey-700); white-space: nowrap; }\n.dash { display: block; width: 28px; height: 3px; margin: 10px 0; background: var(--grey-400); }\n.del { grid-area: del; justify-self: end; width: 44px; height: 44px; margin: -4px 0 0; padding: 0; border: 0; background: transparent; color: var(--ink); display: flex; align-items: center; justify-content: center; cursor: pointer; }\n.del:hover { color: var(--red-500); }\n.del:focus-visible { outline: 2px solid var(--red-500); outline-offset: -2px; }\n\n.l-tablet, .l-mobile { grid-template-columns: 104px auto minmax(0, 1fr) 44px; column-gap: 14px; row-gap: 12px; padding: 20px 0;\n  grid-template-areas: \"img info info del\" \"img qty total total\"; }\n.l-tablet .img, .l-mobile .img { width: 104px; grid-row: 1 / 3; align-self: start; }\n.l-tablet .info, .l-mobile .info, .l-tablet.so .info, .l-mobile.so .info { gap: 4px; grid-column: 2 / 5; grid-row: 1; }\n.l-tablet .name, .l-mobile .name { font-size: 14px; }\n.l-tablet .del, .l-mobile .del { grid-column: 4; grid-row: 1; position: relative; z-index: 1; margin: -10px -10px 0 0; }\n.l-tablet .cat, .l-mobile .cat, .l-tablet .name, .l-mobile .name { padding-right: 36px; }\n.l-tablet .unit-inline, .l-mobile .unit-inline { display: none; }\n.l-tablet .unit, .l-mobile .unit { display: block; }\n.l-tablet .notice, .l-mobile .notice { margin-top: 6px; }\n.l-tablet .qtyc, .l-mobile .qtyc { justify-content: flex-start; }\n.l-tablet.so .qtyc, .l-mobile.so .qtyc { display: none; }\n.l-tablet.so .total, .l-mobile.so .total { grid-column: 2 / 5; }\n.l-tablet .total, .l-mobile .total { align-self: end; padding-top: 0; }\n.l-tablet .dash, .l-mobile .dash { margin: 4px 0 8px; width: 20px; }\n.l-mobile { grid-template-columns: 84px auto minmax(0, 1fr) 44px; }\n.l-mobile .img { width: 84px; }\n.l-mobile .badge { top: 4px; left: 4px; height: 16px; }\n.l-mobile .partner { font-size: 12px; align-items: flex-start; }\n.l-mobile .notice { padding: 12px 14px; }\n`,\n\n  mounted(d2) {\n    d2.on('.del', 'click', () => emit(d2, 'onRemove', (d2.params || {}).sku || true));\n    d2.on('.notice a', 'click', (e) => { if (!(d2.params || {}).notifyHref) e.preventDefault(); emit(d2, 'onNotify', (d2.params || {}).sku || true); });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { qs: refOf(d2.param('quantityStepper')), badge: refOf(d2.param('badge')), icon: refOf(d2.param('iconComponent')) };\n    const so = truthy(p.soldOut);\n    const low = p.lowStock === true || p.lowStock === 'true' ? 'Última unidade!' : (p.lowStock || '');\n    const name = esc(p.name || '[Nome do produto]');\n    const unit = p.unitPrice ? esc(p.unitPrice) : '';\n    const img = p.image ? `<img src=\"${esc(p.image)}\" alt=\"\">` : '<span class=\"ph\">Foto</span>';\n    const badge = so && R.badge ? `<span class=\"badge\">${d2.render(R.badge, { label: p.soldOutLabel || 'Volta já já', isStatus: true, tone: 'ink', layout: 'mobile' }, { key: 'b', width: 'auto', height: 18 })}</span>` : '';\n    const truck = R.icon ? d2.render(R.icon, { name: 'truck', size: 15, iconColor: '#45474b' }, { key: 'tr', width: 15, height: 15 }) : '';\n    const trash = R.icon ? d2.render(R.icon, { name: 'trash', size: 20, iconColor: 'currentColor' }, { key: 'del', width: 20, height: 20 }) : '×';\n    const qty = !so && R.qs ? `<div class=\"qtyc\">${d2.render(R.qs, { size: 'sm', value: Number(p.quantity) || 1, min: 1, max: Number(p.max) > 0 ? Number(p.max) : 99, ariaLabel: 'Quantidade de ' + (p.name || 'produto'), iconComponent: R.icon, layout }, {\n      key: 'q-' + (Number(p.quantity) || 1) + '-' + (p.max || ''), onChange: (v) => emit(d2, 'onQuantityChange', v)\n    })}</div>` : '<div class=\"qtyc\"></div>';\n    const meta = p.meta || unit ? `<p class=\"meta\">${esc(p.meta || '')}${unit ? `<span class=\"unit-inline\">${p.meta ? ' · ' : ''}${unit} cada</span>` : ''}</p>` : '';\n    const notice = so ? `<div class=\"notice\" role=\"status\"><b>${esc(p.soldOutTitle || 'Esgotou enquanto estava na sua sacola.')}</b><span>${esc(p.soldOutText || 'Esse item não entra no total.')} <a href=\"${esc(p.notifyHref || '#')}\">${esc(p.notifyLabel || 'Me avise quando chegar')}</a></span></div>` : '';\n    return `<div class=\"ci l-${layout}${so ? ' so' : ''}\">\n      <div class=\"img\">${img}${badge}</div>\n      <div class=\"info\">\n        ${p.category ? `<span class=\"cat\">${esc(p.category)}</span>` : ''}\n        <p class=\"name\">${p.href ? `<a href=\"${esc(p.href)}\">${name}</a>` : name}</p>\n        ${meta}\n        ${low && !so ? `<p class=\"low\">${esc(low)}</p>` : ''}\n        ${p.partner ? `<div class=\"partner\">${truck}<span>${esc(typeof p.partner === 'string' && p.partner !== 'true' ? p.partner : 'Enviado pelo parceiro')}</span></div>` : ''}\n        ${notice}\n      </div>\n      ${qty}\n      <div class=\"total\">${so ? '<span class=\"dash\" role=\"img\" aria-label=\"Fora do total\"></span>' : `<b>${esc(p.total || '')}</b>`}${unit ? `<span class=\"unit\">${unit} cada</span>` : ''}</div>\n      <button type=\"button\" class=\"del\" aria-label=\"Remover ${name} da sacola\">${trash}</button>\n    </div>`;\n  }\n};"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"category":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"category"}}}},"href":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"href"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"image":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"image"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"lowStock":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"lowStock"}}}},"max":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"max"}}}},"meta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"meta"}}}},"name":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"name"}}}},"notifyHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyHref"}}}},"notifyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notifyLabel"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onQuantityChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onQuantityChange"}}}},"onRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRemove"}}}},"partner":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"partner"}}}},"quantity":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantity"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}},"sku":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sku"}}}},"soldOut":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soldOut"}}}},"soldOutLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soldOutLabel"}}}},"soldOutText":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soldOutText"}}}},"soldOutTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"soldOutTitle"}}}},"total":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"total"}}}},"unitPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"unitPrice"}}}}}}}}}},{"_id":"6abeb197a554b79571c1b7b6","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CartList","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CartList — Loja Lucas Fontana\n// Os itens da sacola sob uma régua preta de 2px. No desktop, uma linha de cabeçalho (Produto · Quantidade · Total) alinhada\n// com a grade dos itens; abaixo de 1200px o cabeçalho some e cada item empilha.\n// Os itens chegam por `items` (parâmetros do CartItem, com `sku`). `headers: false` esconde o cabeçalho.\n// Emite `onQuantityChange` {sku, quantity}, `onRemove` (sku) e `onNotify` (sku). Usa CartItem (e repassa QuantityStepper, Badge e Icon).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [\n  { sku: 'demo-1', category: 'Lavagem', name: 'Shampoo automotivo neutro', unitPrice: 'R$ 89,90', total: 'R$ 179,80', quantity: 2 },\n  { sku: 'demo-2', category: 'Performance', name: '[Nome do produto BPower]', unitPrice: 'R$ 2.500,00', total: 'R$ 2.500,00', quantity: 1, partner: 'Enviado pelo parceiro BPower Motorsport' },\n  { sku: 'demo-3', category: 'Proteção', name: 'Cera líquida de acabamento', unitPrice: 'R$ 119,90', soldOut: true }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.cl { border-top: 2px solid #0b0b0c; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.cols { display: grid; grid-template-columns: 112px minmax(0, 1fr) 110px 140px 44px; column-gap: 20px; padding: 13px 0 12px; border-bottom: 1px solid #e4e4e2;\n  font-size: 10px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase; color: #0b0b0c; }\n.cols > span:first-child { grid-column: 1 / 3; }\n.cols > span:nth-child(2) { text-align: center; }\n.cols > span:nth-child(3) { text-align: right; }\nul { list-style: none; margin: 0; padding: 0; }\n.l-tablet .cols, .l-mobile .cols { display: none; }\n`,\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { ci: refOf(d2.param('cartItem')), qs: refOf(d2.param('quantityStepper')), badge: refOf(d2.param('badge')), icon: refOf(d2.param('iconComponent')) };\n    const items = list(p.items).length ? list(p.items) : (p.items === '' || p.showDemo === false ? [] : DEMO);\n    const ver = p.version == null ? '' : String(p.version);\n    const rows = items.map((it, i) => R.ci ? `<li>${d2.render(R.ci, Object.assign({}, it, { layout, quantityStepper: R.qs, badge: R.badge, iconComponent: R.icon }), {\n      key: 'i-' + (it.sku || i) + '-' + ver + '-' + (it.quantity || 0) + (it.soldOut ? '-so' : ''), style: 'height:auto',\n      onQuantityChange: (v) => emit(d2, 'onQuantityChange', { sku: it.sku, quantity: v }),\n      onRemove: () => emit(d2, 'onRemove', it.sku), onNotify: () => emit(d2, 'onNotify', it.sku)\n    })}</li>` : '').join('');\n    return `<div class=\"cl l-${layout}\">\n      ${p.headers === false || p.headers === 'false' ? '' : '<div class=\"cols\" aria-hidden=\"true\"><span>Produto</span><span>Quantidade</span><span>Total</span><span></span></div>'}\n      <ul aria-label=\"${esc(p.ariaLabel || 'Itens na sacola')}\">${rows}</ul>\n    </div>`;\n  }\n};"}},"ariaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ariaLabel"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"cartItem":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartItem"}}}},"headers":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"headers"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onQuantityChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onQuantityChange"}}}},"onRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRemove"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}},"showDemo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showDemo"}}}},"version":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"version"}}}}}}}}}},{"_id":"6abeb1ac1b1a6d54b50b3e04","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ShippingCalculator","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ShippingCalculator — Loja Lucas Fontana\n// A linha \"Frete\" do resumo da sacola: o valor à direita (\"Informe o CEP\" em cinza antes de calcular), o campo de CEP\n// (máscara 00000-000, 44px) e o botão de contorno \"Calcular\". Embaixo, a nota de 12px em cinza\n// (\"Itens BPower são enviados pelo parceiro, com frete e rastreio próprios.\") ou o erro em vermelho.\n// Emite `onCalculate` com o CEP (8 números). Enquanto o frete não estiver integrado (V2), a sacola mostra\n// \"Calculado no checkout\" como `value` depois de calcular.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst fmt = (v) => { const d = String(v || '').replace(/\\D/g, '').slice(0, 8); return d.length > 5 ? d.slice(0, 5) + '-' + d.slice(5) : d; };\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.sc { display: flex; flex-direction: column; gap: 10px; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.row { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; font-size: 14px; color: #45474b; }\n.row span.v { color: #0b0b0c; font-weight: 700; font-size: 13px; }\n.row span.muted { color: #6b6e73; font-weight: 400; font-size: 14px; }\nform { display: flex; gap: 12px; margin: 0; }\ninput { flex: 1; min-width: 0; height: 44px; padding: 0 16px; border: 1px solid #d9d9d6; border-radius: 2px; background: #ffffff; font: 400 16px ${FF}; color: #0b0b0c; outline: none; }\ninput::placeholder { color: #6b6e73; }\ninput:focus { border-color: #0b0b0c; box-shadow: 0 0 0 1px #0b0b0c; }\ninput.bad { border-color: #c8102e; }\nbutton { flex: none; height: 44px; margin: 0; padding: 0 22px; border: 2px solid #0b0b0c; border-radius: 0; background: transparent; font: 800 12px/1 ${FF}; letter-spacing: .12em; text-transform: uppercase; color: #0b0b0c; cursor: pointer; }\nbutton:hover { background: #0b0b0c; color: #ffffff; }\nbutton:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\np { margin: 0; font-size: 12px; line-height: 1.5; color: #45474b; }\np.err { color: #c8102e; font-weight: 600; }\n.l-mobile form { gap: 8px; }\n.l-mobile input { padding: 0 12px; }\n.l-mobile button { padding: 0 16px; }\n`,\n  mounted(d2) {\n    d2.on('input', 'input', (e, el) => { const v = fmt(el.value); if (v !== el.value) el.value = v; if (d2.root) d2.root.__cep = v; });\n    d2.on('form', 'submit', (e) => { e.preventDefault(); const v = String((d2.root && d2.root.__cep) || (d2.$('input') || {}).value || '').replace(/\\D/g, ''); emit(d2, 'onCalculate', v); });\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const val = p.value ? String(p.value) : '';\n    const zip = fmt((d2.root && d2.root.__cep) || p.defaultZip || '');\n    const note = p.note == null ? 'Itens BPower são enviados pelo parceiro, com frete e rastreio próprios.' : String(p.note);\n    return `<div class=\"sc l-${layout}\">\n      <div class=\"row\"><label for=\"cep\">${esc(p.label || 'Frete')}</label><span class=\"${val ? 'v' : 'muted'}\">${esc(val || p.emptyLabel || 'Informe o CEP')}</span></div>\n      <form novalidate><input id=\"cep\" class=\"${p.error ? 'bad' : ''}\" inputmode=\"numeric\" autocomplete=\"postal-code\" placeholder=\"${esc(p.placeholder || '00000-000')}\" value=\"${esc(zip)}\"${p.error ? ' aria-invalid=\"true\"' : ''}><button type=\"submit\">${esc(p.submitLabel || 'Calcular')}</button></form>\n      ${p.error ? `<p class=\"err\" role=\"alert\">${esc(p.error)}</p>` : ''}\n      ${note ? `<p>${esc(note)}</p>` : ''}\n    </div>`;\n  }\n};"}},"defaultZip":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"defaultZip"}}}},"emptyLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyLabel"}}}},"error":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"error"}}}},"label":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"label"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"note":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"note"}}}},"onCalculate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCalculate"}}}},"placeholder":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"placeholder"}}}},"submitLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"submitLabel"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6abeb1c88f48bcd64453d2b9","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CartSummary","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CartSummary — Auto Super Shop\n// O resumo cinza da sacola: \"RESUMO DA SACOLA\", subtotal com a quantidade de itens, o cupom de desconto, a ShippingCalculator, o total (28px, largo)\n// com \"R$ X no Pix · ou em até Nx de R$ Y no cartão\", o vermelho \"Finalizar compra\" (56px, com seta), \"Continuar comprando\"\n// e duas linhas de confiança (Mercado Pago; conta só na próxima etapa, a sacola fica salva).\n// \"Finalizar compra\" é o único botão vermelho da página. `checkoutDisabled` desativa (ex.: sacola só com itens esgotados).\n// `shipping` recebe os parâmetros da ShippingCalculator (ou `false` para esconder); `rows` [[rótulo, valor]] adiciona linhas.\n// `coupon` (opcional) liga o campo de cupom: { code, applied, label, discount, message, error, busy }.\n//   Sem cupom aplicado: campo + \"Aplicar\" (Enter também aplica), com a mensagem de erro embaixo quando o código não vale.\n//   Com cupom aplicado: selo verde com o código, a linha \"Cupom CÓDIGO −R$ X\" e \"Remover\". Sempre avisa que o desconto não inclui o frete.\n//   Sem o parâmetro `coupon`, o bloco do cupom não aparece.\n// Emite `onCheckout`, `onCalculate` (CEP), `onCouponApply` (texto digitado) e `onCouponRemove`. Usa ShippingCalculator, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst obj = (v) => { if (v && typeof v === 'object' && !Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } } return {}; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst TRUST = [{ icon: 'lock', text: 'Pagamento seguro pelo Mercado Pago: Pix ou cartão.' }, { icon: 'user', text: 'Você entra ou cria sua conta só na próxima etapa. A sacola fica salva.' }];\nconst itemsLabel = (n) => n + (Number(n) === 1 ? ' item' : ' itens');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\nsection { display: flex; flex-direction: column; padding: 32px 28px; background: #f5f5f3; font-family: ${FF}; -webkit-font-smoothing: antialiased; color: #0b0b0c; }\nh2 { margin: 0 0 14px; font-size: 12px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase; }\n.row { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; font-size: 14px; color: #45474b; }\n.row b { color: #0b0b0c; font-weight: 700; font-size: 13px; }\n.row.cd, .row.cd b { color: #0a7a3d; }\n.row.cd b { font-weight: 800; font-size: 14px; }\n.cp { margin: 14px 0 6px; padding: 14px 0 0; border-top: 1px solid #d9d9d6; }\n.cl { margin: 0 0 8px; font-size: 12px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }\n.cf { display: flex; gap: 8px; }\n.cin { flex: 1; min-width: 0; height: 44px; padding: 0 12px; border: 1.5px solid #d9d9d6; background: #ffffff; border-radius: 2px; font: inherit; font-size: 14px; letter-spacing: .06em; text-transform: uppercase; color: #0b0b0c; }\n.cin::placeholder { text-transform: none; letter-spacing: 0; color: #6b6e73; }\n.cin:focus { outline: 2px solid #c8102e; outline-offset: 1px; border-color: #0b0b0c; }\n.cin.bad { border-color: #c8102e; }\n.cb { flex: none; height: 44px; padding: 0 18px; border: 0; border-radius: 2px; background: #0b0b0c; color: #ffffff; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; cursor: pointer; }\n.cb:hover { background: #c8102e; }\n.cb:disabled { opacity: .55; cursor: not-allowed; background: #0b0b0c; }\n.cb:focus-visible, .cx:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.ce { margin: 8px 0 0; font-size: 13px; font-weight: 700; line-height: 1.4; color: #c8102e; }\n.cok { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: #e8f6ee; border: 1px solid #b7e0c7; border-radius: 2px; }\n.cok .mk { flex: none; width: 22px; height: 22px; border-radius: 50%; background: #0a7a3d; color: #ffffff; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; }\n.cok .tx { flex: 1; min-width: 0; font-size: 14px; line-height: 1.35; color: #0b0b0c; }\n.cok .tx b { font-family: ui-monospace, Menlo, Consolas, monospace; letter-spacing: .04em; }\n.cok .tx small { display: block; margin-top: 2px; font-size: 12px; color: #45474b; }\n.cx { flex: none; border: 0; background: none; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: #0b0b0c; border-bottom: 1.5px solid #0b0b0c; cursor: pointer; }\n.cx:hover { color: #c8102e; border-color: #c8102e; }\n.cn { margin: 8px 0 0; font-size: 12px; line-height: 1.45; color: #6b6e73; }\n.ship { margin-top: 8px; }\n.tot { margin: 22px 0; padding-top: 22px; border-top: 1px solid #d9d9d6; display: flex; flex-direction: column; gap: 10px; }\n.tr { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }\n.tr span { font-size: 16px; font-weight: 800; }\n.tr b { font-size: 28px; font-weight: 800; font-stretch: 110%; line-height: 1; white-space: nowrap; }\n.tot p { margin: 0; font-size: 13px; line-height: 1.5; color: #45474b; text-align: right; }\n.cta { height: 56px; }\n.cont { align-self: center; display: inline-flex; align-items: center; min-height: 44px; margin-top: 12px; font-size: 14px; font-weight: 700; color: #0b0b0c; text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1.5px; }\n.cont:hover { color: #c8102e; }\n.cont:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\nul { list-style: none; margin: 12px 0 0; padding: 20px 0 0; border-top: 1px solid #d9d9d6; display: flex; flex-direction: column; gap: 12px; }\nli { display: flex; gap: 12px; align-items: flex-start; font-size: 13px; line-height: 1.5; color: #45474b; }\nli .i { flex: none; display: flex; }\n.l-mobile { padding: 28px 20px; }\n.l-mobile .cta { height: 52px; }\n`,\n  mounted(d2) {\n    d2.on('.cont', 'click', (e, el) => emit(d2, 'onContinue', el.getAttribute('href')));\n    // O clique é delegado (botão nativo): o callback do Button passado por d2.render não dispara no app publicado.\n    const apply = () => { const el = d2.$('.cin'); emit(d2, 'onCouponApply', el ? String(el.value || '').trim() : ''); };\n    d2.on('[data-cp=\"apply\"]', 'click', () => apply());\n    d2.on('[data-cp=\"remove\"]', 'click', () => emit(d2, 'onCouponRemove', true));\n    d2.on('.cin', 'keydown', (e) => { if (e && e.key === 'Enter') { e.preventDefault(); apply(); } });\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { sc: refOf(d2.param('shippingCalculator')), btn: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')) };\n    const trust = p.trust === false || p.trust === 'false' ? [] : (list(p.trust).length ? list(p.trust) : TRUST);\n    const rows = list(p.rows);\n    const count = p.count == null || p.count === '' ? null : Number(p.count);\n    const ship = p.shipping === false || p.shipping === 'false' ? null : obj(p.shipping);\n    const ver = p.version == null ? '' : String(p.version);\n    const shipHtml = ship && R.sc ? `<div class=\"ship\">${d2.render(R.sc, Object.assign({}, ship, { layout }), { key: 'sc-' + (ship.value || '') + (ship.error || '') + ver, style: 'height:auto', onCalculate: (v) => emit(d2, 'onCalculate', v) })}</div>` : '';\n    const cta = R.btn ? `<div class=\"cta\">${d2.render(R.btn, { label: p.checkoutLabel || 'Finalizar compra', variant: 'brand', size: 'lg', fullWidth: 'always', arrow: true, href: p.checkoutHref || '', isDisabled: truthy(p.checkoutDisabled), layout }, {\n      key: 'cta-' + (truthy(p.checkoutDisabled) ? 'd' : 'e'), height: layout === 'mobile' ? 52 : 56, onClick: () => emit(d2, 'onCheckout', true)\n    })}</div>` : '';\n    const pix = p.pixTotal ? esc(p.pixTotal) + ' no Pix' : '';\n    const inst = p.installments ? 'ou em até ' + esc(p.installments) + ' no cartão' : '';\n\n    // Cupom de desconto (só aparece quando a página passa o parâmetro `coupon`)\n    const cu = p.coupon && typeof p.coupon === 'object' ? p.coupon : null;\n    let couponHtml = '';\n    let couponRow = '';\n    if (cu) {\n      const applied = truthy(cu.applied);\n      const busy = truthy(cu.busy);\n      const body = applied\n        ? `<div class=\"cok\"><span class=\"mk\" aria-hidden=\"true\">✓</span><span class=\"tx\"><b>${esc(cu.label || cu.code || '')}</b> aplicado${cu.message ? `<small>${esc(cu.message)}</small>` : ''}</span><button type=\"button\" class=\"cx\" data-cp=\"remove\">Remover</button></div>`\n        : `<div class=\"cf\"><input class=\"cin${cu.error ? ' bad' : ''}\" type=\"text\" value=\"${esc(cu.code || '')}\" placeholder=\"Digite o código\" aria-label=\"Código do cupom\" autocomplete=\"off\" autocapitalize=\"characters\" spellcheck=\"false\"${cu.error ? ' aria-invalid=\"true\"' : ''}><button type=\"button\" class=\"cb\" data-cp=\"apply\"${busy ? ' disabled' : ''}>${busy ? 'Aplicando…' : 'Aplicar'}</button></div>${cu.error ? `<p class=\"ce\" role=\"alert\">${esc(cu.error)}</p>` : ''}`;\n      couponHtml = `<div class=\"cp\"><p class=\"cl\">Cupom de desconto</p>${body}<p class=\"cn\">O desconto vale sobre os produtos e não inclui o frete.</p></div>`;\n      couponRow = applied && cu.discount ? `<div class=\"row cd\"><span>Cupom ${esc(cu.label || cu.code || '')}</span><b>−${esc(cu.discount)}</b></div>` : '';\n    }\n\n    return `<section class=\"l-${layout}\" aria-label=\"${esc(p.title || 'Resumo da sacola')}\">\n      <h2>${esc(p.title || 'Resumo da sacola')}</h2>\n      <div class=\"row\"><span>Subtotal${count != null ? ' (' + itemsLabel(count) + ')' : ''}</span><b>${esc(p.subtotal || 'R$ 0,00')}</b></div>\n      ${couponRow}\n      ${rows.map((r) => `<div class=\"row\"><span>${esc(r[0])}</span><span>${esc(r[1])}</span></div>`).join('')}\n      ${couponHtml}\n      ${shipHtml}\n      <div class=\"tot\"><div class=\"tr\"><span>Total</span><b>${esc(p.total || 'R$ 0,00')}</b></div>${pix || inst ? `<p>${pix}${pix && inst ? ' · ' : ''}${inst}</p>` : ''}</div>\n      ${cta}\n      ${p.continueHref === false || p.continueHref === 'false' ? '' : `<a class=\"cont\" href=\"${esc(p.continueHref || '/produtos')}\">${esc(p.continueLabel || 'Continuar comprando')}</a>`}\n      ${trust.length ? `<ul>${trust.map((t, i) => `<li><span class=\"i\">${R.icon ? d2.render(R.icon, { name: t.icon || 'check', size: 18, iconColor: '#0b0b0c' }, { key: 't' + i, width: 18, height: 18 }) : ''}</span><span>${esc(t.text)}</span></li>`).join('')}</ul>` : ''}\n    </section>`;\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkoutDisabled":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutDisabled"}}}},"checkoutHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutHref"}}}},"checkoutLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutLabel"}}}},"continueHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"continueHref"}}}},"continueLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"continueLabel"}}}},"count":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"count"}}}},"coupon":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"coupon"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"installments":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"installments"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onCalculate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCalculate"}}}},"onCheckout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCheckout"}}}},"onContinue":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onContinue"}}}},"onCouponApply":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCouponApply"}}}},"onCouponRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCouponRemove"}}}},"pixTotal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixTotal"}}}},"rows":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"rows"}}}},"shipping":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shipping"}}}},"shippingCalculator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingCalculator"}}}},"subtotal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subtotal"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"total":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"total"}}}},"trust":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"trust"}}}},"version":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"version"}}}}}}}}}},{"_id":"6ac3c2582677f59ea4da65a1","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CartLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CartLayout — Loja Lucas Fontana\n// O corpo da página da SACOLA: breadcrumb, o título \"SUA SACOLA\" (44/36/30px) com a contagem de itens em cinza ao lado,\n// a lista (CartList) e, no desktop, o resumo (CartSummary) numa coluna fixa de 400px presa na rolagem. Abaixo de 1200px\n// o resumo desce para baixo da lista, na largura toda. Depois, a faixa cinza \"+ Aproveita e leva / Leva junto que combina\"\n// com 4 ProductCards com contorno. Sacola vazia: o EmptyState (\"Sua sacola está vazia\") no lugar da lista e do resumo.\n// Onde a sacola fica: no navegador da pessoa (localStorage `lf-sacola`, uma lista de {sku, qty}), sem precisar de login.\n// Qualquer página adiciona itens gravando nessa mesma chave (e avisando com o evento `lf-sacola` no window).\n// Os dados de cada item (nome, foto, preço, parcelas, desconto Pix, parceiro) vêm SEMPRE do serviço público `productsService`\n// (produtosLoja): a tabela Produtos é fechada para visitante, então o app publicado não consegue ler a tabela direto.\n// Esse serviço só diz se o produto tem estoque (1) ou não (0), sem expor a quantidade: a sacola deixa escolher até 99 por item e\n// quem confere o estoque de verdade é o servidor ao criar o pedido. Item esgotado fica na lista, fora do total.\n// No builder (onde a tabela é legível) o `dataset` continua valendo como reserva e aí o estoque real limita a quantidade.\n// Frete (V2): \"Calcular\" emite `onCalculate` com o CEP e a linha passa a \"Calculado no checkout\".\n// `demoItems` [{sku, qty}] preenche a sacola só quando ela está vazia — use apenas para revisar o layout; tire antes de publicar.\n// Usa Breadcrumb, CartList, CartItem, CartSummary, ShippingCalculator, QuantityStepper, EmptyState, SearchBar, SectionHeader,\n// ProductCard, Price, Badge, Button e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => { if (Array.isArray(v)) return v; if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } } return []; };\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst brl = (n) => 'R$ ' + Number(n || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\nconst PAD = { desktop: '32px 96px 104px', tablet: '28px 40px 72px', mobile: '20px 20px 48px' };\nconst REL_PAD = { desktop: '96px 96px', tablet: '72px 40px', mobile: '56px 20px' };\nconst CARD_H = { desktop: 500, tablet: 470, mobile: 384 };\nconst KEY = 'lf-sacola';\nconst readBag = (k) => { try { const v = JSON.parse(localStorage.getItem(k) || '[]'); return Array.isArray(v) ? v.filter((x) => x && x.sku) : []; } catch (e) { return []; } };\nconst writeBag = (k, bag) => {\n  try { localStorage.setItem(k, JSON.stringify(bag)); } catch (e) { /* sem storage */ }\n  try { window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: bag.reduce((s, x) => s + (Number(x.qty) || 0), 0) } })); } catch (e) { /* sem window */ }\n};\nconst itemsLabel = (n) => n + (Number(n) === 1 ? ' item' : ' itens');\n\n// ---- Cupom de desconto ----\n// O código fica no navegador (chave `lf-cupom`, a mesma que o checkout lê) e o desconto sempre vem do serviço cupomValidar,\n// que recalcula o subtotal no banco. A cada mudança na sacola o cupom é validado de novo (pedido mínimo, por exemplo).\nconst CUPOM_KEY = 'lf-cupom';\nconst LIVE_FALLBACK = { sig: '', itens: [] };\nconst liveOf = (d2) => { const r = d2.root; if (!r) return LIVE_FALLBACK; return r.__live || (r.__live = { sig: '', itens: [] }); };\nconst unwrapRes = (res) => (res && res.data !== undefined ? res.data : res);\nconst validateCoupon = async (d2, code, itens, byUser) => {\n  const st = d2.state;\n  st.cupomBusy = true;\n  try {\n    const out = unwrapRes(await d2.rush('cupomValidar').invoke({ codigo: code, itens }));\n    if (out && out.ok) {\n      st.cupom = out; st.cupomCode = String((out.cupom && out.cupom.codigo) || code).toUpperCase(); st.cupomErr = ''; st.cupomInput = ''; st.cupomFails = 0;\n      try { localStorage.setItem(CUPOM_KEY, st.cupomCode); } catch (e) { /* sem storage */ }\n    } else {\n      st.cupom = null; st.cupomCode = ''; st.cupomInput = code; st.cupomErr = (out && out.error) || 'Cupom inválido ou expirado.';\n      try { localStorage.removeItem(CUPOM_KEY); } catch (e) { /* sem storage */ }\n      if (byUser) { st.cupomFails = (st.cupomFails || 0) + 1; st.cupomLock = Date.now() + (st.cupomFails >= 5 ? 30000 : 1500); }\n    }\n  } catch (e) {\n    if (byUser) { st.cupomErr = 'Não foi possível validar o cupom agora. Tente de novo.'; st.cupomCode = ''; st.cupomInput = code; }\n  }\n  st.cupomBusy = false;\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.cart { width: 100%; background: #ffffff; color: #0b0b0c; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.in { display: flex; flex-direction: column; gap: 16px; }\n.crumb { height: 24px; }\n.head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; margin-bottom: 24px; }\nh1 { margin: 0; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.count { font-size: 15px; color: #6b6e73; }\n.grid { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 48px; align-items: start; }\n.aside { position: sticky; top: 24px; }\n.rel { background: #f5f5f3; }\n.rel .rin { display: flex; flex-direction: column; gap: 40px; }\n.rel .cards { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px; }\n.l-tablet .in { gap: 14px; } .l-tablet .head { margin-bottom: 20px; } .l-tablet h1 { font-size: 36px; }\n.l-tablet .grid, .l-mobile .grid { grid-template-columns: minmax(0, 1fr); gap: 24px; }\n.l-tablet .aside, .l-mobile .aside { position: static; }\n.l-tablet .rel .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }\n.l-mobile .in { gap: 12px; } .l-mobile .head { margin-bottom: 16px; gap: 4px 12px; }\n.l-mobile h1 { font-size: 30px; font-stretch: 112%; letter-spacing: 0; } .l-mobile .count { font-size: 14px; }\n.l-mobile .crumb { height: 30px; }\n.l-mobile .rel .rin { gap: 24px; }\n.l-mobile .rel .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }\n`,\n\n  mounted(d2) {\n    try { const dh = d2.param('dataset') && d2.data ? d2.data('dataset') : null; if (dh && typeof dh.loadAll === 'function') dh.loadAll(); } catch (e) { /* sem DataSet */ }\n    // Produtos pelo serviço público (a tabela é fechada para visitante)\n    try {\n      d2.rush(d2.param('productsService')).invoke({}).then((res) => {\n        const r = res && res.data !== undefined ? res.data : res;\n        d2.state.prodRows = Array.isArray(r) ? r : (r && Array.isArray(r.items) ? r.items : []);\n        d2.state.prodLoaded = true;\n      }).catch(() => { d2.state.prodRows = []; d2.state.prodLoaded = true; });\n    } catch (e) { d2.state.prodRows = []; d2.state.prodLoaded = true; }\n    const p = d2.params || {};\n    const k = p.storageKey || KEY;\n    let bag = readBag(k);\n    if (!bag.length && list(p.demoItems).length && d2.context && d2.context.isBuilder) { bag = list(p.demoItems).map((x) => ({ sku: x.sku, qty: Number(x.qty) || 1 })); writeBag(k, bag); }\n    d2.state.bag = bag;\n    try { d2.state.cep = localStorage.getItem('lf-cep') || ''; } catch (e) { /* sem storage */ }\n    try { d2.state.cupomCode = String(localStorage.getItem(CUPOM_KEY) || '').toUpperCase(); } catch (e) { /* sem storage */ }\n    // Outra aba ou página mexeu na sacola\n    const onStorage = (e) => { if (!e || e.key === k) { d2.state.ver = (d2.state.ver || 0) + 1; d2.state.bag = readBag(k); } };\n    const onBag = () => { const b = readBag(k); if (JSON.stringify(b) !== JSON.stringify(d2.state.bag || [])) { d2.state.ver = (d2.state.ver || 0) + 1; d2.state.bag = b; } };\n    window.addEventListener('storage', onStorage); window.addEventListener('lf-sacola', onBag);\n    d2.onUnmount(() => { window.removeEventListener('storage', onStorage); window.removeEventListener('lf-sacola', onBag); });\n  },\n\n  updated(d2) {\n    const st = d2.state; const live = liveOf(d2);\n    if (!st.cupomCode || st.cupomBusy || !live.itens.length) return;\n    const sig = st.cupomCode + '|' + live.sig;\n    if (st.cupomSig === sig) return;\n    st.cupomSig = sig;\n    validateCoupon(d2, st.cupomCode, live.itens, false);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const r = (n) => refOf(d2.param(n));\n    const R = { bc: r('breadcrumb'), cl: r('cartList'), ci: r('cartItem'), cs: r('cartSummary'), sc: r('shippingCalculator'), qs: r('quantityStepper'),\n      es: r('emptyState'), sb: r('searchBar'), sh: r('sectionHeader'), pc: r('productCard'), price: r('priceComponent'), badge: r('badge'), btn: r('button'), icon: r('iconComponent') };\n    const k = p.storageKey || KEY;\n    const bag = d2.state.bag || [];\n    const save = (next) => { d2.state.ver = (d2.state.ver || 0) + 1; d2.state.bag = next; writeBag(k, next); emit(d2, 'onChange', next); };\n\n    // ——— Dados atuais dos produtos\n    const dh = d2.param('dataset') && d2.data ? d2.data('dataset') : null;\n    const dsRows = dh && typeof dh.records === 'function' ? dh.records() : [];\n    const rows = d2.state.prodLoaded && (d2.state.prodRows || []).length ? d2.state.prodRows : dsRows;\n    const viaService = rows === d2.state.prodRows;\n    const loading = !d2.state.prodLoaded && !rows.length && bag.length;\n    const bySku = {}; rows.forEach((x) => { if (x && x.sku) bySku[x.sku] = x; });\n\n    const items = bag.map((b) => {\n      const x = bySku[b.sku];\n      if (!x) return null;\n      const stock0 = Number(x.estoque) || 0;\n      const stock = viaService && stock0 > 0 ? 99 : stock0; // o serviço público só diz se tem (1) ou não (0)\n      const soldOut = stock <= 0 || x.ativo === false || x.ativo === 'false';\n      const qty = soldOut ? Number(b.qty) || 1 : Math.max(1, Math.min(Number(b.qty) || 1, stock));\n      const unit = Number(x.preco) || 0;\n      return {\n        slug: x.slug, sku: x.sku, category: x.subcategoria || x.categoriaNome || '', name: x.nome, href: '/produto/' + (x.slug || ''), image: x.imagem || '',\n        unitPrice: brl(unit), total: brl(unit * qty), quantity: qty, max: stock || 1, soldOut,\n        lowStock: soldOut ? '' : (stock === 1 ? 'Última unidade!' : (stock <= 3 ? `Restam só ${stock} unidades` : '')),\n        partner: x.origem === 'parceiro' ? 'Enviado pelo parceiro ' + (x.marca || '') : '', _unit: unit, _parc: Number(x.parcelas) || 1\n      };\n    }).filter(Boolean);\n    const live = items.filter((i) => !i.soldOut);\n    const count = live.reduce((s, i) => s + i.quantity, 0);\n    const subtotal = live.reduce((s, i) => s + i._unit * i.quantity, 0);\n    const pixPct = Number(p.pixDiscount) >= 0 && p.pixDiscount !== '' && p.pixDiscount != null ? Number(p.pixDiscount) : 10;\n    const parc = live.reduce((m, i) => Math.max(m, i._parc), 1);\n    const ver = String(d2.state.ver || 0);\n    const st = d2.state; const lv = liveOf(d2);\n    lv.itens = live.map((i) => ({ slug: i.slug, quantidade: i.quantity })); lv.sig = JSON.stringify(lv.itens);\n    if (viaService && bag.length && items.length < bag.length && !lv.cleaning) {\n      lv.cleaning = true;\n      const keep = bag.filter((b2) => bySku[b2.sku]);\n      setTimeout(() => { save(keep); lv.cleaning = false; }, 0);\n    }\n    const cupAct = st.cupom && st.cupomCode ? st.cupom : null;\n    const cupDesc = cupAct ? Math.min(Number(cupAct.desconto) || 0, subtotal) : 0;\n    const totalPagar = Math.max(0, subtotal - cupDesc);\n\n    const crumbs = R.bc ? `<div class=\"crumb\">${d2.render(R.bc, { items: [{ label: 'Início', href: '/' }, { label: 'Sacola' }], layout, scroll: layout === 'mobile' }, { key: 'bc' })}</div>` : '';\n    const head = `<div class=\"head\"><h1>${esc(p.title || 'Sua sacola')}</h1>${items.length ? `<span class=\"count\">${itemsLabel(count)}</span>` : ''}</div>`;\n\n    // ——— Faixa \"Leva junto que combina\"\n    const card = (x, i) => R.pc ? `<div style=\"height:${CARD_H[layout]}px\">${d2.render(R.pc, {\n      name: x.nome, brand: x.subcategoria || x.categoriaNome || '', price: Number(x.preco) || 0, oldPrice: x.precoAntigo ? Number(x.precoAntigo) : undefined,\n      installments: Number(x.parcelas) || 1, href: '/produto/' + (x.slug || ''), sku: x.sku, image: x.imagem || '', outOfStock: !(Number(x.estoque) > 0),\n      partner: x.origem === 'parceiro' ? (String(x.marca || '').split(' ')[0] || 'Parceiro') : '', variant: 'outlined', layout: layout === 'desktop' ? 'desktop' : 'mobile',\n      priceComponent: R.price, button: R.btn, badge: R.badge, iconComponent: R.icon\n    }, {\n      key: 'rel-' + x.sku + '-' + layout,\n      onAdd: () => { const b = (d2.state.bag || []).slice(); const f = b.find((y) => y.sku === x.sku); if (f) f.qty = (Number(f.qty) || 0) + 1; else b.push({ sku: x.sku, qty: 1 }); save(b); emit(d2, 'onAdd', x.sku); },\n      onNotify: () => emit(d2, 'onNotify', x.sku)\n    })}</div>` : '';\n    const inBag = {}; bag.forEach((b) => { inBag[b.sku] = 1; });\n    const related = rows.filter((x) => x && x.ativo !== false && x.ativo !== 'false' && !inBag[x.sku] && Number(x.estoque) > 0)\n      .sort((a, b) => (b.vendas || 0) - (a.vendas || 0)).slice(0, 4);\n    const relHead = R.sh ? d2.render(R.sh, { number: '+', eyebrow: p.relatedEyebrow || 'Aproveita e leva', title: p.relatedTitle || 'Leva junto que combina', layout }, { key: 'rh', style: 'height:auto' }) : '';\n    const relHtml = related.length && p.showRelated !== false && p.showRelated !== 'false'\n      ? `<section class=\"rel\"><div class=\"rin\" style=\"padding:${REL_PAD[layout]}\">${relHead}<div class=\"cards\">${related.map(card).join('')}</div></div></section>` : '';\n\n    if (loading) return `<div class=\"cart l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}</div></div>`;\n\n    // ——— Sacola vazia\n    if (!items.length) {\n      const empty = R.es ? d2.render(R.es, {\n        iconName: 'bag', eyebrow: 'Sacola', title: 'Sua sacola está vazia', description: p.emptyDescription || 'Bora encher? Os produtos que aparecem no canal estão a um clique.',\n        suggestionsLabel: 'Procurando algo?', ctaLabel: 'Ver todos os produtos', ctaHref: p.continueHref || '/lista-de-produtos/q', layout, searchBar: R.sb, button: R.btn, iconComponent: R.icon\n      }, {\n        key: 'empty', style: 'height:auto',\n        onSearch: (q) => { emit(d2, 'onSearch', q); try { location.href = (p.searchHref || '/lista-de-produtos/q') + '?q=' + encodeURIComponent(q || ''); } catch (e) { /* sem navegação */ } },\n        onSuggestion: (q) => { emit(d2, 'onSearch', q); try { location.href = (p.searchHref || '/lista-de-produtos/q') + '?q=' + encodeURIComponent(q || ''); } catch (e) { /* sem navegação */ } }\n      }) : '';\n      return `<div class=\"cart l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout].replace(/\\S+$/, '0')}\">${crumbs}</div>${empty}${relHtml}</div>`;\n    }\n\n    // ——— Lista + resumo\n    const listHtml = R.cl ? d2.render(R.cl, { items: items.map((i) => { const o = Object.assign({}, i); delete o._unit; delete o._parc; return o; }), layout, version: ver, cartItem: R.ci, quantityStepper: R.qs, badge: R.badge, iconComponent: R.icon }, {\n      key: 'list-' + ver, style: 'height:auto',\n      onQuantityChange: (v) => { if (!v) return; const b = (d2.state.bag || []).map((y) => (y.sku === v.sku ? { sku: y.sku, qty: Number(v.quantity) || 1 } : y)); d2.state.ver = (d2.state.ver || 0) + 1; save(b); },\n      onRemove: (sku) => { const b = (d2.state.bag || []).filter((y) => y.sku !== sku); d2.state.ver = (d2.state.ver || 0) + 1; save(b); emit(d2, 'onRemove', sku); },\n      onNotify: (sku) => emit(d2, 'onNotify', sku)\n    }) : '';\n    const pixTotal = Math.max(0, subtotal * (1 - pixPct / 100) - cupDesc);\n    const summary = R.cs ? d2.render(R.cs, {\n      count, subtotal: brl(subtotal), total: brl(totalPagar), pixTotal: pixPct > 0 ? brl(pixTotal) : '',\n      installments: parc > 1 ? `${parc}x` : '',\n      coupon: cupAct\n        ? { applied: true, label: (cupAct.cupom && cupAct.cupom.codigo) || st.cupomCode, discount: brl(cupDesc), message: cupAct.cupom && cupAct.cupom.tipo === 'percentual' ? String(cupAct.cupom.valor).replace('.', ',') + '% de desconto nos produtos' : 'Desconto nos produtos' }\n        : { applied: false, code: st.cupomInput || '', error: st.cupomErr || '', busy: !!st.cupomBusy },\n      shipping: { value: d2.state.shipped ? (p.shippingPendingLabel || 'Calculado no checkout') : '', defaultZip: d2.state.cep || '' },\n      checkoutHref: p.checkoutHref || '/checkout', checkoutDisabled: count === 0, continueHref: p.continueHref || '/lista-de-produtos/q',\n      layout, version: ver, shippingCalculator: R.sc, button: R.btn, iconComponent: R.icon\n    }, {\n      key: 'sum-' + ver + '-' + (d2.state.shipped ? 's' : 'n') + '-' + [cupAct ? 'c' : 'x', st.cupomBusy ? 'b' : 'i', st.cupomErr || '', st.cupomInput || ''].join('~'), style: 'height:auto',\n      onCalculate: (cep) => { try { localStorage.setItem('lf-cep', cep || ''); } catch (e) { /* sem storage */ } d2.state.cep = cep; d2.state.shipped = true; emit(d2, 'onCalculate', cep); },\n      onCouponApply: (v) => {\n        const s = d2.state; if (s.cupomBusy) return;\n        const code = String(v || '').trim().toUpperCase().replace(/[^A-Z0-9_-]/g, '');\n        const l2 = liveOf(d2);\n        if (!code) { s.cupomErr = 'Digite o código do cupom.'; s.cupomInput = ''; return; }\n        if (s.cupomLock && Date.now() < s.cupomLock) { s.cupomErr = (s.cupomFails || 0) >= 5 ? 'Muitas tentativas. Aguarde alguns segundos e tente de novo.' : 'Aguarde um instante para tentar de novo.'; s.cupomInput = code; return; }\n        if (!l2.itens.length) { s.cupomErr = 'Sua sacola está vazia.'; s.cupomInput = code; return; }\n        s.cupomInput = code; s.cupomErr = ''; s.cupomSig = code + '|' + l2.sig;\n        validateCoupon(d2, code, l2.itens, true);\n      },\n      onCouponRemove: () => {\n        const s = d2.state; s.cupom = null; s.cupomCode = ''; s.cupomErr = ''; s.cupomInput = ''; s.cupomSig = '';\n        try { localStorage.removeItem(CUPOM_KEY); } catch (e) { /* sem storage */ }\n      },\n      onCheckout: () => emit(d2, 'onCheckout', { items: live.map((i) => ({ sku: i.sku, qty: i.quantity, unitPrice: i._unit })), subtotal, cep: d2.state.cep || '' })\n    }) : '';\n\n    return `<div class=\"cart l-${layout}\">\n      <div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}\n        <div class=\"grid\"><div class=\"main\">${listHtml}</div><aside class=\"aside\" aria-label=\"Resumo da sacola\">${summary}</aside></div>\n      </div>\n      ${relHtml}\n    </div>`;\n  }\n};\n"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cartItem":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartItem"}}}},"cartList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartList"}}}},"cartSummary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartSummary"}}}},"checkoutHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutHref"}}}},"continueHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"continueHref"}}}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"demoItems":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demoItems"}}}},"emptyDescription":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyDescription"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onAdd":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAdd"}}}},"onCalculate":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCalculate"}}}},"onChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onChange"}}}},"onCheckout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCheckout"}}}},"onNotify":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onNotify"}}}},"onRemove":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onRemove"}}}},"onSearch":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSearch"}}}},"pixDiscount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"pixDiscount"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"productCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productCard"}}}},"productsService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productsService"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}},"relatedEyebrow":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"relatedEyebrow"}}}},"relatedTitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"relatedTitle"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"searchHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchHref"}}}},"sectionHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sectionHeader"}}}},"shippingCalculator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingCalculator"}}}},"shippingPendingLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingPendingLabel"}}}},"showRelated":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"showRelated"}}}},"storageKey":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"storageKey"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6ac427c86bb5213aca5fdfea","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoCategorias","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Categorias (backoffice, só desktop)\n// Uma tela para listar e outra (a mesma) para criar/editar. A navegação lista <-> formulário é interna.\n// Reaproveita Logo, Button, Notice, EmptyState e BackLink do sistema; o resto é HTML simples com os mesmos tokens.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst slugify = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n  .toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');\nconst idOf = (v) => (v && typeof v === 'object' ? (v._id || '') : (v || ''));\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\n\nconst EMPTY_FORM = { nome: '', slug: '', ativa: true, slugTouched: false, legenda: '', imagem: '' };\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  const css = '.pager{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:20px;flex-wrap:wrap}.pg{min-width:40px;height:40px;padding:0 12px;border:1.5px solid #ececea;background:#fff;color:#0b0b0c;font:inherit;font-size:13px;font-weight:700;border-radius:2px;cursor:pointer}.pg.on{background:#0b0b0c;color:#fff;border-color:#0b0b0c}.pg[disabled]{opacity:.35;cursor:not-allowed}.pg:focus-visible{outline:2px solid #c8102e;outline-offset:2px}.dots{padding:0 4px;color:#6b6c70}';\n  return `<style>${css}</style><nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\n// Página atual da lista: volta para a 1 sempre que busca ou filtros mudam (`sig`)\nconst pageOf = (d2, sig, total) => {\n  const r = d2.root;\n  if (!r) return 1;\n  if (r.__pgSig !== sig) { r.__pgSig = sig; r.__pg = 1; }\n  return Math.min(Math.max(1, Number(r.__pg) || 1), Math.max(1, Math.ceil(total / PAGE)));\n};\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.notice.below { margin: 20px 0 0; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }\n.search { flex: 1; min-width: 260px; max-width: 420px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus, .inp:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.chips { display: flex; gap: 8px; }\n.chip { height: 36px; padding: 0 16px; border: 1.5px solid var(--ink); background: var(--white); color: var(--ink); border-radius: 999px; font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; cursor: pointer; }\n.chip[aria-pressed=\"true\"] { background: var(--ink); color: var(--white); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 20px; border-bottom: 1px solid var(--g100); }\ntd { padding: 16px 20px; border-bottom: 1px solid var(--g100); font-size: 15px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.nome { font-weight: 700; }\ntd.slug { color: var(--g500); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 13px; }\n.pill { display: inline-flex; align-items: center; gap: 8px; height: 26px; padding: 0 12px; border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }\n.pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }\n.pill.on { background: var(--g100); color: var(--ink); }\n.pill.off { background: #fbe9ec; color: var(--red); }\n.yes { font-weight: 700; } .no { color: var(--g500); }\n.acts { display: flex; gap: 16px; justify-content: flex-end; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); cursor: pointer; border-bottom: 1.5px solid var(--ink); }\n.lk:hover { color: var(--red); border-color: var(--red); }\n.lk.danger { color: var(--red); border-color: var(--red); }\n.count { margin: 12px 2px 0; color: var(--g500); font-size: 13px; }\n.form { max-width: 640px; padding: 32px; }\n.back { margin-bottom: 20px; }\n.field { margin-bottom: 22px; }\n.lbl { display: block; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 8px; }\n.inp { width: 100%; height: 48px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 15px; color: var(--ink); }\n.inp[readonly] { background: var(--g50); color: var(--g500); }\n.inp.bad { border-color: var(--red); }\n.hint { margin: 6px 0 0; font-size: 13px; color: var(--g500); }\n.err { margin: 6px 0 0; font-size: 13px; font-weight: 700; color: var(--red); }\n.checks { display: grid; gap: 14px; margin: 8px 0 24px; }\n.ck { display: flex; align-items: flex-start; gap: 12px; cursor: pointer; font-size: 15px; }\n.ck input { width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--ink); flex: none; }\n.ck small { display: block; color: var(--g500); font-size: 13px; margin-top: 2px; }\n.warn { margin-bottom: 24px; }\n.btns { display: flex; align-items: center; gap: 12px; padding-top: 8px; }\n.btns .sp { flex: 1; }\n.empty { padding: 24px; }\n.imgbox { display: flex; flex-direction: column; gap: 12px; }\n.pv { display: flex; gap: 14px; align-items: flex-end; }\n.pv figure { margin: 0; }\n.pv .fr { position: relative; overflow: hidden; background-size: cover; background-position: center; background-color: var(--g100); }\n.pv .fr::after { content: ''; position: absolute; inset: 0; background: linear-gradient(0deg, rgba(11,11,12,.92) 0%, rgba(11,11,12,.45) 55%, rgba(11,11,12,.25) 100%); }\n.pv figcaption { margin-top: 4px; font-size: 11px; color: var(--g500); letter-spacing: .06em; text-transform: uppercase; }\n.noimg { width: 160px; height: 120px; display: flex; align-items: center; justify-content: center; background: var(--g50); border: 1.5px dashed var(--g100); color: var(--g500); font-size: 13px; }\n.imgbtns { display: flex; gap: 20px; align-items: center; }\nlabel.lk { display: inline-block; }\n.ovl { position: fixed; inset: 0; background: rgba(11,11,12,.55); display: flex; align-items: flex-start; justify-content: center; padding-top: 200px; z-index: 10; }\n.dlg { width: 480px; max-width: calc(100% - 32px); background: var(--white); border-radius: 2px; padding: 32px; box-shadow: 0 24px 64px rgba(0,0,0,.35); }\n.dlg h2 { margin: 0 0 12px; font-size: 22px; font-weight: 800; }\n.dlg p { margin: 0 0 24px; color: var(--g800); font-size: 15px; line-height: 1.5; }\n.dlg .row { display: flex; gap: 12px; justify-content: flex-end; }\n`,\n\n  mounted(d2) {\n    d2.on('[data-pg]', 'click', (e, el) => { if (d2.root) d2.root.__pg = Number(el.getAttribute('data-pg')) || 1; d2.state.tick = Date.now(); });\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font'; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    const cat = d2.data('categorias');\n    const prod = d2.data('produtos');\n\n    const S = d2.state;\n    const records = () => S.items || [];\n    const countProducts = (id) => { const r = records().find((x) => String(x._id) === String(id)); return r ? Number(r.produtos) || 0 : 0; };\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const reload = async () => {\n      try {\n        const res = await d2.rush('gestaoCategoriasListar').invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.items = Array.isArray(out) ? out : [];\n      } catch (e) { say('error', 'Não foi possível carregar as categorias.'); }\n      S.loaded = true;\n    };\n\n    const openNew = () => { S.editingId = null; S.form = { ...EMPTY_FORM }; S.errors = {}; S.notice = null; S.view = 'form'; };\n    const openEdit = (id) => {\n      const r = records().find((x) => String(x._id) === String(id));\n      if (!r) return;\n      S.editingId = r._id;\n      S.form = { nome: r.nome || '', slug: r.slug || '', ativa: r.ativa !== false, legenda: r.legenda || '', imagem: r.imagem || '', slugTouched: true };\n      S.original = { ativa: r.ativa !== false };\n      S.errors = {}; S.notice = null; S.view = 'form';\n    };\n    const backToList = () => { S.view = 'lista'; S.errors = {}; };\n\n    const askConfirm = (kind, rec, fromForm) => {\n      S.confirm = { kind, id: rec._id, nome: rec.nome || S.form.nome, n: countProducts(rec._id), fromForm: !!fromForm };\n    };\n\n    const validate = () => {\n      const f = S.form || EMPTY_FORM; const errs = {};\n      if (!String(f.nome).trim()) errs.nome = 'Informe o nome da categoria.';\n      \n      const sl = slugify(f.slug);\n      if (!sl) errs.slug = 'Informe o slug da categoria.';\n      else if (records().some((r) => String(r._id) !== String(S.editingId) && String(r.slug).toLowerCase() === sl)) errs.slug = 'Já existe uma categoria com esse slug.';\n      S.errors = errs;\n      return Object.keys(errs).length === 0;\n    };\n\n    const persist = async () => {\n      const f = S.form; const payload = { nome: String(f.nome).trim(), slug: slugify(f.slug), ativa: !!f.ativa, legenda: String(f.legenda || '').trim(), imagem: String(f.imagem || '') };\n      S.busy = true;\n      try {\n        const res = await d2.rush('gestaoCategoriaSalvar').invoke({ id: S.editingId || undefined, ...payload });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (out && out.ok === false) { const err = new Error(out.error || 'erro'); err.userMessage = out.error; throw err; }\n      } finally { S.busy = false; }\n      return payload;\n    };\n\n    const save = async () => {\n      if (S.busy || S.imgBusy || !validate()) return;\n      const f = S.form;\n      if (S.editingId && S.original && S.original.ativa && !f.ativa) {\n        askConfirm('desativar', { _id: S.editingId, nome: f.nome }, true);\n        return;\n      }\n      try {\n        await persist(); await reload();\n        say('success', S.editingId ? 'Categoria atualizada.' : 'Categoria criada.');\n        backToList();\n      } catch (e) { say('error', (e && e.userMessage) || 'Não foi possível salvar a categoria. Tente de novo.'); }\n    };\n\n    const confirmGo = async () => {\n      if (!S.confirm || S.busy) return;\n      const c = { ...S.confirm, n: countProducts(S.confirm.id) };\n      S.busy = true;\n      try {\n        if (c.kind === 'desativar') {\n          if (c.fromForm) { S.busy = false; await persist(); S.busy = true; }\n          await d2.rush('gestaoCategoriaDesativar').invoke({ id: c.id });\n          say('success', 'Categoria desativada. ' + c.n + (c.n === 1 ? ' produto foi desativado.' : ' produtos foram desativados.'));\n        } else {\n          await d2.rush('gestaoCategoriaExcluir').invoke({ id: c.id });\n          say('success', 'Categoria excluída. ' + c.n + (c.n === 1 ? ' produto foi desativado.' : ' produtos foram desativados.'));\n        }\n        S.confirm = null; S.busy = false; await reload(); backToList();\n      } catch (e) { S.busy = false; S.confirm = null; say('error', (e && e.userMessage) || 'Não foi possível concluir a ação. Nada foi alterado.'); }\n    };\n\n    const toggleAtiva = async (id) => {\n      const r = records().find((x) => String(x._id) === String(id)); if (!r) return;\n      if (r.ativa !== false) { askConfirm('desativar', r, false); return; }\n      try { const res = await d2.rush('gestaoCategoriaSalvar').invoke({ id: r._id, nome: r.nome, slug: r.slug, ativa: true, legenda: r.legenda || '', imagem: r.imagem || '' }); const out = res && res.data !== undefined ? res.data : res; if (out && out.ok === false) { say('error', out.error || 'Não foi possível reativar a categoria.'); return; } await reload(); say('success', 'Categoria reativada. Os produtos continuam como estavam.'); }\n      catch (e) { say('error', 'Não foi possível reativar a categoria.'); }\n    };\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'new') openNew(); else if (act === 'save') save(); else if (act === 'back') backToList(); else if (act === 'confirm-go') confirmGo(); else if (act === 'edit') openEdit(id);\n      else if (act === 'toggle') toggleAtiva(id);\n      else if (act === 'delete') { const r = records().find((x) => String(x._id) === String(id)); if (r) askConfirm('excluir', r, false); }\n      else if (act === 'filter') S.filter = el.getAttribute('data-v');\n      else if (act === 'delete-form') askConfirm('excluir', { _id: S.editingId, nome: S.form.nome }, true);\n      else if (act === 'cancel-confirm') S.confirm = null;\n      else if (act === 'img-clear') { S.form = { ...(S.form || EMPTY_FORM), imagem: '' }; S.imgErr = ''; }\n    });\n    d2.on('[data-f]', 'input', (e, el) => {\n      const k = el.getAttribute('data-f'); const f = { ...(S.form || EMPTY_FORM) };\n      f[k] = el.value;\n      if (k === 'nome' && !S.editingId && !f.slugTouched) f.slug = slugify(el.value);\n      if (k === 'slug') f.slugTouched = true;\n      S.form = f;\n    });\n    d2.on('[data-c]', 'change', (e, el) => { S.form = { ...(S.form || EMPTY_FORM), [el.getAttribute('data-c')]: !!el.checked }; });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; });\n    d2.on('[data-up]', 'change', async (e, el) => {\n      const file = el.files && el.files[0]; el.value = '';\n      if (!file) return;\n      S.imgErr = '';\n      if (!/^image\\/(jpeg|png|webp)$/.test(file.type)) { S.imgErr = 'Use uma imagem JPG, PNG ou WebP.'; return; }\n      if (file.size > 5 * 1024 * 1024) { S.imgErr = 'A imagem passa de 5 MB. Escolha uma menor.'; return; }\n      S.imgBusy = true;\n      try {\n        const bmp = await createImageBitmap(file);\n        const w = bmp.width; const h = bmp.height;\n        if (Math.min(w, h) < 900) { S.imgErr = 'A imagem precisa ter pelo menos 900×900 px (esta tem ' + w + '×' + h + ').'; S.imgBusy = false; return; }\n        const side = Math.min(w, h);\n        const cv = document.createElement('canvas'); cv.width = 1200; cv.height = 1200;\n        const cx = cv.getContext('2d'); cx.fillStyle = '#ffffff'; cx.fillRect(0, 0, 1200, 1200);\n        cx.drawImage(bmp, (w - side) / 2, (h - side) / 2, side, side, 0, 0, 1200, 1200);\n        const blob = await new Promise((r) => cv.toBlob(r, 'image/jpeg', 0.86));\n        if (!blob) throw new Error('crop');\n        const b64 = await new Promise((r, j) => { const fr = new FileReader(); fr.onload = () => r(String(fr.result).split(',')[1]); fr.onerror = j; fr.readAsDataURL(blob); });\n        const res = await d2.rush('gestaoCategoriaImagem').invoke({ base64: b64, slug: (S.form && S.form.slug) || 'nova' });\n        const out = res && res.data !== undefined ? res.data : res;\n        const url = out && out.ok ? out.url : '';\n        if (!url) throw new Error('upload');\n        S.form = { ...(S.form || EMPTY_FORM), imagem: url };\n      } catch (err) { try { console.error('GESTAO_IMG', err && err.message, JSON.stringify((err && (err.response || err.data || err.code)) || null)); } catch (x) { /* log */ } S.imgErr = 'Não foi possível enviar a imagem. Tente de novo.'; }\n      S.imgBusy = false;\n    });\n\n    // callbacks que os Buttons reaproveitados disparam\n    S.__fn = { openNew, save, backToList, confirmGo, cancelConfirm: () => { S.confirm = null; }, toggle: toggleAtiva };\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = d2.params || {};\n    const cat = d2.data('categorias');\n    const prod = d2.data('produtos');\n    if (S.view === undefined) {\n      S.view = P.view === 'form' ? 'form' : 'lista';\n      S.filter = 'todas'; S.q = ''; S.form = { ...EMPTY_FORM }; S.errors = {}; S.editingId = null; S.confirm = null; S.notice = null; S.busy = false;\n    }\n    const lazy = (name) => () => { const f = d2.state.__fn; if (f && f[name]) return f[name](); };\n    const fn = { openNew: lazy('openNew'), save: lazy('save'), backToList: lazy('backToList'), confirmGo: lazy('confirmGo'), cancelConfirm: lazy('cancelConfirm') };\n    const btn = (label, variant, act, key, w) => `<span data-act=\"${act}\" style=\"display:inline-block\">${d2.render(P.button, (S.busy || S.imgBusy) && (act === 'save' || act === 'confirm-go') ? { label: act === 'save' ? (S.busy ? 'Salvando…' : 'Enviando imagem…') : 'Aguarde…', variant, size: 'sm', isDisabled: true } : { label, variant, size: 'sm' }, { key: (S.busy || S.imgBusy) && (act === 'save' || act === 'confirm-go') ? key + '-busy' : key, width: w, height: 44 })}</span>`;\n    const rows = (S.items || []).slice().sort((a, b) => String(a.nome).localeCompare(String(b.nome), 'pt-BR'));\n    const nProd = (id) => { const r = rows.find((x) => String(x._id) === String(id)); return r ? Number(r.produtos) || 0 : 0; };\n\n    // variante de design: mostra o overlay de confirmação sem precisar clicar\n    let confirm = S.confirm;\n    if (!confirm && P.demoConfirm && rows.length) {\n      const r = rows.find((x) => x.ativa !== false) || rows[0];\n      confirm = { kind: 'desativar', id: r._id, nome: r.nome, n: nProd(r._id), fromForm: false };\n    }\n\n    if (confirm) confirm = Object.assign({}, confirm, { n: nProd(confirm.id) });\n\n    const header = ''; // o menu fica no componente GestaoMenu (bloco no topo de cada tela)\n    const notice = S.notice ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true }, { key: 'notice', height: 64 })}</div>` : '';\n\n    void S.tick;\n    const noticeBelow = notice ? notice.replace('class=\"notice\"', 'class=\"notice below\"') : '';\n    let main = '';\n    if (S.view === 'form') {\n      const f = S.form || EMPTY_FORM; const e = S.errors || {}; const edit = !!S.editingId;\n      const willCascade = edit && S.original && S.original.ativa;\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${d2.render(P.backLink, { label: 'Voltar para categorias' }, { key: 'back', width: 260, height: 32 })}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Categorias</p><h1>${edit ? 'Editar categoria' : 'Nova categoria'}</h1>\n    <p class=\"sub\">${edit ? 'Altere os dados e salve.' : 'Cadastre uma categoria para organizar os produtos da loja.'}</p></div></div>\n  <div class=\"card form\">\n    <div class=\"field\"><label class=\"lbl\" for=\"f-nome\">Nome</label>\n      <input id=\"f-nome\" class=\"inp ${e.nome ? 'bad' : ''}\" data-f=\"nome\" value=\"${esc(f.nome)}\" placeholder=\"Ex.: Cuidado automotivo\" maxlength=\"80\" autocomplete=\"off\">\n      ${e.nome ? `<p class=\"err\">${esc(e.nome)}</p>` : ''}</div>\n    <div class=\"field\"><label class=\"lbl\" for=\"f-slug\">Slug</label>\n      <input id=\"f-slug\" class=\"inp ${e.slug ? 'bad' : ''}\" data-f=\"slug\" value=\"${esc(f.slug)}\" placeholder=\"nome-da-categoria\" maxlength=\"80\" autocomplete=\"off\" ${edit ? 'readonly' : ''}>\n      ${e.slug ? `<p class=\"err\">${esc(e.slug)}</p>` : `<p class=\"hint\">${edit ? 'O slug não muda depois de criado.' : 'Gerado a partir do nome. Pode editar antes de salvar.'}</p>`}</div>\n    \n    <div class=\"field\"><span class=\"lbl\">Imagem do card</span>\n      <div class=\"imgbox\">${f.imagem ? `<div class=\"pv\">\n        <figure><div class=\"fr\" style=\"width:90px;height:120px;background-image:url('${esc(f.imagem)}')\"></div><figcaption>Desktop</figcaption></figure>\n        <figure><div class=\"fr\" style=\"width:120px;height:100px;background-image:url('${esc(f.imagem)}')\"></div><figcaption>Tablet</figcaption></figure>\n        <figure><div class=\"fr\" style=\"width:80px;height:95px;background-image:url('${esc(f.imagem)}')\"></div><figcaption>Mobile</figcaption></figure></div>` : '<div class=\"noimg\">Sem imagem</div>'}\n        <div class=\"imgbtns\"><label class=\"lk\">${S.imgBusy ? 'Enviando...' : (f.imagem ? 'Trocar imagem' : 'Escolher imagem')}<input type=\"file\" data-up accept=\"image/jpeg,image/png,image/webp\" hidden></label>${f.imagem ? '<button class=\"lk danger\" data-act=\"img-clear\">Remover imagem</button>' : ''}</div></div>\n      ${S.imgErr ? `<p class=\"err\">${esc(S.imgErr)}</p>` : ''}\n      <p class=\"hint\">Imagem quadrada, de 1200×1200 px (mínimo 900×900), JPG, PNG ou WebP, até 5 MB. A tela recorta o centro e converte sozinha. O card corta as bordas conforme a tela (retrato no desktop, paisagem no tablet): deixe o assunto no centro. Evite detalhes no canto superior esquerdo (número) e na parte de baixo (nome e legenda), que ficam sobre um degradê escuro. Obrigatória para a categoria entrar na home.</p></div>\n    <div class=\"field\"><label class=\"lbl\" for=\"f-legenda\">Legenda do card da home</label>\n      <input id=\"f-legenda\" class=\"inp\" data-f=\"legenda\" value=\"${esc(f.legenda)}\" placeholder=\"Ex.: Shampoo, pretinho, cera e cia.\" maxlength=\"60\" autocomplete=\"off\">\n      <p class=\"hint\">Texto curto que aparece embaixo do nome no card da home.</p></div>\n    <div class=\"checks\">\n      <label class=\"ck\"><input type=\"checkbox\" data-c=\"ativa\" ${f.ativa ? 'checked' : ''}><span>Ativa<small>Categoria inativa some da loja e libera as vagas de destaque.</small></span></label>\n      \n    </div>\n    <div class=\"warn\">${d2.render(P.notice, { tone: 'info', body: 'Ao desativar ou excluir uma categoria, todos os produtos dela também são desativados.' + (willCascade ? ' Hoje são ' + nProd(S.editingId) + ' produto(s).' : ''), live: false }, { key: 'warn', height: 72 })}</div>\n    <div class=\"btns\">${btn(edit ? 'Salvar alterações' : 'Criar categoria', 'primary', 'save', 'save', 210)}${btn('Cancelar', 'outline', 'back', 'cancel', 130)}<span class=\"sp\"></span>${edit ? '<button class=\"lk danger\" data-act=\"delete-form\">Excluir categoria</button>' : ''}</div>\n    ${noticeBelow}\n  </div>\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const shown = rows.filter((r) => (S.filter === 'ativas' ? r.ativa !== false : S.filter === 'inativas' ? r.ativa === false : true)\n        && (!q || norm(r.nome).includes(q) || norm(r.slug).includes(q)));\n      const chip = (v, l) => `<button class=\"chip\" data-act=\"filter\" data-v=\"${v}\" aria-pressed=\"${S.filter === v}\">${l}</button>`;\n      const pgCur = pageOf(d2, [S.q, S.filter].join('|'), shown.length);\n      const pgRows = shown.slice((pgCur - 1) * PAGE, pgCur * PAGE);\n      const body = shown.length ? `\n<div class=\"card\"><table>\n  <thead><tr><th>Nome</th><th>Slug</th><th>Status</th><th>Home</th><th>Menu</th><th></th></tr></thead>\n  <tbody>${pgRows.map((r) => `\n    <tr data-key=\"${esc(r._id || 'pending-' + r.slug)}\">\n      <td class=\"nome\">${esc(r.nome)}</td><td class=\"slug\">${esc(r.slug)}</td>\n      <td><span class=\"pill ${r.ativa === false ? 'off' : 'on'}\">${r.ativa === false ? 'Inativa' : 'Ativa'}</span></td>\n      <td class=\"${Number(r.posicao_home) > 0 ? 'yes' : 'no'}\">${Number(r.posicao_home) > 0 ? 'Vaga ' + r.posicao_home : '—'}</td>\n      <td class=\"${Number(r.posicao_menu) > 0 ? 'yes' : 'no'}\">${Number(r.posicao_menu) > 0 ? 'Vaga ' + r.posicao_menu : '—'}</td>\n      <td><div class=\"acts\"><button class=\"lk\" data-act=\"edit\" data-id=\"${esc(r._id)}\">Editar</button>\n        <button class=\"lk\" data-act=\"toggle\" data-id=\"${esc(r._id)}\">${r.ativa === false ? 'Ativar' : 'Desativar'}</button>\n        <button class=\"lk danger\" data-act=\"delete\" data-id=\"${esc(r._id)}\">Excluir</button></div></td>\n    </tr>`).join('')}</tbody></table></div>\n${pagerHtml(pgCur, shown.length, PAGE)}\n<p class=\"count\">${shown.length} de ${rows.length} ${rows.length === 1 ? 'categoria' : 'categorias'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\">Carregando categorias…</p></div>' : `<div class=\"card empty\">${d2.render(P.emptyState, { eyebrow: 'Categorias', title: rows.length ? 'Nenhuma categoria encontrada' : 'Nenhuma categoria cadastrada', description: rows.length ? 'Tente outro nome ou mude o filtro.' : 'Crie a primeira categoria para organizar os produtos da loja.', iconName: 'search' }, { key: 'empty', height: 260 })}</div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Categorias</h1><p class=\"sub\">Organize as categorias da loja e escolha quais aparecem na home e no menu.</p></div>\n    ${btn('Nova categoria', 'primary', 'new', 'new', 200)}</div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por nome ou slug\" value=\"${esc(S.q)}\" aria-label=\"Buscar categoria\">\n    <div class=\"chips\">${chip('todas', 'Todas')}${chip('ativas', 'Ativas')}${chip('inativas', 'Inativas')}</div></div>\n  ${body}\n</div>`;\n    }\n\n    const overlay = confirm ? `\n<div class=\"ovl\"><div class=\"dlg\" role=\"alertdialog\" aria-modal=\"true\">\n  <h2>${confirm.kind === 'excluir' ? 'Excluir' : 'Desativar'} “${esc(confirm.nome)}”?</h2>\n  <p>${confirm.kind === 'excluir' ? 'A categoria será apagada e não dá para desfazer. ' : 'A categoria sai da loja (home, menu e filtros). '}${confirm.n} ${confirm.n === 1 ? 'produto dessa categoria também será desativado' : 'produtos dessa categoria também serão desativados'}.</p>\n  <div class=\"row\">${btn('Cancelar', 'outline', 'cancel-confirm', 'c-cancel', 130)}${btn(confirm.kind === 'excluir' ? 'Excluir categoria' : 'Desativar categoria', confirm.kind === 'excluir' ? 'brand' : 'primary', 'confirm-go', 'c-ok', 230)}</div>\n</div></div>` : '';\n\n    return `<div class=\"root\">${header}${main}${overlay}</div>`;\n  }\n};"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"categorias":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"categorias"}}}},"demoConfirm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demoConfirm"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"produtos":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"produtos"}}}},"view":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"view"}}}}}}}}}},{"_id":"6ac429e6b4eec9d29421e98e","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"HomeCategorias","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// HomeCategorias — grade de categorias da Home, lida da tabela Categorias\n// Mostra só categorias ativas com destaque na home (a busca vem do serviço `categoriasLoja`).\n// Reaproveita o CategoryCard pelo parâmetro `categoryCard`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst COLS = { desktop: 4, tablet: 2, mobile: 2 };\nconst GAP = { desktop: 20, tablet: 16, mobile: 12 };\nconst ROW_H = { desktop: 400, tablet: 280, mobile: 200 };\nconst CAPTIONS = {\n  'cuidado-automotivo': 'Shampoo, pretinho, cera e cia.',\n  'performance': 'Linha BPower Motorsport',\n  'vestuario': 'Camisetas e moletons do canal',\n  'acessorios': 'Bonés, chaveiros e adesivos'\n};\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nlet LOADED = null; let LOADING = null;\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.grid { display: grid; width: 100%; }\n`,\n  mounted(d2) {\n    if (!LOADED) {\n      if (!LOADING) {\n        LOADING = d2.rush('categoriasLoja').invoke({}).then((res) => {\n          const rows = (res && res.data !== undefined ? res.data : res) || [];\n          LOADED = rows.filter((c) => c && c.ativa !== false && Number(c.posicao_home) > 0).sort((a, b) => Number(a.posicao_home) - Number(b.posicao_home));\n        }).catch(() => { LOADING = null; });\n      }\n      const pr = LOADING;\n      if (pr) pr.then(() => { d2.state.tick = Date.now(); });\n    }\n  },\n  render(d2) {\n    void d2.state.tick;\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const card = refOf(d2.param('categoryCard'));\n    const rows = (LOADED || []).slice(0, 4);\n    if (!card || !rows.length) return '<div class=\"grid\"></div>';\n    const h = ROW_H[layout];\n    const cells = rows.map((c, i) => `<div>${d2.render(card, {\n      iconComponent: refOf(d2.param('iconComponent')) || 'babi-lf@Icon',\n      layout, number: i + 1, label: c.nome, caption: c.legenda || '',\n      href: '/lista-de-produtos/q?categoria=' + c.slug, image: c.imagem || '', tone: i % 2 === 0 ? 'graphite' : 'ink'\n    }, { key: 'cat-' + (c._id || c.slug), height: h })}</div>`).join('');\n    return `<div class=\"grid\" style=\"grid-template-columns:repeat(${COLS[layout]},1fr);gap:${GAP[layout]}px\">${cells}</div>`;\n  }\n};"}},"categoryCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"categoryCard"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}}}}}}}},{"_id":"6ac42eb0005d4042c2e33937","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoLogin","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Login (backoffice, só desktop)\n// Reaproveita o LoginForm, Logo, FormField, Button, Notice e Icon da loja; só liga o envio ao login da Gestão.\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n\nexport default {\n  css: `\n:host { display: block; }\n* { box-sizing: border-box; }\n.root { --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3;\n  min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.bar { height: 72px; background: var(--white); border-bottom: 1px solid var(--g100); display: flex; align-items: center; padding: 0 48px; gap: 20px; }\n.bar .tag { font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; background: var(--ink); color: var(--white); padding: 6px 10px; border-radius: 2px; }\n.stage { display: flex; justify-content: center; padding: 72px 24px; }\n.card { width: 520px; max-width: 100%; }\n`,\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font'; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n  },\n  render(d2) {\n    const S = d2.state;\n    const P = d2.params || {};\n    const onSubmit = async (v) => {\n      if (S.busy || !v) return;\n      S.busy = true; S.err = '';\n      try {\n        await d2.auth('auth').login({ credentials: { email: v.email, senha: v.password } });\n        S.ok = true;\n        try { d2.router('router').goTo('/categorias'); } catch (e) { /* segue para o reload */ }\n        if (typeof window !== 'undefined') setTimeout(() => window.location.reload(), 300);\n      } catch (e) {\n        S.err = 'E-mail ou senha incorretos.';\n      }\n      S.busy = false;\n    };\n    const header = `<div class=\"bar\">${d2.render(P.logo, { layout: 'compact', noLink: true }, { key: 'logo', width: 240, height: 52 })}<span class=\"tag\">Gestão</span></div>`;\n    const form = d2.render(P.loginForm, {\n      layout: 'desktop', eyebrow: 'Gestão', title: 'Entrar',\n      description: 'Acesso restrito à equipe de gestão da loja.',\n      autoAdvance: false, submitLabel: S.busy ? 'Entrando...' : 'Entrar',\n      passwordError: S.err || '',\n      forgotLabel: 'Esqueci minha senha', forgotHref: 'mailto:babi.vallim@gmail.com?subject=Gest%C3%A3o%20-%20redefinir%20senha',\n      note: 'A sessão fica ativa neste aparelho. Em computador compartilhado, saia ao terminar.',\n      formField: P.formField, button: P.button, notice: P.notice, iconComponent: P.iconComponent,\n      codeField: P.codeField, resendCode: P.resendCode\n    }, { key: 'form', height: 560, onSubmit });\n    return `<div class=\"root\">${header}<div class=\"stage\"><div class=\"card\">${form}</div></div></div>`;\n  }\n};"}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"codeField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"codeField"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"loginForm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loginForm"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"resendCode":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"resendCode"}}}},"router":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"router"}}}}}}}}}},{"_id":"6ac436822f4d9a7641b7a788","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoDestaques","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Destaques (backoffice, só desktop)\n// A home tem 4 vagas e o menu tem 6. Cada vaga recebe no máximo uma categoria, e cada categoria ocupa no máximo uma vaga por lugar.\n// Reaproveita Logo, Button e Notice da loja; o resto é HTML simples com os mesmos tokens.\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst HOME = 4;\nconst MENU = 6;\nconst PROD = 12;\n// Seção da tela: /destaques (categorias) ou /destaques/produtos (produtos em destaque)\nconst secaoOf = (d2) => {\n  try { if (String(location.pathname || '').indexOf('/destaques/produtos') === 0) return 'produtos'; } catch (e) { /* sem location */ }\n  return String((d2.params || {}).secao || '') === 'produtos' ? 'produtos' : 'categorias';\n};\nconst slotsOf = (items, key, n) => { const a = Array(n).fill(''); (items || []).forEach((c) => { const p = Number(c[key]); if (c.ativa !== false && p >= 1 && p <= n) a[p - 1] = String(c._id); }); return a; };\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70;\n  min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.notice.below { margin: 20px 0 0; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; align-items: start; }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; padding: 28px; }\n.card h2 { margin: 0 0 4px; font-size: 20px; font-weight: 800; }\n.card .cnt { margin: 0 0 20px; color: var(--g500); font-size: 14px; }\n.card .cnt.warn { color: var(--red); font-weight: 700; }\n.slot { display: grid; grid-template-columns: 72px 1fr; align-items: center; gap: 12px; margin-bottom: 12px; }\n.slot .n { font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--g500); }\n.sel { width: 100%; height: 46px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 12px; font: inherit; font-size: 15px; color: var(--ink); }\n.sel:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.wide { margin-top: 24px; }\n.slots2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; }\n.foot { display: flex; align-items: center; gap: 16px; margin-top: 24px; }\n.foot .hint { color: var(--g500); font-size: 13px; }\n`,\n  mounted(d2) {\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font'; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    const S = d2.state;\n    const load = async () => {\n      try {\n        const res = await d2.rush('gestaoCategoriasListar').invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.items = Array.isArray(out) ? out : [];\n        S.home = slotsOf(S.items, 'posicao_home', HOME);\n        S.menu = slotsOf(S.items, 'posicao_menu', MENU);\n        const pres = await d2.rush('gestaoProdutosListar').invoke({});\n        const pout = pres && pres.data !== undefined ? pres.data : pres;\n        S.prods = Array.isArray(pout && pout.produtos) ? pout.produtos : [];\n        S.pslots = Array(PROD).fill('');\n        S.prods.forEach((x) => { const p = Number(x.posicaoDestaque); if (x.ativo !== false && p >= 1 && p <= PROD) S.pslots[p - 1] = String(x._id); });\n        S.dirty = false;\n      } catch (e) { S.notice = { tone: 'error', body: 'Não foi possível carregar as categorias.' }; }\n      S.loaded = true;\n    };\n    const save = async () => {\n      if (S.busy) return;\n      S.busy = true; S.notice = null;\n      const sec = secaoOf(d2);\n      try {\n        if (sec === 'produtos') {\n          const pitems = (S.pslots || []).map((id, i) => (id ? { id: String(id), pos: i + 1 } : null)).filter(Boolean);\n          const res2 = await d2.rush('gestaoDestaquesProdutosSalvar').invoke({ items: pitems });\n          const out2 = res2 && res2.data !== undefined ? res2.data : res2;\n          if (out2 && out2.ok === false) { S.notice = { tone: 'error', body: out2.error || 'Não foi possível salvar os produtos em destaque.' }; }\n          else { await load(); S.notice = { tone: 'success', body: 'Produtos em destaque salvos.' }; }\n        } else {\n          const items = (S.items || []).filter((c) => c.ativa !== false).map((c) => ({\n            id: String(c._id),\n            home: (S.home || []).indexOf(String(c._id)) + 1,\n            menu: (S.menu || []).indexOf(String(c._id)) + 1\n          }));\n          const res = await d2.rush('gestaoDestaquesSalvar').invoke({ items });\n          const out = res && res.data !== undefined ? res.data : res;\n          if (out && out.ok === false) { S.notice = { tone: 'error', body: out.error || 'Não foi possível salvar as categorias em destaque.' }; }\n          else { await load(); S.notice = { tone: 'success', body: 'Categorias em destaque salvas.' }; }\n        }\n      } catch (e) { S.notice = { tone: 'error', body: 'Não foi possível salvar os destaques.' }; }\n      S.busy = false;\n    };\n    d2.on('[data-slot]', 'change', (e, el) => {\n      const [list, idx] = String(el.getAttribute('data-slot')).split(':');\n      const arr = (list === 'home' ? S.home : list === 'prod' ? S.pslots : S.menu).slice();\n      const v = el.value;\n      const k = Number(idx); const prev = arr[k]; const j = v ? arr.indexOf(v) : -1; if (j >= 0 && j !== k) arr[j] = prev; arr[k] = v;\n      if (list === 'home') S.home = arr; else if (list === 'prod') S.pslots = arr; else S.menu = arr;\n      S.dirty = true; S.notice = null;\n    });\n    d2.on('[data-act]', 'click', (e, el) => { if (el.getAttribute('data-act') === 'save-dest') save(); });\n    load();\n  },\n  render(d2) {\n    const S = d2.state;\n    const P = d2.params || {};\n    const actives = (S.items || []).filter((c) => c.ativa !== false).sort((a, b) => String(a.nome).localeCompare(String(b.nome), 'pt-BR'));\n    const header = ''; // o menu fica no componente GestaoMenu (bloco no topo de cada tela)\n    const notice = S.notice ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true }, { key: 'notice-' + S.notice.tone, height: 64 })}</div>` : '';\n    const block = (title, list, arr, n) => {\n      const filled = arr.filter(Boolean).length;\n      const rows = arr.map((cur, i) => `<div class=\"slot\"><span class=\"n\">Vaga ${i + 1}</span><select class=\"sel\" data-slot=\"${list}:${i}\" aria-label=\"${esc(title)} vaga ${i + 1}\">\n        <option value=\"\">— vazia —</option>${actives.map((c) => `<option value=\"${esc(c._id)}\"${String(c._id) === cur ? ' selected' : ''}${list === 'home' && !c.imagem && !(Number(c.posicao_home) > 0) ? ' disabled' : ''}>${esc(c.nome)}${list === 'home' && !c.imagem && !(Number(c.posicao_home) > 0) ? ' (sem imagem)' : ''}</option>`).join('')}</select></div>`).join('');\n      return `<div class=\"card\"><h2>${title}</h2><p class=\"cnt${filled < n ? ' warn' : ''}\">${title.split(' ')[0]} com ${filled} de ${n} vagas preenchidas${filled < n ? '' : ''}</p>${rows}</div>`;\n    };\n    const pact = (S.prods || []).filter((x) => x.ativo !== false).sort((a, b) => String(a.nome).localeCompare(String(b.nome), 'pt-BR'));\n    const prodBlock = () => {\n      const arr = S.pslots || []; const filled = arr.filter(Boolean).length;\n      const rows = arr.map((cur, i) => `<div class=\"slot\"><span class=\"n\">Vaga ${i + 1}</span><select class=\"sel\" data-slot=\"prod:${i}\" aria-label=\"Produtos em destaque vaga ${i + 1}\">\n        <option value=\"\">— vazia —</option>${pact.map((x) => `<option value=\"${esc(x._id)}\"${String(x._id) === cur ? ' selected' : ''}>${esc(x.nome)}${x.imagem ? '' : ' (sem foto)'}</option>`).join('')}</select></div>`).join('');\n      return `<div class=\"card wide\"><h2>Produtos em destaque (carrossel da Home)</h2><p class=\"cnt\">${filled} de ${PROD} vagas preenchidas. A ordem das vagas é a ordem do carrossel.</p><div class=\"slots2\">${rows}</div></div>`;\n    };\n    const sec = secaoOf(d2);\n    const cardsHtml = sec === 'produtos'\n      ? prodBlock()\n      : `<div class=\"cols\">${block('Home (cards de categorias)', 'home', S.home || [], HOME)}${block('Menu (topo da loja)', 'menu', S.menu || [], MENU)}</div>`;\n    const body = !S.loaded ? '<div class=\"card\"><p class=\"sub\">Carregando…</p></div>'\n      : `${cardsHtml}\n        <div class=\"foot\"><span data-act=\"save-dest\" style=\"display:inline-block\">${d2.render(P.button, { label: S.busy ? 'Salvando…' : (sec === 'produtos' ? 'Salvar produtos' : 'Salvar categorias'), variant: 'primary', size: 'sm', isDisabled: !!S.busy }, { key: 'save-' + sec + (S.busy ? '-busy' : ''), width: 230, height: 44 })}</span>\n        <span class=\"hint\">Escolher um item que já está em outra vaga troca as duas de lugar. Só itens ativos aparecem.</span></div>`;\n    return `<div class=\"root\">${header}<div class=\"page\"><div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>${sec === 'produtos' ? 'Produtos em destaque' : 'Categorias em destaque'}</h1><p class=\"sub\">${sec === 'produtos' ? 'Escolha os produtos do carrossel “Produtos em destaque” da home (até 12), na ordem das vagas.' : 'Escolha quais categorias aparecem na home (até 4) e no menu do topo (até 6), na ordem das vagas.'}</p></div></div>${body}${notice ? notice.replace('class=\"notice\"', 'class=\"notice below\"') : ''}</div></div>`;\n  }\n};"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}}}}}}}},{"_id":"6ac5286078f3730f24cd3aef","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoProdutos","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Produtos (backoffice, só desktop)\n// Lista + formulário (criar/editar) na mesma tela; navegação interna. Reaproveita Logo, Button, Notice, EmptyState e BackLink.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\nconst slugify = (s) => norm(s).replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');\nconst num = (v) => { if (v === '' || v == null) return undefined; return Number(String(v).trim().replace(',', '.')); };\nconst brl = (v) => (v == null || v === '' || isNaN(Number(v)) ? '—' : Number(v).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }));\nconst s2 = (v) => (v == null ? '' : String(v));\nconst MB = 1024 * 1024;\nconst FAQ_MAX = 15, FAQ_Q = 200, FAQ_A = 1000;\nconst faqRows = (l) => (Array.isArray(l) ? l : []).map((x) => ({ pergunta: String((x && x.pergunta) || '').trim(), resposta: String((x && x.resposta) || '').trim() }));\n\nconst EMPTY_FORM = { nome: '', slug: '', slugTouched: false, marcaTouched: false, sku: '', categoria: '', subcategoria: '', marca: 'Auto Super', origem: 'proprio', parceiro: '',\n  preco: '', precoAntigo: '', parcelas: '', desconto_pix: '', estoque: '', descricao: '', beneficios: '', ficha_tecnica: '', instrucoes_uso: '', social_video: '', ativo: true, destaque: false };\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  const css = '.pager{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:20px;flex-wrap:wrap}.pg{min-width:40px;height:40px;padding:0 12px;border:1.5px solid #ececea;background:#fff;color:#0b0b0c;font:inherit;font-size:13px;font-weight:700;border-radius:2px;cursor:pointer}.pg.on{background:#0b0b0c;color:#fff;border-color:#0b0b0c}.pg[disabled]{opacity:.35;cursor:not-allowed}.pg:focus-visible{outline:2px solid #c8102e;outline-offset:2px}.dots{padding:0 4px;color:#6b6c70}';\n  return `<style>${css}</style><nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\n// Página atual da lista: volta para a 1 sempre que busca ou filtros mudam (`sig`)\nconst pageOf = (d2, sig, total) => {\n  const r = d2.root;\n  if (!r) return 1;\n  if (r.__pgSig !== sig) { r.__pgSig = sig; r.__pg = 1; }\n  return Math.min(Math.max(1, Number(r.__pg) || 1), Math.max(1, Math.ceil(total / PAGE)));\n};\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.notice.below { margin: 20px 0 0; }\n.faqi { border: 1.5px solid var(--g100); background: var(--g50); padding: 16px; margin-bottom: 12px; }\n.faqh { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }\n.faqn { font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--g500); }\n.faqa { display: flex; gap: 16px; align-items: center; }\n.lk[disabled] { opacity: .35; cursor: not-allowed; }\n.bar a.nav.on { border-bottom-color: var(--red); }\n.page { max-width: 1240px; margin: 0 auto; padding: 40px 48px 64px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\nh3 { margin: 0 0 16px; font-size: 14px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }\n.search { flex: 1; min-width: 240px; max-width: 360px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus, .inp:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.chips { display: flex; gap: 8px; }\n.chip { height: 36px; padding: 0 16px; border: 1.5px solid var(--ink); background: var(--white); color: var(--ink); border-radius: 999px; font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; cursor: pointer; }\n.chip[aria-pressed=\"true\"] { background: var(--ink); color: var(--white); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 16px; border-bottom: 1px solid var(--g100); }\ntd { padding: 14px 16px; border-bottom: 1px solid var(--g100); font-size: 14px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.nome { font-weight: 700; } td.nome small { display: block; margin-top: 3px; font-weight: 400; color: var(--g500); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; }\ntd.num { white-space: nowrap; }\n.pill { display: inline-flex; align-items: center; gap: 8px; height: 26px; padding: 0 12px; border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }\n.pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }\n.pill.on { background: var(--g100); color: var(--ink); } .pill.off { background: #fbe9ec; color: var(--red); }\n.yes { font-weight: 700; } .no { color: var(--g500); font-size: 12px; }\n.acts { display: flex; gap: 14px; justify-content: flex-end; flex-wrap: wrap; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); cursor: pointer; border-bottom: 1.5px solid var(--ink); }\n.lk:hover { color: var(--red); border-color: var(--red); } .lk.danger { color: var(--red); border-color: var(--red); }\nlabel.lk { display: inline-block; }\n.count { margin: 12px 2px 0; color: var(--g500); font-size: 13px; }\n.form { padding: 32px; margin-bottom: 20px; }\n.back { margin-bottom: 20px; }\n.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; } .grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 24px; } .grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0 24px; }\n.field { margin-bottom: 22px; }\n.lbl { display: block; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 8px; }\n.inp { width: 100%; height: 48px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 15px; color: var(--ink); }\ntextarea.inp { height: auto; min-height: 110px; padding: 12px 14px; line-height: 1.45; resize: vertical; }\n.inp[readonly] { background: var(--g50); color: var(--g500); } .inp.bad { border-color: var(--red); }\n.hint { margin: 6px 0 0; font-size: 13px; color: var(--g500); } .err { margin: 6px 0 0; font-size: 13px; font-weight: 700; color: var(--red); }\n.checks { display: grid; gap: 14px; margin: 4px 0 8px; } .radios { display: flex; gap: 28px; padding: 12px 0; }\n.ck { display: flex; align-items: flex-start; gap: 12px; cursor: pointer; font-size: 15px; }\n.ck input { width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--ink); flex: none; }\n.ck small { display: block; color: var(--g500); font-size: 13px; margin-top: 2px; }\n.warn { margin: 16px 0 24px; }\n.btns { display: flex; align-items: center; gap: 12px; padding-top: 8px; } .btns .sp { flex: 1; }\n.empty { padding: 24px; }\n.gal { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }\n.slot { border: 1.5px dashed var(--g100); background: var(--g50); padding: 10px; display: flex; flex-direction: column; gap: 10px; min-height: 190px; }\n.slot.full { border-style: solid; background: var(--white); }\n.slot .pos { font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); }\n.slot .med { width: 100%; aspect-ratio: 1 / 1; background: var(--g100) center / cover no-repeat; display: flex; align-items: center; justify-content: center; color: var(--g500); font-size: 12px; overflow: hidden; }\n.slot .med video { width: 100%; height: 100%; object-fit: cover; }\n.rels { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }\n.rchip { display: inline-flex; align-items: center; gap: 10px; background: var(--g100); border-radius: 999px; padding: 6px 8px 6px 14px; font-size: 13px; font-weight: 700; }\n.rchip button { border: 0; background: var(--ink); color: var(--white); width: 20px; height: 20px; border-radius: 50%; cursor: pointer; font-size: 12px; line-height: 1; }\n.cands { border: 1px solid var(--g100); margin-top: 8px; } .cand { display: flex; justify-content: space-between; align-items: center; padding: 10px 14px; border-bottom: 1px solid var(--g100); font-size: 14px; } .cand:last-child { border-bottom: 0; }\n.ovl { position: fixed; inset: 0; background: rgba(11,11,12,.55); display: flex; align-items: flex-start; justify-content: center; padding-top: 200px; z-index: 10; }\n.dlg { width: 480px; max-width: calc(100% - 32px); background: var(--white); border-radius: 2px; padding: 32px; box-shadow: 0 24px 64px rgba(0,0,0,.35); }\n.dlg h2 { margin: 0 0 12px; font-size: 22px; font-weight: 800; }\n.dlg p { margin: 0 0 24px; color: var(--g800); font-size: 15px; line-height: 1.5; }\n.dlg .row { display: flex; gap: 12px; justify-content: flex-end; }\n`,\n\n  mounted(d2) {\n    d2.on('[data-pg]', 'click', (e, el) => { if (d2.root) d2.root.__pg = Number(el.getAttribute('data-pg')) || 1; d2.state.tick = Date.now(); });\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font'; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    const S = d2.state;\n    const unwrap = (res) => (res && res.data !== undefined ? res.data : res);\n    const call = async (name, body) => unwrap(await d2.rush(name).invoke(body || {}));\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const items = () => S.items || [];\n\n    const reload = async () => {\n      try {\n        const out = await call('gestaoProdutosListar');\n        S.items = Array.isArray(out && out.produtos) ? out.produtos : [];\n        S.cats = Array.isArray(out && out.categorias) ? out.categorias : [];\n        S.parceiros = Array.isArray(out && out.parceiros) ? out.parceiros : [];\n      } catch (e) { say('error', 'Não foi possível carregar os produtos.'); }\n      S.loaded = true;\n    };\n\n    const fillFromServer = (out) => {\n      const p = out.produto;\n      S.editingId = p._id;\n      S.form = { nome: s2(p.nome), slug: s2(p.slug), slugTouched: true, marcaTouched: true, sku: s2(p.sku), categoria: s2(p.categoria), subcategoria: s2(p.subcategoria),\n        marca: s2(p.marca), origem: p.origem === 'parceiro' ? 'parceiro' : 'proprio', parceiro: s2(p.parceiro), preco: s2(p.preco), precoAntigo: s2(p.precoAntigo),\n        parcelas: s2(p.parcelas), desconto_pix: s2(p.desconto_pix), estoque: s2(p.estoque), descricao: s2(p.descricao), beneficios: s2(p.beneficios), ficha_tecnica: s2(p.ficha_tecnica),\n        instrucoes_uso: s2(p.instrucoes_uso), social_video: s2(p.social_video), ativo: p.ativo !== false, destaque: p.destaque === true };\n      S.galeria = out.galeria || [];\n      S.rels = out.relacionados || [];\n      S.faq = Array.isArray(p.faq) ? p.faq.map((x) => ({ pergunta: s2(x && x.pergunta), resposta: s2(x && x.resposta) })) : [];\n      S.original = { ativo: p.ativo !== false, vendas: Number(p.vendas) || 0 };\n      S.errors = {}; S.notice = null; S.upErr = ''; S.relQ = '';\n    };\n\n    const openNew = () => { S.editingId = null; S.form = { ...EMPTY_FORM }; S.galeria = []; S.rels = []; S.faq = []; S.original = null; S.errors = {}; S.notice = null; S.upErr = ''; S.relQ = ''; S.view = 'form'; };\n    const openEdit = async (id) => {\n      if (S.busy) return;\n      S.busy = true;\n      try {\n        const out = await call('gestaoProdutoObter', { id });\n        if (!out || out.ok === false) { say('error', (out && out.error) || 'Não foi possível abrir o produto.'); }\n        else { fillFromServer(out); S.view = 'form'; }\n      } catch (e) { say('error', 'Não foi possível abrir o produto.'); }\n      S.busy = false;\n    };\n    const backToList = () => { S.view = 'lista'; S.errors = {}; };\n    const askConfirm = (kind, rec, fromForm) => { S.confirm = { kind, id: rec._id, nome: rec.nome || (S.form && S.form.nome) || '', ordem: rec.ordem, fromForm: !!fromForm }; };\n\n    const validate = () => {\n      const f = S.form || EMPTY_FORM; const e = {};\n      const empty = (v) => !String(v == null ? '' : v).trim();\n      if (empty(f.nome)) e.nome = 'Informe o nome do produto.';\n      const sl = slugify(f.slug);\n      if (!sl) e.slug = 'Informe o endereço (slug) da página.';\n      else if (items().some((r) => String(r._id) !== String(S.editingId) && String(r.slug || '').toLowerCase() === sl)) e.slug = 'Já existe um produto com esse slug.';\n      if (empty(f.categoria)) e.categoria = 'Escolha uma categoria.';\n      if (empty(f.marca)) e.marca = 'Informe a marca.';\n      if (f.origem === 'parceiro' && empty(f.parceiro)) e.parceiro = 'Escolha o parceiro deste produto.';\n      const preco = num(f.preco);\n      if (preco === undefined || isNaN(preco) || preco <= 0) e.preco = 'Informe um preço maior que zero.';\n      const antigo = num(f.precoAntigo);\n      if (antigo !== undefined && (isNaN(antigo) || antigo <= 0)) e.precoAntigo = 'Preço inválido.';\n      const parc = num(f.parcelas);\n      if (parc === undefined || isNaN(parc) || parc < 1 || parc > 12 || parc % 1 !== 0) e.parcelas = 'Informe as parcelas no cartão, de 1 a 12 (use 1 se o produto não parcela).';\n      const pix = num(f.desconto_pix);\n      if (pix === undefined || isNaN(pix) || pix < 0 || pix > 100) e.desconto_pix = 'Informe o desconto Pix, de 0 a 100% (use 0 se não houver).';\n      const est = num(f.estoque);\n      if (est !== undefined && (isNaN(est) || est < 0 || est % 1 !== 0)) e.estoque = 'Use um número inteiro, zero ou maior.';\n      if (empty(f.descricao)) e.descricao = 'Informe a descrição.';\n      const fqv = faqRows(S.faq).filter((x) => x.pergunta || x.resposta);\n      if (fqv.some((x) => !x.pergunta || !x.resposta)) e.faq = 'Preencha a pergunta e a resposta de cada item do FAQ, ou remova o item vazio.';\n      else if (fqv.length > FAQ_MAX) e.faq = 'O FAQ aceita no máximo ' + FAQ_MAX + ' perguntas.';\n      else if (fqv.some((x) => x.pergunta.length > FAQ_Q || x.resposta.length > FAQ_A)) e.faq = 'No FAQ, a pergunta aceita até ' + FAQ_Q + ' caracteres e a resposta até 1.000.';\n      S.errors = e;\n      return Object.keys(e).length === 0;\n    };\n\n    const doSave = async () => {\n      const f = S.form;\n      const payload = { id: S.editingId || undefined, nome: String(f.nome).trim(), slug: slugify(f.slug), sku: String(f.sku || '').trim(), categoria: f.categoria,\n        subcategoria: String(f.subcategoria || '').trim(), marca: String(f.marca).trim(), origem: f.origem, parceiro: f.origem === 'parceiro' ? f.parceiro : undefined,\n        preco: num(f.preco), precoAntigo: num(f.precoAntigo), parcelas: num(f.parcelas), desconto_pix: num(f.desconto_pix), estoque: num(f.estoque), descricao: String(f.descricao).trim(),\n        beneficios: String(f.beneficios || '').trim(), ficha_tecnica: String(f.ficha_tecnica || '').trim(), instrucoes_uso: String(f.instrucoes_uso || '').trim(),\n        social_video: String(f.social_video || '').trim(), ativo: !!f.ativo, relacionados: S.rels || [], faq: faqRows(S.faq).filter((x) => x.pergunta || x.resposta) };\n      S.busy = true;\n      try {\n        const out = await call('gestaoProdutoSalvar', payload);\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível salvar o produto.'); S.busy = false; return; }\n        const wasNew = !S.editingId;\n        const er0 = out && out.estoque && (out.estoque.data !== undefined ? out.estoque.data : out.estoque);\n        const estErr = er0 && er0.ok === false ? (er0.error || 'erro desconhecido') : '';\n        await reload();\n        if (wasNew && out && out.id) {\n          const o = await call('gestaoProdutoObter', { id: out.id });\n          if (o && o.ok !== false) fillFromServer(o);\n          say('success', 'Produto criado. Agora você pode enviar fotos e vídeos para a galeria.');\n        } else { say('success', 'Produto atualizado.'); backToList(); }\n        if (estErr) say('error', 'Produto salvo, mas o estoque não foi atualizado: ' + estErr);\n      } catch (e) { say('error', 'Não foi possível salvar o produto. Tente de novo.'); }\n      S.busy = false;\n    };\n\n    const save = async () => {\n      if (S.busy || !validate()) return;\n      if (S.editingId && S.original && S.original.ativo && !S.form.ativo) { askConfirm('desativar', { _id: S.editingId, nome: S.form.nome }, true); return; }\n      await doSave();\n    };\n\n    const refreshGaleria = async () => {\n      const o = await call('gestaoProdutoObter', { id: S.editingId });\n      if (o && o.ok !== false) S.galeria = o.galeria || [];\n      await reload();\n    };\n\n    const confirmGo = async () => {\n      if (!S.confirm || S.busy) return;\n      const c = S.confirm;\n      if (c.kind === 'desativar' && c.fromForm) { S.confirm = null; await doSave(); return; }\n      S.busy = true;\n      try {\n        if (c.kind === 'desativar') {\n          const out = await call('gestaoProdutoAtivar', { id: c.id, ativo: false });\n          if (out && out.ok === false) say('error', out.error || 'Não foi possível desativar.'); else say('success', 'Produto desativado. Ele não aparece mais na loja.');\n        } else if (c.kind === 'excluir') {\n          const out = await call('gestaoProdutoExcluir', { id: c.id });\n          if (out && out.ok === false) say('error', out.error || 'Não foi possível excluir.'); else { say('success', 'Produto excluído.'); }\n          if (S.view === 'form') backToList();\n        } else if (c.kind === 'arquivo') {\n          const out = await call('gestaoGaleriaRemover', { id: c.id });\n          if (out && out.ok === false) S.upErr = out.error || 'Não foi possível remover o arquivo.'; else { S.upErr = ''; await refreshGaleria(); }\n        }\n        S.confirm = null; S.busy = false; await reload();\n      } catch (e) { S.busy = false; S.confirm = null; say('error', 'Não foi possível concluir a ação. Nada foi alterado.'); }\n    };\n\n    const toggleAtivo = async (id) => {\n      const r = items().find((x) => String(x._id) === String(id)); if (!r) return;\n      if (r.ativo !== false) { askConfirm('desativar', r, false); return; }\n      try {\n        const out = await call('gestaoProdutoAtivar', { id: r._id, ativo: true });\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível reativar.'); return; }\n        await reload(); say('success', 'Produto reativado.');\n      } catch (e) { say('error', 'Não foi possível reativar o produto.'); }\n    };\n\n    const createParc = async () => {\n      const np = S.np || {}; if (S.busy) return;\n      const nome = String(np.nome || '').trim();\n      if (!nome) { S.np = { ...np, err: 'Informe o nome do parceiro.' }; return; }\n      S.busy = true;\n      try {\n        const out = await call('gestaoParceiroSalvar', { nome, site: String(np.site || '').trim() });\n        if (!out || out.ok === false) { S.np = { ...np, err: (out && out.error) || 'Não foi possível cadastrar o parceiro.' }; }\n        else {\n          await reload();\n          const f = { ...(S.form || EMPTY_FORM), parceiro: out.id }; if (!f.marcaTouched) f.marca = nome;\n          S.form = f; S.np = { open: false };\n        }\n      } catch (e) { S.np = { ...np, err: 'Não foi possível cadastrar o parceiro. Tente de novo.' }; }\n      S.busy = false;\n    };\n\n    const readB64 = (file) => new Promise((r, j) => { const fr = new FileReader(); fr.onload = () => r(String(fr.result).split(',')[1]); fr.onerror = j; fr.readAsDataURL(file); });\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'new') openNew(); else if (act === 'save') save(); else if (act === 'back') backToList(); else if (act === 'confirm-go') confirmGo();\n      else if (act === 'edit') openEdit(id); else if (act === 'toggle') toggleAtivo(id);\n      else if (act === 'delete') { const r = items().find((x) => String(x._id) === String(id)); if (r) askConfirm('excluir', r, false); }\n      else if (act === 'delete-form') askConfirm('excluir', { _id: S.editingId, nome: S.form.nome }, true);\n      else if (act === 'toggle-form') { if (S.form.ativo) { if (S.editingId) askConfirm('desativar', { _id: S.editingId, nome: S.form.nome }, true); } else { S.form = { ...S.form, ativo: true }; } }\n      else if (act === 'cancel-confirm') S.confirm = null;\n      else if (act === 'filter') S.fStatus = el.getAttribute('data-v');\n      else if (act === 'gal-rm') askConfirm('arquivo', { _id: id, nome: 'arquivo', ordem: el.getAttribute('data-o') }, true);\n      else if (act === 'rel-add') { if (!(S.rels || []).includes(id)) S.rels = (S.rels || []).concat([id]); S.relQ = ''; }\n      else if (act === 'rel-rm') S.rels = (S.rels || []).filter((x) => x !== id);\n      else if (act === 'faq-add') { if ((S.faq || []).length < FAQ_MAX) S.faq = (S.faq || []).concat([{ pergunta: '', resposta: '' }]); }\n      else if (act === 'faq-rm') { const i = Number(el.getAttribute('data-i')); S.faq = (S.faq || []).filter((_, k) => k !== i); }\n      else if (act === 'faq-up' || act === 'faq-down') {\n        const i = Number(el.getAttribute('data-i')); const j = act === 'faq-up' ? i - 1 : i + 1; const l = (S.faq || []).slice();\n        if (j >= 0 && j < l.length) { const t = l[i]; l[i] = l[j]; l[j] = t; S.faq = l; }\n      }\n      else if (act === 'parc-toggle') S.np = (S.np && S.np.open) ? { open: false } : { open: true, nome: '', site: '', err: '' };\n      else if (act === 'parc-save') createParc();\n    });\n    d2.on('[data-f]', 'input', (e, el) => {\n      const k = el.getAttribute('data-f'); const f = { ...(S.form || EMPTY_FORM) };\n      f[k] = el.value;\n      if (k === 'nome' && !S.editingId && !f.slugTouched) f.slug = slugify(el.value);\n      if (k === 'slug') f.slugTouched = true;\n      if (k === 'marca') f.marcaTouched = true;\n      S.form = f;\n    });\n    d2.on('[data-s]', 'change', (e, el) => {\n      const k = el.getAttribute('data-s'); const f = { ...(S.form || EMPTY_FORM) };\n      f[k] = el.value;\n      const pNome = (id) => { const p = (S.parceiros || []).find((x) => String(x._id) === String(id)); return p ? p.nome : ''; };\n      if (k === 'origem' && !f.marcaTouched) f.marca = el.value === 'proprio' ? 'Auto Super' : pNome(f.parceiro);\n      if (k === 'parceiro' && !f.marcaTouched) f.marca = pNome(el.value);\n      S.form = f;\n    });\n    d2.on('[data-c]', 'change', (e, el) => { S.form = { ...(S.form || EMPTY_FORM), [el.getAttribute('data-c')]: !!el.checked }; });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; });\n    d2.on('[data-np]', 'input', (e, el) => { S.np = { ...(S.np || {}), [el.getAttribute('data-np')]: el.value }; });\n    d2.on('[data-fc]', 'change', (e, el) => { S.fCat = el.value; });\n    d2.on('[data-fo]', 'change', (e, el) => { S.fOrg = el.value; });\n    d2.on('[data-rq]', 'input', (e, el) => { S.relQ = el.value; });\n    d2.on('[data-fq]', 'input', (e, el) => {\n      const i = Number(el.getAttribute('data-i')); const k = el.getAttribute('data-fq'); const l = (S.faq || []).slice();\n      if (!l[i]) return;\n      l[i] = { ...l[i], [k]: el.value }; S.faq = l;\n    });\n    d2.on('[data-up]', 'change', async (e, el) => {\n      const file = el.files && el.files[0]; const slot = Number(el.getAttribute('data-up')); el.value = '';\n      if (!file || !S.editingId) return;\n      S.upErr = '';\n      const isImg = /^image\\/(jpeg|png|webp)$/.test(file.type); const isVid = /^video\\/(mp4|webm)$/.test(file.type);\n      if (!isImg && !isVid) { S.upErr = 'Formato não aceito. Use JPG, PNG ou WebP para fotos e MP4 ou WebM para vídeos.'; return; }\n      if (isImg && file.size > 5 * MB) { S.upErr = 'A foto passa de 5 MB. Escolha uma menor.'; return; }\n      if (isVid && file.size > 20 * MB) { S.upErr = 'O vídeo passa de 20 MB. Escolha um menor.'; return; }\n      if ((S.galeria || []).some((g) => Number(g.ordem) === slot)) { S.upErr = 'A posição ' + slot + ' da galeria já está ocupada.'; return; }\n      S.upBusy = slot;\n      try {\n        const b64 = await readB64(file);\n        const out = await call('gestaoGaleriaEnviar', { produtoId: S.editingId, ordem: slot, base64: b64, contentType: file.type });\n        if (!out || out.ok === false) S.upErr = (out && out.error) || 'Não foi possível enviar o arquivo.';\n        else await refreshGaleria();\n      } catch (err) { S.upErr = 'Não foi possível enviar o arquivo. Tente de novo.'; }\n      S.upBusy = 0;\n    });\n\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = d2.params || {};\n    if (S.view === undefined) {\n      S.view = P.view === 'form' ? 'form' : 'lista';\n      S.fStatus = 'todos'; S.fCat = ''; S.fOrg = ''; S.q = ''; S.form = { ...EMPTY_FORM }; S.errors = {}; S.editingId = null; S.confirm = null; S.notice = null; S.busy = false; S.galeria = []; S.rels = []; S.faq = []; S.upBusy = 0; S.upErr = ''; S.relQ = '';\n    }\n    const btn = (label, variant, act, key, w) => `<span data-act=\"${act}\" style=\"display:inline-block\">${d2.render(P.button, (S.busy && (act === 'save' || act === 'confirm-go')) ? { label: act === 'save' ? 'Salvando…' : 'Aguarde…', variant, size: 'sm', isDisabled: true } : { label, variant, size: 'sm' }, { key: (S.busy && (act === 'save' || act === 'confirm-go')) ? key + '-busy' : key, width: w, height: 44 })}</span>`;\n    const rows = (S.items || []).slice().sort((a, b) => String(a.nome).localeCompare(String(b.nome), 'pt-BR'));\n    const catName = (id) => { const c = (S.cats || []).find((x) => String(x._id) === String(id)); return c ? c.nome : '—'; };\n    const parcName = (id) => { const p = (S.parceiros || []).find((x) => String(x._id) === String(id)); return p ? p.nome : ''; };\n\n    let confirm = S.confirm;\n    if (!confirm && P.demoConfirm && rows.length) confirm = { kind: 'desativar', id: rows[0]._id, nome: rows[0].nome, fromForm: false };\n\n    const header = ''; // o menu fica no componente GestaoMenu (bloco no topo de cada tela)\n    const notice = S.notice ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true }, { key: 'notice', height: 64 })}</div>` : '';\n\n    void S.tick;\n    const noticeBelow = notice ? notice.replace('class=\"notice\"', 'class=\"notice below\"') : '';\n    let main = '';\n    if (S.view === 'form') {\n      const f = S.form || EMPTY_FORM; const e = S.errors || {}; const edit = !!S.editingId;\n      const err = (k) => (e[k] ? `<p class=\"err\">${esc(e[k])}</p>` : '');\n      const inp = (k, label, ph, opts) => { const o = opts || {}; return `<div class=\"field\"><label class=\"lbl\" for=\"f-${k}\">${label}</label><input id=\"f-${k}\" class=\"inp ${e[k] ? 'bad' : ''}\" data-f=\"${k}\" value=\"${esc(f[k])}\" placeholder=\"${esc(ph || '')}\" ${o.ro ? 'readonly' : ''} autocomplete=\"off\">${e[k] ? err(k) : (o.hint ? `<p class=\"hint\">${o.hint}</p>` : '')}</div>`; };\n      const ta = (k, label, ph, hint) => `<div class=\"field\"><label class=\"lbl\" for=\"f-${k}\">${label}</label><textarea id=\"f-${k}\" class=\"inp ${e[k] ? 'bad' : ''}\" data-f=\"${k}\" placeholder=\"${esc(ph || '')}\">${esc(f[k])}</textarea>${e[k] ? err(k) : (hint ? `<p class=\"hint\">${hint}</p>` : '')}</div>`;\n      const catOpts = (S.cats || []).filter((c) => c.ativa !== false || String(c._id) === String(f.categoria)).map((c) => `<option value=\"${esc(c._id)}\" ${String(c._id) === String(f.categoria) ? 'selected' : ''}>${esc(c.nome)}${c.ativa === false ? ' (inativa)' : ''}</option>`).join('');\n      const parcOpts = (S.parceiros || []).map((p) => `<option value=\"${esc(p._id)}\" ${String(p._id) === String(f.parceiro) ? 'selected' : ''}>${esc(p.nome)}</option>`).join('');\n      const vendas = S.original ? S.original.vendas : 0;\n\n      const slots = [1, 2, 3, 4, 5].map((n) => {\n        const g = (S.galeria || []).find((x) => Number(x.ordem) === n);\n        const isVid = g && /^video\\//.test(String(g.tipo || ''));\n        const media = g ? (isVid ? `<div class=\"med\"><video src=\"${esc(g.url)}\" muted preload=\"metadata\"></video></div>` : `<div class=\"med\" style=\"background-image:url('${esc(g.url)}')\"></div>`) : `<div class=\"med\">${S.upBusy === n ? 'Enviando…' : 'Vazio'}</div>`;\n        const act = g ? `<button class=\"lk danger\" data-act=\"gal-rm\" data-id=\"${esc(g._id)}\" data-o=\"${n}\">Remover</button>` : `<label class=\"lk\">${S.upBusy === n ? 'Enviando…' : 'Enviar'}<input type=\"file\" data-up=\"${n}\" accept=\"image/jpeg,image/png,image/webp,video/mp4,video/webm\" hidden></label>`;\n        return `<div class=\"slot ${g ? 'full' : ''}\" data-key=\"slot-${n}\"><span class=\"pos\">Posição ${n}${g ? (isVid ? ' · vídeo' : ' · foto') : ''}</span>${media}${act}</div>`;\n      }).join('');\n      const galeria = edit\n        ? `<div class=\"gal\">${slots}</div>${S.upErr ? `<p class=\"err\">${esc(S.upErr)}</p>` : ''}<p class=\"hint\">Cada posição (1 a 5) aceita um único arquivo. Fotos JPG, PNG ou WebP até 5 MB; vídeos MP4 ou WebM até 20 MB. A foto da posição 1 vira a foto principal do produto.</p>`\n        : `<p class=\"hint\">Salve o produto para liberar o envio de fotos e vídeos.</p>`;\n\n      const relSel = (S.rels || []).map((id) => { const r = rows.find((x) => String(x._id) === String(id)); return `<span class=\"rchip\" data-key=\"rc-${esc(id)}\">${esc(r ? r.nome : id)}<button data-act=\"rel-rm\" data-id=\"${esc(id)}\" aria-label=\"Remover\">×</button></span>`; }).join('');\n      const rq = norm(S.relQ);\n      const cands = rq ? rows.filter((r) => String(r._id) !== String(S.editingId) && !(S.rels || []).includes(r._id) && r.ativo !== false && (norm(r.nome).includes(rq) || norm(r.sku).includes(rq))).slice(0, 6) : [];\n      const relBox = `<div class=\"rels\">${relSel || '<span class=\"hint\" style=\"margin:0\">Nenhum produto relacionado.</span>'}</div>\n        <input class=\"search\" style=\"max-width:100%\" data-rq placeholder=\"Buscar produto pelo nome ou SKU para relacionar\" value=\"${esc(S.relQ)}\" aria-label=\"Buscar produto relacionado\">\n        ${rq ? `<div class=\"cands\">${cands.length ? cands.map((r) => `<div class=\"cand\" data-key=\"cd-${esc(r._id)}\"><span>${esc(r.nome)}</span><button class=\"lk\" data-act=\"rel-add\" data-id=\"${esc(r._id)}\">Adicionar</button></div>`).join('') : '<div class=\"cand\"><span class=\"no\">Nenhum produto ativo encontrado.</span></div>'}</div>` : ''}`;\n\n      const fq = S.faq || [];\n      const faqItems = fq.map((x, i) => `<div class=\"faqi\" data-key=\"fq-${i}\">\n        <div class=\"faqh\"><span class=\"faqn\">Pergunta ${i + 1}</span><span class=\"faqa\">\n          <button class=\"lk\" data-act=\"faq-up\" data-i=\"${i}\" ${i === 0 ? 'disabled' : ''} aria-label=\"Subir a pergunta ${i + 1}\">↑ Subir</button>\n          <button class=\"lk\" data-act=\"faq-down\" data-i=\"${i}\" ${i === fq.length - 1 ? 'disabled' : ''} aria-label=\"Descer a pergunta ${i + 1}\">↓ Descer</button>\n          <button class=\"lk danger\" data-act=\"faq-rm\" data-i=\"${i}\" aria-label=\"Remover a pergunta ${i + 1}\">Remover</button></span></div>\n        <div class=\"field\"><label class=\"lbl\" for=\"fq-q-${i}\">Pergunta</label><input id=\"fq-q-${i}\" class=\"inp\" data-fq=\"pergunta\" data-i=\"${i}\" maxlength=\"${FAQ_Q}\" value=\"${esc(x.pergunta)}\" placeholder=\"Ex.: Serve em qual carro?\" autocomplete=\"off\"></div>\n        <div class=\"field\"><label class=\"lbl\" for=\"fq-a-${i}\">Resposta</label><textarea id=\"fq-a-${i}\" class=\"inp\" data-fq=\"resposta\" data-i=\"${i}\" maxlength=\"${FAQ_A}\" placeholder=\"Escreva a resposta. Deixe uma linha em branco para separar parágrafos.\">${esc(x.resposta)}</textarea></div>\n      </div>`).join('');\n      const faqBox = `${faqItems || '<p class=\"hint\" style=\"margin:0 0 12px\">Nenhuma pergunta cadastrada. Sem perguntas, a seção não aparece na página do produto.</p>'}\n        ${err('faq')}\n        <div><button class=\"lk\" data-act=\"faq-add\" ${fq.length >= FAQ_MAX ? 'disabled' : ''}>+ Adicionar pergunta</button></div>\n        <p class=\"hint\">${fq.length} de ${FAQ_MAX} perguntas. Pergunta até ${FAQ_Q} caracteres e resposta até 1.000. Aparecem na página do produto, na ordem da lista.</p>`;\n\n      const delBtn = !edit ? '' : '<button class=\"lk danger\" data-act=\"delete-form\">Excluir produto</button>';\n      const togBtn = edit ? `<button class=\"lk\" data-act=\"toggle-form\">${f.ativo ? 'Desativar produto' : 'Marcar como ativo'}</button>` : '';\n\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${d2.render(P.backLink, { label: 'Voltar para produtos' }, { key: 'back', width: 260, height: 32 })}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Produtos</p><h1>${edit ? 'Editar produto' : 'Novo produto'}</h1>\n    <p class=\"sub\">${edit ? 'Altere os dados e salve.' : 'Cadastre um produto próprio ou de uma marca parceira.'}</p></div></div>\n  <div class=\"card form\"><h3>Dados do produto</h3>\n    <div class=\"grid2\">${inp('nome', 'Nome', 'Ex.: Shampoo automotivo neutro')}${inp('slug', 'Slug (endereço da página)', 'shampoo-automotivo-neutro', { ro: edit, hint: edit ? 'O slug não muda depois de criado.' : 'Gerado a partir do nome. Pode editar antes de salvar.' })}</div>\n    <div class=\"grid3\">${inp('sku', 'SKU', 'LF-LAV-001')}\n      <div class=\"field\"><label class=\"lbl\" for=\"f-categoria\">Categoria</label><select id=\"f-categoria\" class=\"inp ${e.categoria ? 'bad' : ''}\" data-s=\"categoria\"><option value=\"\">Escolha…</option>${catOpts}</select>${err('categoria')}</div>\n      ${inp('subcategoria', 'Subcategoria', 'Ex.: Lavagem')}</div>\n    <div class=\"grid2\"><div class=\"field\"><span class=\"lbl\">Origem</span><div class=\"radios\">\n        <label class=\"ck\"><input type=\"radio\" name=\"origem\" data-s=\"origem\" value=\"proprio\" ${f.origem === 'proprio' ? 'checked' : ''}><span>Próprio</span></label>\n        <label class=\"ck\"><input type=\"radio\" name=\"origem\" data-s=\"origem\" value=\"parceiro\" ${f.origem === 'parceiro' ? 'checked' : ''}><span>Parceiro</span></label></div></div>\n      ${f.origem === 'parceiro' ? `<div class=\"field\"><label class=\"lbl\" for=\"f-parceiro\">Parceiro</label><select id=\"f-parceiro\" class=\"inp ${e.parceiro ? 'bad' : ''}\" data-s=\"parceiro\"><option value=\"\">Escolha…</option>${parcOpts}</select>${err('parceiro')}${(S.parceiros || []).length ? '' : '<p class=\"hint\">Nenhum parceiro cadastrado ainda.</p>'}<button class=\"lk\" data-act=\"parc-toggle\" style=\"margin-top:10px\">${S.np && S.np.open ? 'Cancelar novo parceiro' : '+ Novo parceiro'}</button>${S.np && S.np.open ? `<div class=\"grid2\" style=\"margin-top:12px\"><div><input class=\"inp\" data-np=\"nome\" placeholder=\"Nome do parceiro\" value=\"${esc(S.np.nome)}\"></div><div><input class=\"inp\" data-np=\"site\" placeholder=\"Site (opcional)\" value=\"${esc(S.np.site)}\"></div></div>${S.np.err ? `<p class=\"err\">${esc(S.np.err)}</p>` : ''}<div style=\"margin-top:12px\"><button class=\"lk\" data-act=\"parc-save\">Cadastrar parceiro</button></div>` : ''}</div>` : '<div></div>'}</div>\n    ${inp('marca', 'Marca', 'Ex.: Auto Super')}\n    <h3 style=\"margin-top:12px\">Preço</h3>\n    <div class=\"grid4\">${inp('preco', 'Preço (R$)', '129,90')}${inp('precoAntigo', 'Preço \"de\" (riscado)', 'Opcional')}${inp('parcelas', 'Máximo de parcelas', 'Ex.: 3', { hint: 'Obrigatório. Use 1 se o produto não parcela.' })}${inp('desconto_pix', 'Desconto Pix (%)', 'Ex.: 5', { hint: 'Obrigatório. Use 0 se não houver desconto.' })}</div>\n    <h3 style=\"margin-top:12px\">Estoque</h3>\n    <div class=\"grid4\">${inp('estoque', 'Quantidade em estoque', 'Ex.: 10', { hint: 'Com zero, a loja mostra \"volta já já\". Quem pediu aviso recebe um e-mail quando o estoque passa de zero.' })}</div>\n    <h3 style=\"margin-top:12px\">Conteúdo</h3>\n    ${ta('descricao', 'Descrição', 'Conte o que é o produto e para que serve.')}\n    <div class=\"grid2\">${ta('beneficios', 'Benefícios', 'Um benefício por linha.')}${ta('ficha_tecnica', 'Ficha técnica', 'Ex.: Volume: 500 ml')}</div>\n    <div class=\"grid2\">${ta('instrucoes_uso', 'Instruções de uso', 'Como aplicar, em passos curtos.')}${inp('social_video', 'Vídeo (YouTube ou Instagram)', 'https://', { hint: 'Link do vídeo que mostra o produto em uso.' })}</div>\n    <h3 style=\"margin-top:12px\">Visibilidade</h3>\n    <div class=\"checks\">\n      <label class=\"ck\"><input type=\"checkbox\" data-c=\"ativo\" ${f.ativo ? 'checked' : ''}><span>Ativo<small>Produto inativo some da loja, da busca e dos filtros, mesmo que esteja em destaque na home.</small></span></label>\n      <p class=\"hint\" style=\"margin:0\">Para colocar este produto no carrossel “Produtos em destaque” da Home, use a tela <a href=\"/destaques\">Destaques</a>.</p></div>\n    ${edit && S.original && S.original.ativo && !f.ativo ? `<div class=\"warn\">${d2.render(P.notice, { tone: 'info', body: 'Ao salvar, este produto deixa de aparecer na loja. Vamos pedir uma confirmação.', live: false }, { key: 'warn', height: 64 })}</div>` : ''}\n  </div>\n  <div class=\"card form\"><h3>Galeria (fotos e vídeos)</h3>${galeria}</div>\n  <div class=\"card form\"><h3>Produtos relacionados</h3>${relBox}<p class=\"hint\">Aparecem na página do produto, em \"combina com\". Produtos inativos não aparecem na loja.</p></div>\n  <div class=\"card form\"><h3>Perguntas frequentes (FAQ)</h3>${faqBox}</div>\n  <div class=\"btns\">${btn(edit ? 'Salvar alterações' : 'Criar produto', 'primary', 'save', 'save', 210)}${btn('Cancelar', 'outline', 'back', 'cancel', 130)}<span class=\"sp\"></span>${togBtn}${delBtn}</div>\n  ${noticeBelow}\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const shown = rows.filter((r) => (S.fStatus === 'ativos' ? r.ativo !== false : S.fStatus === 'inativos' ? r.ativo === false : true)\n        && (!S.fCat || String(r.categoria) === String(S.fCat)) && (!S.fOrg || r.origem === S.fOrg)\n        && (!q || norm(r.nome).includes(q) || norm(r.sku).includes(q) || norm(r.slug).includes(q) || norm(r.marca).includes(q)));\n      const chip = (v, l) => `<button class=\"chip\" data-act=\"filter\" data-v=\"${v}\" aria-pressed=\"${S.fStatus === v}\">${l}</button>`;\n      const catSel = `<select class=\"search\" style=\"flex:none;min-width:200px\" data-fc aria-label=\"Filtrar por categoria\"><option value=\"\">Todas as categorias</option>${(S.cats || []).map((c) => `<option value=\"${esc(c._id)}\" ${String(c._id) === String(S.fCat) ? 'selected' : ''}>${esc(c.nome)}</option>`).join('')}</select>`;\n      const orgSel = `<select class=\"search\" style=\"flex:none;min-width:160px\" data-fo aria-label=\"Filtrar por origem\"><option value=\"\">Toda origem</option><option value=\"proprio\" ${S.fOrg === 'proprio' ? 'selected' : ''}>Próprio</option><option value=\"parceiro\" ${S.fOrg === 'parceiro' ? 'selected' : ''}>Parceiro</option></select>`;\n      const pgCur = pageOf(d2, [S.q, S.fCat, S.fOrg, S.fStatus].join('|'), shown.length);\n      const pgRows = shown.slice((pgCur - 1) * PAGE, pgCur * PAGE);\n      const body = shown.length ? `\n<div class=\"card\"><table>\n  <thead><tr><th>Produto</th><th>Categoria</th><th>Origem</th><th>Preço</th><th>Estoque</th><th>Galeria</th><th>Status</th><th>Home</th><th></th></tr></thead>\n  <tbody>${pgRows.map((r) => `\n    <tr data-key=\"${esc(r._id)}\">\n      <td class=\"nome\">${esc(r.nome)}<small>${esc(r.sku || r.slug)}</small></td><td>${esc(catName(r.categoria))}</td>\n      <td>${r.origem === 'parceiro' ? 'Parceiro' + (parcName(r.parceiro) ? ' · ' + esc(parcName(r.parceiro)) : '') : 'Próprio'}</td>\n      <td class=\"num\">${brl(r.preco)}</td><td>${(r.estoque == null || r.estoque === '' || isNaN(Number(r.estoque))) ? '—' : Number(r.estoque) > 0 ? Number(r.estoque) : '<span class=\"pill off\">Esgotado</span>'}</td><td class=\"${Number(r.fotos) > 0 ? 'yes' : 'no'}\">${Number(r.fotos) || 0}/5</td>\n      <td><span class=\"pill ${r.ativo === false ? 'off' : 'on'}\">${r.ativo === false ? 'Inativo' : 'Ativo'}</span></td>\n      <td class=\"${Number(r.posicaoDestaque) > 0 ? 'yes' : 'no'}\">${Number(r.posicaoDestaque) > 0 ? 'Vaga ' + r.posicaoDestaque : '—'}</td>\n      <td><div class=\"acts\"><button class=\"lk\" data-act=\"edit\" data-id=\"${esc(r._id)}\">Editar</button>\n        <button class=\"lk\" data-act=\"toggle\" data-id=\"${esc(r._id)}\">${r.ativo === false ? 'Ativar' : 'Desativar'}</button>\n        <button class=\"lk danger\" data-act=\"delete\" data-id=\"${esc(r._id)}\">Excluir</button></div></td>\n    </tr>`).join('')}</tbody></table></div>\n${pagerHtml(pgCur, shown.length, PAGE)}\n<p class=\"count\">${shown.length} de ${rows.length} ${rows.length === 1 ? 'produto' : 'produtos'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\">Carregando produtos…</p></div>' : `<div class=\"card empty\">${d2.render(P.emptyState, { eyebrow: 'Produtos', title: rows.length ? 'Nenhum produto encontrado' : 'Nenhum produto cadastrado', description: rows.length ? 'Tente outro termo ou mude os filtros.' : 'Cadastre o primeiro produto da loja.', iconName: 'search' }, { key: 'empty', height: 260 })}</div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Produtos</h1><p class=\"sub\">Cadastre e organize os produtos da loja. Produto inativo não aparece na loja.</p></div>\n    ${btn('Novo produto', 'primary', 'new', 'new', 190)}</div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por nome, SKU ou marca\" value=\"${esc(S.q)}\" aria-label=\"Buscar produto\">${catSel}${orgSel}\n    <div class=\"chips\">${chip('todos', 'Todos')}${chip('ativos', 'Ativos')}${chip('inativos', 'Inativos')}</div></div>\n  ${body}\n</div>`;\n    }\n\n    let overlay = '';\n    if (confirm) {\n      const k = confirm.kind;\n      const title = k === 'excluir' ? 'Excluir “' + esc(confirm.nome) + '”?' : k === 'arquivo' ? 'Remover o arquivo da posição ' + esc(confirm.ordem) + '?' : 'Desativar “' + esc(confirm.nome) + '”?';\n      const text = k === 'excluir' ? 'O produto será apagado junto com a galeria e os vínculos de produtos relacionados. Não dá para desfazer.'\n        : k === 'arquivo' ? 'O arquivo sai da galeria e a posição fica livre para outro envio.'\n        : 'O produto deixa de aparecer na loja, na busca e nos filtros, mesmo que esteja marcado como destaque na home. Você pode ativar de novo quando quiser.';\n      const okLabel = k === 'excluir' ? 'Excluir produto' : k === 'arquivo' ? 'Remover arquivo' : 'Desativar produto';\n      overlay = `<div class=\"ovl\"><div class=\"dlg\" role=\"alertdialog\" aria-modal=\"true\"><h2>${title}</h2><p>${text}</p>\n  <div class=\"row\">${btn('Cancelar', 'outline', 'cancel-confirm', 'c-cancel', 130)}${btn(okLabel, k === 'desativar' ? 'primary' : 'brand', 'confirm-go', 'c-ok', 230)}</div></div></div>`;\n    }\n\n    return `<div class=\"root\">${header}${main}${overlay}</div>`;\n  }\n};"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"demoConfirm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demoConfirm"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"view":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"view"}}}}}}}}}},{"_id":"6ac52dcf80918a4cab959827","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"HomeDestaques","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// HomeDestaques — Loja Lucas Fontana\n// Carrossel \"Produtos em destaque\" da Home: até 12 produtos ativos, na ordem definida na Gestão (tela Destaques).\n// Rolagem lateral (arrastar/deslizar) com encaixe por card.\n// Desktop: setas nas laterais, FORA da área dos cards (o bloco tem uma margem de 72px de cada lado para elas).\n// Tablet e mobile: tracinhos embaixo (o mesmo padrão da ProductGallery no mobile): um tracinho por \"página\" de 2 cards; o tracinho vermelho é a página atual.\n// Lê o serviço público produtosLoja (só produtos ativos) e as categorias ativas (categoriasLoja). Desenha com ProductCard.\nconst esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst LAYOUT = { desktop: { w: 297, gap: 20, h: 500, vis: 4, arrows: true, pad: 72 }, tablet: { w: 336, gap: 16, h: 495, vis: 2, arrows: false, pad: 0 }, mobile: { w: 169, gap: 12, h: 384, vis: 2, arrows: false, pad: 0 } };\nconst MAX = 12;\nconst DOTS_H = 44;\nlet PRODS = null; let PRODS_LOADING = null; let CATS = null; let CATS_LOADING = null;\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.car { position: relative; width: 100%; }\n.track { display: flex; overflow-x: auto; overflow-y: hidden; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; scrollbar-width: none; overscroll-behavior-x: contain; }\n.track::-webkit-scrollbar { display: none; }\n.cell { flex: 0 0 auto; scroll-snap-align: start; }\n.nav { position: absolute; top: 146px; width: 48px; height: 48px; margin: 0; padding: 0; border-radius: 50%; border: 0; background: #0b0b0c; color: #ffffff; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 2; }\n.nav:hover { background: #c8102e; }\n.nav:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.nav.prev { left: 12px; } .nav.next { right: 12px; }\n.nav svg { width: 20px; height: 20px; }\n.dots { display: flex; justify-content: center; gap: 4px; height: 32px; margin-top: 12px; }\n.dots button { width: 38px; height: 32px; margin: 0; padding: 0 4px; border: 0; background: transparent; display: flex; align-items: center; cursor: pointer; }\n.dots button:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.dots span { display: block; width: 100%; height: 3px; background: #d9d9d6; }\n.dots [aria-current=\"true\"] span { background: #c8102e; }\n.empty { padding: 24px 0; color: #6b6c70; font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; font-size: 15px; }\n`,\n\n  mounted(d2, root) {\n    const tick = () => { d2.state.tick = Date.now(); };\n    if (!PRODS) {\n      if (!PRODS_LOADING) {\n        PRODS_LOADING = d2.rush('produtosLoja').invoke({ destaque: true }).then((res) => { PRODS = (res && res.data !== undefined ? res.data : res) || []; }).catch(() => { PRODS_LOADING = null; });\n      }\n      if (PRODS_LOADING) PRODS_LOADING.then(tick);\n    }\n    if (!CATS) {\n      if (!CATS_LOADING) {\n        CATS_LOADING = d2.rush('categoriasLoja').invoke({}).then((res) => { CATS = (res && res.data !== undefined ? res.data : res) || []; }).catch(() => { CATS_LOADING = null; });\n      }\n      if (CATS_LOADING) CATS_LOADING.then(tick);\n    }\n    const layoutOf = () => LAYOUT[(d2.params || {}).layout] || LAYOUT.desktop;\n    const trackEl = () => (root && root.querySelector ? root.querySelector('.track') : null);\n    // Setas (desktop): rolam 2 cards por clique\n    d2.on('[data-nav]', 'click', (e, el) => {\n      const tr = trackEl(); if (!tr) return;\n      const L = layoutOf(); const dir = Number(el.getAttribute('data-nav')) || 1;\n      tr.scrollBy({ left: dir * (L.w + L.gap) * 2, behavior: 'smooth' });\n    });\n    // Tracinhos (tablet e mobile): levam à página escolhida\n    d2.on('[data-page]', 'click', (e, el) => {\n      const tr = trackEl(); if (!tr) return;\n      const L = layoutOf(); const pg = Number(el.getAttribute('data-page')) || 0;\n      tr.scrollTo({ left: pg * (L.w + L.gap) * L.vis, behavior: 'smooth' });\n    });\n    // Rolagem: atualiza o tracinho vermelho direto no DOM (scroll não borbulha, por isso captura)\n    if (root && root.addEventListener) {\n      root.addEventListener('scroll', (e) => {\n        const tr = trackEl(); if (!tr || e.target !== tr) return;\n        const dots = root.querySelectorAll ? root.querySelectorAll('.dots button') : [];\n        if (!dots.length) return;\n        const L = layoutOf(); const step = (L.w + L.gap) * L.vis; const max = tr.scrollWidth - tr.clientWidth;\n        const page = tr.scrollLeft >= max - 2 ? dots.length - 1 : Math.min(dots.length - 1, Math.round(tr.scrollLeft / step));\n        dots.forEach((b, i) => b.setAttribute('aria-current', i === page ? 'true' : 'false'));\n      }, true);\n    }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    void d2.state.tick;\n    const layout = LAYOUT[p.layout] ? p.layout : 'desktop';\n    const L = LAYOUT[layout];\n    const limit = Math.min(MAX, Number(p.limit) > 0 ? Number(p.limit) : MAX);\n    const cats = {}; (CATS || []).forEach((c) => { cats[String(c._id)] = c.nome; });\n    const rows = (PRODS || []).filter((x) => x && x.ativo !== false && Number(x.posicaoDestaque) > 0)\n      .sort((a, b) => Number(a.posicaoDestaque) - Number(b.posicaoDestaque)).slice(0, limit);\n    if (!rows.length) return PRODS ? '<div class=\"empty\">Nenhum produto em destaque no momento.</div>' : '';\n    const card = (x) => d2.render(p.productCard, {\n      layout, priceComponent: p.priceComponent, button: p.button, badge: p.badge, iconComponent: p.iconComponent,\n      brand: cats[String(x.categoria)] || '', name: x.nome, price: x.preco, oldPrice: x.precoAntigo, installments: x.parcelas,\n      pixDiscount: x.desconto_pix, outOfStock: !(Number(x.estoque) > 0), sku: x.sku, href: '/produto/' + x.slug, image: x.imagem || '', partner: x.parceiroNome || ''\n    }, { key: 'c-' + x._id, style: 'width:100%;height:' + L.h + 'px' });\n    const cells = rows.map((x) => `<div class=\"cell\" data-key=\"${esc(x._id)}\" style=\"width:${L.w}px;height:${L.h}px\">${card(x)}</div>`).join('');\n    const overflow = rows.length > L.vis;\n    const arrows = L.arrows && overflow\n      ? `<button class=\"nav prev\" data-nav=\"-1\" aria-label=\"Produtos anteriores\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M15 5l-7 7 7 7\"/></svg></button><button class=\"nav next\" data-nav=\"1\" aria-label=\"Próximos produtos\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 5l7 7-7 7\"/></svg></button>`\n      : '';\n    const pages = Math.ceil(rows.length / L.vis);\n    const showDots = !L.arrows && pages > 1;\n    const dots = showDots\n      ? `<div class=\"dots\" role=\"tablist\" aria-label=\"Páginas do carrossel\">${Array.from({ length: pages }).map((_, i) => `<button data-page=\"${i}\" role=\"tab\" aria-label=\"Página ${i + 1} de ${pages}\" aria-current=\"${i === 0 ? 'true' : 'false'}\"><span></span></button>`).join('')}</div>`\n      : '';\n    const total = L.h + (showDots ? DOTS_H : 0);\n    return `<div class=\"car\" style=\"height:${total}px;padding:0 ${L.pad}px\"><div class=\"track\" style=\"height:${L.h}px;gap:${L.gap}px\">${cells}</div>${arrows}${dots}</div>`;\n  }\n};"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"limit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"limit"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"productCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productCard"}}}}}}}}}},{"_id":"6ac575cfa8fc5b5f763cb506","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ProdutoConteudo","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ProdutoConteudo — Auto Super Shop\n// O miolo da página /produto/:slug: lê o slug da rota, busca o produto no serviço público produtoLoja e desenha\n// ProductLayout (breadcrumb + galeria + BuyBox), Tabs (Descrição / Como usar / Ficha técnica), a seção de opiniões dos clientes\n// (OpinioesProduto), a seção \"Perguntas frequentes\" (FaqGroup) e a faixa \"Combina com\".\n// Não desenha nada disso: reaproveita ProductLayout, Tabs, OpinioesProduto, FaqGroup (que usa o Accordion), ProductCard, SectionHeader e EmptyState por parâmetro.\n// Estados: carregando (bloco cinza), produto não encontrado ou inativo (EmptyState), erro de rede (EmptyState).\n// O serviço NÃO devolve estoque nem vendas: só `disponivel` (true/false).\n// Abas sem conteúdo não aparecem; sem relacionados cadastrados, a faixa \"Combina com\" some; sem perguntas cadastradas, o FAQ some.\n// FAQ: vem de `produto.faq` (lista de {pergunta, resposta}, na ordem cadastrada na Gestão); só entram pares com pergunta e resposta preenchidas.\n// SEO/GEO: ao carregar, ajusta o título da aba, a meta description, as tags Open Graph, o canonical e o JSON-LD (schema.org Product);\n// a nota média e as opiniões entram no JSON-LD quando o OpinioesProduto termina de carregar (window.__lfAgg);\n// com FAQ, entra também um JSON-LD próprio (schema.org FAQPage).\n// `slugFixo` só serve para pré-visualizar no builder (na rota o slug vem da URL).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst list = (v) => Array.isArray(v) ? v : [];\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst TTL = 60000;\nconst CACHE = {};\nconst LOADING = {};\nconst ERR = {};\nlet SEO_SLUG = '';\nconst MAX_REL = 4;\nconst REL = {\n  desktop: { pad: '96px 96px 96px', hh: 80, gap: 40, cols: 4, cellH: 500, colGap: 20, rowGap: 20 },\n  tablet: { pad: '80px 40px 80px', hh: 90, gap: 32, cols: 2, cellH: 495, colGap: 16, rowGap: 16 },\n  mobile: { pad: '64px 20px 64px', hh: 100, gap: 24, cols: 2, cellH: 384, colGap: 12, rowGap: 12 }\n};\nconst FAQ_PAD = { desktop: '80px 96px 80px', tablet: '64px 40px 64px', mobile: '48px 20px 48px' };\nconst SK_H = { desktop: 760, tablet: 900, mobile: 700 };\n\n// Sacola no navegador (mesma chave da página Sacola): [{sku, qty}]\nconst addToBag = (sku, qty) => {\n  if (!sku) return;\n  try {\n    const bag = JSON.parse(localStorage.getItem('lf-sacola') || '[]');\n    const l = Array.isArray(bag) ? bag : [];\n    const f = l.find((y) => y && y.sku === sku);\n    if (f) f.qty = (Number(f.qty) || 0) + (Number(qty) || 1); else l.push({ sku, qty: Number(qty) || 1 });\n    localStorage.setItem('lf-sacola', JSON.stringify(l));\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: l.reduce((s, y) => s + (Number(y.qty) || 0), 0) } }));\n  } catch (e) { /* sem storage */ }\n};\n\nconst routerOf = (d2) => {\n  try { const r = d2.router('appRouter'); if (r && typeof r.goTo === 'function') return r; } catch (e) { /* tenta o padrão */ }\n  try { const r = d2.router(); if (r && typeof r.goTo === 'function') return r; } catch (e) { /* sem router */ }\n  return null;\n};\nconst go = (d2, path) => {\n  const r = routerOf(d2);\n  if (r) { try { r.goTo(path); return; } catch (e) { /* cai para o navegador */ } }\n  if (typeof window !== 'undefined') window.location.assign(path);\n};\nconst slugOf = (d2) => {\n  let s = '';\n  const r = routerOf(d2);\n  try { s = (r && r.params && r.params.slug) || ''; } catch (e) { s = ''; }\n  if (!s && typeof window !== 'undefined' && window.location) {\n    const m = String(window.location.pathname || '').match(/\\/produto\\/([^\\/?#]+)/);\n    if (m) s = m[1];\n  }\n  if (!s) s = String(d2.param('slugFixo', '') || '');\n  try { s = decodeURIComponent(s); } catch (e) { /* mantém */ }\n  return String(s).trim().toLowerCase();\n};\n\nconst ensure = (d2, slug) => {\n  if (!slug || LOADING[slug] || ERR[slug]) return;\n  const c = CACHE[slug];\n  if (c && Date.now() - c.t < TTL) return;\n  LOADING[slug] = d2.rush('produtoService').invoke({ slug }).then((res) => {\n    const out = (res && res.data !== undefined ? res.data : res) || {};\n    CACHE[slug] = { t: Date.now(), out: out && out.ok ? out : { ok: false } };\n  }).catch(() => { ERR[slug] = true; }).then(() => { delete LOADING[slug]; d2.state.tick = Date.now(); });\n};\n\nconst slugify = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');\nconst LISTA = '/lista-de-produtos/q';\nconst lines = (t) => String(t == null ? '' : t).split(/\\n/).map((s) => s.trim()).filter(Boolean);\nconst asSpecs = (t) => {\n  const ls = lines(t);\n  const pairs = ls.map((l) => { const m = l.match(/^([^:]{1,40}):\\s*(.+)$/); return m ? [m[1].trim(), m[2].trim()] : null; });\n  return ls.length >= 2 && pairs.every(Boolean) ? pairs : null;\n};\nconst buildTabs = (pr) => {\n  const items = [];\n  const benefits = lines(pr.beneficios);\n  if (String(pr.descricao || '').trim() || benefits.length) items.push({ label: 'Descrição', body: pr.descricao || '', benefits });\n  const uso = String(pr.instrucoes_uso || '').trim();\n  const video = String(pr.social_video || '').trim();\n  if (uso || video) items.push(Object.assign({ label: 'Como usar', body: uso }, video ? { videoHref: video, videoLabel: 'Ver o vídeo de aplicação' } : {}));\n  const ft = String(pr.ficha_tecnica || '').trim();\n  if (ft) { const specs = asSpecs(ft); items.push(specs ? { label: 'Ficha técnica', specs } : { label: 'Ficha técnica', body: ft }); }\n  return items;\n};\nconst buildImages = (pr, gal) => {\n  const g = list(gal).slice().sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0)).filter((x) => x && x.url);\n  const isVid = (x) => String(x.tipo || '').indexOf('video') === 0;\n  const imgs = g.filter((x) => !isVid(x)).map((x, k) => ({ image: x.url, alt: pr.nome + ' — foto ' + (k + 1) }));\n  const vids = g.filter(isVid).map((x) => ({ video: true, href: x.url, label: 'Vídeo' }));\n  const all = imgs.concat(vids);\n  if (all.length) return all;\n  if (pr.imagem) return [{ image: pr.imagem, alt: pr.nome }];\n  return [{ label: 'Sem foto' }];\n};\n// Perguntas frequentes do produto: só pares com pergunta e resposta preenchidas\nconst faqList = (pr) => list(pr && pr.faq)\n  .map((x) => ({ pergunta: String((x && x.pergunta) || '').trim(), resposta: String((x && x.resposta) || '').trim() }))\n  .filter((x) => x.pergunta && x.resposta);\n\n// ---- SEO / GEO ----\nconst setMeta = (attr, key, content) => {\n  if (typeof document === 'undefined' || !document.head) return;\n  let el = document.head.querySelector(`meta[${attr}=\"${key}\"]`);\n  if (!el) { el = document.createElement('meta'); el.setAttribute(attr, key); document.head.appendChild(el); }\n  el.setAttribute('content', content || '');\n};\nconst setLdId = (id, obj) => {\n  if (typeof document === 'undefined' || !document.head) return;\n  let el = document.getElementById(id);\n  if (!obj) { if (el && el.parentNode) el.parentNode.removeChild(el); return; }\n  if (!el) { el = document.createElement('script'); el.id = id; el.type = 'application/ld+json'; document.head.appendChild(el); }\n  el.textContent = JSON.stringify(obj);\n};\nconst setLd = (obj) => setLdId('lf-jsonld-product', obj);\nconst setCanonical = (href) => {\n  if (typeof document === 'undefined' || !document.head) return;\n  let el = document.head.querySelector('link[rel=\"canonical\"]');\n  if (!el) { el = document.createElement('link'); el.setAttribute('rel', 'canonical'); document.head.appendChild(el); }\n  el.setAttribute('href', href);\n};\n// Nota média e opiniões (calculadas pelo OpinioesProduto) no JSON-LD do produto\nconst addAgg = (ld, agg) => {\n  if (!agg) return ld;\n  ld.aggregateRating = { '@type': 'AggregateRating', ratingValue: agg.media.toFixed(1), reviewCount: agg.total, bestRating: 5, worstRating: 1 };\n  ld.review = agg.reviews.map((o) => ({\n    '@type': 'Review', name: o.titulo, reviewBody: o.texto, datePublished: String(o.data || '').slice(0, 10),\n    reviewRating: { '@type': 'Rating', ratingValue: o.nota, bestRating: 5, worstRating: 1 },\n    author: { '@type': 'Person', name: o.nome || 'Cliente' }\n  }));\n  return ld;\n};\nconst applySeo = (slug, out) => {\n  if (typeof document === 'undefined') return;\n  if (!out || !out.ok) { document.title = 'Produto não encontrado | Auto Super Shop'; setLd(null); setLdId('lf-jsonld-faq', null); SEO_SLUG = slug; return; }\n  const pr = out.produto;\n  const imgs = buildImages(pr, out.galeria).map((x) => x.image).filter(Boolean);\n  const url = (typeof window !== 'undefined' && window.location ? window.location.origin : '') + '/produto/' + pr.slug;\n  const desc = String(pr.descricao || '').replace(/\\s+/g, ' ').trim().slice(0, 155);\n  const title = pr.nome + ' | Auto Super Shop';\n  document.title = title;\n  setMeta('name', 'description', desc);\n  setMeta('property', 'og:title', title);\n  setMeta('property', 'og:description', desc);\n  setMeta('property', 'og:type', 'product');\n  setMeta('property', 'og:url', url);\n  if (imgs[0]) setMeta('property', 'og:image', imgs[0]);\n  setCanonical(url);\n  const ld = {\n    '@context': 'https://schema.org', '@type': 'Product', name: pr.nome, sku: pr.sku || undefined, description: desc || undefined,\n    image: imgs.length ? imgs : undefined, category: pr.categoriaNome || undefined, url,\n    brand: { '@type': 'Brand', name: pr.marca || pr.parceiroNome || 'Auto Super' },\n    offers: { '@type': 'Offer', url, priceCurrency: 'BRL', price: Number(pr.preco || 0).toFixed(2), itemCondition: 'https://schema.org/NewCondition', availability: pr.disponivel ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock' }\n  };\n  const ag = typeof window !== 'undefined' ? window.__lfAgg : null;\n  if (ag && ag.slug === pr.slug && ag.agg) addAgg(ld, ag.agg);\n  setLd(ld);\n  const fq = faqList(pr);\n  setLdId('lf-jsonld-faq', fq.length ? {\n    '@context': 'https://schema.org', '@type': 'FAQPage',\n    mainEntity: fq.map((x) => ({ '@type': 'Question', name: x.pergunta, acceptedAnswer: { '@type': 'Answer', text: x.resposta } }))\n  } : null);\n  SEO_SLUG = slug;\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.pc { width: 100%; background: #ffffff; font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.sk { width: 100%; background: #f5f5f3; }\n.note { padding: 24px; font-family: ${FF}; font-size: 14px; color: #6b6c70; }\n.rel { width: 100%; background: #f5f5f3; }\n.rel .grid { display: grid; width: 100%; }\n.rel .cell { min-width: 0; }\n.faq { width: 100%; background: #ffffff; }\n.faq .faqin { width: 100%; max-width: 880px; }\n`,\n\n  mounted(d2) {\n    ensure(d2, slugOf(d2));\n  },\n\n  updated(d2) {\n    const slug = slugOf(d2);\n    ensure(d2, slug);\n    const c = CACHE[slug];\n    if (c && SEO_SLUG !== slug) applySeo(slug, c.out);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    void d2.state.tick;\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      pl: refOf(d2.param('productLayout')), tabs: refOf(d2.param('tabs')), card: refOf(d2.param('productCard')),\n      sh: refOf(d2.param('sectionHeader')), empty: refOf(d2.param('emptyState')), bc: refOf(d2.param('breadcrumb')),\n      gal: refOf(d2.param('productGallery')), buy: refOf(d2.param('buyBox')), badge: refOf(d2.param('badge')),\n      benefits: refOf(d2.param('benefitList')), button: refOf(d2.param('button')), icon: refOf(d2.param('iconComponent')),\n      price: refOf(d2.param('priceComponent')), qty: refOf(d2.param('quantityStepper')), pd: refOf(d2.param('productDetails')),\n      cl: refOf(d2.param('checkList')), qc: refOf(d2.param('quoteCard')), search: refOf(d2.param('searchBar')),\n      op: refOf(d2.param('opinioes')), rs: refOf(d2.param('ratingStars')), rc: refOf(d2.param('reviewCard')),\n      ff: refOf(d2.param('formField')), nt: refOf(d2.param('notice')),\n      faq: refOf(d2.param('faqGroup')), acc: refOf(d2.param('accordion'))\n    };\n    const slug = slugOf(d2);\n\n    const emptyView = (variant, eyebrow, title, description) => R.empty\n      ? `<div class=\"pc\">${d2.render(R.empty, {\n          layout, eyebrow, title, description, ctaLabel: 'Ver todos os produtos', ctaHref: '/lista-de-produtos/q',\n          searchBar: R.search, button: R.button, iconComponent: R.icon\n        }, {\n          key: 'empty-' + variant + '-' + layout, style: 'height:auto',\n          onSearch: (v) => go(d2, '/lista-de-produtos/q?q=' + encodeURIComponent(v || '')),\n          onSuggestion: (v) => go(d2, '/lista-de-produtos/q?q=' + encodeURIComponent(v || ''))\n        })}</div>`\n      : '';\n    const notFound = () => emptyView('nf', 'Produto não encontrado', 'Esse produto não está mais na garagem',\n      'O link pode estar errado ou o produto saiu do ar. Que tal dar uma olhada nos outros produtos da Auto Super?');\n\n    if (!slug) {\n      return d2.context && d2.context.isBuilder\n        ? `<div class=\"note\">ProdutoConteudo — na página publicada o slug vem da URL (/produto/:slug). Para pré-visualizar aqui, preencha o parâmetro slugFixo.</div>`\n        : notFound();\n    }\n    if (ERR[slug]) return emptyView('err', 'Algo deu errado', 'Não foi possível carregar o produto', 'Confira sua conexão e atualize a página em instantes.');\n    const entry = CACHE[slug];\n    if (!entry) return `<div class=\"sk\" role=\"status\" aria-busy=\"true\" aria-label=\"Carregando produto\" style=\"min-height:${SK_H[layout]}px\"></div>`;\n    if (!entry.out.ok) return notFound();\n\n    const out = entry.out;\n    const pr = out.produto;\n\n    const crumbs = [{ label: 'Início', href: '/' }];\n    if (pr.categoriaNome) crumbs.push(pr.categoriaSlug ? { label: pr.categoriaNome, href: LISTA + '?categoria=' + pr.categoriaSlug } : { label: pr.categoriaNome });\n    if (pr.subcategoria) crumbs.push(pr.categoriaSlug ? { label: pr.subcategoria, href: LISTA + '?categoria=' + pr.categoriaSlug + '&subcategoria=' + slugify(pr.subcategoria) } : { label: pr.subcategoria });\n    crumbs.push({ label: pr.nome });\n\n    const nParc = Number(pr.parcelas) || 0;\n    const benefits = [\n      pr.parceiroNome\n        ? { icon: 'truck', title: 'Enviado pela ' + pr.parceiroNome, text: 'O parceiro envia direto para você e informa o código de rastreio.' }\n        : { icon: 'truck', title: 'Enviamos para todo o Brasil', text: 'O frete é calculado no fechamento do pedido.' },\n      { icon: 'card', title: nParc > 1 ? 'Pix ou cartão em até ' + nParc + 'x' : 'Pix ou cartão', text: 'Pagamento seguro, com antifraude em todas as compras.' },\n      { icon: 'swap', title: 'Troca e devolução', link: { label: 'Ver política de trocas', href: '/politicas/trocas-e-devolucoes' } }\n    ];\n    const product = {\n      name: pr.nome, category: pr.categoriaNome || '', sku: pr.sku || '', price: pr.preco, oldPrice: pr.precoAntigo,\n      installments: pr.parcelas, pixDiscount: pr.desconto_pix, available: !!pr.disponivel, partner: pr.parceiroNome || '', benefits,\n      slug: pr.slug, formField: R.ff, notice: R.nt\n    };\n\n    const top = R.pl ? d2.render(R.pl, {\n      layout, product, images: buildImages(pr, out.galeria), crumbs,\n      breadcrumb: R.bc, productGallery: R.gal, buyBox: R.buy, badge: R.badge, priceComponent: R.price,\n      quantityStepper: R.qty, button: R.button, benefitList: R.benefits, iconComponent: R.icon\n    }, {\n      key: 'pl-' + pr._id + '-' + layout, style: 'height:auto',\n      onBuyNow: () => go(d2, '/sacola'),\n      onNavigate: (h) => { if (h) go(d2, h); }\n    }) : '';\n\n    const tabItems = buildTabs(pr);\n    const tabs = R.tabs && tabItems.length ? d2.render(R.tabs, {\n      layout, items: tabItems, productDetails: R.pd, checkList: R.cl, quoteCard: R.qc, iconComponent: R.icon\n    }, { key: 'tabs-' + pr._id + '-' + layout, style: 'height:auto' }) : '';\n\n    // Opiniões dos clientes: acima do FAQ e de \"Combina com\"\n    const reviews = R.op ? d2.render(R.op, {\n      layout, slug: pr.slug, ratingStars: R.rs, reviewCard: R.rc, button: R.button, formField: R.ff, notice: R.nt, iconComponent: R.icon\n    }, { key: 'op-' + pr._id + '-' + layout, style: 'height:auto' }) : '';\n\n    // Perguntas frequentes: depois das opiniões e antes de \"Combina com\"; some quando o produto não tem perguntas\n    const faqItems = faqList(pr).map((x) => ({ question: x.pergunta, answer: x.resposta }));\n    const faq = R.faq && R.acc && faqItems.length\n      ? `<section class=\"faq\" aria-label=\"Perguntas frequentes\" style=\"padding:${FAQ_PAD[layout]}\"><div class=\"faqin\">${d2.render(R.faq, {\n          layout, title: 'Perguntas frequentes', anchor: 'faq-produto', items: faqItems, accordion: R.acc, iconComponent: R.icon, single: false\n        }, { key: 'faq-' + pr._id + '-' + layout, style: 'height:auto' })}</div></section>`\n      : '';\n\n    const L = REL[layout];\n    const rels = list(out.relacionados).slice(0, MAX_REL);\n    const card = (x) => d2.render(R.card, {\n      layout, priceComponent: R.price, button: R.button, badge: R.badge, iconComponent: R.icon,\n      brand: x.categoriaNome || '', name: x.nome, price: x.preco, oldPrice: x.precoAntigo, installments: x.parcelas,\n      pixDiscount: x.desconto_pix, outOfStock: !x.disponivel, sku: x.sku, href: '/produto/' + x.slug,\n      image: x.imagem || '', partner: x.parceiroNome || ''\n    }, {\n      key: 'rc-' + x._id + '-' + layout, style: 'width:100%;height:' + L.cellH + 'px',\n      onAdd: (sku) => addToBag(sku || x.sku, 1)\n    });\n    const related = R.card && R.sh && rels.length\n      ? `<section class=\"rel\" aria-label=\"Combina com\" style=\"padding:${L.pad}\">\n          <div style=\"height:${L.hh}px;margin-bottom:${L.gap}px\">${d2.render(R.sh, { eyebrow: 'Combina com', title: 'Leva junto que combina', number: '+', layout }, { key: 'sh-' + layout, height: L.hh })}</div>\n          <div class=\"grid\" style=\"grid-template-columns:repeat(${L.cols},minmax(0,1fr));column-gap:${L.colGap}px;row-gap:${L.rowGap}px\">\n            ${rels.map((x) => `<div class=\"cell\" data-key=\"${esc(x._id)}\" style=\"height:${L.cellH}px\">${card(x)}</div>`).join('')}\n          </div>\n        </section>`\n      : '';\n\n    return `<div class=\"pc\">${top}${tabs}${reviews}${faq}${related}</div>`;\n  }\n};\n"}},"accordion":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"accordion"}}}},"appRouter":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"appRouter"}}}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"benefitList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"benefitList"}}}},"breadcrumb":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"breadcrumb"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"buyBox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"buyBox"}}}},"checkList":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkList"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"faqGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"faqGroup"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"opinioes":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"opinioes"}}}},"priceComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"priceComponent"}}}},"productCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productCard"}}}},"productDetails":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productDetails"}}}},"productGallery":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productGallery"}}}},"productLayout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productLayout"}}}},"produtoService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"produtoService"}}}},"quantityStepper":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quantityStepper"}}}},"quoteCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"quoteCard"}}}},"ratingStars":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ratingStars"}}}},"reviewCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"reviewCard"}}}},"searchBar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"searchBar"}}}},"sectionHeader":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sectionHeader"}}}},"slugFixo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"slugFixo"}}}},"tabs":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tabs"}}}}}}}}}},{"_id":"6ac58b647a27742c85817866","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoMenu","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// GestaoMenu — Gestão (backoffice, só desktop)\n// A barra do topo de TODAS as telas da Gestão: logo, selo \"Gestão\", links das áreas e o botão \"Sair\".\n// Para incluir uma área nova no menu, basta acrescentar um item em ITEMS (as telas não têm mais menu próprio).\n// O item ativo vem do parâmetro `active` (\"categorias\", \"destaques\" ou \"produtos\"); sem ele, vale o endereço da página.\n// Reaproveita Logo e Button da loja; o logout usa a estratégia de login recebida em `auth`.\nconst ITEMS = [\n  { key: 'categorias', label: 'Categorias', href: '/categorias' },\n  { key: 'destaques', label: 'Destaques', href: '/destaques' },\n  { key: 'produtos', label: 'Produtos', href: '/produtos' },\n  { key: 'pedidos', label: 'Pedidos', href: '/pedidos' },\n  { key: 'cupons', label: 'Cupons', href: '/cupons' },\n  { key: 'opinioes', label: 'Opiniões', href: '/opinioes' },\n  { key: 'mensagens', label: 'Mensagens', href: '/mensagens' }\n];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst activeOf = (d2) => {\n  const a = String(d2.param('active', '') || '');\n  if (ITEMS.some((it) => it.key === a)) return a;\n  let path = '';\n  try { path = (typeof window !== 'undefined' && window.location && window.location.pathname) || ''; } catch (e) { path = ''; }\n  const hit = ITEMS.find((it) => path === it.href || path.indexOf(it.href + '/') === 0);\n  if (hit) return hit.key;\n  return path === '' || path === '/' ? 'categorias' : '';\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.bar { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea;\n  width: 100%; height: 72px; background: var(--white); border-bottom: 1px solid var(--g100); display: flex; align-items: center; padding: 0 48px; gap: 20px; color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.tag { font-size: 11px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; background: var(--ink); color: var(--white); padding: 6px 10px; border-radius: 2px; }\n.links { display: flex; align-items: center; }\n.nav { margin-left: 24px; font-size: 13px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); border-bottom: 2px solid var(--red); padding: 6px 0; text-decoration: none; }\n.nav:not(.on) { border-bottom-color: transparent; }\n.nav:hover { border-bottom-color: var(--red); }\n.nav:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }\n.sp { flex: 1; }\n.bd { display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; margin-left: 8px; padding: 0 6px; border-radius: 999px; background: var(--red); color: #fff; font-size: 11px; font-weight: 800; letter-spacing: 0; vertical-align: 1px; }\n.out { display: inline-block; }\n`,\n\n  mounted(d2) {\n    d2.rush('gestaoMensagensContagem').invoke({}).then((res) => {\n      const o = res && res.data !== undefined ? res.data : res;\n      if (o && o.ok) d2.state.cnt = { total: o.total, nao: o.naoLidas };\n    }).catch(() => { /* sem contagem */ });\n    d2.on('[data-act=\"sair\"]', 'click', async () => {\n      try { await d2.auth('auth').logout(); } catch (e) { /* segue para recarregar */ }\n      if (typeof window !== 'undefined') window.location.assign('/');\n    });\n  },\n\n  render(d2) {\n    const logoRef = refOf(d2.param('logo'));\n    const btnRef = refOf(d2.param('button'));\n    const active = activeOf(d2);\n    const logo = logoRef ? d2.render(logoRef, { layout: 'compact', noLink: true }, { key: 'logo', width: 240, height: 52 }) : '';\n    const links = ITEMS.map((it) => `<a class=\"nav${it.key === active ? ' on' : ''}\" href=\"${esc(it.href)}\" data-key=\"${esc(it.key)}\"${it.key === active ? ' aria-current=\"page\"' : ''}>${esc(it.label)}${it.key === 'mensagens' && d2.state.cnt && d2.state.cnt.nao > 0 ? `<span class=\"bd\" title=\"${d2.state.cnt.nao} não lidas de ${d2.state.cnt.total}\">${d2.state.cnt.nao}</span>` : ''}</a>`).join('');\n    const sair = btnRef ? `<span class=\"out\" data-act=\"sair\">${d2.render(btnRef, { label: 'Sair', variant: 'outline', size: 'sm' }, { key: 'sair', width: 110, height: 44 })}</span>` : '';\n    return `<div class=\"bar\">${logo}<span class=\"tag\">Gestão</span><nav class=\"links\" aria-label=\"Gestão\">${links}</nav><span class=\"sp\"></span>${sair}</div>`;\n  }\n};\n"}},"active":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"active"}}}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}}}}}}}},{"_id":"6ac6951a5da28ca8d58bea51","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"RatingStars","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// RatingStars — Loja Lucas Fontana\n// Cinco estrelas preenchidas conforme a nota (arredondada para o inteiro mais próximo): vermelhas as cheias, cinza as vazias.\n// `value` é a nota (0 a 5), `size` o tamanho de cada estrela em px (padrão 16), `color` e `emptyColor` mudam as cores.\n// Não desenha a estrela: usa o Icon (nome \"star\") pelo parâmetro `iconComponent`.\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n\nexport default {\n  css: `\n:host { display: inline-block; }\n* { box-sizing: border-box; }\n.st { display: inline-flex; align-items: center; gap: 2px; }\n`,\n  render(d2) {\n    const p = d2.params || {};\n    const v = Math.max(0, Math.min(5, Math.round(Number(p.value) || 0)));\n    const size = Number(p.size) > 0 ? Number(p.size) : 16;\n    const on = p.color || '#F5B301';\n    const off = p.emptyColor || '#D9D9D6';\n    const iconRef = refOf(d2.param('iconComponent'));\n    const stars = [1, 2, 3, 4, 5].map((i) => (iconRef\n      ? d2.render(iconRef, { name: 'star', size, iconColor: i <= v ? on : off }, { key: 'st' + i + (i <= v ? 'on' : 'off'), width: size, height: size, style: 'flex:none' })\n      : '')).join('');\n    return `<span class=\"st\" role=\"img\" aria-label=\"Nota ${v} de 5\">${stars}</span>`;\n  }\n};\n"}},"color":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"color"}}}},"emptyColor":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyColor"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"size":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"size"}}}},"value":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"value"}}}}}}}}}},{"_id":"6ac69525721e51b15dc8a9dd","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"ReviewCard","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// ReviewCard — Loja Lucas Fontana\n// Uma opinião de cliente: estrelas e data na primeira linha, título, texto, foto (se o cliente enviou) e o nome abreviado (\"Maria S.\").\n// Mesma largura da coluna: o espaço de cima, de baixo e a linha fina entre opiniões fazem parte do card.\n// `data` aceita a data em texto ISO (vem do serviço) e sai como dd/mm/aaaa. `layout`: desktop, tablet ou mobile.\n// Não desenha a estrela: usa RatingStars (`ratingStars`), que usa o Icon (`iconComponent`).\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst fmtDate = (v) => { try { const d = new Date(v); return isNaN(d.getTime()) ? '' : d.toLocaleDateString('pt-BR'); } catch (e) { return ''; } };\n// Amplia a foto num modal com fundo escuro, por cima de tudo (fica no body da página). Fecha no X, no Esc ou clicando fora da foto.\nconst openLightbox = (src, alt, opener) => {\n  if (typeof document === 'undefined' || !src) return;\n  const old = document.getElementById('lf-lightbox'); if (old) old.remove();\n  const ov = document.createElement('div');\n  ov.id = 'lf-lightbox'; ov.setAttribute('role', 'dialog'); ov.setAttribute('aria-modal', 'true'); ov.setAttribute('aria-label', alt || 'Foto enviada pelo cliente');\n  ov.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(11,11,12,.88);cursor:zoom-out';\n  const img = document.createElement('img');\n  img.src = src; img.alt = alt || '';\n  img.style.cssText = 'max-width:min(92vw,1200px);max-height:88vh;object-fit:contain;border-radius:2px;background:#fff;box-shadow:0 10px 40px rgba(0,0,0,.5);cursor:default';\n  const btn = document.createElement('button');\n  btn.type = 'button'; btn.setAttribute('aria-label', 'Fechar'); btn.textContent = '×';\n  btn.style.cssText = 'position:absolute;top:16px;right:20px;width:44px;height:44px;border:0;border-radius:50%;background:#fff;color:#0b0b0c;font-size:28px;line-height:1;cursor:pointer';\n  const prev = document.body.style.overflow;\n  const onKey = (e) => {\n    if (e.key === 'Escape') { e.stopPropagation(); close(); }\n    else if (e.key === 'Tab') { e.preventDefault(); btn.focus(); }\n  };\n  const close = () => {\n    document.removeEventListener('keydown', onKey, true);\n    ov.remove(); document.body.style.overflow = prev;\n    try { if (opener && opener.focus) opener.focus(); } catch (e) { /* sem foco */ }\n  };\n  ov.addEventListener('click', (e) => { if (e.target !== img) close(); });\n  btn.addEventListener('click', close);\n  ov.appendChild(img); ov.appendChild(btn); document.body.appendChild(ov);\n  document.body.style.overflow = 'hidden';\n  document.addEventListener('keydown', onKey, true);\n  btn.focus();\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.rv { --ink: #0b0b0c; --g100: #e4e4e2; --g500: #6b6e73; --g700: #45474b;\n  display: flex; flex-direction: column; gap: 10px; padding: 22px 0; border-bottom: 1px solid var(--g100);\n  font-family: ${FF}; color: var(--ink); -webkit-font-smoothing: antialiased; }\n.top { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }\n.date { font-size: 13px; color: var(--g500); }\n.ttl { font-size: 16px; font-weight: 800; line-height: 1.3; overflow-wrap: anywhere; }\n.txt { margin: 0; font-size: 15px; line-height: 1.6; color: #2b2d30; white-space: pre-line; overflow-wrap: anywhere; }\n.im { display: inline-block; align-self: flex-start; padding: 0; margin: 0; background: none; line-height: 0; border: 1px solid var(--g100); border-radius: 2px; cursor: zoom-in; }\n.im:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }\n.im img { display: block; width: 160px; height: 160px; object-fit: cover; border-radius: 2px; }\n.l-mobile .im img { width: 120px; height: 120px; }\n.who { font-size: 13px; font-weight: 700; color: var(--ink); }\n`,\n  mounted(d2) {\n    d2.on('[data-zoom]', 'click', (e, el) => openLightbox(el.getAttribute('data-src'), el.getAttribute('data-alt'), el));\n  },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = ['desktop', 'tablet', 'mobile'].indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const rs = refOf(d2.param('ratingStars'));\n    const icon = refOf(d2.param('iconComponent'));\n    const nota = Number(p.nota) || 0;\n    const stars = rs ? d2.render(rs, { value: nota, size: 16, iconComponent: icon }, { key: 'rs', width: 88, height: 16 }) : '';\n    const img = p.imagem ? `<button type=\"button\" class=\"im\" data-zoom data-src=\"${esc(p.imagem)}\" data-alt=\"${esc('Foto enviada por ' + (p.nome || 'cliente'))}\" aria-label=\"Ampliar a foto enviada por ${esc(p.nome || 'cliente')}\"><img src=\"${esc(p.imagem)}\" alt=\"\" loading=\"lazy\"></button>` : '';\n    return `<article class=\"rv l-${layout}\">\n      <div class=\"top\"><span>${stars}</span><span class=\"date\">${esc(fmtDate(p.data))}</span></div>\n      <strong class=\"ttl\">${esc(p.titulo)}</strong>\n      <p class=\"txt\">${esc(p.texto)}</p>\n      ${img}\n      <span class=\"who\">${esc(p.nome || 'Cliente')}</span>\n    </article>`;\n  }\n};\n"}},"data":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"data"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"imagem":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"imagem"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"nome":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"nome"}}}},"nota":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"nota"}}}},"ratingStars":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ratingStars"}}}},"texto":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"texto"}}}},"titulo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"titulo"}}}}}}}}}},{"_id":"6ac6956df121324ec027224f","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OpinioesProduto","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// OpinioesProduto — Loja Lucas Fontana\n// A seção \"Quem comprou, aprovou?\" da página de produto: resumo (média, distribuição por estrela, botão de avaliar),\n// formulário de avaliação (nota, título, texto e uma foto opcional) e a lista de opiniões, com \"Ordenar\" e \"Ver mais avaliações\".\n// Qualquer pessoa lê. Só cliente logado avalia (uma avaliação por produto); quem não está logado vai para /login.\n// As avaliações são publicadas na hora e o nome sai abreviado (\"Maria S.\"). Quem apaga é a Gestão (aba Opiniões).\n// Dados: serviço opinioesProduto (ler) e opiniaoEnviar (gravar). `slug` é o produto da página.\n// Não desenha estrela, campo nem botão: usa RatingStars, ReviewCard, FormField, Notice, Button e Icon pelos parâmetros.\n// SEO: ao carregar, acrescenta nota média e avaliações ao JSON-LD do produto (schema.org AggregateRating e Review).\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst NOTA_TXT = ['', 'Ruim', 'Regular', 'Bom', 'Muito bom', 'Excelente'];\nconst SORT_OPTS = [{ value: 'recentes', label: 'Mais recentes' }, { value: 'melhores', label: 'Melhores notas' }, { value: 'piores', label: 'Piores notas' }];\nconst time = (o) => { const t = new Date(o && o.data).getTime(); return isNaN(t) ? 0 : t; };\nconst SORTERS = {\n  recentes: (a, b) => time(b) - time(a),\n  melhores: (a, b) => (b.nota - a.nota) || (time(b) - time(a)),\n  piores: (a, b) => (a.nota - b.nota) || (time(b) - time(a))\n};\nconst fmtMedia = (m) => Number(m || 0).toFixed(1).replace('.', ',');\n\n// Texto do formulário e foto escolhida ficam fora do estado reativo (não redesenha a cada letra)\nconst rvOf = (d2) => {\n  const r = d2.root;\n  if (!r) return { titulo: '', texto: '', base64: '', ct: '' };\n  if (!r.__lfRv) r.__lfRv = { titulo: '', texto: '', base64: '', ct: '' };\n  return r.__lfRv;\n};\n\nconst load = (d2, slug, force) => {\n  const r = d2.root;\n  if (!r || !slug) return;\n  if (!force && r.__lfSlug === slug) return;\n  r.__lfSlug = slug;\n  d2.rush('opinioesProduto').invoke({ slug }).then((res) => {\n    const out = (res && res.data !== undefined ? res.data : res) || {};\n    d2.state.data = out && out.ok ? out : { ok: false };\n    d2.state.loaded = true;\n    if (!d2.state.shown) d2.state.shown = 5;\n  }).catch(() => { d2.state.data = { ok: false }; d2.state.loaded = true; });\n};\n\n// Foto: lê, reduz para no máximo 1600 px e converte para JPEG (a imagem final fica bem abaixo de 5 MB)\nconst readImage = async (file) => {\n  const okType = ['image/jpeg', 'image/png', 'image/webp'].indexOf(file.type) >= 0;\n  if (!okType) throw new Error('fmt');\n  if (file.size > 15 * 1024 * 1024) throw new Error('big');\n  const bmp = await createImageBitmap(file);\n  const sc = Math.min(1, 1600 / Math.max(bmp.width, bmp.height));\n  const w = Math.max(1, Math.round(bmp.width * sc)); const h = Math.max(1, Math.round(bmp.height * sc));\n  const cv = document.createElement('canvas'); cv.width = w; cv.height = h;\n  const cx = cv.getContext('2d'); cx.fillStyle = '#fff'; cx.fillRect(0, 0, w, h); cx.drawImage(bmp, 0, 0, w, h);\n  const blob = await new Promise((r) => cv.toBlob(r, 'image/jpeg', 0.85));\n  if (!blob) throw new Error('crop');\n  const b64 = await new Promise((r, j) => { const fr = new FileReader(); fr.onload = () => r(String(fr.result).split(',')[1]); fr.onerror = j; fr.readAsDataURL(blob); });\n  return { base64: b64, ct: 'image/jpeg', preview: 'data:image/jpeg;base64,' + b64 };\n};\n\n// ---- SEO: nota média e avaliações no JSON-LD do produto (o ProdutoConteudo cria o script; aqui só complementa) ----\nconst aggOf = (data) => {\n  const r = (data && data.resumo) || {};\n  const total = Number(r.total) || 0;\n  if (!total) return null;\n  return { media: Number(r.media) || 0, total, reviews: (data.opinioes || []).slice(0, 5) };\n};\nconst applyAgg = (ld, agg) => {\n  if (!agg) { delete ld.aggregateRating; delete ld.review; return ld; }\n  ld.aggregateRating = { '@type': 'AggregateRating', ratingValue: agg.media.toFixed(1), reviewCount: agg.total, bestRating: 5, worstRating: 1 };\n  ld.review = agg.reviews.map((o) => ({\n    '@type': 'Review', name: o.titulo, reviewBody: o.texto, datePublished: String(o.data || '').slice(0, 10),\n    reviewRating: { '@type': 'Rating', ratingValue: o.nota, bestRating: 5, worstRating: 1 },\n    author: { '@type': 'Person', name: o.nome || 'Cliente' }\n  }));\n  return ld;\n};\nconst syncLd = (slug, data) => {\n  try {\n    const agg = aggOf(data);\n    window.__lfAgg = { slug, agg };\n    const el = document.getElementById('lf-jsonld-product');\n    if (!el) return;\n    const ld = JSON.parse(el.textContent || '{}');\n    if (String(ld.url || '').indexOf('/produto/' + slug) < 0) return;\n    el.textContent = JSON.stringify(applyAgg(ld, agg));\n  } catch (e) { /* sem JSON-LD */ }\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.op { --ink: #0b0b0c; --g50: #f5f5f3; --g100: #e4e4e2; --g300: #d9d9d6; --g500: #6b6e73; --g700: #45474b; --red: #c8102e;\n  width: 100%; display: flex; flex-direction: column; gap: 28px; background: #fff; color: var(--ink);\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.op.l-desktop { padding: 16px 96px 96px; }\n.op.l-tablet { padding: 16px 40px 80px; }\n.op.l-mobile { padding: 16px 20px 64px; }\n.hair { height: 1px; background: var(--g100); margin-bottom: 36px; }\n.l-tablet .hair, .l-mobile .hair { margin-bottom: 20px; }\n.hd { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }\n.tt { display: flex; flex-direction: column; gap: 10px; }\n.eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .28em; color: var(--g500); text-transform: uppercase; }\n.eyebrow .r { color: var(--red); }\nh2 { margin: 0; font-size: 40px; font-weight: 800; font-stretch: 115%; line-height: 1; text-transform: uppercase; }\n.l-tablet h2 { font-size: 32px; }\n.l-mobile h2 { font-size: 26px; }\n.sort { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--g500); }\n.sort .sel { width: 190px; }\n.body { display: flex; gap: 48px; align-items: flex-start; }\n.l-tablet .body, .l-mobile .body { flex-direction: column; gap: 32px; align-items: stretch; }\n.sum { width: 360px; flex: none; display: grid; gap: 24px; padding: 28px; background: var(--g50); }\n.l-tablet .sum { width: 100%; grid-template-columns: auto 1fr; column-gap: 48px; align-items: center; }\n.l-mobile .sum { width: 100%; padding: 24px; }\n.cta { display: flex; flex-direction: column; gap: 12px; }\n.l-tablet .cta { grid-column: 1 / -1; }\n.avg strong { display: block; font-size: 64px; font-weight: 800; font-stretch: 115%; line-height: 1; margin-bottom: 8px; }\n.l-mobile .avg strong { font-size: 56px; }\n.cnt { display: block; margin-top: 8px; font-size: 13px; color: var(--g500); }\n.dist { display: flex; flex-direction: column; gap: 8px; min-width: 0; }\n.row { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--g700); }\n.row .n { width: 52px; flex: none; }\n.row .bar { flex: 1; height: 8px; background: var(--g100); position: relative; }\n.row .bar i { position: absolute; left: 0; top: 0; bottom: 0; background: var(--ink); }\n.row .q { width: 32px; flex: none; text-align: right; }\n.note { font-size: 12px; line-height: 1.5; color: var(--g500); }\n.ctab { display: block; width: 100%; }\n.col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 20px; }\n.list { display: flex; flex-direction: column; }\n.more { display: flex; justify-content: center; }\n.empty { padding: 28px; background: var(--g50); font-size: 15px; line-height: 1.5; color: var(--g700); }\n.sk { min-height: 360px; background: var(--g50); }\n.form { display: flex; flex-direction: column; gap: 18px; padding: 24px; border: 1.5px solid var(--ink); }\n.fh { display: flex; align-items: center; justify-content: space-between; gap: 12px; }\n.fh strong { font-size: 18px; font-weight: 800; font-stretch: 110%; text-transform: uppercase; }\n.lk { min-height: 44px; padding: 0; border: 0; background: transparent; font: inherit; font-size: 14px; font-weight: 600; color: var(--g700); text-decoration: underline; cursor: pointer; }\n.fld { display: flex; flex-direction: column; gap: 8px; }\n.lbl { font-size: 13px; font-weight: 700; }\n.pick { display: flex; align-items: center; gap: 4px; }\n.pk { display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0; border: 0; background: transparent; cursor: pointer; }\n.pk:focus-visible { outline: 2px solid var(--ink); outline-offset: 1px; }\n.pl { margin-left: 8px; font-size: 14px; font-weight: 700; }\n.up { display: inline-flex; align-items: center; justify-content: center; align-self: flex-start; height: 44px; padding: 0 20px; border: 1.5px solid var(--ink); border-radius: 2px; background: #fff; font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; cursor: pointer; }\n.up:hover { background: var(--ink); color: #fff; }\n.pv { display: flex; align-items: flex-end; gap: 16px; }\n.pv img { width: 96px; height: 96px; object-fit: cover; border: 1px solid var(--g100); border-radius: 2px; }\n.hint { font-size: 12px; color: var(--g500); }\n.err { font-size: 13px; font-weight: 600; color: var(--red); }\n.btns { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }\n`,\n\n  mounted(d2) {\n    const S = d2.state;\n    const slugOf = () => String((d2.params || {}).slug || '');\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act');\n      const data = S.data || {};\n      if (act === 'open') {\n        if (!data.logado) { if (typeof window !== 'undefined') window.location.assign('/login'); return; }\n        S.open = true; S.formErr = ''; S.sent = false;\n      } else if (act === 'cancel') {\n        S.open = false; S.formErr = '';\n      } else if (act === 'pick') {\n        S.nota = Number(el.getAttribute('data-n')) || 0;\n      } else if (act === 'more') {\n        S.shown = (Number(S.shown) || 5) + 5;\n      } else if (act === 'rmimg') {\n        const rv = rvOf(d2); rv.base64 = ''; rv.ct = ''; S.imgPrev = ''; S.imgErr = '';\n      } else if (act === 'send') {\n        send();\n      }\n    });\n\n    d2.on('input[data-file]', 'change', async (e, el) => {\n      const f = el.files && el.files[0];\n      if (!f) return;\n      S.imgErr = '';\n      try {\n        const img = await readImage(f);\n        const rv = rvOf(d2); rv.base64 = img.base64; rv.ct = img.ct; S.imgPrev = img.preview;\n      } catch (err) {\n        S.imgErr = err && err.message === 'fmt' ? 'Formato não aceito. Use JPG, PNG ou WebP.' : 'Não foi possível ler essa imagem. Tente outra.';\n      }\n      try { el.value = ''; } catch (x) { /* sem input */ }\n    });\n\n    const send = async () => {\n      if (S.busy) return;\n      const rv = rvOf(d2);\n      S.formErr = '';\n      if (!S.nota) { S.formErr = 'Escolha uma nota de 1 a 5 estrelas.'; return; }\n      S.busy = true;\n      try {\n        const res = await d2.rush('opiniaoEnviar').invoke({ slug: slugOf(), nota: S.nota, titulo: rv.titulo, texto: rv.texto, base64: rv.base64 || '', contentType: rv.ct || '' });\n        const out = (res && res.data !== undefined ? res.data : res) || {};\n        if (out && out.ok) {\n          rv.titulo = ''; rv.texto = ''; rv.base64 = ''; rv.ct = '';\n          S.nota = 0; S.imgPrev = ''; S.imgErr = ''; S.open = false; S.sent = true; S.sort = 'recentes';\n          load(d2, slugOf(), true);\n        } else {\n          S.formErr = (out && out.error) || 'Não foi possível enviar a sua avaliação. Tente de novo.';\n        }\n      } catch (err) { S.formErr = 'Não foi possível enviar a sua avaliação. Tente de novo.'; }\n      S.busy = false;\n    };\n\n    load(d2, slugOf());\n  },\n\n  updated(d2) {\n    const slug = String((d2.params || {}).slug || '');\n    load(d2, slug);\n    const r = d2.root;\n    if (r && d2.state.data && r.__lfLdFor !== d2.state.data) { r.__lfLdFor = d2.state.data; syncLd(slug, d2.state.data); }\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const S = d2.state;\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = {\n      stars: refOf(d2.param('ratingStars')), card: refOf(d2.param('reviewCard')), btn: refOf(d2.param('button')),\n      ff: refOf(d2.param('formField')), notice: refOf(d2.param('notice')), icon: refOf(d2.param('iconComponent'))\n    };\n    if (!S.loaded) return `<section class=\"op l-${layout}\" id=\"avaliacoes\" aria-busy=\"true\"><div class=\"sk\"></div></section>`;\n    const data = S.data || {};\n    if (!data.ok) return '';\n\n    const res = data.resumo || {};\n    const total = Number(res.total) || 0;\n    const media = Number(res.media) || 0;\n    const logado = !!data.logado;\n    const ja = !!data.jaAvaliou;\n    const sort = SORTERS[S.sort] ? S.sort : 'recentes';\n    const rv = rvOf(d2);\n\n    // ---- Resumo\n    const avgStars = R.stars ? d2.render(R.stars, { value: media, size: 20, iconComponent: R.icon }, { key: 'avg', width: 108, height: 20 }) : '';\n    const rows = [5, 4, 3, 2, 1].map((n) => {\n      const q = Number(res['c' + n]) || 0;\n      const w = total ? Math.round((q / total) * 100) : 0;\n      return `<span class=\"row\" data-key=\"r${n}\"><span class=\"n\">${n} estrelas</span><span class=\"bar\"><i style=\"width:${w}%\"></i></span><span class=\"q\">${q}</span></span>`;\n    }).join('');\n    const note = ja ? 'Obrigado! A sua avaliação já aparece aqui.'\n      : (logado ? 'A sua avaliação aparece aqui logo depois de enviada.' : 'Qualquer pessoa pode ler as avaliações. Para avaliar, entre na sua conta.');\n    const cta = R.btn ? `<span class=\"ctab\" data-act=\"${ja ? 'none' : 'open'}\">${d2.render(R.btn, { label: ja ? 'Você já avaliou' : 'Avaliar este produto', variant: 'outline', size: 'lg', fullWidth: 'always', isDisabled: ja, layout }, { key: 'cta', style: 'width:100%;height:52px' })}</span>` : '';\n    const summary = `<div class=\"sum\">\n      <div class=\"avg\"><strong>${total ? fmtMedia(media) : '–'}</strong>${avgStars}<span class=\"cnt\">${total} ${total === 1 ? 'avaliação' : 'avaliações'}</span></div>\n      <div class=\"dist\">${rows}</div>\n      <div class=\"cta\">${cta}<span class=\"note\">${esc(note)}</span></div>\n    </div>`;\n\n    // ---- Ordenar\n    const sortHtml = total > 1 && R.ff\n      ? `<div class=\"sort\"><span>Ordenar</span><div class=\"sel\">${d2.render(R.ff, { layout, ariaLabel: 'Ordenar avaliações', value: sort, options: SORT_OPTS, iconComponent: R.icon }, { key: 'sort', style: 'height:48px', onChange: (v) => { S.sort = SORTERS[v] ? v : 'recentes'; S.shown = 5; } })}</div></div>`\n      : '';\n\n    // ---- Formulário\n    let form = '';\n    if (S.open && logado && !ja && R.ff) {\n      const nota = Number(S.nota) || 0;\n      const pick = [1, 2, 3, 4, 5].map((i) => `<button type=\"button\" class=\"pk\" role=\"radio\" data-act=\"pick\" data-n=\"${i}\" aria-checked=\"${nota === i}\" aria-label=\"${i} ${i === 1 ? 'estrela' : 'estrelas'}\">${R.icon ? d2.render(R.icon, { name: 'star', size: 32, iconColor: i <= nota ? '#F5B301' : '#D9D9D6' }, { key: 'pk' + i + (i <= nota ? 'on' : 'off'), width: 32, height: 32 }) : ''}</button>`).join('');\n      const fTitulo = d2.render(R.ff, { layout, label: 'Título', placeholder: 'Ex.: Muito bom e chegou rápido', maxLength: 60, value: rv.titulo, iconComponent: R.icon }, { key: 'f-titulo', style: 'height:auto', onInput: (v) => { rv.titulo = v; } });\n      const fTexto = d2.render(R.ff, { layout, label: 'Conta pra gente o que achou', placeholder: 'Como foi usar? O que você mais gostou? Recomenda pra quem?', multiline: true, maxLength: 1000, counter: true, hint: 'Seu nome aparece abreviado, como “Maria S.”. Mínimo de 20 caracteres.', value: rv.texto, iconComponent: R.icon }, { key: 'f-texto', style: 'height:auto', onInput: (v) => { rv.texto = v; } });\n      const foto = `<div class=\"fld\"><span class=\"lbl\">Foto (opcional)</span>\n        ${S.imgPrev\n          ? `<div class=\"pv\"><img src=\"${esc(S.imgPrev)}\" alt=\"Prévia da foto\"><button type=\"button\" class=\"lk\" data-act=\"rmimg\">Remover foto</button></div>`\n          : '<label class=\"up\">Escolher foto<input type=\"file\" data-file accept=\"image/jpeg,image/png,image/webp\" hidden></label>'}\n        <span class=\"hint\">JPG, PNG ou WebP. A gente reduz o tamanho sozinho.</span>\n        ${S.imgErr ? `<span class=\"err\">${esc(S.imgErr)}</span>` : ''}</div>`;\n      const send = R.btn ? `<span data-act=\"send\" style=\"display:inline-block\">${d2.render(R.btn, { label: S.busy ? 'Enviando...' : 'Publicar avaliação', variant: 'primary', size: 'lg', isDisabled: !!S.busy, layout }, { key: 'send', width: 240, height: 52 })}</span>` : '';\n      const err = S.formErr && R.notice ? d2.render(R.notice, { tone: 'error', body: S.formErr, live: true, iconComponent: R.icon, layout }, { key: 'ferr', style: 'height:auto' }) : '';\n      form = `<div class=\"form\">\n        <div class=\"fh\"><strong>Sua avaliação</strong><button type=\"button\" class=\"lk\" data-act=\"cancel\">Cancelar</button></div>\n        <div class=\"fld\"><span class=\"lbl\" id=\"op-nota\">Quantas estrelas esse produto merece?</span>\n          <div class=\"pick\" role=\"radiogroup\" aria-labelledby=\"op-nota\">${pick}<span class=\"pl\">${esc(NOTA_TXT[nota] || '')}</span></div></div>\n        ${fTitulo}${fTexto}${foto}\n        <div class=\"btns\">${send}</div>${err}\n      </div>`;\n    }\n\n    // ---- Mensagem de sucesso\n    const sent = S.sent && R.notice ? d2.render(R.notice, { tone: 'success', body: '**Valeu pela avaliação!** Ela já aparece aqui na lista.', live: true, iconComponent: R.icon, layout }, { key: 'sent', style: 'height:auto' }) : '';\n\n    // ---- Lista\n    const sorted = (data.opinioes || []).slice().sort(SORTERS[sort]);\n    const shown = Math.min(sorted.length, Number(S.shown) || 5);\n    const cards = R.card ? sorted.slice(0, shown).map((o) => d2.render(R.card, {\n      nota: o.nota, titulo: o.titulo, texto: o.texto, nome: o.nome, data: o.data, imagem: o.imagem || '', layout, ratingStars: R.stars, iconComponent: R.icon\n    }, { key: 'rv-' + o._id, style: 'height:auto' })).join('') : '';\n    const list = total\n      ? `<div class=\"list\">${cards}</div>`\n      : '<div class=\"empty\">Ainda não há avaliações para este produto. Seja a primeira pessoa a contar o que achou.</div>';\n    const more = sorted.length > shown && R.btn\n      ? `<div class=\"more\"><span data-act=\"more\" style=\"display:inline-block\">${d2.render(R.btn, { label: 'Ver mais avaliações', variant: 'outline', size: 'md', layout }, { key: 'more', width: 260, height: 48 })}</span></div>`\n      : '';\n\n    return `<section class=\"op l-${layout}\" id=\"avaliacoes\" aria-label=\"Opinião dos clientes\">\n      <div class=\"hair\"></div>\n      <div class=\"hd\"><div class=\"tt\"><span class=\"eyebrow\"><span class=\"r\">★</span> Opinião dos clientes</span><h2>Quem comprou, aprovou?</h2></div>${sortHtml}</div>\n      <div class=\"body\">${summary}<div class=\"col\">${form}${sent}${list}${more}</div></div>\n    </section>`;\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"ratingStars":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ratingStars"}}}},"reviewCard":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"reviewCard"}}}},"slug":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"slug"}}}}}}}}}},{"_id":"6ac69610d0e6a463e356a5ae","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoOpinioes","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Opiniões (backoffice, só desktop)\n// Duas telas na mesma página: a lista de produtos que já têm opinião de cliente (quantidade, nota média e a última data)\n// e, ao clicar em \"Ver opiniões\", todas as opiniões daquele produto, com a opção de excluir.\n// Opinião é publicada na hora pela loja; aqui só se apaga (com confirmação). Excluir não pode ser desfeito.\n// Reaproveita Button, Notice, EmptyState e BackLink do sistema; o resto é HTML simples com os mesmos tokens da Gestão.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst fmtDate = (v) => { try { const d = new Date(v); return isNaN(d.getTime()) ? '' : d.toLocaleDateString('pt-BR'); } catch (e) { return ''; } };\nconst fmtNota = (n) => Number(n || 0).toFixed(1).replace('.', ',');\nconst stars = (n) => { const v = Math.max(0, Math.min(5, Math.round(Number(n) || 0))); return `<span class=\"st\" role=\"img\" aria-label=\"Nota ${v} de 5\">${'★'.repeat(v)}<span class=\"off\">${'★'.repeat(5 - v)}</span></span>`; };\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  const css = '.pager{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:20px;flex-wrap:wrap}.pg{min-width:40px;height:40px;padding:0 12px;border:1.5px solid #ececea;background:#fff;color:#0b0b0c;font:inherit;font-size:13px;font-weight:700;border-radius:2px;cursor:pointer}.pg.on{background:#0b0b0c;color:#fff;border-color:#0b0b0c}.pg[disabled]{opacity:.35;cursor:not-allowed}.pg:focus-visible{outline:2px solid #c8102e;outline-offset:2px}.dots{padding:0 4px;color:#6b6c70}';\n  return `<style>${css}</style><nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\n// Página atual da lista: volta para a 1 sempre que busca ou filtros mudam (`sig`)\nconst pageOf = (d2, sig, total) => {\n  const r = d2.root;\n  if (!r) return 1;\n  if (r.__pgSig !== sig) { r.__pgSig = sig; r.__pg = 1; }\n  return Math.min(Math.max(1, Number(r.__pg) || 1), Math.max(1, Math.ceil(total / PAGE)));\n};\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.back { margin-bottom: 16px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; }\n.search { flex: 1; min-width: 260px; max-width: 420px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 20px; border-bottom: 1px solid var(--g100); }\ntd { padding: 16px 20px; border-bottom: 1px solid var(--g100); font-size: 15px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.nome { font-weight: 700; }\ntd small { display: block; margin-top: 2px; color: var(--g500); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; font-weight: 400; }\ntd.dt { color: var(--g500); }\n.st { color: var(--red); letter-spacing: 2px; font-size: 15px; white-space: nowrap; }\n.st .off { color: #d9d9d6; }\n.acts { display: flex; gap: 16px; justify-content: flex-end; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); text-decoration: underline; text-underline-offset: 4px; cursor: pointer; }\n.lk.danger { color: var(--red); }\n.count { margin: 12px 0 0; color: var(--g500); font-size: 13px; }\n.empty { padding: 8px; }\n.sum { display: flex; gap: 32px; align-items: center; margin-bottom: 16px; padding: 20px 24px; background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\n.sum strong { font-size: 32px; font-weight: 800; }\n.sum span.l { display: block; font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--g500); }\n.rev { display: grid; grid-template-columns: 1fr auto; gap: 8px 24px; padding: 20px 24px; border-bottom: 1px solid var(--g100); }\n.rev:last-child { border-bottom: 0; }\n.rev .tt { font-size: 16px; font-weight: 800; margin: 6px 0 4px; overflow-wrap: anywhere; }\n.rev .tx { margin: 0; font-size: 15px; line-height: 1.55; color: var(--g800); white-space: pre-line; overflow-wrap: anywhere; }\n.rev .who { margin-top: 8px; font-size: 13px; color: var(--g500); }\n.rev .who b { color: var(--ink); }\n.rev img { display: block; width: 96px; height: 96px; object-fit: cover; margin-top: 10px; border: 1px solid var(--g100); border-radius: 2px; }\n.ovl { position: absolute; inset: 0; z-index: 30; display: flex; align-items: flex-start; justify-content: center; padding-top: 160px; background: rgba(11, 11, 12, .55); }\n.dlg { width: 480px; max-width: 92%; padding: 32px; background: var(--white); border-radius: 2px; }\n.dlg h2 { margin: 0 0 12px; font-size: 22px; font-weight: 800; }\n.dlg p { margin: 0 0 24px; color: var(--g800); font-size: 15px; line-height: 1.5; }\n.dlg .row { display: flex; gap: 12px; justify-content: flex-end; }\n`,\n\n  mounted(d2) {\n    const S = d2.state;\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const reload = async () => {\n      try {\n        const res = await d2.rush('gestaoOpinioesListar').invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.ops = out && out.ok ? (out.opinioes || []) : [];\n        S.prods = out && out.ok ? (out.produtos || []) : [];\n      } catch (e) { say('error', 'Não foi possível carregar as opiniões.'); }\n      S.loaded = true;\n    };\n    const doDelete = async () => {\n      if (!S.confirm || S.busy) return;\n      const c = S.confirm;\n      S.busy = true;\n      try {\n        const res = await d2.rush('gestaoOpiniaoExcluir').invoke({ id: c.id });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (out && out.ok === false) say('error', out.error || 'Não foi possível excluir a opinião.');\n        else { say('success', 'Opinião excluída.'); await reload(); }\n        const left = (S.ops || []).filter((o) => o.produto === S.pid).length;\n        if (S.view === 'produto' && left === 0) S.view = 'lista';\n      } catch (e) { say('error', 'Não foi possível excluir a opinião. Nada foi alterado.'); }\n      S.confirm = null; S.busy = false;\n    };\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'ver') { S.pid = id; S.view = 'produto'; S.notice = null; }\n      else if (act === 'back') { S.view = 'lista'; S.notice = null; }\n      else if (act === 'del') { S.confirm = { id, titulo: el.getAttribute('data-t') || '' }; }\n      else if (act === 'cancel') { S.confirm = null; }\n      else if (act === 'confirm-go') doDelete();\n    });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; });\n    d2.on('[data-pg]', 'click', (e, el) => { if (d2.root) d2.root.__pg = Number(el.getAttribute('data-pg')) || 1; S.tick = Date.now(); });\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = Object.assign({}, d2.params || {}, { button: refOf(d2.param('button')), notice: refOf(d2.param('notice')), backLink: refOf(d2.param('backLink')), emptyState: refOf(d2.param('emptyState')), iconComponent: refOf(d2.param('iconComponent')) });\n    if (S.view === undefined) { S.view = 'lista'; S.q = ''; S.confirm = null; S.notice = null; S.busy = false; S.pid = ''; }\n    const btn = (label, variant, act, key, w, id) => `<span data-act=\"${act}\"${id ? ` data-id=\"${esc(id)}\"` : ''} style=\"display:inline-block\">${d2.render(P.button, S.busy && (act === 'save' || act === 'confirm-go') ? { label: act === 'save' ? 'Salvando…' : 'Aguarde…', variant, size: 'sm', isDisabled: true } : { label, variant, size: 'sm' }, { key: S.busy && (act === 'save' || act === 'confirm-go') ? key + '-busy' : key, width: w, height: 44 })}</span>`;\n    void S.tick;\n    const notice = S.notice && P.notice\n      ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true, iconComponent: P.iconComponent }, { key: 'notice', style: 'height:auto' })}</div>` : '';\n\n    const ops = S.ops || [];\n    const prods = S.prods || [];\n    const by = {};\n    ops.forEach((o) => { (by[o.produto] = by[o.produto] || []).push(o); });\n\n    let main = '';\n    if (S.view === 'produto') {\n      const pr = prods.find((x) => x._id === S.pid) || { nome: 'Produto', sku: '' };\n      const list = (by[S.pid] || []).slice().sort((a, b) => String(b.data).localeCompare(String(a.data)));\n      const total = list.length;\n      const media = total ? list.reduce((s, o) => s + (Number(o.nota) || 0), 0) / total : 0;\n      const items = list.map((o) => `\n<div class=\"rev\" data-key=\"${esc(o._id)}\">\n  <div>${stars(o.nota)}<div class=\"tt\">${esc(o.titulo)}</div><p class=\"tx\">${esc(o.texto)}</p>\n    ${o.imagem ? `<a href=\"${esc(o.imagem)}\" target=\"_blank\" rel=\"noopener\"><img src=\"${esc(o.imagem)}\" alt=\"Foto enviada pelo cliente\"></a>` : ''}\n    <div class=\"who\"><b>${esc(o.clienteNome || 'Cliente')}</b>${o.clienteEmail ? ' · ' + esc(o.clienteEmail) : ''} · ${esc(fmtDate(o.data))}</div></div>\n  <div><button class=\"lk danger\" data-act=\"del\" data-id=\"${esc(o._id)}\" data-t=\"${esc(o.titulo)}\">Excluir</button></div>\n</div>`).join('');\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${P.backLink ? d2.render(P.backLink, { label: 'Voltar para opiniões' }, { key: 'back', width: 260, height: 32 }) : ''}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Opiniões</p><h1>${esc(pr.nome)}</h1><p class=\"sub\">${esc(pr.sku || '')}</p></div></div>\n  ${notice}\n  <div class=\"sum\"><div><span class=\"l\">Nota média</span><strong>${fmtNota(media)}</strong></div><div><span class=\"l\">Opiniões</span><strong>${total}</strong></div><div>${stars(media)}</div></div>\n  <div class=\"card\">${items || '<div class=\"rev\"><span>Este produto não tem mais opiniões.</span></div>'}</div>\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const linhas = prods.filter((p) => by[p._id]).map((p) => {\n        const a = by[p._id];\n        const media = a.reduce((s, o) => s + (Number(o.nota) || 0), 0) / a.length;\n        const ult = a.map((o) => String(o.data)).sort().slice(-1)[0];\n        return { p, total: a.length, media, ult };\n      }).filter((r) => !q || norm(r.p.nome).includes(q) || norm(r.p.sku).includes(q))\n        .sort((a, b) => String(b.ult).localeCompare(String(a.ult)));\n      const pgCur = pageOf(d2, S.q, linhas.length);\n      const pgRows = linhas.slice((pgCur - 1) * PAGE, pgCur * PAGE);\n      const body = linhas.length ? `\n<div class=\"card\"><table>\n  <thead><tr><th>Produto</th><th>Opiniões</th><th>Nota média</th><th>Última opinião</th><th></th></tr></thead>\n  <tbody>${pgRows.map((r) => `\n    <tr data-key=\"${esc(r.p._id)}\">\n      <td class=\"nome\">${esc(r.p.nome)}<small>${esc(r.p.sku || '')}</small></td>\n      <td>${r.total}</td>\n      <td>${stars(r.media)} <span class=\"dt\">${fmtNota(r.media)}</span></td>\n      <td class=\"dt\">${esc(fmtDate(r.ult))}</td>\n      <td><div class=\"acts\"><button class=\"lk\" data-act=\"ver\" data-id=\"${esc(r.p._id)}\">Ver opiniões</button></div></td>\n    </tr>`).join('')}</tbody></table></div>\n${pagerHtml(pgCur, linhas.length, PAGE)}<p class=\"count\">${linhas.length} ${linhas.length === 1 ? 'produto com opinião' : 'produtos com opinião'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\" style=\"padding:16px\">Carregando opiniões…</p></div>'\n        : `<div class=\"card empty\">${P.emptyState ? d2.render(P.emptyState, { eyebrow: 'Opiniões', title: S.q ? 'Nenhum produto encontrado' : 'Nenhuma opinião ainda', description: S.q ? 'Tente outro nome ou código.' : 'Quando um cliente avaliar um produto na loja, ele aparece aqui.', iconName: 'search' }, { key: 'empty', height: 260 }) : ''}</div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Opiniões</h1><p class=\"sub\">Produtos que já receberam opinião de clientes. Veja as opiniões de cada um e apague o que não deve ficar na loja.</p></div></div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por nome ou código do produto\" value=\"${esc(S.q)}\" aria-label=\"Buscar produto\"></div>\n  ${body}\n</div>`;\n    }\n\n    const overlay = S.confirm ? `\n<div class=\"ovl\"><div class=\"dlg\" role=\"alertdialog\" aria-modal=\"true\">\n  <h2>Excluir esta opinião?</h2>\n  <p>${S.confirm.titulo ? '“' + esc(S.confirm.titulo) + '” ' : ''}será apagada da loja e não dá para desfazer.</p>\n  <div class=\"row\">${btn('Cancelar', 'outline', 'cancel', 'c-cancel', 130)}${btn(S.busy ? 'Excluindo...' : 'Excluir opinião', 'primary', 'confirm-go', 'c-ok', 200)}</div>\n</div></div>` : '';\n\n    return `<div class=\"root\">${main}${overlay}</div>`;\n  }\n};\n"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}}}}}}}},{"_id":"6ac69b40862cb6202e1c5315","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoDestaquesAbas","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// GestaoDestaquesAbas — Gestão (backoffice, só desktop)\n// As abas da área Destaques: Categorias, Produtos, Carrossel e Banner. Cada aba é uma página (e um endereço) própria.\n// Para incluir uma aba nova, basta acrescentar um item em ABAS. O item ativo vem do parâmetro `active`\n// (\"categorias\", \"produtos\", \"carrossel\" ou \"banner\"); sem ele, vale o endereço da página.\nconst ABAS = [\n  { key: 'categorias', label: 'Categorias', href: '/destaques' },\n  { key: 'produtos', label: 'Produtos', href: '/destaques/produtos' },\n  { key: 'carrossel', label: 'Carrossel', href: '/destaques/carrossel' },\n  { key: 'banner', label: 'Banner', href: '/destaques/banner' }\n];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst activeOf = (d2) => {\n  const a = String(d2.param('active', '') || '');\n  if (ABAS.some((x) => x.key === a)) return a;\n  let path = '';\n  try { path = String((typeof window !== 'undefined' && window.location && window.location.pathname) || ''); } catch (e) { path = ''; }\n  const hit = ABAS.slice().reverse().find((x) => path === x.href || path.indexOf(x.href + '/') === 0);\n  return hit ? hit.key : 'categorias';\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.ab { --red: #c8102e; --ink: #0b0b0c; --g100: #ececea; --g500: #6b6c70;\n  width: 100%; height: 56px; background: #fff; border-bottom: 1px solid var(--g100);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.in { display: flex; align-items: stretch; gap: 32px; max-width: 1120px; height: 100%; margin: 0 auto; padding: 0 48px; }\n.tab { display: flex; align-items: center; padding: 0 2px; border-bottom: 3px solid transparent; font-size: 13px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--g500); text-decoration: none; }\n.tab:hover { color: var(--ink); }\n.tab.on { color: var(--ink); border-bottom-color: var(--red); }\n.tab:focus-visible { outline: 2px solid var(--red); outline-offset: -4px; }\n`,\n  render(d2) {\n    const active = activeOf(d2);\n    const links = ABAS.map((x) => `<a class=\"tab${x.key === active ? ' on' : ''}\" href=\"${esc(x.href)}\" data-key=\"${esc(x.key)}\"${x.key === active ? ' aria-current=\"page\"' : ''}>${esc(x.label)}</a>`).join('');\n    return `<div class=\"ab\"><nav class=\"in\" aria-label=\"Destaques\">${links}</nav></div>`;\n  }\n};\n"}},"active":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"active"}}}}}}}}}},{"_id":"6ac69b8d9cddc83ab29eea79","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoBanners","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Banners (backoffice, só desktop)\n// Duas telas com o mesmo código: \"Carrossel\" (/destaques/carrossel, até 4 banners em ordem) e \"Banner\" de parceiro (/destaques/banner, um só).\n// Carrossel: lista com subir/descer, editar, excluir e \"Novo banner\". Parceiro: um formulário só, com liga/desliga.\n// O formulário tem textos, botão (para onde leva: categoria, produto, todos os produtos ou um link livre, como site de parceiro, YouTube ou Instagram),\n// a descrição da imagem e uma imagem só (a loja corta no centro para cada tela).\n// A imagem é reduzida para no máximo 2400 px antes de subir. O mesmo texto vale em todas as telas: escreva curto o bastante para caber no celular.\n// Depois de salvar, a mensagem de sucesso fica em verde, abaixo do botão Salvar. Reaproveita Button, Notice, EmptyState e BackLink.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst MAX_CARR = 4;\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst CROP = {\n  carrossel: { desktop: [1920, 774], tablet: [1536, 1040], mobile: [780, 1120] },\n  parceiro: { desktop: [1248, 1040], tablet: [1376, 774], mobile: [700, 524] }\n};\nconst SHOWN = {\n  carrossel: { desktop: '1440 × 580 (proporção larga)', tablet: '768 × 520', mobile: '390 × 560 (retrato)' },\n  parceiro: { desktop: '624 × 520', tablet: '688 × 387', mobile: '350 × 262' }\n};\nconst SLOTS = [['desktop', 'imagem', 'Desktop', true], ['tablet', 'imagem_tablet', 'Tablet', false], ['mobile', 'imagem_mobile', 'Mobile', false]];\nconst DESTINOS = [\n  { v: '', l: 'Sem botão' }, { v: 'categoria', l: 'Uma categoria da loja' }, { v: 'produto', l: 'Um produto da loja' },\n  { v: 'todos', l: 'Todos os produtos' }, { v: 'link', l: 'Link livre (site, YouTube, Instagram…)' }\n];\nconst blank = () => ({ id: '', ativo: true, eyebrow: '', titulo: '', texto: '', botao: '', destino_tipo: '', destino_valor: '', alt: '', imagem: '', imagem_tablet: '', imagem_mobile: '', numero: '', pontos: '', tom: 'ink', inverter: false });\nconst toDraft = (b) => Object.assign(blank(), {\n  id: b._id, ativo: b.ativo !== false, eyebrow: b.eyebrow || '', titulo: b.titulo || '', texto: b.texto || '', botao: b.botao || '',\n  destino_tipo: b.destino_tipo || '', destino_valor: b.destino_valor || '', alt: b.alt || '', imagem: b.imagem || '',\n  imagem_tablet: b.imagem_tablet || '', imagem_mobile: b.imagem_mobile || '', numero: b.numero || '', pontos: b.pontos || '',\n  tom: b.tom === 'graphite' ? 'graphite' : 'ink', inverter: !!b.inverter\n});\nconst modoOf = (d2) => {\n  try {\n    const path = String(location.pathname || '');\n    if (path.indexOf('/destaques/banner') === 0) return 'parceiro';\n    if (path.indexOf('/destaques/carrossel') === 0) return 'carrossel';\n  } catch (e) { /* sem location */ }\n  return String((d2.params || {}).tipo || '') === 'parceiro' ? 'parceiro' : 'carrossel';\n};\n// Reduz a imagem (maior lado até 2400 px) e converte para JPEG. O corte de cada tela (desktop, tablet e celular) é feito pela loja, no centro.\nconst fitTo = async (file) => {\n  if (['image/jpeg', 'image/png', 'image/webp'].indexOf(file.type) < 0) throw new Error('fmt');\n  const bmp = await createImageBitmap(file);\n  const sc = Math.min(1, 2400 / Math.max(bmp.width, bmp.height));\n  const W = Math.max(1, Math.round(bmp.width * sc)); const H = Math.max(1, Math.round(bmp.height * sc));\n  const cv = document.createElement('canvas'); cv.width = W; cv.height = H;\n  const cx = cv.getContext('2d'); cx.fillStyle = '#fff'; cx.fillRect(0, 0, W, H);\n  cx.drawImage(bmp, 0, 0, W, H);\n  const blob = await new Promise((r) => cv.toBlob(r, 'image/jpeg', 0.86));\n  if (!blob) throw new Error('crop');\n  return new Promise((r, j) => { const fr = new FileReader(); fr.onload = () => r(String(fr.result).split(',')[1]); fr.onerror = j; fr.readAsDataURL(blob); });\n};\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.back { margin-bottom: 16px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.notice.below { margin: 20px 0 0; }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\n.form { padding: 28px; display: flex; flex-direction: column; gap: 22px; margin-bottom: 20px; }\n.form h3 { margin: 0; font-size: 16px; font-weight: 800; }\n.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }\n.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }\n.lbl { font-size: 13px; font-weight: 700; }\n.hint { font-size: 12px; color: var(--g500); line-height: 1.4; }\n.inp, .sel, .ta { width: 100%; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 12px; font: inherit; font-size: 15px; color: var(--ink); }\n.inp, .sel { height: 46px; }\n.ta { padding: 10px 12px; min-height: 84px; resize: vertical; line-height: 1.45; }\n.inp:focus, .sel:focus, .ta:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.ck { display: flex; align-items: flex-start; gap: 10px; font-size: 15px; font-weight: 600; cursor: pointer; }\n.ck input { margin-top: 3px; width: 18px; height: 18px; accent-color: var(--ink); }\n.ck small { display: block; margin-top: 2px; font-size: 12px; font-weight: 400; color: var(--g500); }\n.imgs { display: block; }\n.pv.big { height: 220px; }\n.prevs { display: flex; gap: 20px; align-items: flex-end; margin: 4px 0; }\n.prevs figure { margin: 0; text-align: center; font-size: 11px; color: var(--g500); }\n.prevs .fr { background-size: cover; background-position: center; border: 1px solid var(--g100); border-radius: 2px; margin-bottom: 4px; }\n.slot { display: flex; flex-direction: column; gap: 8px; }\n.pv { display: flex; align-items: center; justify-content: center; height: 150px; background: var(--g50); border: 1px dashed #c9c9c6; border-radius: 2px; color: var(--g500); font-size: 13px; overflow: hidden; }\n.pv img { width: 100%; height: 100%; object-fit: cover; }\n.btns { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }\n.sp { flex: 1; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); text-decoration: underline; text-underline-offset: 4px; cursor: pointer; }\n.lk.danger { color: var(--red); }\n.lk[disabled] { opacity: .35; cursor: not-allowed; }\n.up { cursor: pointer; }\n.err { margin: 0; font-size: 13px; font-weight: 600; color: var(--red); }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 20px; border-bottom: 1px solid var(--g100); }\ntd { padding: 14px 20px; border-bottom: 1px solid var(--g100); font-size: 15px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.n { width: 48px; font-weight: 800; }\ntd.th img { display: block; width: 128px; height: 52px; object-fit: cover; border: 1px solid var(--g100); border-radius: 2px; }\ntd.th .ph { display: flex; align-items: center; justify-content: center; width: 128px; height: 52px; background: var(--g50); color: var(--g500); font-size: 11px; border: 1px dashed #c9c9c6; }\ntd small { display: block; margin-top: 2px; color: var(--g500); font-size: 12px; }\n.pill { display: inline-flex; align-items: center; gap: 8px; height: 26px; padding: 0 12px; border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }\n.pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }\n.pill.on { background: var(--g100); color: var(--ink); }\n.pill.off { background: #fbe9ec; color: var(--red); }\n.acts { display: flex; gap: 16px; justify-content: flex-end; align-items: center; }\n.count { margin: 12px 0 0; color: var(--g500); font-size: 13px; }\n.ovl { position: absolute; inset: 0; z-index: 30; display: flex; align-items: flex-start; justify-content: center; padding-top: 160px; background: rgba(11, 11, 12, .55); }\n.dlg { width: 480px; max-width: 92%; padding: 32px; background: var(--white); border-radius: 2px; }\n.dlg h2 { margin: 0 0 12px; font-size: 22px; font-weight: 800; }\n.dlg p { margin: 0 0 24px; color: var(--g800); font-size: 15px; line-height: 1.5; }\n.dlg .row { display: flex; gap: 12px; justify-content: flex-end; }\n`,\n\n  mounted(d2) {\n    const S = d2.state;\n    const root = d2.root;\n    const modo = () => modoOf(d2);\n    const draft = () => { if (!root.__lfBf) root.__lfBf = blank(); return root.__lfBf; };\n    const say = (tone, body) => { S.notice = { tone, body }; S.noticeWhere = ''; };\n    const mine = () => (S.items || []).filter((b) => b.tipo === modo()).sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0));\n\n    const reload = async () => {\n      try {\n        const res = await d2.rush('gestaoBannersListar').invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.items = out && out.ok ? (out.banners || []) : [];\n        S.cats = out && out.ok ? (out.categorias || []) : [];\n        S.prods = out && out.ok ? (out.produtos || []) : [];\n        if (modo() === 'parceiro' && !S.parcInit) {\n          S.parcInit = true;\n          const b = mine()[0];\n          root.__lfBf = b ? toDraft(b) : blank();\n          S.view = 'form';\n        }\n      } catch (e) { say('error', 'Não foi possível carregar os banners.'); }\n      S.loaded = true;\n    };\n\n    const openNew = () => { root.__lfBf = blank(); S.view = 'form'; S.notice = null; S.upErr = ''; S.tick = Date.now(); };\n    const openEdit = (id) => {\n      const b = (S.items || []).find((x) => x._id === id);\n      if (!b) return;\n      root.__lfBf = toDraft(b); S.view = 'form'; S.notice = null; S.upErr = ''; S.tick = Date.now();\n    };\n    const toList = () => { S.view = 'lista'; S.notice = null; };\n\n    const save = async () => {\n      if (S.busy || S.upBusy) return;\n      const f = draft();\n      S.busy = true; S.notice = null;\n      const known = new Set((S.items || []).map((b) => b._id));\n      try {\n        const res = await d2.rush('gestaoBannerSalvar').invoke({ id: f.id || undefined, tipo: modo(), ativo: f.ativo, eyebrow: f.eyebrow, titulo: f.titulo, texto: f.texto, botao: f.botao, destino_tipo: f.destino_tipo, destino_valor: f.destino_valor, alt: f.alt, imagem: f.imagem, imagem_tablet: '', imagem_mobile: '', numero: f.numero, pontos: f.pontos, tom: f.tom, inverter: f.inverter });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível salvar o banner.'); }\n        else {\n          await reload();\n          if (!f.id) { const novo = mine().find((b) => !known.has(b._id)); if (novo) f.id = novo._id; }\n          say('success', modo() === 'parceiro' ? 'Banner de parceiro salvo.' : 'Banner salvo.');\n        }\n      } catch (e) { say('error', 'Não foi possível salvar o banner. Tente de novo.'); }\n      S.busy = false;\n    };\n\n    const del = async () => {\n      if (!S.confirm || S.busy) return;\n      S.busy = true;\n      try {\n        const res = await d2.rush('gestaoBannerExcluir').invoke({ id: S.confirm.id });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (out && out.ok === false) say('error', out.error || 'Não foi possível excluir o banner.');\n        else { await reload(); S.view = 'lista'; say('success', 'Banner excluído.'); }\n      } catch (e) { say('error', 'Não foi possível excluir o banner. Nada foi alterado.'); }\n      S.confirm = null; S.busy = false;\n    };\n\n    const move = async (id, dir) => {\n      if (S.busy) return;\n      const l = mine().map((b) => b._id);\n      const i = l.indexOf(id); const j = i + dir;\n      if (i < 0 || j < 0 || j >= l.length) return;\n      const t = l[i]; l[i] = l[j]; l[j] = t;\n      S.busy = true;\n      try {\n        await d2.rush('gestaoBannersOrdenar').invoke({ items: l.map((x, k) => ({ id: x, ordem: k + 1 })) });\n        await reload(); say('success', 'Ordem atualizada.');\n      } catch (e) { say('error', 'Não foi possível mudar a ordem.'); }\n      S.busy = false;\n    };\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'new') openNew();\n      else if (act === 'edit') openEdit(id);\n      else if (act === 'back') toList();\n      else if (act === 'save') save();\n      else if (act === 'up') move(id, -1);\n      else if (act === 'down') move(id, 1);\n      else if (act === 'delete') { const b = (S.items || []).find((x) => x._id === id); S.confirm = { id, titulo: b ? b.titulo : '' }; }\n      else if (act === 'delete-form') { S.confirm = { id: draft().id, titulo: draft().titulo }; }\n      else if (act === 'cancel') S.confirm = null;\n      else if (act === 'confirm-go') del();\n      else if (act === 'img-clear') { const k = el.getAttribute('data-k'); draft()[k] = ''; S.tick = Date.now(); }\n    });\n    d2.on('[data-f]', 'input', (e, el) => { draft()[el.getAttribute('data-f')] = el.value; });\n    d2.on('select[data-s]', 'change', (e, el) => {\n      const k = el.getAttribute('data-s');\n      draft()[k] = el.value;\n      if (k === 'destino_tipo') draft().destino_valor = '';\n      S.tick = Date.now();\n    });\n    d2.on('input[data-c]', 'change', (e, el) => { draft()[el.getAttribute('data-c')] = el.checked; });\n    d2.on('input[data-up]', 'change', async (e, el) => {\n      const slot = el.getAttribute('data-up'); const key = el.getAttribute('data-k');\n      const f = el.files && el.files[0];\n      if (!f) return;\n      S.upErr = ''; S.upBusy = slot;\n      try {\n        const b64 = await fitTo(f);\n        const res = await d2.rush('gestaoBannerImagem').invoke({ base64: b64, tag: modo() + '-' + slot + '-' + Date.now() });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (!out || !out.ok || !out.url) throw new Error((out && out.error) || 'upload');\n        draft()[key] = out.url;\n      } catch (err) {\n        S.upErr = err && err.message === 'fmt' ? 'Formato não aceito. Use JPG, PNG ou WebP.' : ((err && err.message && err.message !== 'upload' && err.message !== 'crop') ? err.message : 'Não foi possível enviar a imagem. Tente de novo.');\n      }\n      S.upBusy = ''; S.tick = Date.now();\n      try { el.value = ''; } catch (x) { /* sem input */ }\n    });\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = Object.assign({}, d2.params || {}, { button: refOf(d2.param('button')), notice: refOf(d2.param('notice')), backLink: refOf(d2.param('backLink')), emptyState: refOf(d2.param('emptyState')), iconComponent: refOf(d2.param('iconComponent')) });\n    void S.tick;\n    const modo = modoOf(d2);\n    const parceiro = modo === 'parceiro';\n    if (S.view === undefined) { S.view = parceiro ? 'form' : 'lista'; S.notice = null; S.busy = false; S.confirm = null; S.upErr = ''; S.upBusy = ''; }\n    const btn = (label, variant, act, key, w, id) => `<span data-act=\"${act}\"${id ? ` data-id=\"${esc(id)}\"` : ''} style=\"display:inline-block\">${d2.render(P.button, (S.busy || S.upBusy) && (act === 'save' || act === 'confirm-go') ? { label: act === 'save' ? (S.busy ? 'Salvando…' : 'Enviando imagem…') : 'Aguarde…', variant, size: 'sm', isDisabled: true } : { label, variant, size: 'sm' }, { key: (S.busy || S.upBusy) && (act === 'save' || act === 'confirm-go') ? key + '-busy' : key, width: w, height: 44 })}</span>`;\n    const noticeHtml = S.notice && P.notice\n      ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true, iconComponent: P.iconComponent }, { key: 'notice-' + S.notice.tone, style: 'height:auto' })}</div>` : '';\n    const noticeBelow = noticeHtml.replace('class=\"notice\"', 'class=\"notice below\"');\n    const items = (S.items || []).filter((b) => b.tipo === modo).sort((a, b) => (Number(a.ordem) || 0) - (Number(b.ordem) || 0));\n    const titulo = parceiro ? 'Banner de parceiro' : 'Carrossel da Home';\n    const subtitulo = parceiro\n      ? 'O painel escuro de parceria que aparece na home (hoje, a BPower). Só existe um: ligue ou desligue e edite os textos e as imagens.'\n      : `Os banners do topo da home, até ${MAX_CARR}, na ordem em que passam (a troca é automática, a cada 6 segundos). Sem banner ativo, a home mostra o carrossel padrão.`;\n\n    let main = '';\n    if (!S.loaded) {\n      main = '<div class=\"card form\"><p class=\"sub\">Carregando…</p></div>';\n    } else if (S.view === 'form' || parceiro) {\n      const f = d2.root && d2.root.__lfBf ? d2.root.__lfBf : blank();\n      const edit = !!f.id;\n      const input = (k, label, ph, max, hint) => `<div class=\"field\"><label class=\"lbl\" for=\"f-${k}\">${label}</label><input id=\"f-${k}\" class=\"inp\" data-f=\"${k}\" value=\"${esc(f[k])}\" placeholder=\"${esc(ph || '')}\" maxlength=\"${max}\" autocomplete=\"off\">${hint ? `<span class=\"hint\">${hint}</span>` : ''}</div>`;\n      const area = (k, label, ph, max, hint) => `<div class=\"field\"><label class=\"lbl\" for=\"f-${k}\">${label}</label><textarea id=\"f-${k}\" class=\"ta\" data-f=\"${k}\" placeholder=\"${esc(ph || '')}\" maxlength=\"${max}\">${esc(f[k])}</textarea>${hint ? `<span class=\"hint\">${hint}</span>` : ''}</div>`;\n      const dest = DESTINOS.map((o) => `<option value=\"${o.v}\"${f.destino_tipo === o.v ? ' selected' : ''}>${o.l}</option>`).join('');\n      let destVal = '';\n      if (f.destino_tipo === 'categoria') destVal = `<div class=\"field\"><label class=\"lbl\" for=\"f-dv\">Categoria</label><select id=\"f-dv\" class=\"sel\" data-s=\"destino_valor\"><option value=\"\">Escolha…</option>${(S.cats || []).map((c) => `<option value=\"${esc(c.slug)}\"${f.destino_valor === c.slug ? ' selected' : ''}>${esc(c.nome)}</option>`).join('')}</select></div>`;\n      else if (f.destino_tipo === 'produto') destVal = `<div class=\"field\"><label class=\"lbl\" for=\"f-dv\">Produto</label><select id=\"f-dv\" class=\"sel\" data-s=\"destino_valor\"><option value=\"\">Escolha…</option>${(S.prods || []).map((p) => `<option value=\"${esc(p.slug)}\"${f.destino_valor === p.slug ? ' selected' : ''}>${esc(p.nome)}</option>`).join('')}</select></div>`;\n      else if (f.destino_tipo === 'link') destVal = input('destino_valor', 'Link', 'https://www.youtube.com/…', 300, 'Começa com https:// (site de parceiro, YouTube, Instagram…) ou com / (uma página da loja).');\n      else destVal = '<div></div>';\n      const url = f.imagem;\n      const PROP = parceiro ? [[160, 133, 'Desktop'], [141, 79, 'Tablet'], [105, 79, 'Mobile']] : [[160, 64, 'Desktop'], [120, 81, 'Tablet'], [80, 115, 'Mobile']];\n      const slots = `<div class=\"slot\" data-key=\"slot-img\"><span class=\"lbl\">Imagem do banner (obrigatória)</span>\n          <div class=\"pv big\">${S.upBusy ? 'Enviando…' : (url ? `<img src=\"${esc(url)}\" alt=\"Prévia da imagem\">` : 'Sem imagem')}</div>\n          <div class=\"btns\"><label class=\"lk up\">${url ? 'Trocar imagem' : 'Escolher imagem'}<input type=\"file\" data-up=\"desktop\" data-k=\"imagem\" accept=\"image/jpeg,image/png,image/webp\" hidden></label></div>\n          ${url ? `<div class=\"prevs\">${PROP.map((q) => `<figure><div class=\"fr\" style=\"width:${q[0]}px;height:${q[1]}px;background-image:url('${esc(url)}')\"></div><figcaption>${q[2]}</figcaption></figure>`).join('')}</div>` : ''}\n          <span class=\"hint\">Uma imagem só: a loja corta no centro para cada tela (larga no desktop, em pé no celular). Use uma imagem grande, de preferência larga, com o assunto no meio. JPG, PNG ou WebP, até 5 MB.</span></div>`;\n      const extras = parceiro ? `\n        <h3>Parceria</h3>\n        <div class=\"grid2\"><div class=\"field\"><label class=\"lbl\" for=\"f-tom\">Cor do fundo</label><select id=\"f-tom\" class=\"sel\" data-s=\"tom\"><option value=\"ink\"${f.tom === 'ink' ? ' selected' : ''}>Preto</option><option value=\"graphite\"${f.tom === 'graphite' ? ' selected' : ''}>Grafite</option></select></div><div></div></div>\n        ${area('pontos', 'Vantagens (uma por linha)', 'Enviado direto pelo parceiro\\nGarantia da loja oficial', 240, 'Até 4 linhas curtas, com um check ao lado. Deixe vazio para não mostrar.')}\n        <label class=\"ck\"><input type=\"checkbox\" data-c=\"inverter\"${f.inverter ? ' checked' : ''}><span>Foto à esquerda<small>Troca o lado: por padrão, o texto fica à esquerda e a foto à direita.</small></span></label>` : '';\n      const delBtn = edit && !parceiro ? '<button class=\"lk danger\" data-act=\"delete-form\">Excluir banner</button>' : '';\n      main = `\n<div class=\"page\">\n  ${parceiro ? '' : `<div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${P.backLink ? d2.render(P.backLink, { label: 'Voltar para o carrossel' }, { key: 'back', width: 260, height: 32 }) : ''}</span></div>`}\n  <div class=\"head\"><div><p class=\"eyebrow\">Destaques</p><h1>${parceiro ? titulo : (edit ? 'Editar banner' : 'Novo banner')}</h1><p class=\"sub\">${subtitulo}</p></div></div>\n  <div class=\"card form\">\n    <label class=\"ck\"><input type=\"checkbox\" data-c=\"ativo\"${f.ativo ? ' checked' : ''}><span>Ativo<small>${parceiro ? 'Desligado, o painel some da home.' : 'Banner inativo não aparece no carrossel.'}</small></span></label>\n    <h3>Textos</h3>\n    ${input('eyebrow', 'Texto pequeno acima do título', parceiro ? 'Parceria oficial' : 'Loja oficial do canal', 40, 'Até 40 caracteres.')}\n    ${area('titulo', 'Título', 'Testado no canal.\\nAprovado na garagem.', 60, 'Até 60 caracteres. Enter quebra a linha. Escreva curto: o mesmo texto aparece no desktop, no tablet e no celular.')}\n    ${area('texto', 'Texto de apoio', 'Os produtos que aparecem nos vídeos do Lucas…', 160, 'Até 160 caracteres. Opcional.')}\n    <h3>Botão</h3>\n    <div class=\"grid2\">${input('botao', 'Texto do botão', 'Ver os mais vendidos', 24, 'Até 24 caracteres. Deixe vazio para não mostrar botão.')}\n      <div class=\"field\"><label class=\"lbl\" for=\"f-dt\">Para onde o botão leva</label><select id=\"f-dt\" class=\"sel\" data-s=\"destino_tipo\">${dest}</select></div></div>\n    ${destVal !== '<div></div>' ? `<div class=\"grid2\">${destVal}<div></div></div>` : ''}\n    ${extras}\n    <h3>Imagens</h3>\n    <div class=\"imgs\">${slots}</div>\n    ${S.upErr ? `<p class=\"err\">${esc(S.upErr)}</p>` : ''}\n    ${input('alt', 'Descrição da imagem (para leitores de tela)', 'Lucas Fontana lavando um carro', 120, 'Opcional, mas ajuda quem usa leitor de tela e o Google.')}\n  </div>\n  <div class=\"btns\">${btn(S.busy ? 'Salvando...' : (parceiro ? 'Salvar banner' : (edit ? 'Salvar banner' : 'Criar banner')), 'primary', 'save', 'save', 220)}${parceiro ? '' : btn('Cancelar', 'outline', 'back', 'cancel', 130)}<span class=\"sp\"></span>${delBtn}</div>\n  ${noticeBelow}\n</div>`;\n    } else {\n      const total = items.length;\n      const rows = items.map((b, i) => `\n    <tr data-key=\"${esc(b._id)}\">\n      <td class=\"n\">${i + 1}</td>\n      <td class=\"th\">${b.imagem ? `<img src=\"${esc(b.imagem)}\" alt=\"\">` : '<div class=\"ph\">Sem imagem</div>'}</td>\n      <td><b>${esc(b.titulo).split('\\n').join(' · ')}</b>${b.eyebrow ? `<small>${esc(b.eyebrow)}</small>` : ''}</td>\n      <td>${b.botao ? esc(b.botao) + `<small>${esc(b.link || '')}</small>` : '<small>Sem botão</small>'}</td>\n      <td><span class=\"pill ${b.ativo ? 'on' : 'off'}\">${b.ativo ? 'Ativo' : 'Inativo'}</span></td>\n      <td><div class=\"acts\"><button class=\"lk\" data-act=\"up\" data-id=\"${esc(b._id)}\" aria-label=\"Subir\"${i === 0 ? ' disabled' : ''}>↑</button><button class=\"lk\" data-act=\"down\" data-id=\"${esc(b._id)}\" aria-label=\"Descer\"${i === total - 1 ? ' disabled' : ''}>↓</button>\n        <button class=\"lk\" data-act=\"edit\" data-id=\"${esc(b._id)}\">Editar</button><button class=\"lk danger\" data-act=\"delete\" data-id=\"${esc(b._id)}\">Excluir</button></div></td>\n    </tr>`).join('');\n      const body = total\n        ? `<div class=\"card\"><table><thead><tr><th>#</th><th>Imagem</th><th>Título</th><th>Botão</th><th>Status</th><th></th></tr></thead><tbody>${rows}</tbody></table></div><p class=\"count\">${total} de ${MAX_CARR} banners${total >= MAX_CARR ? '. Limite atingido: exclua um para criar outro.' : '.'}</p>`\n        : `<div class=\"card form\">${P.emptyState ? d2.render(P.emptyState, { eyebrow: 'Carrossel', title: 'Nenhum banner cadastrado', description: 'A home está mostrando o carrossel padrão. Crie o primeiro banner para trocar.', iconName: 'image' }, { key: 'empty', height: 260 }) : ''}</div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Destaques</p><h1>${titulo}</h1><p class=\"sub\">${subtitulo}</p></div>${total < MAX_CARR ? btn('Novo banner', 'primary', 'new', 'new', 200) : ''}</div>\n  ${noticeHtml}\n  ${body}\n</div>`;\n    }\n\n    const overlay = S.confirm ? `\n<div class=\"ovl\"><div class=\"dlg\" role=\"alertdialog\" aria-modal=\"true\">\n  <h2>Excluir este banner?</h2>\n  <p>${S.confirm.titulo ? '“' + esc(S.confirm.titulo).split('\\n').join(' ') + '” ' : ''}será apagado da home e não dá para desfazer.</p>\n  <div class=\"row\">${btn('Cancelar', 'outline', 'cancel', 'c-cancel', 130)}${btn(S.busy ? 'Excluindo...' : 'Excluir banner', 'primary', 'confirm-go', 'c-ok', 200)}</div>\n</div></div>` : '';\n\n    return `<div class=\"root\">${main}${overlay}</div>`;\n  }\n};\n"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"tipo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"tipo"}}}}}}}}}},{"_id":"6ac69bfeb820ae714e6a65a1","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"HomeHero","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// HomeHero — carrossel de abertura da Home, lido da tabela Banners da Home (tipo carrossel)\n// Mostra até 4 banners ativos, na ordem cadastrada na Gestão (Destaques > Carrossel), com troca automática a cada 6 segundos.\n// Sem nenhum banner ativo, mostra o carrossel padrão (FALLBACK abaixo), para a home nunca ficar sem abertura.\n// Reaproveita o Hero (que desenha slides, setas, bolinhas e foto) pelo parâmetro `hero`, e Button e Icon pelos parâmetros `button` e `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FALLBACK = [\n  { eyebrow: 'No canal da Auto Super', title: 'Assiste primeiro.\\nCompra depois.', text: 'Vídeo novo toda semana. Tudo que aparece por lá, você encontra aqui.', ctaLabel: 'Assistir no YouTube', ctaHref: 'https://www.youtube.com/' },\n  { eyebrow: 'A loja oficial do canal', title: 'Testado no canal.\\nAprovado na garagem.', text: 'Os produtos que aparecem nos vídeos da Auto Super, direto da loja oficial.', ctaLabel: 'Ver todos os produtos', ctaHref: '/lista-de-produtos/q' },\n  { eyebrow: 'Parceria BPower Motorsport', title: 'Performance\\nde verdade.', text: 'A linha de performance que a Auto Super usa e recomenda, enviada direto pela BPower.', ctaLabel: 'Ver linha BPower', ctaHref: '/lista-de-produtos/q?categoria=performance' }\n];\nlet DATA = null;\nlet LOADING = null;\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; }\n* { box-sizing: border-box; }\n.wait { width: 100%; height: 100%; min-height: 360px; background: #0b0b0c; }\n`,\n  mounted(d2) {\n    if (DATA || LOADING) { if (LOADING) LOADING.then(() => { d2.state.tick = Date.now(); }); return; }\n    LOADING = d2.rush('bannersHome').invoke({}).then((res) => {\n      const out = (res && res.data !== undefined ? res.data : res) || {};\n      DATA = out && out.ok ? out : { ok: false };\n    }).catch(() => { DATA = { ok: false }; }).then(() => { LOADING = null; d2.state.tick = Date.now(); });\n  },\n  render(d2) {\n    void d2.state.tick;\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const hero = refOf(d2.param('hero'));\n    if (!DATA || !hero) return '<div class=\"wait\"></div>';\n    const slides = DATA.carrossel && DATA.carrossel.length ? DATA.carrossel : FALLBACK;\n    return d2.render(hero, {\n      layout, slides, autoplay: 6, button: refOf(d2.param('button')), iconComponent: refOf(d2.param('iconComponent'))\n    }, { key: 'hero-' + layout + '-' + slides.length });\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"hero":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"hero"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}}}}}}}},{"_id":"6ac69c0878f3730f24d0291a","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"HomeParceiro","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// HomeParceiro — painel de destaque do parceiro na Home, lido da tabela Banners da Home (tipo parceiro)\n// Só existe um. Ligado na Gestão (Destaques > Banner), mostra os textos, a lista de vantagens, o botão e a foto cadastrados.\n// Desligado na Gestão, o painel some da home. Se ainda não houver nada cadastrado, mostra o painel padrão (FALLBACK abaixo).\n// Reaproveita o FeaturePanel (que desenha o painel) pelo parâmetro `panel`, e Button e Icon por `button` e `iconComponent`.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FALLBACK = {\n  tone: 'ink', eyebrow: 'Parceria oficial', title: 'Performance\\nBPower Motorsport',\n  body: 'A linha de performance que o Lucas usa e recomenda. Produtos enviados diretamente pela BPower, com a garantia da loja oficial.',\n  points: [], ctaLabel: 'Ver linha BPower', ctaHref: '/lista-de-produtos/q?categoria=performance'\n};\nlet DATA = null;\nlet LOADING = null;\nconst lines = (t) => String(t == null ? '' : t).split('\\n').map((s) => s.trim()).filter(Boolean).slice(0, 4);\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.wait { width: 100%; min-height: 520px; background: #0b0b0c; }\n`,\n  mounted(d2) {\n    if (DATA || LOADING) { if (LOADING) LOADING.then(() => { d2.state.tick = Date.now(); }); return; }\n    LOADING = d2.rush('bannersHome').invoke({}).then((res) => {\n      const out = (res && res.data !== undefined ? res.data : res) || {};\n      DATA = out && out.ok ? out : { ok: false };\n    }).catch(() => { DATA = { ok: false }; }).then(() => { LOADING = null; d2.state.tick = Date.now(); });\n  },\n  render(d2) {\n    void d2.state.tick;\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const panel = refOf(d2.param('panel'));\n    if (!DATA || !panel) return '<div class=\"wait\"></div>';\n    let cfg = null;\n    if (DATA.parceiro) cfg = Object.assign({}, DATA.parceiro, { points: lines(DATA.parceiro.pontos) });\n    else if (!DATA.temParceiro) cfg = FALLBACK;\n    if (!cfg) return '';\n    return d2.render(panel, {\n      layout, tone: cfg.tone === 'graphite' ? 'graphite' : 'ink', eyebrow: cfg.eyebrow || '', title: cfg.title || '',\n      body: cfg.body || '', points: cfg.points || [], ctaLabel: cfg.ctaLabel || '', ctaHref: cfg.ctaHref || '',\n      image: cfg.image || '', imageTablet: cfg.imageTablet || '', imageMobile: cfg.imageMobile || '', imageAlt: cfg.imageAlt || '',\n      reverse: !!cfg.reverse, button: refOf(d2.param('button')), iconComponent: refOf(d2.param('iconComponent'))\n    }, { key: 'panel-' + layout });\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"panel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"panel"}}}}}}}}}},{"_id":"6ac6f77ad0e6a463e357669c","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoMensagens","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Mensagens (backoffice, só desktop)\n// As mensagens do formulário \"Fale com a Auto Super\". Duas telas na mesma página:\n// a lista (mais novas primeiro, com busca, filtro por assunto, \"só não lidas\" e paginação de 20 em 20, mais o total e as não lidas)\n// e, ao abrir uma mensagem, o detalhe completo: contato, e-mail e WhatsApp com link, empresa e proposta (quando \"Quero contratar\"),\n// se a pessoa estava logada e a mensagem inteira. Abrir marca como lida; dá para marcar de volta como não lida.\n// Só leitura: não existe excluir nem editar. Reaproveita Button, BackLink, EmptyState e Notice; o resto é HTML simples com os tokens da Gestão.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst fmtDT = (v) => { try { const d = new Date(v); return isNaN(d.getTime()) ? '' : d.toLocaleDateString('pt-BR') + ' ' + d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\nconst ASSUNTOS = [['elogio', 'Elogio'], ['video', 'Sugestão de vídeo'], ['produto', 'Ideia de produto'], ['contratar', 'Quero contratar a Auto Super'], ['outro', 'Outro assunto']];\nconst PROPOSTAS = { publi: 'Publi / conteúdo patrocinado', evento: 'Evento', entrevista: 'Entrevista / imprensa', outro: 'Outro' };\nconst wpp = (n) => { const d = String(n || '').replace(/[^0-9]/g, ''); if (!d) return ''; return 'https://wa.me/' + (d.length <= 11 ? '55' + d : d); };\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  return `<nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\nconst PAGER_CSS = `\n.pager { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 20px; flex-wrap: wrap; }\n.pg { min-width: 40px; height: 40px; padding: 0 12px; border: 1.5px solid #ececea; background: #fff; color: #0b0b0c; font: inherit; font-size: 13px; font-weight: 700; border-radius: 2px; cursor: pointer; }\n.pg.on { background: #0b0b0c; color: #fff; border-color: #0b0b0c; }\n.pg[disabled] { opacity: .35; cursor: not-allowed; }\n.pg:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.dots { padding: 0 4px; color: #6b6c70; }`;\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.back { margin-bottom: 16px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }\n.search { flex: 1; min-width: 240px; max-width: 380px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.sel { height: 44px; min-width: 200px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 12px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus, .sel:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.chip { height: 44px; padding: 0 16px; border: 1.5px solid var(--g100); background: var(--white); color: var(--ink); font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; border-radius: 2px; cursor: pointer; }\n.chip[aria-pressed=\"true\"] { background: var(--ink); color: var(--white); border-color: var(--ink); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 16px; border-bottom: 1px solid var(--g100); }\ntd { padding: 14px 16px; border-bottom: 1px solid var(--g100); font-size: 14px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntr.nova td { background: #fffaf0; }\ntd.dot { width: 28px; padding-right: 0; }\n.dt { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--red); }\n.dt.off { background: transparent; border: 1.5px solid var(--g100); }\ntd.nome { font-weight: 700; }\ntd small { display: block; margin-top: 2px; color: var(--g500); font-size: 12px; font-weight: 400; }\ntd.data { color: var(--g500); white-space: nowrap; }\ntd.msg { color: var(--g800); max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.pill { display: inline-flex; align-items: center; height: 26px; padding: 0 12px; border-radius: 999px; background: var(--g100); font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }\n.pill.hire { background: var(--ink); color: var(--white); }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); text-decoration: underline; text-underline-offset: 4px; cursor: pointer; }\n.count { margin: 12px 0 0; color: var(--g500); font-size: 13px; text-align: center; }\n.empty { padding: 8px; }\n.det { padding: 32px; display: flex; flex-direction: column; gap: 28px; }\n.dh { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }\n.dh .when { color: var(--g500); font-size: 14px; }\n.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 32px; }\n.fld span.l { display: block; margin-bottom: 4px; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); }\n.fld div.v { font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }\n.fld a { color: var(--ink); text-underline-offset: 3px; }\n.fld a:hover { color: var(--red); }\n.body { padding: 24px; background: var(--g50); border-left: 4px solid var(--ink); font-size: 16px; line-height: 1.65; white-space: pre-line; overflow-wrap: anywhere; color: var(--g800); }\n.btns { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }\n.reply { display: inline-flex; align-items: center; height: 44px; padding: 0 22px; background: var(--ink); color: var(--white); border-radius: 2px; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; text-decoration: none; }\n.reply:hover { background: var(--red); }\n${PAGER_CSS}\n`,\n\n  mounted(d2) {\n    const S = d2.state;\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const reload = async () => {\n      try {\n        const res = await d2.rush('gestaoMensagensListar').invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.msgs = out && out.ok ? (out.mensagens || []) : [];\n        S.total = out && out.ok ? Number(out.total) || 0 : 0;\n        S.naoLidas = out && out.ok ? Number(out.naoLidas) || 0 : 0;\n      } catch (e) { say('error', 'Não foi possível carregar as mensagens.'); }\n      S.loaded = true;\n    };\n    const marcar = async (id, lida) => {\n      try {\n        const res = await d2.rush('gestaoMensagemMarcar').invoke({ id, lida });\n        const out = res && res.data !== undefined ? res.data : res;\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível atualizar a mensagem.'); return; }\n        S.msgs = (S.msgs || []).map((m) => (m._id === id ? Object.assign({}, m, { lida }) : m));\n        S.naoLidas = (S.msgs || []).filter((m) => !m.lida).length;\n      } catch (e) { say('error', 'Não foi possível atualizar a mensagem.'); }\n    };\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'open') {\n        S.id = id; S.view = 'detalhe'; S.notice = null;\n        const m = (S.msgs || []).find((x) => x._id === id);\n        if (m && !m.lida) marcar(id, true);\n      } else if (act === 'back') { S.view = 'lista'; S.notice = null; }\n      else if (act === 'unread') { marcar(id, false); S.view = 'lista'; }\n      else if (act === 'only-new') { S.fNew = !S.fNew; S.page = 1; }\n    });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; S.page = 1; });\n    d2.on('select[data-fa]', 'change', (e, el) => { S.fA = el.value; S.page = 1; });\n    d2.on('[data-pg]', 'click', (e, el) => { S.page = Number(el.getAttribute('data-pg')) || 1; });\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = Object.assign({}, d2.params || {}, { button: refOf(d2.param('button')), notice: refOf(d2.param('notice')), backLink: refOf(d2.param('backLink')), emptyState: refOf(d2.param('emptyState')), iconComponent: refOf(d2.param('iconComponent')) });\n    if (S.view === undefined) { S.view = 'lista'; S.q = ''; S.fA = ''; S.fNew = false; S.page = 1; S.id = ''; S.notice = null; }\n    const msgs = S.msgs || [];\n    const notice = S.notice && P.notice\n      ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true, iconComponent: P.iconComponent }, { key: 'notice', style: 'height:auto' })}</div>` : '';\n    const pill = (m) => `<span class=\"pill${m.assunto === 'contratar' ? ' hire' : ''}\">${esc(m.assuntoRotulo || m.assunto)}</span>`;\n    let main = '';\n\n    if (S.view === 'detalhe') {\n      const m = msgs.find((x) => x._id === S.id);\n      if (!m) { S.view = 'lista'; return `<div class=\"root\"></div>`; }\n      const w = wpp(m.whatsapp);\n      const hire = m.assunto === 'contratar';\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${P.backLink ? d2.render(P.backLink, { label: 'Voltar para as mensagens' }, { key: 'back', width: 260, height: 32 }) : ''}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Mensagem</p><h1>${esc(m.nome)}</h1><p class=\"sub\">${esc(m.email)}</p></div></div>\n  ${notice}\n  <div class=\"card det\">\n    <div class=\"dh\">${pill(m)}<span class=\"when\">Recebida em ${esc(fmtDT(m.data))}</span></div>\n    <div class=\"grid\">\n      <div class=\"fld\"><span class=\"l\">Nome</span><div class=\"v\">${esc(m.nome)}</div></div>\n      <div class=\"fld\"><span class=\"l\">E-mail</span><div class=\"v\"><a href=\"mailto:${esc(m.email)}\">${esc(m.email)}</a></div></div>\n      <div class=\"fld\"><span class=\"l\">WhatsApp</span><div class=\"v\">${m.whatsapp ? `<a href=\"${esc(w)}\" target=\"_blank\" rel=\"noopener\">${esc(m.whatsapp)}</a>` : '—'}</div></div>\n      <div class=\"fld\"><span class=\"l\">Quem enviou</span><div class=\"v\">${m.logado ? 'Cliente logado' + (m.clienteNome ? ' · ' + esc(m.clienteNome) : '') : 'Visitante'}</div></div>\n      ${hire ? `<div class=\"fld\"><span class=\"l\">Empresa ou veículo</span><div class=\"v\">${esc(m.empresa || '—')}</div></div>\n      <div class=\"fld\"><span class=\"l\">Tipo de proposta</span><div class=\"v\">${esc(PROPOSTAS[m.tipoProposta] || m.tipoProposta || '—')}</div></div>` : ''}\n    </div>\n    <div class=\"fld\"><span class=\"l\">Mensagem</span><div class=\"body\">${esc(m.mensagem)}</div></div>\n    <div class=\"btns\"><a class=\"reply\" href=\"mailto:${esc(m.email)}?subject=${encodeURIComponent('Re: ' + (m.assuntoRotulo || 'Sua mensagem para o Lucas'))}\">Responder por e-mail</a>\n      <button class=\"lk\" data-act=\"unread\" data-id=\"${esc(m._id)}\">Marcar como não lida</button></div>\n  </div>\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const filtradas = msgs.filter((m) => (!S.fA || m.assunto === S.fA) && (!S.fNew || !m.lida)\n        && (!q || norm(m.nome).includes(q) || norm(m.email).includes(q) || norm(m.mensagem).includes(q) || norm(m.empresa).includes(q)));\n      const pages = Math.max(1, Math.ceil(filtradas.length / PAGE));\n      const cur = Math.min(Math.max(1, Number(S.page) || 1), pages);\n      const slice = filtradas.slice((cur - 1) * PAGE, cur * PAGE);\n      const rows = slice.map((m) => `\n    <tr class=\"${m.lida ? '' : 'nova'}\" data-key=\"${esc(m._id)}\">\n      <td class=\"dot\"><span class=\"dt${m.lida ? ' off' : ''}\" title=\"${m.lida ? 'Lida' : 'Nova'}\"></span></td>\n      <td class=\"data\">${esc(fmtDT(m.data))}</td>\n      <td>${pill(m)}</td>\n      <td class=\"nome\">${esc(m.nome)}${m.logado ? '<small>Cliente logado</small>' : ''}</td>\n      <td>${esc(m.email)}</td>\n      <td class=\"msg\">${esc(m.mensagem)}</td>\n      <td><button class=\"lk\" data-act=\"open\" data-id=\"${esc(m._id)}\">Abrir</button></td>\n    </tr>`).join('');\n      const body = slice.length\n        ? `<div class=\"card\"><table><thead><tr><th></th><th>Recebida</th><th>Assunto</th><th>Nome</th><th>E-mail</th><th>Mensagem</th><th></th></tr></thead><tbody>${rows}</tbody></table></div>${pagerHtml(cur, filtradas.length, PAGE)}<p class=\"count\">${(cur - 1) * PAGE + 1}–${(cur - 1) * PAGE + slice.length} de ${filtradas.length} ${filtradas.length === 1 ? 'mensagem' : 'mensagens'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\" style=\"padding:16px\">Carregando mensagens…</p></div>'\n        : `<div class=\"card empty\">${P.emptyState ? d2.render(P.emptyState, { eyebrow: 'Mensagens', title: msgs.length ? 'Nenhuma mensagem encontrada' : 'Nenhuma mensagem ainda', description: msgs.length ? 'Tente outro termo ou limpe os filtros.' : 'As mensagens enviadas pelo Fale com a Auto Super aparecem aqui.', iconName: 'message' }, { key: 'empty', height: 260 }) : ''}</div>`;\n      const nTotal = S.loaded ? (Number(S.total) || msgs.length) : 0;\n      const nNovas = S.loaded ? (Number(S.naoLidas) || 0) : 0;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Mensagens</h1><p class=\"sub\">${S.loaded ? `<b>${nTotal}</b> ${nTotal === 1 ? 'mensagem' : 'mensagens'} recebidas · <b>${nNovas}</b> ${nNovas === 1 ? 'não lida' : 'não lidas'}` : 'Recados enviados pelo formulário Fale com a Auto Super.'}</p></div></div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por nome, e-mail ou texto\" value=\"${esc(S.q)}\" aria-label=\"Buscar mensagem\">\n    <select class=\"sel\" data-fa aria-label=\"Filtrar por assunto\"><option value=\"\">Todos os assuntos</option>${ASSUNTOS.map((a) => `<option value=\"${a[0]}\"${S.fA === a[0] ? ' selected' : ''}>${a[1]}</option>`).join('')}</select>\n    <button class=\"chip\" data-act=\"only-new\" aria-pressed=\"${!!S.fNew}\">Só não lidas</button></div>\n  ${body}\n</div>`;\n    }\n    return `<div class=\"root\">${main}</div>`;\n  }\n};\n"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}}}}}}}},{"_id":"6ac78451d8a0830023774b52","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"TesteSessaoDados","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// TESTE TEMPORÁRIO — faz login e chama serviços na mesma página (apagar depois)\nexport default {\n  css: `:host{display:block} pre{margin:0;font:12px monospace;padding:8px}`,\n  render() { return '<pre>harness pronto</pre>'; },\n  mounted(d2) {\n    window.__t = {\n      login: async (email, senha) => { try { await d2.auth(d2.param('auth')).login({ credentials: { email, senha } }); return 'ok'; } catch (e) { return 'ERRO ' + (e && e.message); } },\n      logout: async () => { try { await d2.auth(d2.param('auth')).logout(); return 'ok'; } catch (e) { return 'ERRO ' + (e && e.message); } },\n      call: async (key, payload) => { try { const r = await d2.rush(d2.param(key)).invoke(payload || {}); return (r && r.data !== undefined) ? r.data : r; } catch (e) { return { erro: String(e && e.message) }; } }\n    };\n  }\n};"}},"auth":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"auth"}}}},"dadosLer":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dadosLer"}}}},"dadosSalvar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dadosSalvar"}}}},"endExcluir":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"endExcluir"}}}},"endListar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"endListar"}}}},"endPrincipal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"endPrincipal"}}}},"endSalvar":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"endSalvar"}}}}}}}}}},{"_id":"6ac803c2483c5ff5d5bc88e8","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CheckoutSummary","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CheckoutSummary — Auto Super Shop\n// O resumo do pedido da tela de finalizar compra: título \"Resumo do pedido\" + \"Editar sacola\", a lista de itens\n// (foto, nome, detalhe, preço da linha e, nos itens de parceiro, \"Enviado pela …\" em vermelho), subtotal, cupom de desconto\n// (só quando existe), desconto Pix (só quando existe), frete, o Total em destaque com a observação embaixo (\"Pagamento à vista no Pix\"),\n// o aceite dos Termos e Condições de Venda e o botão \"Continuar para pagamento\" com a nota de segurança e o link \"Voltar para a sacola\".\n// `items: [{ sku, name, detail, image, quantity, price, partner, soldOut }]` (preço já formatado). Os valores de subtotal,\n// desconto, frete e total chegam prontos (texto) de quem usa — o componente não calcula nada.\n// `coupon` (opcional): { status: 'ok' | 'invalid', code, discount, message }. 'ok' mostra o cupom já validado (selo verde, o código e o valor\n// descontado, mais a linha verde \"Cupom CÓDIGO −R$ X\" no resumo); 'invalid' mostra o aviso vermelho de que o cupom não vale mais.\n// Aqui o cupom só é exibido: para trocar ou tirar, o cliente volta à sacola (\"Editar sacola\").\n// `termsChecked` marca o aceite; o botão só fica ativo com `isDisabled: false`. `busy: true` troca o rótulo por \"Aguarde…\"\n// e trava o botão. O link dos termos entra no `termsLabel` como `[Termos e Condições de Venda](^/politicas/termos-de-venda)`\n// (o `^` abre em outra aba).\n// Avisa `onTermsChange` (true/false) e `onSubmit` ao clicar no botão. O clique é delegado (span data-act=\"submit\"),\n// porque o callback do Button passado por d2.render não dispara no app publicado.\n// Não desenha ícone, caixa de seleção nem botão: usa Icon, Checkbox e Button por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst DEMO = [\n  { sku: 'A', name: 'Shampoo automotivo neutro', detail: 'Qtd.: 2', image: '', quantity: 2, price: 'R$ 00,00', partner: '' },\n  { sku: 'B', name: 'Pretinho para pneus', detail: 'Qtd.: 1', image: '', quantity: 1, price: 'R$ 00,00', partner: '' },\n  { sku: 'C', name: '[Nome do produto BPower]', detail: 'Qtd.: 1', image: '', quantity: 1, price: 'R$ 0.000,00', partner: 'Enviado pela BPower' }\n];\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.cs { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-100: #f5f5f3; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2; --green: #0a7a3d;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; background: var(--grey-100); color: var(--ink); padding: 24px; display: flex; flex-direction: column; gap: 16px; width: 100%; }\n.top { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }\nh2 { margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; }\n.edit { font-size: 13px; font-weight: 600; color: var(--ink); text-decoration: underline; white-space: nowrap; }\n.items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }\n.it { display: grid; grid-template-columns: 56px minmax(0, 1fr) auto; gap: 12px; align-items: start; padding: 12px 0; border-top: 1px solid var(--line); }\n.it:first-child { border-top: 0; padding-top: 0; }\n.ph { width: 56px; height: 56px; background: #e9e9e6; display: flex; align-items: center; justify-content: center; font-size: 9px; font-weight: 800; letter-spacing: .1em; color: var(--grey-500); overflow: hidden; }\n.ph img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.nm { display: flex; flex-direction: column; gap: 2px; min-width: 0; }\n.nm b { font-size: 14px; font-weight: 700; line-height: 1.3; }\n.nm span { font-size: 12px; color: var(--grey-700); line-height: 1.4; }\n.nm .pt { color: var(--red-500); font-weight: 700; }\n.pr { font-size: 14px; font-weight: 700; white-space: nowrap; }\n.so { color: var(--red-500); }\n.cpn { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px; border-radius: 2px; border: 1px solid #b7e0c7; background: #e8f6ee; }\n.cpn .mk { flex: none; width: 22px; height: 22px; border-radius: 50%; background: var(--green); color: var(--white); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; }\n.cpn .tx { flex: 1; min-width: 0; font-size: 14px; line-height: 1.4; }\n.cpn .tx b { font-family: ui-monospace, Menlo, Consolas, monospace; letter-spacing: .04em; }\n.cpn .tx small { display: block; margin-top: 2px; font-size: 12px; color: var(--grey-700); }\n.cpn.bad { border-color: #f0b8c0; background: #fbe9ec; }\n.cpn.bad .mk { background: var(--red-500); }\n.cpn.bad .tx a { color: var(--ink); font-weight: 700; text-decoration: underline; }\n.rows { display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--line); padding-top: 14px; }\n.row { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; color: var(--grey-700); }\n.row b { color: var(--ink); font-weight: 600; }\n.row.cd, .row.cd b { color: var(--green); }\n.row.cd b { font-weight: 800; }\n.tot { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; border-top: 1px solid var(--line); padding-top: 14px; }\n.tot span { font-size: 15px; font-weight: 700; }\n.tot strong { font-size: 30px; font-weight: 800; font-stretch: 112%; line-height: 1; letter-spacing: -.01em; }\n.tn { margin: -8px 0 0; text-align: right; font-size: 13px; color: var(--grey-700); }\n.act { display: block; }\n.sec { margin: 0; text-align: center; font-size: 12px; line-height: 1.5; color: var(--grey-500); }\n.back { display: block; text-align: center; font-size: 13px; font-weight: 700; color: var(--ink); text-decoration: underline; }\n.l-mobile.cs { padding: 20px 16px; }\n.l-mobile .tot strong { font-size: 26px; }\n`,\n\n  mounted(d2) {\n    FONT();\n    d2.on('[data-act=\"submit\"]', 'click', () => {\n      const p = d2.params || {};\n      if (truthy(p.isDisabled) || truthy(p.busy)) return;\n      emit(d2, 'onSubmit', true);\n    });\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const R = { btn: refOf(d2.param('button')), cb: refOf(d2.param('checkbox')), icon: refOf(d2.param('iconComponent')) };\n    const list = arr(p.items).length ? arr(p.items) : DEMO;\n    const n = Number(p.count) > 0 ? Number(p.count) : list.reduce((s, i) => s + (Number(i.quantity) || 1), 0);\n    const busy = truthy(p.busy);\n    const disabled = truthy(p.isDisabled) || busy;\n    const checked = truthy(p.termsChecked);\n\n    const rows = list.map((i) => `<li class=\"it\" data-key=\"${esc(i.sku)}\">\n      <div class=\"ph\">${i.image ? `<img src=\"${esc(i.image)}\" alt=\"\" loading=\"lazy\">` : 'FOTO'}</div>\n      <div class=\"nm\"><b>${esc(i.name)}</b><span>${esc(i.detail)}</span>${i.partner ? `<span class=\"pt\">${esc(i.partner)}</span>` : ''}</div>\n      <div class=\"pr${truthy(i.soldOut) ? ' so' : ''}\">${esc(i.price)}</div>\n    </li>`).join('');\n\n    // Cupom: já validado (ok) ou que deixou de valer (invalid). Aqui só aparece; para mudar, o cliente volta à sacola.\n    const cu = p.coupon && typeof p.coupon === 'object' ? p.coupon : null;\n    const cuOk = !!cu && cu.status === 'ok';\n    const cuBad = !!cu && cu.status === 'invalid';\n    const cartHref = esc(p.cartHref || '/sacola');\n    const couponBox = cuOk\n      ? `<div class=\"cpn\" role=\"status\"><span class=\"mk\" aria-hidden=\"true\">✓</span><span class=\"tx\">Cupom <b>${esc(cu.code)}</b> aplicado${cu.message ? `<small>${esc(cu.message)}</small>` : ''}</span></div>`\n      : cuBad\n        ? `<div class=\"cpn bad\" role=\"alert\"><span class=\"mk\" aria-hidden=\"true\">!</span><span class=\"tx\">${esc(cu.message || 'O cupom não vale mais.')} <a href=\"${cartHref}\">Voltar para a sacola</a><small>O desconto foi retirado do total.</small></span></div>`\n        : '';\n\n    const lines = [\n      `<div class=\"row\"><span>Subtotal (${n} ${n === 1 ? 'item' : 'itens'})</span><b>${esc(p.subtotal || 'R$ 0,00')}</b></div>`,\n      cuOk && cu.discount ? `<div class=\"row cd\"><span>Cupom ${esc(cu.code)}</span><b>−${esc(cu.discount)}</b></div>` : '',\n      p.discount ? `<div class=\"row\"><span>${esc(p.discountLabel || 'Desconto Pix')}</span><b>${esc(p.discount)}</b></div>` : '',\n      `<div class=\"row\"><span>Frete</span><b>${esc(p.shipping || 'R$ 0,00')}</b></div>`\n    ].join('');\n\n    const terms = R.cb ? d2.render(R.cb, {\n      name: 'termos', checked, top: true, layout, iconComponent: R.icon, error: p.termsError || '',\n      label: p.termsLabel || 'Li e aceito os [Termos e Condições de Venda](^/politicas/termos-de-venda).'\n    }, { key: 'terms-' + (checked ? '1' : '0') + '-' + (p.termsError ? 'e' : 'n'), onChange: (v) => emit(d2, 'onTermsChange', !!v) }) : '';\n\n    const cta = R.btn ? `<span class=\"act\" data-act=\"submit\">${d2.render(R.btn, {\n      label: busy ? 'Aguarde…' : (p.ctaLabel || 'Continuar para pagamento'), variant: 'brand', size: 'lg', fullWidth: 'always', glyph: 'lock',\n      isDisabled: disabled, layout\n    }, { key: 'cta-' + (busy ? 'b' : '') + (disabled ? 'd' : 'e'), style: 'width:100%' })}</span>` : '';\n\n    return `<div class=\"cs l-${layout}\">\n      <div class=\"top\"><h2>Resumo do pedido</h2><a class=\"edit\" href=\"${cartHref}\">Editar sacola</a></div>\n      <ul class=\"items\">${rows}</ul>\n      ${couponBox}\n      <div class=\"rows\">${lines}</div>\n      <div class=\"tot\"><span>Total</span><strong>${esc(p.total || 'R$ 0,00')}</strong></div>\n      ${p.totalNote ? `<p class=\"tn\">${esc(p.totalNote)}</p>` : ''}\n      ${terms}\n      ${cta}\n      <p class=\"sec\">${esc(p.secureNote || 'Você será levado ao ambiente seguro do Mercado Pago para concluir o pagamento. A loja não guarda dados de cartão.')}</p>\n      <a class=\"back\" href=\"${cartHref}\">Voltar para a sacola</a>\n    </div>`;\n  }\n};\n"}},"busy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"busy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cartHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartHref"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"count":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"count"}}}},"coupon":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"coupon"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"discount":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"discount"}}}},"discountLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"discountLabel"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"isDisabled":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"isDisabled"}}}},"items":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"items"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"onTermsChange":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onTermsChange"}}}},"secureNote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"secureNote"}}}},"shipping":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shipping"}}}},"subtotal":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subtotal"}}}},"termsChecked":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"termsChecked"}}}},"termsError":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"termsError"}}}},"termsLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"termsLabel"}}}},"total":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"total"}}}},"totalNote":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"totalNote"}}}}}}}}}},{"_id":"6ac804350c72e90e99d24179","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"CheckoutLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// CheckoutLayout — Loja Lucas Fontana\n// O corpo da página FINALIZAR COMPRA: barra de etapas (2 · Entrega e pagamento), título, e duas colunas no desktop — à esquerda\n// as três seções numeradas (01 Endereço de entrega, 02 Quem vai receber, 03 Forma de pagamento) e à direita o CheckoutSummary\n// (resumo do pedido, aceite dos Termos de Venda e botão \"Continuar para pagamento\"), preso na rolagem. Abaixo de 1200px o resumo\n// desce para baixo das seções. O cabeçalho, o aviso do topo e o rodapé ficam na artboard, como na Sacola.\n// Itens: a sacola mora no navegador (localStorage `lf-sacola`, lista de {sku, qty}) e os dados de cada item vêm do serviço público\n// `productsService` (produtosLoja), porque a tabela Produtos é fechada para visitante: nome, foto, preço, `parcelas`, `desconto_pix`,\n// origem/marca (parceiro). O serviço só diz se tem estoque (1) ou não (0); quem confere a quantidade é o servidor ao criar o pedido.\n// No builder, onde a tabela é legível, o `dataset` vale como reserva. Item esgotado trava o botão. Sacola vazia leva de volta\n// para `cartHref` (/sacola).\n// Sessão: `sessionService` (minhaSessaoContato) diz se há cliente logado e o nome dele. Sem sessão a página manda para\n// `loginHref` com `?voltar=` apontando para `checkoutPath` — o login já devolve a pessoa para cá.\n// Funil do pedido: assim que a cliente logada abre o checkout com itens, `startService` (checkoutIniciar) cria — ou atualiza, se ela\n// já tem um rascunho de menos de 24 h — o pedido com status \"checkout_iniciado\". Em \"Continuar para pagamento\", `createService`\n// (checkoutCriarPedido) confere tudo no servidor e passa o MESMO pedido para \"aguardando_pagamento\", já com endereço, quem recebe,\n// forma de pagamento e aceite dos termos. Se o servidor recusar (estoque, endereço…), a mensagem aparece na tela.\n// 01 Endereço: `listService` (enderecosListar) traz os endereços da conta, o principal vem marcado. \"+ Cadastrar novo endereço\" abre o\n// formulário ali mesmo (apelido em chips, CEP com ViaCEP, rua, número, complemento, bairro, cidade, UF) com \"Salvar na minha conta\"\n// (marcado). Salvando, grava com `saveService` (enderecoSalvar) ao continuar; desmarcado, o endereço vale só neste pedido\n// (vai no pedido como endereço novo).\n// 02 Quem vai receber: o nome do cadastro vem marcado; \"Outra pessoa\" abre nome e telefone (vão no pedido).\n// 03 Pagamento: Pix ou cartão. Pix mostra o total com o desconto Pix de cada produto; cartão mostra \"em até Nx\" pelo MENOR\n// \"Parcelas sem juros\" entre os itens. Dados do cartão e parcelas ficam no Mercado Pago.\n// Frete: ainda sem integração. `shippingCarrier`, `shippingDays` e `shippingPrice` (número) chegam por parâmetro; sem preço, mostra\n// \"Frete a calcular\". Itens de parceiro não entram no frete: \"enviados direto pelo parceiro, com rastreio próprio\".\n// Quando o servidor aceita, avisa `onSubmit` com { pedido, items, address, recipient, payment, installments, totals, termsAccepted }.\n// A ida ao Mercado Pago fica para a próxima etapa.\n// No builder (hostname .data2.qa) ou com `demo: true`, não consulta a sessão, não cria pedido e não redireciona: usa itens e endereços de exemplo.\n// Cliques das opções e botões são delegados (data-act), pelo mesmo motivo do CheckoutSummary.\n// Usa StepIndicator, CheckoutSummary, FormSection, FormField, ChipGroup, Checkbox, Button, Notice e Icon por parâmetro.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst truthy = (v) => v === true || v === 'true' || v === 1 || v === '1';\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst obj = (v) => {\n  if (v && typeof v === 'object' && !Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return j && typeof j === 'object' ? j : {}; } catch (e) { return {}; } }\n  return {};\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst brl = (n) => 'R$ ' + Number(n || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\nconst KEY = 'lf-sacola';\nconst PAD = { desktop: '32px 96px 104px', tablet: '28px 40px 72px', mobile: '20px 20px 56px' };\nconst readBag = (k) => { try { const v = JSON.parse(localStorage.getItem(k) || '[]'); return Array.isArray(v) ? v.filter((x) => x && x.sku) : []; } catch (e) { return []; } };\nconst UFS = 'AC AL AP AM BA CE DF ES GO MA MT MS MG PA PB PR PE PI RJ RN RS RO RR SC SP SE TO'.split(' ');\nconst hash = (v) => { const t = JSON.stringify(v) || ''; let h = 0; for (let i = 0; i < t.length; i++) h = (h * 31 + t.charCodeAt(i)) | 0; return (h >>> 0).toString(36); };\nconst box = (d2) => d2.root || d2;\nconst digits = (v) => String(v || '').replace(/\\D/g, '');\nconst sortMain = (list) => list.slice().sort((a, b) => (b.main ? 1 : 0) - (a.main ? 1 : 0));\nconst inBuilder = () => typeof location !== 'undefined' && /data2\\.qa$/.test(String(location.hostname || ''));\nconst isDemo = (p) => truthy(p.demo) || inBuilder();\nconst DEMO_ITEMS = [{ sku: 'LF-LAV-001', qty: 2 }, { sku: 'LF-ACB-002', qty: 1 }, { sku: 'BP-ADM-011', qty: 1 }];\nconst DEMO_ADDR = [\n  { id: 'demo-1', label: 'Casa', main: true, cep: '00000-000', street: '[Rua]', number: '[Nº]', complement: '[Complemento]', district: '[Bairro]', city: '[Cidade]', uf: 'UF' },\n  { id: 'demo-2', label: 'Trabalho', main: false, cep: '00000-000', street: '[Rua]', number: '[Nº]', complement: '', district: '[Bairro]', city: '[Cidade]', uf: 'UF' }\n];\nconst STEPS = ['Sacola', 'Entrega e pagamento', 'Pagamento no Mercado Pago', 'Confirmação'];\nconst STEPS_MOBILE = ['Sacola', 'Entrega e pagamento', 'Pagamento', 'Pronto'];\nconst LABELS = [{ value: 'Casa', label: 'Casa' }, { value: 'Trabalho', label: 'Trabalho' }, { value: 'outro', label: 'Outro' }];\nconst EMPTY_FORM = { labelChoice: 'Casa', labelOther: '', cep: '', street: '', number: '', complement: '', district: '', city: '', uf: '', save: true };\nconst FKEYS = ['cep', 'street', 'number', 'complement', 'district', 'city', 'uf'];\n\nconst callSvc = async (d2, name, payload) => {\n  try { const r = await d2.rush(d2.param(name)).invoke(payload || {}); return (r && r.data !== undefined ? r.data : r) || {}; } catch (e) { return { ok: false, motivo: 'rede' }; }\n};\nconst goLogin = (d2) => {\n  const p = d2.params || {};\n  d2.state.redirecting = true;\n  try { location.replace((p.loginHref || '/login') + '?voltar=' + encodeURIComponent(p.checkoutPath || '/checkout')); } catch (e) { /* sem navegação */ }\n};\nconst loadProducts = async (d2) => {\n  const r = await callSvc(d2, 'productsService', {});\n  d2.state.prodRows = Array.isArray(r) ? r : (r && Array.isArray(r.items) ? r.items : []);\n  d2.state.prodLoaded = true;\n};\nconst boot = async (d2) => {\n  const p = d2.params || {};\n  const b = box(d2);\n  loadProducts(d2);\n  if (isDemo(p)) {\n    b.__list = sortMain(DEMO_ADDR.map((a) => Object.assign({}, a)));\n    d2.state.sess = { logado: true, nome: p.demoName || '[Nome completo do cliente]' };\n    d2.state.sel = (b.__list.find((a) => a.main) || b.__list[0]).id;\n    d2.state.ready = true;\n    return;\n  }\n  const s = await callSvc(d2, 'sessionService', {});\n  if (s.motivo === 'rede' || (s.logado !== true && s.logado !== false)) { d2.state.bootErr = 'Não deu pra carregar agora. Confere sua conexão e atualiza a página.'; return; }\n  if (!s.logado) { goLogin(d2); return; }\n  d2.state.sess = { logado: true, nome: s.nome || '', email: s.email || '' };\n  const r = await callSvc(d2, 'listService', {});\n  if (r.ok) {\n    b.__list = sortMain(arr(r.addresses).map(obj).map((x) => Object.assign({}, x, { main: truthy(x.main) })));\n    const first = b.__list.find((a) => a.main) || b.__list[0];\n    d2.state.sel = first ? first.id : '';\n    if (!b.__list.length) d2.state.mode = 'form';\n    d2.state.ready = true;\n    return;\n  }\n  if (r.motivo === 'sem_sessao') { goLogin(d2); return; }\n  d2.state.bootErr = 'Não deu pra carregar seus endereços agora. Atualiza a página e tenta de novo.';\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.co { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-100: #f5f5f3; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  width: 100%; background: var(--white); color: var(--ink); font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.in { display: flex; flex-direction: column; gap: 20px; }\n.head { display: flex; flex-direction: column; gap: 8px; }\nh1 { margin: 0; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1; letter-spacing: -.01em; text-transform: uppercase; }\n.sub { margin: 0; font-size: 15px; line-height: 1.5; color: var(--grey-700); }\n.grid { display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 48px; align-items: start; }\n.main { display: flex; flex-direction: column; gap: 24px; min-width: 0; }\n.aside { position: sticky; top: 24px; min-width: 0; }\n.sec { border: 1px solid var(--line); background: var(--white); padding: 24px; display: flex; flex-direction: column; gap: 20px; }\n.sh { display: flex; align-items: center; gap: 12px; }\n.sq { flex: none; width: 36px; height: 36px; background: var(--ink); display: flex; align-items: center; justify-content: center; }\n.st { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }\n.n { font-size: 11px; font-weight: 800; letter-spacing: .16em; color: var(--red-500); }\nh2 { margin: 0; font-size: 20px; font-weight: 800; font-stretch: 115%; line-height: 1.05; text-transform: uppercase; }\n.lnk { font-size: 13px; font-weight: 700; color: var(--ink); text-decoration: underline; white-space: nowrap; }\n.opts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: stretch; }\n.opt { display: flex; align-items: flex-start; gap: 12px; width: 100%; padding: 16px; margin: 0; text-align: left; font: inherit; color: inherit; background: var(--white); border: 1px solid var(--line); cursor: pointer; }\n.opt:hover { border-color: var(--grey-500); }\n.opt.on { border: 2px solid var(--ink); padding: 15px; }\n.dot { flex: none; width: 18px; height: 18px; margin-top: 1px; border: 1.5px solid var(--grey-500); border-radius: 50%; background: var(--white); display: flex; align-items: center; justify-content: center; }\n.opt.on .dot { border-color: var(--red-500); }\n.opt.on .dot::after { content: ''; width: 10px; height: 10px; border-radius: 50%; background: var(--red-500); }\n.ob { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }\n.ot { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: 15px; font-weight: 800; line-height: 1.3; }\n.ot .ic { display: inline-flex; flex: none; }\n.tag { display: inline-block; background: var(--red-500); color: var(--white); font-size: 10px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; padding: 3px 6px; line-height: 1; }\n.tag.g { background: var(--grey-700); }\n.od { font-size: 13px; line-height: 1.5; color: var(--grey-700); }\n.pr { font-size: 15px; font-weight: 800; color: var(--ink); }\n.add { align-self: flex-start; margin: 0; padding: 13px 20px; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--ink); background: var(--white); border: 1.5px dashed var(--grey-500); cursor: pointer; }\n.add:hover { border-color: var(--ink); }\n.edit { align-self: flex-start; margin: 0; padding: 0; font: inherit; font-size: 13px; font-weight: 700; color: var(--ink); background: none; border: 0; text-decoration: underline; cursor: pointer; }\n.ship { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--line); padding-top: 16px; }\n.ship p { margin: 0; display: flex; align-items: center; gap: 10px; font-size: 14px; line-height: 1.4; color: var(--grey-700); }\n.ship p b { color: var(--ink); }\n.bar { display: flex; align-items: center; gap: 10px; padding: 14px 16px; background: var(--grey-100); font-size: 14px; line-height: 1.4; color: var(--grey-700); }\n.note { margin: 0; font-size: 12px; line-height: 1.5; color: var(--grey-500); }\n.serr { margin: 0; font-size: 14px; font-weight: 600; color: var(--red-500); }\n.form { display: flex; flex-direction: column; gap: 20px; }\n.form h3 { margin: 0; font-size: 16px; font-weight: 800; font-stretch: 115%; line-height: 1.05; text-transform: uppercase; }\n.btns { display: flex; flex-wrap: wrap; gap: 12px; }\n.btns > * { flex: 0 0 auto; }\n.load { margin: 0; font-size: 15px; color: var(--grey-700); }\n.l-tablet .in { gap: 18px; } .l-tablet h1 { font-size: 36px; }\n.l-tablet .grid, .l-mobile .grid { grid-template-columns: minmax(0, 1fr); gap: 24px; }\n.l-tablet .aside, .l-mobile .aside { position: static; }\n.l-mobile .in { gap: 16px; } .l-mobile h1 { font-size: 30px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .sec { padding: 20px 16px; gap: 16px; } .l-mobile h2 { font-size: 17px; }\n.l-mobile .opts { grid-template-columns: minmax(0, 1fr); gap: 12px; }\n.l-mobile .btns { flex-direction: column; } .l-mobile .btns > * { width: 100%; }\n`,\n\n  mounted(d2) {\n    FONT();\n    const p = d2.params || {};\n    const b = box(d2);\n    try { const dh = d2.param('dataset') && d2.data ? d2.data('dataset') : null; if (dh && typeof dh.loadAll === 'function') dh.loadAll(); } catch (e) { /* sem DataSet */ }\n    const k = p.storageKey || KEY;\n    d2.state.bag = readBag(k);\n    d2.state.bagReady = true;\n    const onStorage = (e) => { if (!e || e.key === k) d2.state.bag = readBag(k); };\n    window.addEventListener('storage', onStorage); window.addEventListener('lf-sacola', onStorage);\n    d2.onUnmount(() => { window.removeEventListener('storage', onStorage); window.removeEventListener('lf-sacola', onStorage); });\n    d2.on('[data-act]', 'click', (e, el) => {\n      const h = b.__h || {};\n      const a = el.getAttribute('data-act');\n      if (h[a]) h[a](el, e);\n    });\n    if (!b.__booted) { b.__booted = true; boot(d2); }\n  },\n\n  updated(d2) {\n    const b = box(d2);\n    const p = d2.params || {};\n    const st = d2.state;\n    const c = b.__calc;\n    // Funil: a cliente logada abriu o checkout com itens → cria (ou atualiza) o rascunho do pedido, uma vez por visita\n    if (!b.__started && st.ready && st.prodLoaded && !st.bootErr && !st.redirecting && c && c.live && c.live.length && !isDemo(p)) {\n      b.__started = true;\n      let cupIn = '';\n      try { cupIn = String(localStorage.getItem('lf-cupom') || '').toUpperCase(); } catch (e) { /* sem storage */ }\n      callSvc(d2, 'startService', { itens: c.live.map((i) => ({ slug: i.slug, quantidade: i.quantity })), cupom: cupIn }).then((r) => {\n        if (r && r.ok && r.pedido) {\n          b.__pedido = r.pedido;\n          // Cupom da sacola: o servidor revalida e devolve o desconto (ou o motivo de não valer mais)\n          const cu = r.pedido.cupom || {};\n          if (cupIn) {\n            if (cu.codigo) d2.state.cupom = { status: 'ok', codigo: cu.codigo, desconto: Number(cu.desconto) || 0 };\n            else {\n              d2.state.cupom = { status: 'invalid', codigo: cupIn, erro: cu.erro || 'Cupom inválido ou expirado.' };\n              try { localStorage.removeItem('lf-cupom'); } catch (e) { /* sem storage */ }\n            }\n          }\n        }\n      });\n    }\n    if (!b.__focus) return;\n    const sel = b.__focus; b.__focus = '';\n    const find = (root) => {\n      if (!root) return null;\n      const hit = root.querySelector ? root.querySelector(sel) : null;\n      if (hit) return hit;\n      const all = root.querySelectorAll ? root.querySelectorAll('*') : [];\n      for (const el of all) { if (el.shadowRoot) { const r = find(el.shadowRoot); if (r) return r; } }\n      return null;\n    };\n    setTimeout(() => { const el = find(d2.root && d2.root.shadowRoot ? d2.root.shadowRoot : d2.root) || find(document); if (el) try { el.focus({ preventScroll: false }); } catch (e) { /* sem foco */ } }, 60);\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const b = box(d2);\n    const R = {\n      icon: refOf(d2.param('iconComponent')), btn: refOf(d2.param('button')), cb: refOf(d2.param('checkbox')), fs: refOf(d2.param('formSection')),\n      chip: refOf(d2.param('chipGroup')), notice: refOf(d2.param('notice')), si: refOf(d2.param('stepIndicator')), sum: refOf(d2.param('checkoutSummary'))\n    };\n    const demo = isDemo(p);\n    const k = p.storageKey || KEY;\n    const ic = (name, size, color, key) => (R.icon ? d2.render(R.icon, { name, size, iconColor: color || 'currentColor' }, { key, width: size, height: size, style: 'flex:none' }) : '');\n    const st = d2.state;\n    const sess = st.sess || {};\n    const gen = st.gen || 0;\n    const mode = st.mode || 'list';\n    const sel = st.sel || '';\n    const recip = st.recip || 'self';\n    const pay = st.pay || 'pix';\n    const errors = st.errors || {};\n    const oErrors = st.oErrors || {};\n    const list = b.__list || [];\n    if (!b.__f) b.__f = Object.assign({}, EMPTY_FORM);\n    if (!b.__o) b.__o = { name: '', phone: '' };\n\n    // ——— Itens (serviço público de produtos; a tabela é fechada para visitante)\n    const dh = d2.param('dataset') && d2.data ? d2.data('dataset') : null;\n    const dsRows = dh && typeof dh.records === 'function' ? dh.records() : [];\n    const rows = st.prodLoaded && (st.prodRows || []).length ? st.prodRows : dsRows;\n    const viaService = rows === st.prodRows;\n    const loadingData = !st.prodLoaded && !rows.length;\n    let bag = st.bag || [];\n    if (!bag.length && demo) bag = DEMO_ITEMS;\n    const bySku = {}; rows.forEach((x) => { if (x && x.sku) bySku[x.sku] = x; });\n    const items = bag.map((bi) => {\n      const x = bySku[bi.sku];\n      if (!x) return null;\n      const stock0 = Number(x.estoque) || 0;\n      const stock = viaService && stock0 > 0 ? 99 : stock0; // o serviço público só diz se tem (1) ou não (0)\n      const soldOut = stock <= 0 || x.ativo === false || x.ativo === 'false';\n      const qty = soldOut ? Number(bi.qty) || 1 : Math.max(1, Math.min(Number(bi.qty) || 1, stock));\n      const isPartner = x.origem === 'parceiro';\n      const brand = String(x.marca || '').split(' ')[0];\n      return {\n        sku: x.sku, slug: x.slug || '', name: x.nome, image: x.imagem || '', quantity: qty, soldOut, unit: Number(x.preco) || 0, parc: Math.max(1, Number(x.parcelas) || 1),\n        pixPct: Math.max(0, Number(x.desconto_pix) || 0), partner: isPartner, brand,\n        detail: (x.subcategoria ? x.subcategoria + ' · ' : '') + 'Qtd.: ' + qty\n      };\n    }).filter(Boolean);\n    const live = items.filter((i) => !i.soldOut);\n    const gone = items.filter((i) => i.soldOut);\n    const count = live.reduce((s, i) => s + i.quantity, 0);\n    const subtotal = live.reduce((s, i) => s + i.unit * i.quantity, 0);\n    const pixOff = live.reduce((s, i) => s + i.unit * i.quantity * i.pixPct / 100, 0);\n    const own = live.filter((i) => !i.partner);\n    const partners = live.filter((i) => i.partner);\n    const parc = live.length ? Math.max(1, Math.min.apply(null, live.map((i) => i.parc))) : 1;\n    const shipKnown = p.shippingPrice !== undefined && p.shippingPrice !== null && p.shippingPrice !== '' && !isNaN(Number(p.shippingPrice));\n    const shipPrice = shipKnown ? Number(p.shippingPrice) : null;\n    const ship = own.length && shipPrice != null ? shipPrice : 0;\n    const cupSt = st.cupom && st.cupom.status === 'ok' ? st.cupom : null;\n    const cupDesc = cupSt ? Math.min(Number(cupSt.desconto) || 0, subtotal) : 0;\n    const cupCode = cupSt ? String(cupSt.codigo || '') : '';\n    const discount = pay === 'pix' ? pixOff : 0;\n    const total = Math.max(0, subtotal - cupDesc - discount + ship);\n    const totalPix = Math.max(0, subtotal - cupDesc - pixOff + ship);\n    const totalCard = Math.max(0, subtotal - cupDesc + ship);\n    const shipLabel = own.length ? (shipPrice == null ? 'A calcular' : (shipPrice === 0 ? 'Grátis' : brl(shipPrice))) : 'Pelo parceiro';\n    const partnerNames = Array.from(new Set(partners.map((i) => i.brand).filter(Boolean)));\n    const termsOn = truthy(st.terms);\n    b.__calc = { cupDesc, cupCode, live, count, subtotal, pixOff, discount, ship, total, parc, shipPrice, own: own.length, partners: partners.length, gone: gone.length };\n\n    if (st.bagReady && !bag.length && !demo && !loadingData && !b.__toCart) {\n      b.__toCart = true;\n      try { location.replace(p.cartHref || '/sacola'); } catch (e) { /* sem navegação */ }\n    }\n\n    const crumbs = R.si ? d2.render(R.si, { steps: layout === 'mobile' ? STEPS_MOBILE : STEPS, current: 2, layout, ariaLabel: 'Etapas da compra' }, { key: 'steps-' + layout, style: 'height:auto' }) : '';\n    const head = `<div class=\"head\"><h1>${esc(p.title || 'Finalizar compra')}</h1><p class=\"sub\">${esc(p.subtitle || 'Confira para onde vai, quem recebe e como você quer pagar.')}</p></div>`;\n\n    // ——— Antes de carregar\n    if (st.redirecting) return `<div class=\"co l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}<p class=\"load\" role=\"status\">Levando você para entrar na sua conta…</p></div></div>`;\n    if (st.bootErr) return `<div class=\"co l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}<p class=\"serr\" role=\"alert\">${esc(st.bootErr)}</p></div></div>`;\n    if (!st.ready || loadingData) return `<div class=\"co l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}<p class=\"load\" role=\"status\">Carregando seu pedido…</p></div></div>`;\n\n    // ——— Funções de ação (sempre pegam o estado atual)\n    const fresh = () => d2.state;\n    const validateForm = (f) => {\n      const e = {};\n      if (f.labelChoice === 'outro' && !String(f.labelOther || '').trim()) e.labelOther = 'Dá um nome pra esse endereço (ex.: Casa da mãe).';\n      if (digits(f.cep).length !== 8) e.cep = 'Confere o CEP: são 8 números.';\n      if (!String(f.street || '').trim()) e.street = 'Informe a rua ou avenida.';\n      if (!String(f.number || '').trim()) e.number = 'Informe o número (ou S/N).';\n      if (!String(f.district || '').trim()) e.district = 'Informe o bairro.';\n      if (!String(f.city || '').trim()) e.city = 'Informe a cidade.';\n      const uf = String(f.uf || '').trim().toUpperCase();\n      if (!uf) e.uf = 'Informe o estado.'; else if (UFS.indexOf(uf) < 0) e.uf = 'Use a sigla do estado (ex.: SP).';\n      return e;\n    };\n    const cleanOf = (f) => ({\n      label: f.labelChoice === 'outro' ? String(f.labelOther).trim() : f.labelChoice, cep: String(f.cep).trim(), street: String(f.street).trim(), number: String(f.number).trim(),\n      complement: String(f.complement || '').trim(), district: String(f.district).trim(), city: String(f.city).trim(), uf: String(f.uf).trim().toUpperCase()\n    });\n    const lookup = (dg) => {\n      if (typeof fetch !== 'function' || b.__lastCep === dg) return;\n      b.__lastCep = dg;\n      d2.state.cepState = 'loading';\n      fetch('https://viacep.com.br/ws/' + dg + '/json/')\n        .then((r) => (r.ok ? r.json() : Promise.reject(new Error('http'))))\n        .then((j) => {\n          if (digits(b.__f.cep) !== dg) return;\n          const e = Object.assign({}, fresh().errors || {});\n          if (!j || j.erro) {\n            e.cep = 'CEP não encontrado. Confere o número ou preenche o endereço à mão.';\n            b.__focus = 'input[name=\"cep\"]';\n            d2.state.errors = e; d2.state.cepState = 'notfound'; d2.state.gen = (fresh().gen || 0) + 1;\n            return;\n          }\n          Object.assign(b.__f, { street: j.logradouro || b.__f.street, district: j.bairro || b.__f.district, city: j.localidade || b.__f.city, uf: j.uf || b.__f.uf });\n          delete e.cep;\n          ['street', 'district', 'city', 'uf'].forEach((x) => { if (b.__f[x]) delete e[x]; });\n          b.__focus = j.logradouro ? 'input[name=\"number\"]' : 'input[name=\"street\"]';\n          d2.state.errors = e; d2.state.cepState = j.logradouro ? 'found' : 'city'; d2.state.gen = (fresh().gen || 0) + 1;\n        })\n        .catch(() => {\n          if (digits(b.__f.cep) !== dg) return;\n          b.__focus = 'input[name=\"street\"]';\n          d2.state.cepState = 'error'; d2.state.gen = (fresh().gen || 0) + 1;\n        });\n    };\n    const onField = (ev) => {\n      if (!ev || !ev.key) return;\n      b.__f[ev.key] = ev.value;\n      if (ev.key === 'cep') { const dg = digits(ev.value); if (dg.length === 8) lookup(dg); else b.__lastCep = ''; }\n      const cur = fresh().errors || {};\n      if (cur[ev.key]) { const n = Object.assign({}, cur); delete n[ev.key]; d2.state.errors = n; }\n    };\n    const onOther = (ev) => {\n      if (!ev || !ev.key) return;\n      b.__o[ev.key] = ev.value;\n      const cur = fresh().oErrors || {};\n      if (cur[ev.key]) { const n = Object.assign({}, cur); delete n[ev.key]; d2.state.oErrors = n; }\n    };\n    const openForm = (editNew) => {\n      b.__f = editNew && b.__new ? Object.assign({}, b.__new.form) : Object.assign({}, EMPTY_FORM);\n      b.__lastCep = ''; d2.state.errors = {}; d2.state.cepState = ''; d2.state.addrErr = ''; d2.state.gen = (fresh().gen || 0) + 1; d2.state.mode = 'form';\n    };\n    const closeForm = () => { d2.state.errors = {}; d2.state.cepState = ''; d2.state.mode = 'list'; };\n    const confirmForm = () => {\n      const e = validateForm(b.__f);\n      d2.state.errors = e;\n      if (Object.keys(e).length) return;\n      b.__new = { form: Object.assign({}, b.__f), clean: cleanOf(b.__f), save: b.__f.save !== false };\n      d2.state.sel = 'new'; d2.state.addrErr = ''; d2.state.srvErr = ''; d2.state.mode = 'list';\n    };\n    const submit = async () => {\n      const s = fresh();\n      if (s.busy || truthy(p.busy)) return;\n      const c = b.__calc || {};\n      const myList = b.__list || [];\n      const selNow = s.sel || '';\n      const recipNow = s.recip || 'self';\n      let addr = null; let addrErr = '';\n      if ((s.mode || 'list') === 'form') addrErr = 'Confirme o novo endereço em “Usar este endereço” ou cancele.';\n      else if (selNow === 'new' && b.__new) addr = Object.assign({ id: '', saved: false, main: false }, b.__new.clean);\n      else { const f = myList.find((a) => a.id === selNow); if (f) addr = Object.assign({ saved: true }, f); else addrErr = 'Escolha o endereço de entrega.'; }\n      const oe = {};\n      if (recipNow === 'other') {\n        if (!String(b.__o.name || '').trim()) oe.name = 'Informe o nome de quem vai receber.';\n        const ph = digits(b.__o.phone);\n        if (ph.length < 10 || ph.length > 11) oe.phone = 'Confere o telefone: DDD + número.';\n      }\n      d2.state.addrErr = addrErr; d2.state.oErrors = oe;\n      if (addrErr || Object.keys(oe).length) return;\n      if (!c.live || !c.live.length || c.gone > 0) { d2.state.srvErr = 'Sua sacola tem item esgotado ou está vazia. Volte para a sacola para ajustar.'; return; }\n      if (!truthy(s.terms)) return;\n      d2.state.busy = true; d2.state.srvErr = '';\n      const payNow = s.pay || 'pix';\n      const recName = recipNow === 'other' ? String(b.__o.name).trim() : ((s.sess || {}).nome || '');\n      if (addr && !addr.saved && b.__new && b.__new.save && !demo) {\n        const r = await callSvc(d2, 'saveService', { id: '', main: false, address: Object.assign({}, b.__new.clean, { recipient: recName || 'Destinatário' }) });\n        if (!r.ok) {\n          d2.state.busy = false;\n          if (r.erros && typeof r.erros === 'object') { d2.state.srvErr = 'Confere os dados do endereço novo e tenta de novo.'; return; }\n          d2.state.srvErr = r.motivo === 'sem_sessao' ? 'Sua sessão expirou. Entre de novo para continuar.' : 'Não deu pra salvar o endereço agora. Confere sua conexão e tenta de novo.';\n          return;\n        }\n        b.__list = sortMain(arr(r.addresses).map(obj).map((x) => Object.assign({}, x, { main: truthy(x.main) })));\n        const hit = b.__list.find((a) => digits(a.cep) === digits(addr.cep) && String(a.number) === String(addr.number) && String(a.street) === String(addr.street));\n        if (hit) { addr = Object.assign({ saved: true }, hit); d2.state.sel = hit.id; b.__new = null; }\n        emit(d2, 'onAddressSaved', { label: addr.label });\n      }\n      // Pedido: o servidor confere tudo de novo e passa o rascunho para \"aguardando_pagamento\"\n      let pedido = null;\n      if (!demo) {\n        const body = {\n          pedido_id: (b.__pedido && b.__pedido.id) || '',\n          itens: c.live.map((i) => ({ slug: i.slug, quantidade: i.quantity })),\n          pagamento_forma: payNow === 'card' ? 'cartao' : 'pix', parcelas: 1,\n          cupom: c.cupCode || '',\n          aceite_termos: true, termos_versao: 'v1',\n          recebedor: { tipo: recipNow, nome: recipNow === 'other' ? String(b.__o.name).trim() : '', telefone: recipNow === 'other' ? digits(b.__o.phone) : '' }\n        };\n        if (addr.saved && addr.id) body.endereco = addr.id;\n        else body.endereco_novo = { label: addr.label, cep: addr.cep, street: addr.street, number: addr.number, complement: addr.complement, district: addr.district, city: addr.city, uf: addr.uf };\n        const r = await callSvc(d2, 'createService', body);\n        if (r && !r.ok && r.motivo === 'pagamento_em_andamento' && /^https:\\/\\/([a-z0-9-]+\\.)*mercadopago\\.com(\\.br)?\\//i.test(String(r.checkout_url || ''))) {\n          // Já existe um pagamento aberto para este pedido: leva a pessoa direto para ele (o botão fica em \"Aguarde…\" até a página mudar)\n          if (!b.__pshow && typeof window !== 'undefined') { b.__pshow = true; window.addEventListener('pageshow', (ev) => { if (ev.persisted) d2.state.busy = false; }); }\n          try { window.location.assign(String(r.checkout_url)); return; } catch (e2) { /* cai na mensagem de erro abaixo */ }\n        }\n        if (!r.ok) {\n          d2.state.busy = false;\n          d2.state.srvErr = r.motivo === 'sem_sessao' ? 'Sua sessão expirou. Entre de novo para continuar.' : (r.error || 'Não deu pra registrar o pedido agora. Confere sua conexão e tenta de novo.');\n          return;\n        }\n        pedido = r.pedido || null;\n        b.__pedido = pedido;\n        if (!r.checkout_url) { d2.state.busy = false; d2.state.srvErr = 'Não foi possível abrir o pagamento agora. Tente novamente.'; return; }\n        if (!b.__pshow && typeof window !== 'undefined') { b.__pshow = true; window.addEventListener('pageshow', (ev) => { if (ev.persisted) d2.state.busy = false; }); }\n        try { window.location.assign(r.checkout_url); } catch (e) { d2.state.busy = false; d2.state.srvErr = 'Não foi possível abrir o pagamento agora. Tente novamente.'; }\n        return;\n      }\n      const payload = {\n        pedido,\n        items: c.live.map((i) => ({ sku: i.sku, slug: i.slug, qty: i.quantity, unitPrice: i.unit, pixDiscountPct: i.pixPct })),\n        address: addr,\n        recipient: { type: recipNow, name: recName, phone: recipNow === 'other' ? digits(b.__o.phone) : '' },\n        payment: payNow, installments: payNow === 'card' ? c.parc : 1,\n        totals: { subtotal: c.subtotal, coupon: c.cupCode ? { code: c.cupCode, discount: c.cupDesc } : null, pixDiscount: payNow === 'pix' ? c.pixOff : 0, shipping: c.ship, total: Math.max(0, c.subtotal - c.cupDesc - (payNow === 'pix' ? c.pixOff : 0) + c.ship) },\n        termsAccepted: true\n      };\n      emit(d2, 'onSubmit', payload);\n      d2.state.busy = false;\n    };\n    b.__h = {\n      addr: (el) => { d2.state.sel = el.getAttribute('data-id'); d2.state.addrErr = ''; },\n      newaddr: () => openForm(false),\n      editnew: () => openForm(true),\n      cancelform: () => closeForm(),\n      useaddr: () => confirmForm(),\n      recip: (el) => { d2.state.recip = el.getAttribute('data-id'); d2.state.oErrors = {}; },\n      pay: (el) => { d2.state.pay = el.getAttribute('data-id'); },\n      submit: () => submit()\n    };\n\n    // ——— Seção 01 · endereço\n    const head3 = (num, title, iconName, action) => `<div class=\"sh\"><span class=\"sq\">${ic(iconName, 18, '#ffffff', 'sq-' + num)}</span><div class=\"st\"><span class=\"n\">${num}</span><h2>${title}</h2></div>${action || ''}</div>`;\n    const addrLines = (a) => {\n      const l1 = [a.street, a.number].filter(Boolean).join(', ') + (a.complement ? ' — ' + a.complement : '');\n      const l2 = [a.district, [a.city, a.uf].filter(Boolean).join('/')].filter(Boolean).join(' — ') + (a.cep ? ' · CEP ' + a.cep : '');\n      return `${esc(l1)}<br>${esc(l2)}`;\n    };\n    const addrOpt = (a, extraTag, extraAct) => `<div><button type=\"button\" class=\"opt${sel === a.id ? ' on' : ''}\" data-act=\"addr\" data-id=\"${esc(a.id)}\" role=\"radio\" aria-checked=\"${sel === a.id ? 'true' : 'false'}\"><span class=\"dot\"></span><span class=\"ob\"><span class=\"ot\">${esc(a.label)}${a.main ? '<span class=\"tag\">Principal</span>' : ''}${extraTag || ''}</span><span class=\"od\">${addrLines(a)}</span></span></button>${extraAct || ''}</div>`;\n\n    let addrBody;\n    if (mode === 'form') {\n      const f = b.__f;\n      const cs = st.cepState || '';\n      const cepHint = cs === 'found' ? 'Endereço encontrado. Confere e completa com o número.'\n        : cs === 'city' ? 'Esse CEP vale para a cidade toda: preenche a rua e o bairro.'\n        : cs === 'error' ? 'Não deu pra buscar o CEP agora. Preenche o endereço à mão.' : '';\n      const chips = R.chip ? d2.render(R.chip, { label: 'Nome do endereço', options: LABELS, value: f.labelChoice, name: 'apelido', layout }, {\n        key: 'chips-' + gen, onChange: (v) => { b.__f.labelChoice = v; d2.state.labelChoice = v; }\n      }) : '';\n      const other = (st.labelChoice || f.labelChoice) === 'outro' && R.fs ? d2.render(R.fs, {\n        layout, formField: refOf(d2.param('formField')), iconComponent: R.icon, version: 'lo' + gen, values: { labelOther: f.labelOther || '' }, errors: errors.labelOther ? { labelOther: errors.labelOther } : {},\n        rows: [{ fields: [{ key: 'labelOther', label: 'Como você quer chamar esse endereço?', placeholder: 'Ex.: Casa da mãe, Oficina', maxLength: 30 }] }]\n      }, { key: 'other-' + gen + '-' + hash(errors.labelOther || ''), onInput: onField, onBlur: onField }) : '';\n      const fs = R.fs ? d2.render(R.fs, {\n        layout, formField: refOf(d2.param('formField')), iconComponent: R.icon, version: String(gen),\n        values: FKEYS.reduce((o, x) => { o[x] = f[x] || ''; return o; }, {}),\n        errors: FKEYS.reduce((o, x) => { if (errors[x]) o[x] = errors[x]; return o; }, {}),\n        rows: [\n          { fields: [{ key: 'cep', label: 'CEP', mask: 'cep', placeholder: '00000-000', autoComplete: 'postal-code', hint: cepHint, linkLabel: 'Não sei meu CEP', linkHref: 'https://buscacepinter.correios.com.br/app/endereco/index.php' }, { kind: 'spacer' }] },\n          { fields: [{ key: 'street', label: 'Rua / avenida', autoComplete: 'address-line1' }] },\n          { keep: true, fields: [{ key: 'number', label: 'Número', placeholder: 'Ou S/N' }, { key: 'complement', label: 'Complemento', optional: true, placeholder: 'Apto, bloco, referência', autoComplete: 'address-line2' }] },\n          { fields: [{ key: 'district', label: 'Bairro' }, { key: 'city', label: 'Cidade', autoComplete: 'address-level2' }] },\n          { fields: [{ key: 'uf', label: 'Estado', mask: 'uf', placeholder: 'UF', maxLength: 2, autoComplete: 'address-level1' }, { kind: 'spacer' }] }\n        ]\n      }, { key: 'fs-' + gen + '-' + hash(errors), onInput: onField, onBlur: onField }) : '';\n      const saveCb = R.cb ? d2.render(R.cb, { name: 'salvar', checked: f.save !== false, top: true, layout, iconComponent: R.icon, label: 'Salvar este endereço na minha conta ((fica pronto nas próximas compras))' }, {\n        key: 'save-' + gen, onChange: (v) => { b.__f.save = !!v; }\n      }) : '';\n      const useBtn = R.btn ? `<span data-act=\"useaddr\">${d2.render(R.btn, { label: 'Usar este endereço', variant: 'primary', layout }, { key: 'use-' + gen })}</span>` : '';\n      const canCancel = list.length > 0 || !!b.__new;\n      const cancel = R.btn && canCancel ? `<span data-act=\"cancelform\">${d2.render(R.btn, { label: 'Cancelar', variant: 'outline', layout }, { key: 'cancel-' + gen })}</span>` : '';\n      addrBody = `<div class=\"form\"><h3>${b.__new ? 'Editar novo endereço' : 'Novo endereço'}</h3>${chips}${other}${fs}${saveCb}<div class=\"btns\">${useBtn}${cancel}</div></div>`;\n    } else {\n      const cards = list.map((a) => addrOpt(a, '', '')).join('');\n      const tmp = b.__new ? addrOpt(Object.assign({ id: 'new', main: false }, b.__new.clean), `<span class=\"tag g\">${b.__new.save ? 'Será salvo na conta' : 'Só neste pedido'}</span>`, '<button type=\"button\" class=\"edit\" data-act=\"editnew\" style=\"margin-top:8px\">Editar</button>') : '';\n      const add = `<button type=\"button\" class=\"add\" data-act=\"newaddr\">+ Cadastrar novo endereço</button>`;\n      const lineShip = own.length\n        ? (shipPrice != null && p.shippingCarrier\n          ? `<p>${ic('truck', 18, '#0b0b0c', 'tr1')}<span><b>Entrega: ${esc(p.shippingCarrier)}${p.shippingDays ? ' · ' + esc(p.shippingDays) + ' dias úteis' : ''}</b> — frete ${esc(shipLabel)}</span></p>`\n          : `<p>${ic('truck', 18, '#0b0b0c', 'tr1')}<span><b>Entrega: frete a calcular</b> — o valor e o prazo aparecem antes do pagamento.</span></p>`)\n        : '';\n      const linePartner = partners.length\n        ? `<p>${ic('truck', 18, '#6b6e73', 'tr2')}<span>Os itens ${esc(partnerNames.length ? partnerNames.join(', ') : 'do parceiro')} são enviados direto pelo parceiro, com rastreio próprio.</span></p>` : '';\n      const ships = lineShip || linePartner ? `<div class=\"ship\">${lineShip}${linePartner}</div>` : '';\n      addrBody = `<div class=\"opts\" role=\"radiogroup\" aria-label=\"Endereço de entrega\">${cards}${tmp}</div>${add}${ships}`;\n    }\n    const manage = list.length && mode !== 'form' ? `<a class=\"lnk\" href=\"${esc(p.manageHref || '/perfil/meus-enderecos')}\">Gerenciar</a>` : '';\n    const sec1 = `<section class=\"sec\" aria-labelledby=\"h-end\">${head3('01', '<span id=\"h-end\">Endereço de entrega</span>', 'pin', manage)}${addrBody}${st.addrErr ? `<p class=\"serr\" role=\"alert\">${esc(st.addrErr)}</p>` : ''}</section>`;\n\n    // ——— Seção 02 · quem vai receber\n    const selfName = sess.nome || '[Nome completo do cliente]';\n    const optRec = (id, title, desc) => `<button type=\"button\" class=\"opt${recip === id ? ' on' : ''}\" data-act=\"recip\" data-id=\"${id}\" role=\"radio\" aria-checked=\"${recip === id ? 'true' : 'false'}\"><span class=\"dot\"></span><span class=\"ob\"><span class=\"ot\">${esc(title)}</span><span class=\"od\">${esc(desc)}</span></span></button>`;\n    const otherForm = recip === 'other' && R.fs ? d2.render(R.fs, {\n      layout, formField: refOf(d2.param('formField')), iconComponent: R.icon, version: 'o' + hash(oErrors),\n      values: { name: b.__o.name || '', phone: b.__o.phone || '' }, errors: oErrors,\n      rows: [\n        { fields: [{ key: 'name', label: 'Nome de quem vai receber', placeholder: 'Nome completo', autoComplete: 'off' }] },\n        { fields: [{ key: 'phone', label: 'Telefone', mask: 'phone', placeholder: '(00) 00000-0000', hint: 'Se o entregador precisar falar com quem recebe.', autoComplete: 'off' }, { kind: 'spacer' }] }\n      ]\n    }, { key: 'oth-' + hash(oErrors), onInput: onOther, onBlur: onOther }) : '';\n    const sec2 = `<section class=\"sec\" aria-labelledby=\"h-rec\">${head3('02', '<span id=\"h-rec\">Quem vai receber</span>', 'user', '')}<div class=\"opts\" role=\"radiogroup\" aria-label=\"Quem vai receber\">${optRec('self', selfName, 'Você — o nome do cadastro')}${optRec('other', 'Outra pessoa', 'Alguém vai receber por você')}</div>${otherForm}<p class=\"note\">A nota fiscal continua no nome e CPF/CNPJ de quem compra.</p></section>`;\n\n    // ——— Seção 03 · forma de pagamento\n    const pixIcon = '<svg class=\"ic\" width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0b0b0c\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M12 3l6 6-3 3 3 3-6 6-6-6 3-3-3-3 6-6Z\"/></svg>';\n    const optPay = (id, iconHtml, title, desc, price) => `<button type=\"button\" class=\"opt${pay === id ? ' on' : ''}\" data-act=\"pay\" data-id=\"${id}\" role=\"radio\" aria-checked=\"${pay === id ? 'true' : 'false'}\"><span class=\"dot\"></span><span class=\"ob\"><span class=\"ot\">${iconHtml}${esc(title)}</span><span class=\"od\">${esc(desc)}</span><span class=\"pr\">${esc(price)}</span></span></button>`;\n    const cardDesc = parc > 1 ? `Em até ${parc}x. Você escolhe as parcelas no Mercado Pago.` : 'À vista. Você informa os dados do cartão no Mercado Pago.';\n    const infoTxt = pay === 'pix' ? 'Ao continuar, o Mercado Pago gera o QR Code e o código “copia e cola” do Pix.' : 'Ao continuar, você informa os dados do cartão e escolhe as parcelas no ambiente seguro do Mercado Pago.';\n    const sec3 = `<section class=\"sec\" aria-labelledby=\"h-pag\">${head3('03', '<span id=\"h-pag\">Forma de pagamento</span>', 'card', '')}<div class=\"opts\" role=\"radiogroup\" aria-label=\"Forma de pagamento\">${optPay('pix', pixIcon, 'Pix', 'Aprovação na hora. O QR Code aparece no Mercado Pago.', brl(totalPix) + ' à vista')}${optPay('card', ic('card', 18, '#0b0b0c', 'cd'), 'Cartão de crédito', cardDesc, brl(totalCard))}</div><div class=\"bar\">${ic('lock', 18, '#45474b', 'lk')}<span>${esc(infoTxt)}</span></div></section>`;\n\n    const goneNotice = gone.length && R.notice ? d2.render(R.notice, { iconName: 'bag', live: true, layout, iconComponent: R.icon, body: '**Tem item esgotado na sua sacola.** Volte para a sacola e remova para continuar a compra.' }, { key: 'gone-' + gone.length }) : '';\n    const srv = st.srvErr ? `<p class=\"serr\" role=\"alert\">${esc(st.srvErr)}</p>` : '';\n\n    // ——— Resumo\n    const sumItems = items.map((i) => ({ sku: i.sku, name: i.name, detail: i.detail, image: i.image, quantity: i.quantity, price: i.soldOut ? 'Esgotado' : brl(i.unit * i.quantity), partner: i.partner ? 'Enviado pela ' + (i.brand || 'parceiro') : '', soldOut: i.soldOut }));\n    const sumParams = {\n      items: sumItems, count, subtotal: brl(subtotal), discount: discount > 0 ? '− ' + brl(discount) : '', discountLabel: 'Desconto Pix', shipping: shipLabel, total: brl(total),\n      totalNote: pay === 'pix' ? 'Pagamento à vista no Pix' : (parc > 1 ? `Em até ${parc}x no cartão. Juros, se houver, são informados no Mercado Pago.` : 'Pagamento à vista no cartão'),\n      termsChecked: termsOn, termsLabel: `Li e aceito os [Termos e Condições de Venda](^${p.termsHref || '/politicas/termos-de-venda'}).`,\n      coupon: st.cupom ? (st.cupom.status === 'ok'\n        ? { status: 'ok', code: st.cupom.codigo, discount: brl(cupDesc), message: 'Desconto sobre os produtos. Não inclui o frete.' }\n        : { status: 'invalid', code: st.cupom.codigo, message: 'O cupom ' + st.cupom.codigo + ' não vale mais: ' + st.cupom.erro }) : null,\n      isDisabled: !termsOn || !live.length || gone.length > 0, busy: !!st.busy || truthy(p.busy), cartHref: p.cartHref || '/sacola', layout\n    };\n    const summary = R.sum ? d2.render(R.sum, Object.assign({ button: R.btn, checkbox: R.cb, iconComponent: R.icon }, sumParams), {\n      key: 'sum-' + hash(sumParams), style: 'height:auto',\n      onTermsChange: (v) => { d2.state.terms = !!v; },\n      onSubmit: () => { const h = b.__h || {}; if (h.submit) h.submit(); }\n    }) : '';\n\n    return `<div class=\"co l-${layout}\"><div class=\"in\" style=\"padding:${PAD[layout]}\">${crumbs}${head}\n      <div class=\"grid\"><div class=\"main\">${goneNotice}${sec1}${sec2}${sec3}</div><aside class=\"aside\" aria-label=\"Resumo do pedido\">${summary}${srv}</aside></div>\n    </div></div>`;\n  }\n};\n"}},"badge":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"badge"}}}},"busy":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"busy"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"cartHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cartHref"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"checkoutPath":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutPath"}}}},"checkoutSummary":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkoutSummary"}}}},"chipGroup":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"chipGroup"}}}},"createService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"createService"}}}},"dataset":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"dataset"}}}},"demo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demo"}}}},"demoName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demoName"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"formSection":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formSection"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"listService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"listService"}}}},"loginHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"loginHref"}}}},"manageHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"manageHref"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"onAddressSaved":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onAddressSaved"}}}},"onSubmit":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onSubmit"}}}},"productsService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"productsService"}}}},"saveService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"saveService"}}}},"sessionService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sessionService"}}}},"shippingCarrier":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingCarrier"}}}},"shippingDays":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingDays"}}}},"shippingPrice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"shippingPrice"}}}},"startService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"startService"}}}},"stepIndicator":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"stepIndicator"}}}},"storageKey":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"storageKey"}}}},"subtitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subtitle"}}}},"termsHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"termsHref"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}}}}}}}},{"_id":"6ac80449e00852cfc3561de4","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"LegalLayout","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// LegalLayout — Loja Lucas Fontana\n// O corpo de uma página de texto legal (Termos e Condições de Venda, e depois Trocas e devoluções, Privacidade etc.):\n// kicker pequeno, título em caixa alta (44/36/30px), \"Última atualização: …\", um parágrafo de abertura e as seções numeradas.\n// `sections: [{ title, body }]` — o `body` aceita parágrafos separados por uma linha em branco e **negrito** entre asteriscos duplos.\n// `kicker` (padrão \"Políticas\"), `title`, `updated` (texto livre, ex.: \"08/10/2026\"), `intro`.\n// A coluna de leitura tem no máximo 760px de largura, para o texto ficar confortável. Não tem botão nem ícone.\n// Fica entre o AnnouncementBar/Header e o Footer da artboard, como as outras páginas.\nconst LAYOUTS = ['desktop', 'tablet', 'mobile'];\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst arr = (v) => {\n  if (Array.isArray(v)) return v;\n  if (typeof v === 'string' && v.trim()) { try { const j = JSON.parse(v); return Array.isArray(j) ? j : []; } catch (e) { return []; } }\n  return [];\n};\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst PAD = { desktop: '64px 96px 120px', tablet: '48px 40px 88px', mobile: '32px 20px 64px' };\nconst rich = (t) => esc(t).replace(/\\*\\*(.+?)\\*\\*/g, '<b>$1</b>');\nconst paras = (t) => String(t == null ? '' : t).split(/\\r?\\n\\s*\\r?\\n/).map((x) => x.trim()).filter(Boolean).map((x) => `<p>${rich(x)}</p>`).join('');\n\nexport default {\n  css: `\n:host { display: block; width: 100%; }\n* { box-sizing: border-box; }\n.lg { --red-500: #c8102e; --ink: #0b0b0c; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  width: 100%; background: #ffffff; color: var(--ink); font-family: ${FF}; -webkit-font-smoothing: antialiased; }\n.col { box-sizing: content-box; max-width: 760px; display: flex; flex-direction: column; gap: 32px; }\n.kicker { margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase; color: var(--red-500); }\nh1 { margin: 0; font-size: 44px; font-weight: 800; font-stretch: 115%; line-height: 1.02; letter-spacing: -.01em; text-transform: uppercase; }\n.upd { margin: 0; font-size: 14px; color: var(--grey-500); }\n.intro { margin: 0; font-size: 17px; line-height: 1.65; color: var(--grey-700); }\n.head { display: flex; flex-direction: column; gap: 12px; padding-bottom: 28px; border-bottom: 1px solid var(--line); }\n.sec { display: flex; flex-direction: column; gap: 10px; }\nh2 { margin: 0; font-size: 20px; font-weight: 800; font-stretch: 112%; line-height: 1.2; }\nh2 span { color: var(--red-500); margin-right: 8px; }\n.sec p { margin: 0; font-size: 16px; line-height: 1.7; color: var(--grey-700); }\n.sec b { color: var(--ink); }\n.l-tablet h1 { font-size: 36px; }\n.l-mobile h1 { font-size: 30px; font-stretch: 112%; letter-spacing: 0; }\n.l-mobile .col { gap: 24px; } .l-mobile h2 { font-size: 18px; } .l-mobile .sec p { font-size: 15px; } .l-mobile .intro { font-size: 16px; }\n`,\n  mounted() { FONT(); },\n  render(d2) {\n    const p = d2.params || {};\n    const layout = LAYOUTS.indexOf(p.layout) >= 0 ? p.layout : 'desktop';\n    const secs = arr(p.sections);\n    const body = secs.map((s, i) => `<section class=\"sec\" data-key=\"s${i}\"><h2><span>${i + 1}.</span>${esc(s && s.title)}</h2>${paras(s && s.body)}</section>`).join('');\n    return `<div class=\"lg l-${layout}\"><div class=\"col\" style=\"padding:${PAD[layout]}\">\n      <div class=\"head\"><p class=\"kicker\">${esc(p.kicker || 'Políticas')}</p><h1>${esc(p.title || 'Termos e condições de venda')}</h1>${p.updated ? `<p class=\"upd\">Última atualização: ${esc(p.updated)}</p>` : ''}${p.intro ? `<p class=\"intro\">${rich(p.intro)}</p>` : ''}</div>\n      ${body}\n    </div></div>`;\n  }\n};\n"}},"intro":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"intro"}}}},"kicker":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"kicker"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"sections":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"sections"}}}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"updated":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"updated"}}}}}}}}}},{"_id":"6ac80d65edc6767b321ab366","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoPedidos","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Pedidos (backoffice, só desktop)\n// Os pedidos feitos na loja. Duas telas na mesma página, como em Mensagens:\n// a lista (mais novos primeiro, com busca por número, cliente ou e-mail, filtro por status do pedido e do pagamento,\n// total de pedidos e paginação de 20 em 20) e, ao abrir um pedido, o detalhe completo: status e datas, cliente e pagador,\n// endereço de entrega e envio (quem envia, código de rastreio), os itens com quantidade e valores, resumo financeiro\n// (subtotal, desconto, frete, total), forma de pagamento, ids do Mercado Pago, estoque e aceite dos termos.\n// Só leitura: não existe editar nem excluir. Reaproveita BackLink, EmptyState e Notice; o resto é HTML simples com os tokens da Gestão.\n// Os dados vêm do serviço protegido `listService` (gestaoPedidosListar), que devolve { ok, total, pedidos }.\n// Os rótulos de status conhecem os valores comuns (aguardando pagamento, aprovado, enviado, entregue, cancelado, estornado…);\n// um valor que não conhece aparece como está, só com a primeira letra maiúscula.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst fmtD = (v) => { try { const d = new Date(v); return !v || isNaN(d.getTime()) ? '' : d.toLocaleDateString('pt-BR'); } catch (e) { return ''; } };\nconst fmtDT = (v) => { try { const d = new Date(v); return !v || isNaN(d.getTime()) ? '' : d.toLocaleDateString('pt-BR') + ' ' + d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } catch (e) { return ''; } };\nconst brl = (n) => 'R$ ' + Number(n || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });\nconst wpp = (n) => { const d = String(n || '').replace(/[^0-9]/g, ''); if (!d) return ''; return 'https://wa.me/' + (d.length <= 11 ? '55' + d : d); };\nconst arr = (v) => (Array.isArray(v) ? v : []);\nconst obj = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : {});\nconst pick = (o, keys) => { for (const k of keys) { if (o && o[k] != null && o[k] !== '') return o[k]; } return ''; };\n\n// Rótulos e tom de cada status. tom: ok (escuro), wait (cinza), bad (vermelho)\nconst STATUS = {\n  checkout_iniciado: ['Checkout iniciado', 'wait'], novo: ['Novo', 'wait'],\n  aguardando_pagamento: ['Aguardando pagamento', 'wait'], pendente: ['Aguardando pagamento', 'wait'], criado: ['Aguardando pagamento', 'wait'], aguardando: ['Aguardando pagamento', 'wait'],\n  pago: ['Pago', 'ok'], pagamento_aprovado: ['Pagamento aprovado', 'ok'], aprovado: ['Pagamento aprovado', 'ok'], approved: ['Pagamento aprovado', 'ok'], processed: ['Pagamento aprovado', 'ok'],\n  em_separacao: ['Em separação', 'ok'], preparando: ['Em separação', 'ok'],\n  enviado: ['Enviado', 'ok'], entregue: ['Entregue', 'ok'],\n  recusado: ['Recusado', 'bad'], recusado_ou_expirado: ['Recusado ou expirado', 'bad'], expirado: ['Expirado', 'bad'], rejected: ['Recusado', 'bad'], failed: ['Recusado', 'bad'], expired: ['Expirado', 'bad'],\n  cancelado: ['Cancelado', 'bad'], estornado: ['Estornado', 'bad'], refunded: ['Estornado', 'bad']\n};\nconst statusOf = (v) => {\n  const k = norm(v).replace(/[\\s-]+/g, '_');\n  if (!k) return ['—', 'wait'];\n  if (STATUS[k]) return STATUS[k];\n  const t = String(v).replace(/_/g, ' ');\n  return [t.charAt(0).toUpperCase() + t.slice(1), 'wait'];\n};\nconst FORMA = { pix: 'Pix', cartao: 'Cartão de crédito', card: 'Cartão de crédito', credit_card: 'Cartão de crédito' };\nconst formaOf = (p) => { const f = FORMA[norm(p.pagamentoForma)] || (p.pagamentoForma ? String(p.pagamentoForma) : '—'); return f + (Number(p.parcelas) > 1 ? ' em ' + p.parcelas + 'x' : ''); };\nconst ENVIO = { lucas: 'Auto Super', parceiro: 'Parceiro' };\nconst itemOf = (i) => {\n  const o = obj(i);\n  const qty = Number(pick(o, ['quantity', 'quantidade', 'qtd', 'qty'])) || 1;\n  const unit = Number(pick(o, ['unitPrice', 'preco_unitario', 'precoUnitario', 'preco', 'valor_unitario', 'price'])) || 0;\n  const tot = Number(pick(o, ['total', 'subtotal', 'valor_total'])) || unit * qty;\n  return { name: pick(o, ['name', 'nome', 'produto_nome', 'titulo', 'produto']) || '(sem nome)', sku: pick(o, ['sku']), qty, unit, tot, partner: pick(o, ['marca', 'brand', 'parceiro']) };\n};\nconst addrLines = (e) => {\n  const o = obj(e);\n  const rua = pick(o, ['street', 'logradouro', 'rua']); const num = pick(o, ['number', 'numero']); const comp = pick(o, ['complement', 'complemento']);\n  const bairro = pick(o, ['district', 'bairro']); const cidade = pick(o, ['city', 'cidade']); const uf = pick(o, ['uf', 'estado']); const cep = pick(o, ['cep']);\n  const l1 = [rua, num].filter(Boolean).join(', ') + (comp ? ' — ' + comp : '');\n  const l2 = [bairro, [cidade, uf].filter(Boolean).join('/')].filter(Boolean).join(' — ');\n  return { label: pick(o, ['label', 'apelido']), l1, l2, cep, quem: pick(o, ['recebedor_nome', 'recipient', 'destinatario', 'recebedor', 'quem_recebe']), tel: pick(o, ['recebedor_telefone', 'recipient_phone', 'telefone_recebedor', 'telefone', 'phone']) };\n};\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  return `<nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\nconst PAGER_CSS = `\n.pager { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 20px; flex-wrap: wrap; }\n.pg { min-width: 40px; height: 40px; padding: 0 12px; border: 1.5px solid #ececea; background: #fff; color: #0b0b0c; font: inherit; font-size: 13px; font-weight: 700; border-radius: 2px; cursor: pointer; }\n.pg.on { background: #0b0b0c; color: #fff; border-color: #0b0b0c; }\n.pg[disabled] { opacity: .35; cursor: not-allowed; }\n.pg:focus-visible { outline: 2px solid #c8102e; outline-offset: 2px; }\n.dots { padding: 0 4px; color: #6b6c70; }`;\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.page { max-width: 1120px; margin: 0 auto; padding: 40px 48px 64px; }\n.back { margin-bottom: 16px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }\n.search { flex: 1; min-width: 240px; max-width: 380px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.sel { height: 44px; min-width: 200px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 12px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus, .sel:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; overflow-x: auto; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 12px; border-bottom: 1px solid var(--g100); white-space: nowrap; }\nth.r, td.r { text-align: right; }\ntd { padding: 14px 12px; border-bottom: 1px solid var(--g100); font-size: 14px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.num { font-weight: 800; white-space: nowrap; }\ntd.nome { font-weight: 700; max-width: 260px; overflow-wrap: anywhere; }\ntd small { display: block; margin-top: 2px; color: var(--g500); font-size: 12px; font-weight: 400; }\ntd.data { color: var(--g500); white-space: nowrap; }\ntd.tot { font-weight: 800; white-space: nowrap; }\n.pill { display: inline-flex; align-items: center; height: 26px; padding: 0 12px; border-radius: 999px; background: var(--g100); font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; white-space: nowrap; }\n.pill.ok { background: var(--ink); color: var(--white); }\n.pill.bad { background: var(--red); color: var(--white); }\n.pill.warn { background: #fff1cc; color: #7a5400; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); text-decoration: underline; text-underline-offset: 4px; cursor: pointer; }\n.count { margin: 12px 0 0; color: var(--g500); font-size: 13px; text-align: center; }\n.empty { padding: 8px; }\n.det { display: flex; flex-direction: column; gap: 24px; }\n.box { padding: 28px 32px; display: flex; flex-direction: column; gap: 20px; }\n.box h2 { margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase; color: var(--g500); }\n.pills { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px 32px; }\n.fld span.l { display: block; margin-bottom: 4px; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); }\n.fld div.v { font-size: 16px; font-weight: 600; overflow-wrap: anywhere; }\n.fld div.v small { display: block; margin-top: 2px; font-size: 13px; font-weight: 400; color: var(--g500); }\n.fld a { color: var(--ink); text-underline-offset: 3px; }\n.fld a:hover { color: var(--red); }\n.items th, .items td { padding: 12px 0; }\n.items td:first-child, .items th:first-child { padding-left: 0; }\n.sum { margin-left: auto; width: 100%; max-width: 360px; display: flex; flex-direction: column; gap: 8px; }\n.sum .row { display: flex; justify-content: space-between; gap: 16px; font-size: 15px; color: var(--g800); }\n.sum .row.t { margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--g100); font-size: 20px; font-weight: 800; color: var(--ink); }\n.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 14px; }\n${PAGER_CSS}\n`,\n\n  mounted(d2) {\n    const S = d2.state;\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const reload = async () => {\n      try {\n        const res = await d2.rush(d2.param('listService')).invoke({});\n        const out = res && res.data !== undefined ? res.data : res;\n        S.orders = out && out.ok ? arr(out.pedidos) : [];\n        S.total = out && out.ok ? Number(out.total) || 0 : 0;\n        if (!(out && out.ok)) say('error', 'Não foi possível carregar os pedidos.');\n      } catch (e) { say('error', 'Não foi possível carregar os pedidos.'); }\n      S.loaded = true;\n    };\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'open') { S.id = id; S.view = 'detalhe'; S.notice = null; }\n      else if (act === 'back') { S.view = 'lista'; S.notice = null; }\n      else if (act === 'prob') { S.fR = !S.fR; S.page = 1; }\n      else if (act === 'copy') {\n        const u = el.getAttribute('data-url') || '';\n        try { navigator.clipboard.writeText(u).then(() => say('success', 'Link do pagamento copiado.'), () => say('error', 'Não deu para copiar. Selecione o link e copie à mão.')); }\n        catch (err) { say('error', 'Não deu para copiar. Selecione o link e copie à mão.'); }\n      }\n    });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; S.page = 1; });\n    d2.on('select[data-fs]', 'change', (e, el) => { S.fS = el.value; S.page = 1; });\n    d2.on('select[data-fp]', 'change', (e, el) => { S.fP = el.value; S.page = 1; });\n    d2.on('[data-pg]', 'click', (e, el) => { S.page = Number(el.getAttribute('data-pg')) || 1; });\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = { button: refOf(d2.param('button')), notice: refOf(d2.param('notice')), backLink: refOf(d2.param('backLink')), emptyState: refOf(d2.param('emptyState')), iconComponent: refOf(d2.param('iconComponent')) };\n    if (S.view === undefined) { S.view = 'lista'; S.q = ''; S.fS = ''; S.fP = ''; S.page = 1; S.id = ''; S.notice = null; }\n    const orders = S.orders || [];\n    const notice = S.notice && P.notice\n      ? `<div class=\"notice\">${d2.render(P.notice, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true, iconComponent: P.iconComponent }, { key: 'notice', style: 'height:auto' })}</div>` : '';\n    const pill = (v) => { const s = statusOf(v); return `<span class=\"pill ${s[1]}\">${esc(s[0])}</span>`; };\n    const when = (p) => p.criadoEm || p.data;\n    let main = '';\n\n    if (S.view === 'detalhe') {\n      const p = orders.find((x) => x._id === S.id);\n      if (!p) { S.view = 'lista'; return `<div class=\"root\"></div>`; }\n      const items = arr(p.itens).map(itemOf);\n      const qtdTotal = items.reduce((s, i) => s + i.qty, 0);\n      const a = addrLines(p.entrega);\n      const w = wpp(p.pagadorWhatsapp);\n      const itensSem = arr(p.revisaoEstoqueItens).map((i) => `${i.nome} (pedido: ${i.quantidade}, em estoque: ${i.estoque})`).join('; ');\n      const alerta = (p.revisaoEstoque || p.revisaoPagamento) ? `<div class=\"card box\" style=\"border-left:4px solid #b8222c\"><h2>Precisa de atenção</h2>${p.revisaoEstoque ? `<p style=\"margin:0 0 8px\"><b>Faltou estoque.</b> O pagamento foi aprovado, mas não havia saldo para baixar${itensSem ? ': ' + esc(itensSem) : ''}. O estoque não foi baixado. Decida entre repor o estoque ou cancelar e reembolsar no Mercado Pago.</p>` : ''}${p.revisaoPagamento ? `<p style=\"margin:0\"><b>Pagamento divergente.</b> O valor ou a order do Mercado Pago não bate com o pedido. Confira no Mercado Pago antes de enviar.</p>` : ''}</div>` : '';\n      const linkOk = norm(p.status) === 'aguardando_pagamento' && p.mpCheckoutUrl && /^https:\\/\\/([a-z0-9-]+\\.)*mercadopago\\.com(\\.br)?\\//i.test(String(p.mpCheckoutUrl));\n      const linkPag = linkOk ? `<div class=\"fld\" style=\"grid-column:1 / -1\"><span class=\"l\">Link do pagamento</span><div class=\"v\"><a href=\"${esc(p.mpCheckoutUrl)}\" target=\"_blank\" rel=\"noopener\" class=\"mono\" style=\"word-break:break-all\">${esc(p.mpCheckoutUrl)}</a></div><div style=\"display:flex;gap:16px;align-items:center;margin-top:10px\"><button class=\"lk\" data-act=\"copy\" data-url=\"${esc(p.mpCheckoutUrl)}\">Copiar link</button>${p.mpExpiraEm ? `<small>Válido até ${esc(fmtDT(p.mpExpiraEm))}</small>` : ''}</div></div>` : '';\n      const subtotal = Number(p.subtotal) || items.reduce((s, i) => s + i.tot, 0);\n      const itemRows = items.map((i, n) => `<tr data-key=\"i${n}\"><td>${esc(i.name)}${i.sku ? `<small>SKU ${esc(i.sku)}</small>` : ''}</td><td class=\"r\">${i.qty}</td><td class=\"r\">${brl(i.unit)}</td><td class=\"r tot\">${brl(i.tot)}</td></tr>`).join('');\n      const f = (l, v) => `<div class=\"fld\"><span class=\"l\">${l}</span><div class=\"v\">${v || '—'}</div></div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${P.backLink ? d2.render(P.backLink, { label: 'Voltar para os pedidos' }, { key: 'back', width: 260, height: 32 }) : ''}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Pedido</p><h1>${esc(p.numero || '—')}</h1><p class=\"sub\">Feito em ${esc(fmtDT(when(p)) || '—')}${p.clienteNome || p.pagadorNome ? ' · ' + esc(p.clienteNome || p.pagadorNome) : ''}</p></div></div>\n  ${notice}\n  <div class=\"det\">\n    <div class=\"card box\"><h2>Situação</h2>\n      <div class=\"pills\">${pill(p.status)}${norm(p.status) === 'checkout_iniciado' ? '<span class=\"sub\" style=\"margin:0\">Saiu antes de ir para o pagamento</span>' : `<span class=\"sub\" style=\"margin:0\">Pagamento:</span>${pill(p.pagamentoStatus)}`}${p.revisaoEstoque ? '<span class=\"pill warn\">Revisar estoque</span>' : ''}</div>\n      <div class=\"grid\">\n        ${f('Criado em', esc(fmtDT(when(p))))}\n        ${f('Pago em', esc(fmtDT(p.pagoEm)))}\n        ${f('Enviado em', esc(fmtDT(p.enviadoEm)))}\n        ${f('Entregue em', esc(fmtDT(p.entregueEm)))}\n      </div>\n    </div>\n    <div class=\"card box\"><h2>Itens do pedido · ${qtdTotal} ${qtdTotal === 1 ? 'unidade' : 'unidades'}</h2>\n      <table class=\"items\"><thead><tr><th>Produto</th><th class=\"r\">Qtd.</th><th class=\"r\">Valor unit.</th><th class=\"r\">Total</th></tr></thead><tbody>${itemRows || '<tr><td colspan=\"4\">Sem itens registrados.</td></tr>'}</tbody></table>\n      <div class=\"sum\">\n        <div class=\"row\"><span>Subtotal</span><span>${brl(subtotal)}</span></div>\n        ${p.cupom && Number(p.cupomDesconto) > 0 ? `<div class=\"row\"><span>Cupom ${esc(p.cupom)}</span><span>− ${brl(p.cupomDesconto)}</span></div>` : ''}\n        ${Number(p.desconto) > 0 ? `<div class=\"row\"><span>Desconto Pix</span><span>− ${brl(p.desconto)}</span></div>` : ''}\n        <div class=\"row\"><span>Frete</span><span>${Number(p.frete) > 0 ? brl(p.frete) : (p.envioPor === 'parceiro' ? 'Pelo parceiro' : 'R$ 0,00')}</span></div>\n        <div class=\"row t\"><span>Total</span><span>${brl(p.total)}</span></div>\n      </div>\n    </div>\n    ${alerta}<div class=\"card box\"><h2>Pagamento</h2>\n      <div class=\"grid\">\n        ${f('Forma de pagamento', esc(formaOf(p)))}\n        ${f('Status do pagamento', esc(statusOf(p.pagamentoStatus)[0]))}\n        ${f('Mercado Pago · pedido', p.mpOrderId ? `<span class=\"mono\">${esc(p.mpOrderId)}</span>` : '')}\n        ${f('Mercado Pago · pagamento', p.mpPaymentId ? `<span class=\"mono\">${esc(p.mpPaymentId)}</span>` : '')}\n        ${linkPag}\n      </div>\n    </div>\n    <div class=\"card box\"><h2>Cliente e pagador</h2>\n      <div class=\"grid\">\n        ${f('Conta do cliente', esc(p.clienteNome))}\n        ${f('Pagador', esc(p.pagadorNome))}\n        ${f('E-mail', p.pagadorEmail ? `<a href=\"mailto:${esc(p.pagadorEmail)}\">${esc(p.pagadorEmail)}</a>` : '')}\n        ${f('WhatsApp', p.pagadorWhatsapp ? `<a href=\"${esc(w)}\" target=\"_blank\" rel=\"noopener\">${esc(p.pagadorWhatsapp)}</a>` : '')}\n        ${f(esc(String(p.pagadorTipoDocumento || 'Documento').toUpperCase()), esc(p.pagadorDocumento))}\n        ${f('Aceita comunicações', p.aceitaComunicacao ? 'Sim' : 'Não')}\n      </div>\n    </div>\n    <div class=\"card box\"><h2>Entrega</h2>\n      <div class=\"grid\">\n        ${f('Endereço', a.l1 || a.l2 ? `${esc(a.label ? a.label + ' · ' : '')}${esc(a.l1)}<small>${esc(a.l2)}${a.cep ? ' · CEP ' + esc(a.cep) : ''}</small>` : '')}\n        ${f('Quem recebe', a.quem || a.tel ? `${esc(a.quem)}${a.tel ? `<small>${esc(a.tel)}</small>` : ''}` : '')}\n        ${f('Enviado por', esc(ENVIO[norm(p.envioPor)] || p.envioPor))}\n        ${f('Status do envio', p.statusEnvio ? esc(statusOf(p.statusEnvio)[0]) : '')}\n        ${f('Código de rastreio', p.codigoRastreio ? `<span class=\"mono\">${esc(p.codigoRastreio)}</span>` : '')}\n      </div>\n    </div>\n    <div class=\"card box\"><h2>Controle</h2>\n      <div class=\"grid\">\n        ${f('Estoque baixado', p.estoqueBaixado ? 'Sim' : 'Não')}\n        ${f('Revisão de estoque', p.revisaoEstoque ? 'Precisa de revisão' : 'Não')}\n        ${f('Revisão de pagamento', p.revisaoPagamento ? 'Precisa de revisão' : 'Não')}\n        ${f('Termos aceitos', esc(p.termosVersao))}\n        ${f('Aceite em', esc(fmtDT(p.termosAceiteEm)))}\n      </div>\n    </div>\n  </div>\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const sk = (v) => norm(v).replace(/[\\s-]+/g, '_');\n      const filtradas = orders.filter((p) => (!S.fS || sk(p.status) === S.fS) && (!S.fP || sk(p.pagamentoStatus) === S.fP) && (!S.fR || p.revisaoEstoque || p.revisaoPagamento)\n        && (!q || norm(p.numero).includes(q) || norm(p.clienteNome).includes(q) || norm(p.pagadorNome).includes(q) || norm(p.pagadorEmail).includes(q)));\n      const pages = Math.max(1, Math.ceil(filtradas.length / PAGE));\n      const cur = Math.min(Math.max(1, Number(S.page) || 1), pages);\n      const slice = filtradas.slice((cur - 1) * PAGE, cur * PAGE);\n      const opts = (key) => { const seen = {}; orders.forEach((p) => { const k = sk(p[key]); if (k && !seen[k]) seen[k] = statusOf(p[key])[0]; }); return Object.keys(seen).sort().map((k) => [k, seen[k]]); };\n      const rows = slice.map((p) => {\n        const nItens = arr(p.itens).reduce((s, i) => s + (itemOf(i).qty), 0);\n        return `\n    <tr data-key=\"${esc(p._id)}\"${(p.revisaoEstoque || p.revisaoPagamento) ? ' style=\"background:#fff5f5\"' : ''}>\n      <td class=\"num\">${esc(p.numero || '—')}</td>\n      <td class=\"data\">${esc(fmtDT(when(p)))}</td>\n      <td class=\"nome\">${esc(p.clienteNome || p.pagadorNome || '—')}${p.pagadorEmail ? `<small>${esc(p.pagadorEmail)}</small>` : ''}</td>\n      <td class=\"r\">${nItens}</td>\n      <td class=\"r tot\">${brl(p.total)}${((Number(p.desconto) || 0) + (Number(p.cupomDesconto) || 0)) > 0 ? '<small>− ' + brl((Number(p.desconto) || 0) + (Number(p.cupomDesconto) || 0)) + ' em descontos</small>' : ''}</td>\n      <td>${norm(p.status) === 'checkout_iniciado' ? '—<small>Não chegou ao pagamento</small>' : `${esc(formaOf(p))}<small>${esc(statusOf(p.pagamentoStatus)[0])}</small>`}</td>\n      <td>${pill(p.status)}${p.revisaoEstoque ? '<small style=\"color:#b8222c;font-weight:700\">Revisar estoque</small>' : ''}${p.revisaoPagamento ? '<small style=\"color:#b8222c;font-weight:700\">Revisar pagamento</small>' : ''}</td>\n      <td><button class=\"lk\" data-act=\"open\" data-id=\"${esc(p._id)}\">Abrir</button></td>\n    </tr>`;\n      }).join('');\n      const body = slice.length\n        ? `<div class=\"card\"><table><thead><tr><th>Pedido</th><th>Data</th><th>Cliente</th><th class=\"r\">Itens</th><th class=\"r\">Total</th><th>Pagamento</th><th>Status</th><th></th></tr></thead><tbody>${rows}</tbody></table></div>${pagerHtml(cur, filtradas.length, PAGE)}<p class=\"count\">${(cur - 1) * PAGE + 1}–${(cur - 1) * PAGE + slice.length} de ${filtradas.length} ${filtradas.length === 1 ? 'pedido' : 'pedidos'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\" style=\"padding:16px\">Carregando pedidos…</p></div>'\n        : `<div class=\"card empty\">${P.emptyState ? d2.render(P.emptyState, { eyebrow: 'Pedidos', title: orders.length ? 'Nenhum pedido encontrado' : 'Nenhum pedido ainda', description: orders.length ? 'Tente outro termo ou limpe os filtros.' : 'Os pedidos feitos na loja aparecem aqui.', iconName: 'bag' }, { key: 'empty', height: 260 }) : ''}</div>`;\n      const nProb = orders.filter((p) => p.revisaoEstoque || p.revisaoPagamento).length;\n      const probBtn = `<button data-act=\"prob\" aria-pressed=\"${S.fR ? 'true' : 'false'}\" style=\"height:44px;padding:0 18px;border:1px solid #0b0b0c;background:${S.fR ? '#0b0b0c' : '#fff'};color:${S.fR ? '#fff' : '#0b0b0c'};font-weight:700;font-size:13px;cursor:pointer\">Precisam de atenção${nProb ? ' (' + nProb + ')' : ''}</button>`;\n      const nTotal = S.loaded ? (Number(S.total) || orders.length) : 0;\n      const nAband = orders.filter((p) => norm(p.status) === 'checkout_iniciado').length;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Pedidos</h1><p class=\"sub\">${S.loaded ? `<b>${nTotal}</b> ${nTotal === 1 ? 'pedido' : 'pedidos'}${nAband ? ` · <b>${nAband}</b> ${nAband === 1 ? 'checkout' : 'checkouts'} sem pagamento` : ''}` : 'Os pedidos feitos na loja.'}</p></div></div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por número, cliente ou e-mail\" value=\"${esc(S.q)}\" aria-label=\"Buscar pedido\">\n    <select class=\"sel\" data-fs aria-label=\"Filtrar por status do pedido\"><option value=\"\">Todos os status</option>${opts('status').map((o) => `<option value=\"${esc(o[0])}\"${S.fS === o[0] ? ' selected' : ''}>${esc(o[1])}</option>`).join('')}</select>\n    <select class=\"sel\" data-fp aria-label=\"Filtrar por status do pagamento\"><option value=\"\">Todos os pagamentos</option>${opts('pagamentoStatus').map((o) => `<option value=\"${esc(o[0])}\"${S.fP === o[0] ? ' selected' : ''}>${esc(o[1])}</option>`).join('')}</select>${probBtn}</div>\n  ${body}\n</div>`;\n    }\n    return `<div class=\"root\">${main}</div>`;\n  }\n};\n"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"listService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"listService"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}}}}}}}},{"_id":"6aceb39436e69ceacd76f824","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"GestaoCupons","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// Gestão — Cupons (backoffice, só desktop)\n// Lista + formulário (criar/editar) na mesma tela; navegação interna. Reaproveita Button, Notice, EmptyState e BackLink.\n// Cupom de desconto da loja: tipo (porcentagem ou valor fixo), valor, validade, pedido mínimo, limite de usos e \"um uso por cliente\".\n// O código não muda depois de criado (os pedidos guardam o código usado). Cupom que já foi usado em pedido não pode ser excluído, só desativado.\n// Serviços: gestaoCuponsListar, gestaoCupomSalvar, gestaoCupomAtivar, gestaoCupomExcluir.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst norm = (s) => String(s || '').normalize('NFD').replace(/[\\u0300-\\u036f]/g, '').toLowerCase();\nconst num = (v) => { if (v === '' || v == null) return undefined; return Number(String(v).trim().replace(',', '.')); };\nconst brl = (v) => (v == null || v === '' || isNaN(Number(v)) ? '—' : Number(v).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }));\nconst s2 = (v) => (v == null ? '' : String(v));\nconst dmy = (s) => { const m = String(s || '').match(/^(\\d{4})-(\\d{2})-(\\d{2})$/); return m ? m[3] + '/' + m[2] + '/' + m[1] : ''; };\nconst hoje = () => { const d = new Date(); const p = (n) => String(n).padStart(2, '0'); return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate()); };\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst EMPTY_FORM = { codigo: '', tipo: 'percentual', valor: '', inicio: '', fim: '', valor_minimo: '', limite_total: '', um_por_cliente: false, ativo: true };\nconst descTxt = (r) => (r.tipo === 'percentual' ? s2(r.valor).replace('.', ',') + '%' : brl(r.valor));\nconst statusOf = (r) => {\n  if (r.ativo === false) return ['off', 'Inativo'];\n  if (r.fim && r.fim < hoje()) return ['off', 'Expirado'];\n  if (r.inicio && r.inicio > hoje()) return ['on', 'Agendado'];\n  if (Number(r.limite_total) > 0 && Number(r.usos) >= Number(r.limite_total)) return ['off', 'Esgotado'];\n  return ['on', 'Ativo'];\n};\nconst validadeTxt = (r) => {\n  if (r.inicio && r.fim) return dmy(r.inicio) + ' a ' + dmy(r.fim);\n  if (r.fim) return 'até ' + dmy(r.fim);\n  if (r.inicio) return 'a partir de ' + dmy(r.inicio);\n  return 'Sem prazo';\n};\n\n// ---- Paginação (mesmo trecho em todas as listas da Gestão) ----\nconst PAGE = 20;\nconst pagerHtml = (page, total, size) => {\n  const pages = Math.max(1, Math.ceil(total / size));\n  if (pages <= 1) return '';\n  const cur = Math.min(Math.max(1, page), pages);\n  const nums = [];\n  for (let i = 1; i <= pages; i++) {\n    if (i === 1 || i === pages || Math.abs(i - cur) <= 1) nums.push(i);\n    else if (nums[nums.length - 1] !== '…') nums.push('…');\n  }\n  const b = (label, p, dis, on, aria) => `<button class=\"pg${on ? ' on' : ''}\" data-pg=\"${p}\"${dis ? ' disabled' : ''}${on ? ' aria-current=\"page\"' : ''} aria-label=\"${aria}\">${label}</button>`;\n  const css = '.pager{display:flex;align-items:center;justify-content:center;gap:6px;margin-top:20px;flex-wrap:wrap}.pg{min-width:40px;height:40px;padding:0 12px;border:1.5px solid #ececea;background:#fff;color:#0b0b0c;font:inherit;font-size:13px;font-weight:700;border-radius:2px;cursor:pointer}.pg.on{background:#0b0b0c;color:#fff;border-color:#0b0b0c}.pg[disabled]{opacity:.35;cursor:not-allowed}.pg:focus-visible{outline:2px solid #c8102e;outline-offset:2px}.dots{padding:0 4px;color:#6b6c70}';\n  return `<style>${css}</style><nav class=\"pager\" aria-label=\"Paginação\">${b('‹ Anterior', cur - 1, cur === 1, false, 'Página anterior')}${nums.map((n) => (n === '…' ? '<span class=\"dots\">…</span>' : b(n, n, false, n === cur, 'Página ' + n))).join('')}${b('Próxima ›', cur + 1, cur === pages, false, 'Próxima página')}</nav>`;\n};\nconst pageOf = (d2, sig, total) => {\n  const r = d2.root;\n  if (!r) return 1;\n  if (r.__pgSig !== sig) { r.__pgSig = sig; r.__pg = 1; }\n  return Math.min(Math.max(1, Number(r.__pg) || 1), Math.max(1, Math.ceil(total / PAGE)));\n};\n\nexport default {\n  css: `\n:host { display: block; height: 100%; overflow-y: auto; }\n* { box-sizing: border-box; }\n.root { --red: #c8102e; --ink: #0b0b0c; --white: #fff; --g100: #ececea; --g50: #f5f5f3; --g500: #6b6c70; --g800: #2a2b2e;\n  position: relative; min-height: 100%; background: var(--g50); color: var(--ink);\n  font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif; -webkit-font-smoothing: antialiased; }\n.notice.below { margin: 20px 0 0; }\n.page { max-width: 1240px; margin: 0 auto; padding: 40px 48px 64px; }\n.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; }\n.eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--red); margin: 0 0 8px; }\nh1 { margin: 0; font-size: 34px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }\nh3 { margin: 0 0 16px; font-size: 14px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }\n.sub { margin: 8px 0 0; color: var(--g500); font-size: 15px; }\n.notice { margin-bottom: 16px; }\n.tools { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }\n.search { flex: 1; min-width: 240px; max-width: 360px; height: 44px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 14px; color: var(--ink); }\n.search:focus, .inp:focus { outline: 2px solid var(--red); outline-offset: 1px; border-color: var(--ink); }\n.chips { display: flex; gap: 8px; }\n.chip { height: 36px; padding: 0 16px; border: 1.5px solid var(--ink); background: var(--white); color: var(--ink); border-radius: 999px; font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; cursor: pointer; }\n.chip[aria-pressed=\"true\"] { background: var(--ink); color: var(--white); }\n.card { background: var(--white); border: 1px solid var(--g100); border-radius: 2px; }\ntable { width: 100%; border-collapse: collapse; }\nth { text-align: left; font-size: 11px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; color: var(--g500); padding: 14px 16px; border-bottom: 1px solid var(--g100); }\ntd { padding: 14px 16px; border-bottom: 1px solid var(--g100); font-size: 14px; vertical-align: middle; }\ntr:last-child td { border-bottom: 0; }\ntd.nome { font-weight: 800; font-family: ui-monospace, Menlo, Consolas, monospace; letter-spacing: .04em; } td.nome small { display: block; margin-top: 3px; font-weight: 400; color: var(--g500); font-family: 'Archivo', system-ui, sans-serif; letter-spacing: 0; font-size: 12px; }\ntd.num { white-space: nowrap; }\n.pill { display: inline-flex; align-items: center; gap: 8px; height: 26px; padding: 0 12px; border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; }\n.pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }\n.pill.on { background: var(--g100); color: var(--ink); } .pill.off { background: #fbe9ec; color: var(--red); }\n.no { color: var(--g500); font-size: 13px; }\n.acts { display: flex; gap: 14px; justify-content: flex-end; flex-wrap: wrap; }\n.lk { background: none; border: 0; padding: 0; font: inherit; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--ink); cursor: pointer; border-bottom: 1.5px solid var(--ink); }\n.lk:hover { color: var(--red); border-color: var(--red); } .lk.danger { color: var(--red); border-color: var(--red); }\n.count { margin: 12px 2px 0; color: var(--g500); font-size: 13px; }\n.form { padding: 32px; margin-bottom: 20px; }\n.back { margin-bottom: 20px; }\n.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; } .grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0 24px; }\n.field { margin-bottom: 22px; }\n.lbl { display: block; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 8px; }\n.inp { width: 100%; height: 48px; border: 1.5px solid var(--g100); background: var(--white); border-radius: 2px; padding: 0 14px; font: inherit; font-size: 15px; color: var(--ink); }\n.inp[readonly] { background: var(--g50); color: var(--g500); } .inp.bad { border-color: var(--red); }\n.inp.code { font-family: ui-monospace, Menlo, Consolas, monospace; letter-spacing: .06em; text-transform: uppercase; }\n.hint { margin: 6px 0 0; font-size: 13px; color: var(--g500); } .err { margin: 6px 0 0; font-size: 13px; font-weight: 700; color: var(--red); }\n.checks { display: grid; gap: 14px; margin: 4px 0 8px; }\n.ck { display: flex; align-items: flex-start; gap: 12px; cursor: pointer; font-size: 15px; }\n.ck input { width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--ink); flex: none; }\n.ck small { display: block; color: var(--g500); font-size: 13px; margin-top: 2px; }\n.warn { margin: 16px 0 24px; }\n.btns { display: flex; align-items: center; gap: 12px; padding-top: 8px; } .btns .sp { flex: 1; }\n.empty { padding: 24px; }\n.ovl { position: fixed; inset: 0; background: rgba(11,11,12,.55); display: flex; align-items: flex-start; justify-content: center; padding-top: 200px; z-index: 10; }\n.dlg { width: 480px; max-width: calc(100% - 32px); background: var(--white); border-radius: 2px; padding: 32px; box-shadow: 0 24px 64px rgba(0,0,0,.35); }\n.dlg h2 { margin: 0 0 12px; font-size: 22px; font-weight: 800; }\n.dlg p { margin: 0 0 24px; color: var(--g800); font-size: 15px; line-height: 1.5; }\n.dlg .row { display: flex; gap: 12px; justify-content: flex-end; }\n`,\n\n  mounted(d2) {\n    d2.on('[data-pg]', 'click', (e, el) => { if (d2.root) d2.root.__pg = Number(el.getAttribute('data-pg')) || 1; d2.state.tick = Date.now(); });\n    if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n      const l = document.createElement('link');\n      l.id = 'lf-archivo-font'; l.rel = 'stylesheet';\n      l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n      document.head.appendChild(l);\n    }\n    const S = d2.state;\n    const unwrap = (res) => (res && res.data !== undefined ? res.data : res);\n    const call = async (name, body) => unwrap(await d2.rush(name).invoke(body || {}));\n    const say = (tone, body) => { S.notice = { tone, body }; };\n    const items = () => S.items || [];\n\n    const reload = async () => {\n      try {\n        const out = await call('gestaoCuponsListar');\n        S.items = Array.isArray(out && out.cupons) ? out.cupons : [];\n      } catch (e) { say('error', 'Não foi possível carregar os cupons.'); }\n      S.loaded = true;\n    };\n\n    const openFrom = (r) => {\n      S.editingId = r._id;\n      S.form = { codigo: s2(r.codigo), tipo: r.tipo === 'valor' ? 'valor' : 'percentual', valor: s2(r.valor), inicio: s2(r.inicio), fim: s2(r.fim),\n        valor_minimo: Number(r.valor_minimo) > 0 ? s2(r.valor_minimo) : '', limite_total: Number(r.limite_total) > 0 ? s2(r.limite_total) : '',\n        um_por_cliente: r.um_por_cliente === true, ativo: r.ativo !== false };\n      S.original = { ativo: r.ativo !== false, pedidos: Number(r.pedidos) || 0, usos: Number(r.usos) || 0 };\n      S.errors = {}; S.notice = null; S.view = 'form';\n    };\n    const openNew = () => { S.editingId = null; S.form = { ...EMPTY_FORM }; S.original = null; S.errors = {}; S.notice = null; S.view = 'form'; };\n    const openEdit = (id) => { const r = items().find((x) => String(x._id) === String(id)); if (r) openFrom(r); };\n    const backToList = () => { S.view = 'lista'; S.errors = {}; };\n    const askConfirm = (kind, rec, fromForm) => { S.confirm = { kind, id: rec._id, nome: rec.codigo || (S.form && S.form.codigo) || '', fromForm: !!fromForm }; };\n\n    const validate = () => {\n      const f = S.form || EMPTY_FORM; const e = {};\n      const cod = String(f.codigo || '').trim();\n      if (!cod) e.codigo = 'Informe o código do cupom.';\n      else if (cod.length < 3) e.codigo = 'O código precisa ter pelo menos 3 letras ou números.';\n      else if (items().some((r) => String(r._id) !== String(S.editingId) && String(r.codigo).toUpperCase() === cod.toUpperCase())) e.codigo = 'Já existe um cupom com esse código.';\n      const v = num(f.valor);\n      if (v === undefined || isNaN(v) || v <= 0) e.valor = 'Informe um desconto maior que zero.';\n      else if (f.tipo === 'percentual' && v > 100) e.valor = 'O desconto em porcentagem vai de 1 a 100.';\n      if (f.inicio && !DATE_RE.test(f.inicio)) e.inicio = 'Data de início inválida.';\n      if (f.fim && !DATE_RE.test(f.fim)) e.fim = 'Data de fim inválida.';\n      if (!e.inicio && !e.fim && f.inicio && f.fim && f.fim < f.inicio) e.fim = 'O fim da validade não pode ser antes do início.';\n      const mn = num(f.valor_minimo);\n      if (mn !== undefined && (isNaN(mn) || mn < 0)) e.valor_minimo = 'O pedido mínimo não pode ser negativo.';\n      const lt = num(f.limite_total);\n      if (lt !== undefined && (isNaN(lt) || lt < 0 || lt % 1 !== 0)) e.limite_total = 'Use um número inteiro, zero ou maior.';\n      S.errors = e;\n      return Object.keys(e).length === 0;\n    };\n\n    const doSave = async () => {\n      const f = S.form;\n      const payload = { id: S.editingId || undefined, codigo: String(f.codigo || '').trim().toUpperCase(), tipo: f.tipo, valor: num(f.valor),\n        inicio: f.inicio || '', fim: f.fim || '', valor_minimo: num(f.valor_minimo) || 0, limite_total: num(f.limite_total) || 0,\n        um_por_cliente: !!f.um_por_cliente, ativo: !!f.ativo };\n      const wasNew = !S.editingId;\n      S.busy = true;\n      try {\n        const out = await call('gestaoCupomSalvar', payload);\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível salvar o cupom.'); S.busy = false; return; }\n        await reload();\n        const saved = out && out.id ? items().find((x) => String(x._id) === String(out.id)) : null;\n        if (saved) openFrom(saved);\n        say('success', wasNew ? 'Cupom criado.' : 'Cupom atualizado.');\n      } catch (e) { say('error', 'Não foi possível salvar o cupom. Tente de novo.'); }\n      S.busy = false;\n    };\n\n    const save = async () => {\n      if (S.busy || !validate()) return;\n      if (S.editingId && S.original && S.original.ativo && !S.form.ativo) { askConfirm('desativar', { _id: S.editingId, codigo: S.form.codigo }, true); return; }\n      await doSave();\n    };\n\n    const confirmGo = async () => {\n      if (!S.confirm || S.busy) return;\n      const c = S.confirm;\n      if (c.kind === 'desativar' && c.fromForm) { S.confirm = null; await doSave(); return; }\n      S.busy = true;\n      try {\n        if (c.kind === 'desativar') {\n          const out = await call('gestaoCupomAtivar', { id: c.id, ativo: false });\n          if (out && out.ok === false) say('error', out.error || 'Não foi possível desativar.'); else say('success', 'Cupom desativado. Ele não vale mais na loja.');\n        } else if (c.kind === 'excluir') {\n          const out = await call('gestaoCupomExcluir', { id: c.id });\n          if (out && out.ok === false) say('error', out.error || 'Não foi possível excluir.');\n          else { say('success', 'Cupom excluído.'); if (S.view === 'form') S.view = 'lista'; }\n        }\n        S.confirm = null; S.busy = false; await reload();\n      } catch (e) { S.busy = false; S.confirm = null; say('error', 'Não foi possível concluir a ação. Nada foi alterado.'); }\n    };\n\n    const toggleAtivo = async (id) => {\n      const r = items().find((x) => String(x._id) === String(id)); if (!r || S.busy) return;\n      if (r.ativo !== false) { askConfirm('desativar', r, false); return; }\n      try {\n        const out = await call('gestaoCupomAtivar', { id: r._id, ativo: true });\n        if (out && out.ok === false) { say('error', out.error || 'Não foi possível reativar.'); return; }\n        await reload(); say('success', 'Cupom reativado.');\n      } catch (e) { say('error', 'Não foi possível reativar o cupom.'); }\n    };\n\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act'); const id = el.getAttribute('data-id');\n      if (act === 'new') openNew(); else if (act === 'save') save(); else if (act === 'back') backToList(); else if (act === 'confirm-go') confirmGo();\n      else if (act === 'edit') openEdit(id); else if (act === 'toggle') toggleAtivo(id);\n      else if (act === 'delete') { const r = items().find((x) => String(x._id) === String(id)); if (r) askConfirm('excluir', r, false); }\n      else if (act === 'delete-form') askConfirm('excluir', { _id: S.editingId, codigo: S.form.codigo }, true);\n      else if (act === 'toggle-form') { if (S.form.ativo) { if (S.editingId) askConfirm('desativar', { _id: S.editingId, codigo: S.form.codigo }, true); else S.form = { ...S.form, ativo: false }; } else { S.form = { ...S.form, ativo: true }; } }\n      else if (act === 'cancel-confirm') S.confirm = null;\n      else if (act === 'filter') S.fStatus = el.getAttribute('data-v');\n    });\n    d2.on('[data-f]', 'input', (e, el) => {\n      const k = el.getAttribute('data-f'); const f = { ...(S.form || EMPTY_FORM) };\n      f[k] = k === 'codigo' ? el.value.toUpperCase().replace(/[^A-Z0-9_-]/g, '') : el.value;\n      S.form = f;\n    });\n    d2.on('[data-s]', 'change', (e, el) => { S.form = { ...(S.form || EMPTY_FORM), [el.getAttribute('data-s')]: el.value }; });\n    d2.on('[data-c]', 'change', (e, el) => { S.form = { ...(S.form || EMPTY_FORM), [el.getAttribute('data-c')]: !!el.checked }; });\n    d2.on('[data-q]', 'input', (e, el) => { S.q = el.value; });\n\n    reload();\n  },\n\n  render(d2) {\n    const S = d2.state;\n    const P = d2.params || {};\n    // Os módulos (Button, Notice, BackLink, EmptyState) chegam como referência de módulo: normaliza para o id antes de desenhar.\n    const refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\n    const MB = refOf(d2.param('button')); const MN = refOf(d2.param('notice')); const MBL = refOf(d2.param('backLink')); const ME = refOf(d2.param('emptyState'));\n    if (S.view === undefined) {\n      S.view = P.view === 'form' ? 'form' : 'lista';\n      S.fStatus = 'todos'; S.q = ''; S.form = { ...EMPTY_FORM }; S.errors = {}; S.editingId = null; S.confirm = null; S.notice = null; S.busy = false;\n    }\n    const btn = (label, variant, act, key, w) => `<span data-act=\"${act}\" style=\"display:inline-block\">${d2.render(MB, (S.busy && (act === 'save' || act === 'confirm-go')) ? { label: act === 'save' ? 'Salvando…' : 'Aguarde…', variant, size: 'sm', isDisabled: true } : { label, variant, size: 'sm' }, { key: (S.busy && (act === 'save' || act === 'confirm-go')) ? key + '-busy' : key, width: w, height: 44 })}</span>`;\n    const rows = (S.items || []).slice();\n\n    let confirm = S.confirm;\n    if (!confirm && P.demoConfirm && rows.length) confirm = { kind: 'desativar', id: rows[0]._id, nome: rows[0].codigo, fromForm: false };\n\n    const notice = S.notice ? `<div class=\"notice\">${d2.render(MN, { tone: S.notice.tone === 'error' ? 'error' : 'success', body: S.notice.body, live: true }, { key: 'notice', height: 64 })}</div>` : '';\n    void S.tick;\n    const noticeBelow = notice ? notice.replace('class=\"notice\"', 'class=\"notice below\"') : '';\n    let main = '';\n    if (S.view === 'form') {\n      const f = S.form || EMPTY_FORM; const e = S.errors || {}; const edit = !!S.editingId;\n      const err = (k) => (e[k] ? `<p class=\"err\">${esc(e[k])}</p>` : '');\n      const inp = (k, label, ph, opts) => { const o = opts || {}; return `<div class=\"field\"><label class=\"lbl\" for=\"f-${k}\">${label}</label><input id=\"f-${k}\" class=\"inp ${o.cls || ''} ${e[k] ? 'bad' : ''}\" data-f=\"${k}\" ${o.type ? 'type=\"' + o.type + '\"' : ''} value=\"${esc(f[k])}\" placeholder=\"${esc(ph || '')}\" ${o.ro ? 'readonly' : ''} autocomplete=\"off\">${e[k] ? err(k) : (o.hint ? `<p class=\"hint\">${o.hint}</p>` : '')}</div>`; };\n      const pedidos = S.original ? S.original.pedidos : 0;\n      const usos = S.original ? S.original.usos : 0;\n      const pct = f.tipo === 'percentual';\n      const delBtn = !edit ? '' : '<button class=\"lk danger\" data-act=\"delete-form\">Excluir cupom</button>';\n      const togBtn = edit ? `<button class=\"lk\" data-act=\"toggle-form\">${f.ativo ? 'Desativar cupom' : 'Marcar como ativo'}</button>` : '';\n      main = `\n<div class=\"page\">\n  <div class=\"back\"><span data-act=\"back\" style=\"display:inline-block\">${d2.render(MBL, { label: 'Voltar para cupons' }, { key: 'back', width: 260, height: 32 })}</span></div>\n  <div class=\"head\"><div><p class=\"eyebrow\">Cupons</p><h1>${edit ? 'Editar cupom' : 'Novo cupom'}</h1>\n    <p class=\"sub\">${edit ? 'Altere as regras e salve. ' + (pedidos ? 'Usado em ' + usos + (usos === 1 ? ' pedido' : ' pedidos') + (pedidos !== usos ? ' (' + pedidos + ' contando cancelados e não finalizados)' : '') + '.' : 'Ainda não foi usado.') : 'Crie um código de desconto para a sacola e o checkout.'}</p></div></div>\n  <div class=\"card form\"><h3>Desconto</h3>\n    <div class=\"grid3\">${inp('codigo', 'Código', 'BEMVINDO10', { cls: 'code', ro: edit, hint: edit ? 'O código não muda depois de criado.' : 'Só letras, números, - e _. O cliente pode digitar em maiúsculas ou minúsculas.' })}\n      <div class=\"field\"><label class=\"lbl\" for=\"f-tipo\">Tipo de desconto</label><select id=\"f-tipo\" class=\"inp\" data-s=\"tipo\"><option value=\"percentual\" ${pct ? 'selected' : ''}>Porcentagem (%)</option><option value=\"valor\" ${!pct ? 'selected' : ''}>Valor fixo (R$)</option></select></div>\n      ${inp('valor', pct ? 'Desconto (%)' : 'Desconto (R$)', pct ? 'Ex.: 10' : 'Ex.: 25,00', { hint: pct ? 'De 1 a 100. Incide sobre o valor dos produtos, não sobre o frete.' : 'Valor em reais. Nunca passa do valor dos produtos. Não incide sobre o frete.' })}</div>\n    <h3 style=\"margin-top:12px\">Regras (opcionais)</h3>\n    <div class=\"grid3\">${inp('inicio', 'Vale a partir de', '', { type: 'date', hint: 'Vazio = já vale.' })}${inp('fim', 'Vale até', '', { type: 'date', hint: 'Vazio = sem data final. O último dia vale inteiro.' })}${inp('valor_minimo', 'Pedido mínimo (R$)', 'Ex.: 150,00', { hint: 'Soma dos produtos. Vazio = sem mínimo.' })}</div>\n    <div class=\"grid3\">${inp('limite_total', 'Limite total de usos', 'Ex.: 100', { hint: 'Conta pedidos pagos e aguardando pagamento. Vazio = sem limite.' })}</div>\n    <div class=\"checks\">\n      <label class=\"ck\"><input type=\"checkbox\" data-c=\"um_por_cliente\" ${f.um_por_cliente ? 'checked' : ''}><span>Um uso por cliente<small>Cada cliente pode usar este cupom em um pedido só.</small></span></label>\n      <label class=\"ck\"><input type=\"checkbox\" data-c=\"ativo\" ${f.ativo ? 'checked' : ''}><span>Ativo<small>Cupom inativo não vale na sacola nem no checkout.</small></span></label></div>\n    ${edit && S.original && S.original.ativo && !f.ativo ? `<div class=\"warn\">${d2.render(MN, { tone: 'info', body: 'Ao salvar, este cupom deixa de valer na loja. Vamos pedir uma confirmação.', live: false }, { key: 'warn', height: 64 })}</div>` : ''}\n  </div>\n  <div class=\"btns\">${btn(edit ? 'Salvar alterações' : 'Criar cupom', 'primary', 'save', 'save', 210)}${btn('Cancelar', 'outline', 'back', 'cancel', 130)}<span class=\"sp\"></span>${togBtn}${delBtn}</div>\n  ${noticeBelow}\n</div>`;\n    } else {\n      const q = norm(S.q);\n      const shown = rows.filter((r) => (S.fStatus === 'ativos' ? r.ativo !== false : S.fStatus === 'inativos' ? r.ativo === false : true)\n        && (!q || norm(r.codigo).includes(q)));\n      const chip = (v, l) => `<button class=\"chip\" data-act=\"filter\" data-v=\"${v}\" aria-pressed=\"${S.fStatus === v}\">${l}</button>`;\n      const pgCur = pageOf(d2, [S.q, S.fStatus].join('|'), shown.length);\n      const pgRows = shown.slice((pgCur - 1) * PAGE, pgCur * PAGE);\n      const body = shown.length ? `\n<div class=\"card\"><table>\n  <thead><tr><th>Código</th><th>Desconto</th><th>Validade</th><th>Pedido mínimo</th><th>Usos</th><th>Status</th><th></th></tr></thead>\n  <tbody>${pgRows.map((r) => { const st = statusOf(r); return `\n    <tr data-key=\"${esc(r._id)}\">\n      <td class=\"nome\">${esc(r.codigo)}<small>${r.um_por_cliente ? 'Um uso por cliente' : 'Sem limite por cliente'}</small></td>\n      <td class=\"num\">${esc(descTxt(r))}</td><td class=\"num\">${esc(validadeTxt(r))}</td>\n      <td class=\"num\">${Number(r.valor_minimo) > 0 ? brl(r.valor_minimo) : '<span class=\"no\">—</span>'}</td>\n      <td class=\"num\">${Number(r.usos) || 0}${Number(r.limite_total) > 0 ? ' / ' + Number(r.limite_total) : ''}</td>\n      <td><span class=\"pill ${st[0]}\">${st[1]}</span></td>\n      <td><div class=\"acts\"><button class=\"lk\" data-act=\"edit\" data-id=\"${esc(r._id)}\">Editar</button>\n        <button class=\"lk\" data-act=\"toggle\" data-id=\"${esc(r._id)}\">${r.ativo === false ? 'Ativar' : 'Desativar'}</button>\n        <button class=\"lk danger\" data-act=\"delete\" data-id=\"${esc(r._id)}\">Excluir</button></div></td>\n    </tr>`; }).join('')}</tbody></table></div>\n${pagerHtml(pgCur, shown.length, PAGE)}\n<p class=\"count\">${shown.length} de ${rows.length} ${rows.length === 1 ? 'cupom' : 'cupons'}</p>`\n        : !S.loaded ? '<div class=\"card empty\"><p class=\"sub\">Carregando cupons…</p></div>' : `<div class=\"card empty\">${d2.render(ME, { eyebrow: 'Cupons', title: rows.length ? 'Nenhum cupom encontrado' : 'Nenhum cupom cadastrado', description: rows.length ? 'Tente outro termo ou mude o filtro.' : 'Crie o primeiro cupom de desconto da loja.', iconName: 'search' }, { key: 'empty', height: 260 })}</div>`;\n      main = `\n<div class=\"page\">\n  <div class=\"head\"><div><p class=\"eyebrow\">Gestão</p><h1>Cupons</h1><p class=\"sub\">Crie códigos de desconto para a sacola. O desconto vale sobre os produtos, não sobre o frete.</p></div>\n    ${btn('Novo cupom', 'primary', 'new', 'new', 190)}</div>\n  ${notice}\n  <div class=\"tools\"><input class=\"search\" data-q placeholder=\"Buscar por código\" value=\"${esc(S.q)}\" aria-label=\"Buscar cupom\">\n    <div class=\"chips\">${chip('todos', 'Todos')}${chip('ativos', 'Ativos')}${chip('inativos', 'Inativos')}</div></div>\n  ${body}\n</div>`;\n    }\n\n    let overlay = '';\n    if (confirm) {\n      const k = confirm.kind;\n      const title = k === 'excluir' ? 'Excluir o cupom “' + esc(confirm.nome) + '”?' : 'Desativar o cupom “' + esc(confirm.nome) + '”?';\n      const text = k === 'excluir' ? 'O cupom será apagado. Se ele já foi usado em algum pedido, o sistema não deixa excluir: nesse caso, desative.'\n        : 'O cupom deixa de valer na sacola e no checkout. Quem já tem o código na sacola perde o desconto. Você pode ativar de novo quando quiser.';\n      const okLabel = k === 'excluir' ? 'Excluir cupom' : 'Desativar cupom';\n      overlay = `<div class=\"ovl\"><div class=\"dlg\" role=\"alertdialog\" aria-modal=\"true\"><h2>${title}</h2><p>${text}</p>\n  <div class=\"row\">${btn('Cancelar', 'outline', 'cancel-confirm', 'c-cancel', 130)}${btn(okLabel, k === 'desativar' ? 'primary' : 'brand', 'confirm-go', 'c-ok', 230)}</div></div></div>`;\n    }\n\n    return `<div class=\"root\">${main}${overlay}</div>`;\n  }\n};\n"}},"backLink":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"backLink"}}}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"demoConfirm":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"demoConfirm"}}}},"emptyState":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"emptyState"}}}},"notice":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"notice"}}}},"view":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"view"}}}}}}}}}},{"_id":"6ad00cca6f596585a97fa411","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"OrderResult","package":"babi-lf","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 INK = '#0b0b0c', RED = '#b8222c', MUTE = '#6b6b6b', LINE = '#e4e4e0', SOFT = '#f5f5f3';\nconst PAD = { desktop: '56px 96px 120px', tablet: '44px 40px 96px', mobile: '28px 20px 72px' };\nconst H1 = { desktop: 44, tablet: 36, mobile: 28 };\nconst METODOS = { master: 'Mastercard', visa: 'Visa', amex: 'American Express', elo: 'Elo', hipercard: 'Hipercard', diners: 'Diners', pix: 'Pix' };\nconst MAX_TENTATIVAS = 24;\nconst INTERVALO = 5000;\n\nconst esc = (s) => String(s == null ? '' : s).replace(/[&<>\"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\nconst brl = (n) => { const v = Number(n); return isFinite(v) ? v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }) : 'R$ 0,00'; };\nconst num = (n) => { const v = Number(n); return isFinite(v) ? v : 0; };\nconst cepFmt = (c) => { const s = String(c || '').replace(/\\D/g, ''); return s.length === 8 ? s.slice(0, 5) + '-' + s.slice(5) : s; };\nconst okUrl = (u) => /^https:\\/\\/([a-z0-9-]+\\.)*mercadopago\\.com(\\.br)?\\//i.test(String(u || ''));\n\nconst ICON = {\n  ok: '<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12.5l4.5 4.5L19 7.5\"/></svg>',\n  wait: '<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"8.5\"/><path d=\"M12 7.5V12l3 2\"/></svg>',\n  x: '<svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#fff\" stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M6.5 6.5l11 11M17.5 6.5l-11 11\"/></svg>'\n};\n\nfunction numeroDaUrl(d2) {\n  let n = '';\n  try { const p = d2.router().params; n = (p && (p.numero || p.id)) || ''; } catch (e) { n = ''; }\n  if (!n && typeof window !== 'undefined') {\n    const m = window.location.pathname.match(/\\/pedido\\/([^/]+)\\/resultado/i);\n    n = m ? decodeURIComponent(m[1]) : '';\n  }\n  return String(n || '').trim();\n}\n\nfunction retornoMp() {\n  try { return new URLSearchParams(window.location.search).get('r') || ''; } catch (e) { return ''; }\n}\n\nfunction limparSacola() {\n  try {\n    window.localStorage.removeItem('lf-sacola');\n    window.dispatchEvent(new CustomEvent('lf-sacola', { detail: { count: 0 } }));\n  } catch (e) { /* sem acesso ao armazenamento: segue sem limpar */ }\n}\n\nfunction tipoDe(d) {\n  if (!d || d.ok === false) return 'erro';\n  const st = String(d.status || '');\n  if (st === 'pago' || st === 'enviado' || st === 'entregue') return 'ok';\n  if (st === 'cancelado' || st === 'expirado') return 'falha';\n  if (st === 'estornado') return 'estornado';\n  if (st === 'aguardando_pagamento' && String(d.pagamentoStatus || '') === 'recusado') return 'recusado';\n  return 'pendente';\n}\n\nasync function carregar(d2, ctx) {\n  const numero = numeroDaUrl(d2);\n  try {\n    const r = await d2.rush('statusService').invoke({ numero });\n    const dado = (r && r.ok === undefined && r.data !== undefined) ? r.data : r;\n    d2.state.data = dado || { ok: false, motivo: 'nao_encontrado' };\n  } catch (e) {\n    d2.state.data = d2.state.data || { ok: false, motivo: 'indisponivel' };\n  }\n  d2.state.loading = false;\n  const tipo = tipoDe(d2.state.data);\n  if (tipo === 'ok' && !ctx.limpou) { ctx.limpou = true; limparSacola(); }\n  const soPagar = tipo === 'pendente' && !retornoMp() && !!(d2.state.data && d2.state.data.pagarUrl);\n  if (tipo === 'pendente' && !soPagar && ctx.tentativas < MAX_TENTATIVAS) {\n    ctx.tentativas += 1;\n    d2.state.tentativas = ctx.tentativas;\n    ctx.timer = setTimeout(() => carregar(d2, ctx), INTERVALO);\n  } else if (tipo === 'pendente' && !soPagar) {\n    d2.state.esgotou = true;\n  }\n}\n\nfunction resumo(d, lay) {\n  const itens = Array.isArray(d.itens) ? d.itens : [];\n  const linhas = [['Subtotal', brl(d.subtotal)]];\n  if (num(d.desconto) > 0) linhas.push(['Desconto Pix', '− ' + brl(d.desconto)]);\n  if (num(d.cupomDesconto) > 0) linhas.push(['Cupom ' + esc(d.cupom || ''), '− ' + brl(d.cupomDesconto)]);\n  linhas.push(['Frete', brl(d.frete)]);\n  const itensHtml = itens.map((i, k) => `<li data-key=\"${k}\"><span class=\"in\">${esc(i.nome)}<small>Qtd.: ${esc(i.quantidade)}${i.parceiro ? ' · Enviado pela ' + esc(i.parceiro) : ''}</small></span><b>${brl(num(i.precoUnitario) * num(i.quantidade))}</b></li>`).join('');\n  const juros = num(d.totalPago) > num(d.total) + 0.009 ? `<p class=\"nt\">Total pago com juros do cartão: <b>${brl(d.totalPago)}</b></p>` : '';\n  return `<section class=\"box\"><h2>Resumo do pedido</h2><ul class=\"its\">${itensHtml}</ul><div class=\"rows\">${linhas.map((l) => `<div class=\"r\"><span>${l[0]}</span><b>${l[1]}</b></div>`).join('')}</div><div class=\"tot\"><span>Total</span><strong>${brl(d.total)}</strong></div>${juros}</section>`;\n}\n\nfunction lateral(d) {\n  const e = d.entrega || {};\n  const rua = [e.logradouro, e.numero].filter(Boolean).join(', ') + (e.complemento ? ' — ' + e.complemento : '');\n  const cid = [e.cidade, e.estado].filter(Boolean).join(' / ');\n  const end = [rua, e.bairro, cid, e.cep ? 'CEP ' + cepFmt(e.cep) : ''].filter((x) => x && String(x).trim());\n  const entrega = end.length ? `<section class=\"box\"><h2>Entrega</h2><p class=\"ad\"><b>${esc(e.recebedor_nome || e.destinatario || '')}</b>${end.map((x) => `<span>${esc(x)}</span>`).join('')}</p></section>` : '';\n  const met = METODOS[String(d.pagamentoMetodo || '').toLowerCase()] || (d.pagamentoForma === 'pix' ? 'Pix' : 'Cartão de crédito');\n  const parc = d.pagamentoForma === 'pix' ? '' : (num(d.parcelas) > 1 ? ' em ' + num(d.parcelas) + 'x' : ' à vista');\n  const pag = `<section class=\"box\"><h2>Pagamento</h2><p class=\"ad\"><b>${esc(met)}${esc(parc)}</b><span>Aprovado</span></p></section>`;\n  return entrega + pag;\n}\n\nfunction botoes(a, b) {\n  return `<div class=\"cta\">${a}${b || ''}</div>`;\n}\nconst btnP = (href, txt) => `<a class=\"btn p\" href=\"${esc(href)}\">${esc(txt)}</a>`;\nconst btnS = (href, txt) => `<a class=\"btn s\" href=\"${esc(href)}\">${esc(txt)}</a>`;\n\nfunction cabeca(icone, cor, eyebrow, titulo, sub, lay) {\n  return `<header class=\"hd\"><span class=\"ic\" style=\"background:${cor}\">${icone}</span><p class=\"ey\">${eyebrow}</p><h1 style=\"font-size:${H1[lay]}px\">${esc(titulo)}</h1><p class=\"sb\">${sub}</p></header>`;\n}\n\nexport default {\n  css: `\n    :host { display: block; }\n    .rs { font-family: 'Archivo', system-ui, sans-serif; color: ${INK}; background: #fff; }\n    .in0 { max-width: 1240px; margin: 0 auto; }\n    .hd { max-width: 720px; }\n    .ic { width: 56px; height: 56px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; }\n    .ey { margin: 22px 0 0; font-size: 12px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: ${RED}; }\n    h1 { margin: 10px 0 0; font-weight: 800; line-height: 1.08; letter-spacing: -.01em; font-stretch: 112%; }\n    .sb { margin: 14px 0 0; font-size: 17px; line-height: 1.5; color: ${MUTE}; }\n    .grid { display: grid; gap: 32px; margin-top: 40px; align-items: start; }\n    .l-desktop .grid { grid-template-columns: 1.5fr 1fr; }\n    .l-tablet .grid { grid-template-columns: 1.3fr 1fr; gap: 24px; }\n    .l-mobile .grid { grid-template-columns: 1fr; gap: 20px; margin-top: 28px; }\n    .col { display: grid; gap: 20px; }\n    .box { background: ${SOFT}; padding: 28px; }\n    .l-mobile .box { padding: 20px; }\n    .box h2 { margin: 0 0 18px; font-size: 12px; font-weight: 800; letter-spacing: .18em; text-transform: uppercase; }\n    .its { list-style: none; margin: 0; padding: 0; }\n    .its li { display: flex; justify-content: space-between; gap: 16px; padding: 14px 0; border-bottom: 1px solid ${LINE}; font-size: 15px; }\n    .its li:first-child { padding-top: 0; }\n    .in { display: grid; gap: 4px; font-weight: 600; }\n    .in small { font-size: 13px; font-weight: 400; color: ${MUTE}; }\n    .rows { padding: 16px 0 0; display: grid; gap: 8px; }\n    .r { display: flex; justify-content: space-between; font-size: 15px; color: ${MUTE}; }\n    .r b { color: ${INK}; font-weight: 600; }\n    .tot { display: flex; justify-content: space-between; align-items: baseline; margin-top: 16px; padding-top: 16px; border-top: 1px solid ${LINE}; font-weight: 700; }\n    .tot strong { font-size: 30px; font-weight: 800; font-stretch: 112%; }\n    .nt { margin: 12px 0 0; font-size: 13px; color: ${MUTE}; }\n    .ad { margin: 0; display: grid; gap: 4px; font-size: 15px; line-height: 1.45; }\n    .ad span { color: ${MUTE}; }\n    .cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; }\n    .l-mobile .cta { flex-direction: column; }\n    .btn { display: inline-flex; align-items: center; justify-content: center; height: 52px; padding: 0 28px; font-size: 13px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; text-decoration: none; border: 2px solid ${INK}; box-sizing: border-box; }\n    .btn.p { background: ${INK}; color: #fff; }\n    .btn.s { background: #fff; color: ${INK}; }\n    .btn.r { background: ${RED}; border-color: ${RED}; color: #fff; }\n    .sk { height: 16px; background: ${SOFT}; margin-top: 14px; max-width: 520px; }\n    .sk.a { width: 60px; height: 60px; border-radius: 50%; max-width: none; margin: 0; }\n    .sk.b { height: 40px; max-width: 420px; }\n    .spin { width: 18px; height: 18px; border: 2px solid ${LINE}; border-top-color: ${INK}; border-radius: 50%; display: inline-block; vertical-align: -3px; margin-right: 10px; animation: giro 0.9s linear infinite; }\n    @keyframes giro { to { transform: rotate(360deg); } }\n    .wt { margin-top: 24px; font-size: 15px; color: ${MUTE}; }\n    a:focus-visible { outline: 3px solid ${INK}; outline-offset: 2px; }\n  `,\n  render(d2) {\n    const lay = ['desktop', 'tablet', 'mobile'].indexOf(String(d2.param('layout', 'desktop'))) >= 0 ? String(d2.param('layout', 'desktop')) : 'desktop';\n    const st = d2.state;\n    const d = st.data;\n    let corpo = '';\n    if (st.loading && !d) {\n      corpo = `<div class=\"sk a\"></div><div class=\"sk b\"></div><div class=\"sk\"></div><p class=\"wt\" role=\"status\"><span class=\"spin\"></span>Confirmando seu pagamento…</p>`;\n    } else {\n      const tipo = tipoDe(d);\n      if (tipo === 'erro') {\n        const sem = d && d.motivo === 'sem_sessao';\n        const fora = d && d.motivo === 'indisponivel';\n        const tit = sem ? 'Entre para ver seu pedido' : (fora ? 'Não conseguimos carregar agora' : 'Não encontramos este pedido');\n        const sub = sem ? 'Sua sessão expirou. Entre na sua conta para acompanhar o pagamento.' : (fora ? 'Seu pagamento não é afetado. Recarregue a página em instantes ou veja a lista dos seus pedidos.' : 'Confira o link ou veja a lista dos seus pedidos.');\n        corpo = cabeca(ICON.x, RED, 'Pedido', tit, sub, lay) + botoes(sem ? btnP('/login', 'Entrar') : btnP('/perfil/meus-pedidos', 'Ver meus pedidos'), btnS('/', 'Ir para a loja'));\n      } else if (tipo === 'ok') {\n        corpo = cabeca(ICON.ok, INK, 'Pedido ' + esc(d.numero), 'Pagamento aprovado', 'Recebemos o seu pedido e o pagamento foi confirmado. Você pode acompanhar tudo em Meus pedidos.', lay)\n          + `<div class=\"grid\">${resumo(d, lay)}<div class=\"col\">${lateral(d)}</div></div>`\n          + botoes(btnP('/perfil/meus-pedidos', 'Ver meus pedidos'), btnS('/', 'Continuar comprando'));\n      } else if (tipo === 'pendente' && !retornoMp() && d.pagarUrl && okUrl(d.pagarUrl)) {\n        corpo = cabeca(ICON.wait, MUTE, 'Pedido ' + esc(d.numero), 'Falta concluir o pagamento', 'Seu pedido está reservado por 1 hora. Conclua o pagamento no Mercado Pago para confirmar a compra.', lay)\n          + `<div class=\"grid\">${resumo(d, lay)}<div class=\"col\">${lateral(d).replace('<span>Aprovado</span>', '<span>Aguardando pagamento</span>')}</div></div>`\n          + botoes(btnP(d.pagarUrl, 'Concluir pagamento no Mercado Pago'), btnS('/perfil/meus-pedidos', 'Ver meus pedidos'));\n      } else if (tipo === 'pendente') {\n        const sub = st.esgotou\n          ? 'Ainda não recebemos a confirmação do pagamento. Assim que ele for aprovado, o pedido aparece atualizado em Meus pedidos.'\n          : 'Isso costuma levar poucos instantes. Esta página atualiza sozinha, não precisa recarregar.';\n        const pagar = (d.pagarUrl && okUrl(d.pagarUrl)) ? btnP(d.pagarUrl, 'Concluir pagamento no Mercado Pago') : '';\n        corpo = cabeca(ICON.wait, MUTE, 'Pedido ' + esc(d.numero), 'Estamos confirmando seu pagamento', sub, lay)\n          + (st.esgotou ? '' : `<p class=\"wt\" role=\"status\"><span class=\"spin\"></span>Aguardando a confirmação…</p>`)\n          + `<div class=\"grid\">${resumo(d, lay)}<div class=\"col\">${lateral(d).replace('<span>Aprovado</span>', '<span>Aguardando confirmação</span>')}</div></div>`\n          + botoes(pagar || btnP('/perfil/meus-pedidos', 'Ver meus pedidos'), pagar ? btnS('/perfil/meus-pedidos', 'Ver meus pedidos') : btnS('/', 'Continuar comprando'));\n      } else if (tipo === 'recusado') {\n        const pagar = (d.pagarUrl && okUrl(d.pagarUrl)) ? btnP(d.pagarUrl, 'Tentar de novo no Mercado Pago') : btnP('/sacola', 'Voltar para a sacola');\n        corpo = cabeca(ICON.x, RED, 'Pedido ' + esc(d.numero), 'O pagamento não foi aprovado', 'O Mercado Pago não aprovou este pagamento. Você pode tentar outro cartão ou usar outra forma de pagamento. Nenhum valor foi cobrado.', lay)\n          + botoes(pagar, btnS('/perfil/meus-pedidos', 'Ver meus pedidos'));\n      } else if (tipo === 'estornado') {\n        corpo = cabeca(ICON.x, MUTE, 'Pedido ' + esc(d.numero), 'Pagamento estornado', 'O valor deste pedido foi devolvido. Se tiver dúvidas, fale com a gente pelos canais de atendimento.', lay)\n          + botoes(btnP('/perfil/meus-pedidos', 'Ver meus pedidos'), btnS('/', 'Ir para a loja'));\n      } else {\n        corpo = cabeca(ICON.x, RED, 'Pedido ' + esc(d.numero), 'Este pedido não foi concluído', 'O tempo para pagar acabou ou o pedido foi cancelado. Seus produtos continuam na sacola para você tentar de novo.', lay)\n          + botoes(btnP('/sacola', 'Voltar para a sacola'), btnS('/', 'Continuar comprando'));\n      }\n    }\n    return `<div class=\"rs l-${lay}\"><div class=\"in0\" style=\"padding:${PAD[lay]}\">${corpo}</div></div>`;\n  },\n  mounted(d2) {\n    d2.state.loading = true;\n    d2.state.tentativas = 0;\n    d2.state.esgotou = false;\n    const ctx = { tentativas: 0, timer: null, limpou: false };\n    d2.onUnmount(() => { if (ctx.timer) clearTimeout(ctx.timer); });\n    carregar(d2, ctx);\n  }\n};"}},"layout":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"layout"}}}},"statusService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"statusService"}}}}}}}}}},{"_id":"6ad0f51380f9c6023b75d9f6","component":"data2-core-types@Component","system":"6ab58b1260cbe7aa719c6d7b","params":{"extends":"data2-core-primitives@Block","component":"TeaserPage","package":"babi-lf","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"// TeaserPage — Auto Super Shop\n// A tela de teaser, no layout aprovado (fundo preto com brilho vermelho, faixa quadriculada no topo, logo + Instagram,\n// etiqueta \"EM BREVE\", título \"A loja oficial da Auto Super está chegando\" com \"CHEGANDO\" em vermelho, escudo grande,\n// texto de apoio, três avisos com check vermelho, cartão branco com o formulário, \"SHOP\" em contorno ao fundo,\n// faixa vermelha inclinada com o letreiro branco passando sem parar e rodapé). Sem contador: a data ainda não existe.\n// Formulário \"Quero ser avisado em primeira mão\": nome (obrigatório), e-mail e WhatsApp (basta um dos dois, ou os dois),\n// \"O que eu gostaria de ver na loja da Auto Super\" (opcional, até 1.000 caracteres) e o aceite de comunicações\n// (obrigatório, começa desmarcado, com link para a Política de privacidade).\n// Botão \"Entrar na lista\": desabilitado e em \"Enviando…\" enquanto envia. Campo escondido anti-robô (honeypot).\n// Envio: com `saveService` ligado, chama o serviço com { nome, email, whatsapp, feedback, consentimento, consentimentoEm, origem }.\n// O serviço devolve { ok: false, error } para falhar. Sem `saveService`, só emite `onSubmit` (nada é gravado).\n// Depois de enviar, o cartão troca para \"Você está na lista!\" com o canal escolhido; o resto da página continua igual.\n// Parâmetros de texto (opcionais): `igHandle` (padrão \"@autosuperbr\") e `igUrl` (padrão o Instagram da Auto Super). O YouTube e a Política de\n// privacidade são fixos na tela. A Política de privacidade abre num modal (fecha no X, no \"Entendi\", no Esc ou clicando fora).\n// Sem razão social nem CNPJ por enquanto.\n// A faixa vermelha anima só com CSS (translateX) e para sozinha para quem pede \"reduzir movimento\".\n// Responsiva sozinha (desktop, tablet e celular na mesma tela): usa a largura da janela. Não desenha logo, campo, caixa nem botão:\n// usa Logo, FormField, Checkbox, Button e Icon pelos parâmetros.\nconst esc = (s) => String(s == null ? '' : s)\n  .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\nconst refOf = (v) => { const r = (v && typeof v === 'object' ? (typeof v.getReference === 'function' ? v.getReference() : v.reference) : v) || ''; return r && r.indexOf('@') < 0 ? 'babi-lf@' + r : r; };\nconst FONT = () => {\n  if (typeof document !== 'undefined' && document.head && !document.getElementById('lf-archivo-font')) {\n    const l = document.createElement('link');\n    l.id = 'lf-archivo-font';\n    l.rel = 'stylesheet';\n    l.href = 'https://fonts.googleapis.com/css2?family=Archivo:ital,wdth,wght@0,62..125,100..900;1,62..125,100..900&display=swap';\n    document.head.appendChild(l);\n  }\n};\nconst emit = (d2, evt, v) => { try { d2.emit(evt, v); } catch (err) { /* sem handler */ } };\nconst FF = \"'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif\";\nconst SHIELD = 'https://babi-lf.data2.media/file/uzMIajNbdxWai9BVmG3Bj';\nconst bucket = () => {\n  const w = typeof window !== 'undefined' ? window.innerWidth : 1280;\n  return w < 768 ? 'mobile' : (w < 1200 ? 'tablet' : 'desktop');\n};\nconst digits = (s) => String(s || '').replace(/[^0-9]/g, '');\nconst validEmail = (v) => {\n  const i = v.indexOf('@');\n  const dot = v.lastIndexOf('.');\n  return i > 0 && v.indexOf(' ') < 0 && v.indexOf('@', i + 1) < 0 && dot > i + 1 && dot < v.length - 1;\n};\nconst unwrap = (res) => (res && res.data !== undefined ? res.data : res) || {};\nconst store = (d2) => (d2.root\n  ? (d2.root.__tz || (d2.root.__tz = { name: '', email: '', phone: '', feedback: '', consent: false, hp: '' }))\n  : { name: '', email: '', phone: '', feedback: '', consent: false, hp: '' });\nconst IG = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"5\"/><circle cx=\"12\" cy=\"12\" r=\"4\"/><circle cx=\"17.5\" cy=\"6.5\" r=\"0.8\" fill=\"currentColor\"/></svg>';\n\nconst YT = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><rect x=\"2.5\" y=\"5\" width=\"19\" height=\"14\" rx=\"4\"/><path d=\"m10 9 5 3-5 3V9Z\"/></svg>';\nconst X = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M6 6l12 12M18 6 6 18\"/></svg>';\nconst IG_URL = 'https://www.instagram.com/autosuperbr';\nconst YT_URL = 'https://www.youtube.com/@AutosuperBr';\nconst sec = (h, body) => `<h3>${h}</h3>${body}`;\nconst POLICY = `<p class=\"upd\">Última atualização: outubro de 2026.</p>\n<p>Esta Política de Privacidade explica, de forma simples, como a AutoSuper (“nós”) trata os dados pessoais de quem entra na lista de espera da loja oficial Auto Super Shop, em conformidade com a Lei Geral de Proteção de Dados (Lei nº 13.709/2018, a LGPD).</p>`\n  + sec('1. Quais dados coletamos', `<p>No formulário “Quero ser avisado em primeira mão”, coletamos:</p><ul><li>seu nome;</li><li>seu e-mail e/ou seu WhatsApp, o que você escolher informar;</li><li>o texto opcional “O que eu gostaria de ver na loja da Auto Super”, se você preencher;</li><li>o registro do seu aceite: que você autorizou o contato, com data e hora.</li></ul><p>Não pedimos CPF, endereço nem dados de pagamento neste formulário.</p>`)\n  + sec('2. Para que usamos seus dados', `<ul><li>avisar você quando a loja oficial abrir;</li><li>enviar lançamentos e novidades por e-mail e/ou WhatsApp;</li><li>entender o que o público espera encontrar na loja;</li><li>comprovar que você autorizou o contato.</li></ul>`)\n  + sec('3. Base legal', `<p>Tratamos seus dados com base no seu consentimento (art. 7º, I, da LGPD), dado ao marcar a caixa de autorização do formulário. Você pode retirar o consentimento a qualquer momento, sem custo.</p>`)\n  + sec('4. Com quem compartilhamos', `<p>Não vendemos seus dados. Podemos compartilhá-los com empresas que nos ajudam a operar o site e as comunicações (como hospedagem, banco de dados e envio de e-mails e mensagens), que só podem usá-los para essa finalidade e têm dever de confidencialidade. Também podemos compartilhá-los com autoridades quando a lei exigir.</p>`)\n  + sec('5. Armazenamento e segurança', `<p>Adotamos medidas técnicas e administrativas razoáveis para proteger seus dados contra acessos não autorizados, perda ou uso indevido. Guardamos os dados enquanto você estiver na lista, até você pedir a exclusão, ou pelo prazo que a lei exigir. Nenhum sistema é totalmente imune a falhas, mas trabalhamos para reduzir os riscos.</p>`)\n  + sec('6. Seus direitos', `<p>Você pode pedir, a qualquer momento:</p><ul><li>a confirmação de que tratamos seus dados e o acesso a eles;</li><li>a correção de dados incompletos, inexatos ou desatualizados;</li><li>a anonimização, o bloqueio ou a eliminação de dados desnecessários ou tratados fora da lei;</li><li>a portabilidade dos dados, quando aplicável;</li><li>a informação sobre com quem compartilhamos seus dados;</li><li>a eliminação dos dados tratados com base no consentimento;</li><li>a revogação do consentimento.</li></ul>`)\n  + sec('7. Como falar com a gente e sair da lista', `<p>Para exercer seus direitos, tirar dúvidas ou sair da lista, envie uma mensagem direta para <a href=\"${IG_URL}\" target=\"_blank\" rel=\"noopener\">@autosuperbr no Instagram</a>. Quando a loja abrir, divulgaremos também outros canais de atendimento.</p>`)\n  + sec('8. Crianças e adolescentes', `<p>Este formulário não é direcionado a menores de 18 anos. Se identificarmos o cadastro de um menor sem a autorização de um responsável, excluiremos os dados.</p>`)\n  + sec('9. Mudanças nesta política', `<p>Podemos atualizar esta política quando necessário. A versão mais recente ficará sempre disponível nesta página, com a data da última atualização.</p>`);\nconst openPrivacyOf = (d2) => () => {\n  d2.state.privacy = true;\n  setTimeout(() => { try { if (window.location.hash) window.history.replaceState(null, '', window.location.pathname + window.location.search); } catch (e) { /* sem histórico */ } }, 0);\n};\n\nexport default {\n  css: `\n:host { display: block; width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; background: #0b0b0c; }\n* { box-sizing: border-box; }\nh1, h2, p, ul { margin: 0; }\n.page { --red-500: #c8102e; --ink: #0b0b0c; --white: #ffffff; --grey-300: #a7a9ad; --grey-500: #6b6e73; --grey-700: #45474b; --line: #e4e4e2;\n  font-family: ${FF}; -webkit-font-smoothing: antialiased; position: relative; min-height: 100%; color: var(--white);\n  display: flex; flex-direction: column; overflow: hidden;\n  background:\n    radial-gradient(ellipse 60% 42% at 90% 4%, rgba(200,16,46,.42), transparent 70%),\n    radial-gradient(ellipse 45% 36% at 6% 66%, rgba(200,16,46,.16), transparent 70%),\n    repeating-linear-gradient(115deg, rgba(255,255,255,.03) 0 1px, transparent 1px 38px),\n    var(--ink); }\n.wrap { position: relative; z-index: 2; width: 100%; max-width: 1250px; margin: 0 auto; padding: 0 24px; }\n.checker { flex: none; height: 8px; background: conic-gradient(#ffffff 25%, #0b0b0c 0 50%, #ffffff 0 75%, #0b0b0c 0) 0 0 / 16px 16px; }\n\n.top { display: flex; align-items: center; justify-content: space-between; padding-top: 28px; }\n.logo { width: 320px; max-width: 70%; height: 56px; margin-left: -8px; }\n.ig { display: inline-flex; align-items: center; gap: 10px; color: var(--white); text-decoration: none; font-size: 12px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; }\n.ig svg { width: 20px; height: 20px; flex: none; }\na.ig:hover { color: var(--grey-300); }\n.ig:focus-visible, .foot a:focus-visible { outline: 2px solid var(--white); outline-offset: 4px; }\n\n.hero { position: relative; display: grid; grid-template-columns: 1fr 320px; column-gap: 40px; align-items: center; padding-top: 40px; padding-bottom: 48px; }\n.tag { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 28px; padding: 9px 20px 9px 15px; background: var(--red-500); color: var(--white);\n  font-size: 12px; font-weight: 800; letter-spacing: .3em; text-transform: uppercase; clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%); }\n.tag i { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--white); }\n.title { font-size: 96px; line-height: 1.02; font-weight: 900; font-style: italic; font-stretch: 125%; letter-spacing: -.01em; text-transform: uppercase; color: var(--white); }\n.title span { display: block; }\n.title em { display: block; font-style: italic; color: var(--red-500); }\n.shield { display: block; justify-self: end; width: 320px; height: auto; filter: drop-shadow(0 24px 50px rgba(200,16,46,.45)); }\n\n.stage { position: relative; overflow: hidden; padding-bottom: 110px; }\n.wm { position: absolute; left: 4%; bottom: -80px; z-index: 1; font-size: 440px; line-height: .8; font-weight: 900; font-style: italic; font-stretch: 125%;\n  text-transform: uppercase; color: transparent; -webkit-text-stroke: 2px rgba(255,255,255,.09); white-space: nowrap; pointer-events: none; user-select: none; }\n.lower { display: grid; grid-template-columns: 1fr 600px; gap: 80px; align-items: start; }\n.intro { padding-top: 16px; }\n.intro p { max-width: 520px; margin-bottom: 30px; font-size: 18px; line-height: 1.6; color: #d6d6d8; }\n.intro strong { color: var(--white); font-weight: 700; }\n.bullets { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 14px; }\n.bullets li { display: flex; align-items: center; gap: 12px; font-size: 15px; font-weight: 700; line-height: 1.35; color: var(--white); }\n.chk { flex: none; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; border-radius: 2px; background: var(--red-500); }\n\n.card { position: relative; display: flex; flex-direction: column; gap: 18px; padding: 36px; background: var(--white); color: var(--ink);\n  border-top: 4px solid var(--red-500); box-shadow: 0 30px 80px rgba(0,0,0,.55); }\n.card::after { content: ''; position: absolute; top: -4px; right: 56px; width: 56px; height: 4px; background: linear-gradient(90deg, #0b0b0c 0 16px, transparent 16px 40px, #0b0b0c 40px 56px); }\n.card-title { font-size: 26px; font-weight: 900; font-style: italic; font-stretch: 118%; line-height: 1.1; text-transform: uppercase; color: var(--ink); }\n.group-hint { margin-bottom: -6px; font-size: 14px; line-height: 1.45; color: var(--grey-700); }\n.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }\n.err { font-size: 13px; font-weight: 600; line-height: 1.4; color: var(--red-500); }\n.hp { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; opacity: 0; overflow: hidden; }\n.send { display: block; margin-top: 4px; }\n.done { display: flex; flex-direction: column; gap: 14px; padding: 8px 0 4px; }\n.done-text { font-size: 17px; line-height: 1.55; color: var(--grey-700); }\n\n.band { position: relative; z-index: 3; width: calc(100% + 80px); margin: -24px -40px 0; padding: 16px 0; overflow: hidden; background: var(--red-500);\n  transform: rotate(-2deg); box-shadow: 0 12px 40px rgba(0,0,0,.45); }\n.track { display: flex; align-items: center; width: max-content; animation: tz-marquee 32s linear infinite; will-change: transform; }\n.it { flex: none; margin-right: 28px; font-size: 40px; line-height: 1.1; font-weight: 900; font-style: italic; font-stretch: 125%; text-transform: uppercase; white-space: nowrap; color: var(--white); }\n.dia { flex: none; width: 11px; height: 11px; margin-right: 28px; background: var(--ink); transform: rotate(45deg); }\n@keyframes tz-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }\n@media (prefers-reduced-motion: reduce) { .track { animation: none; } }\n\n.foot { position: relative; z-index: 2; margin-top: 56px; border-top: 1px solid rgba(255,255,255,.12); }\n.foot .wrap { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding-top: 28px; padding-bottom: 28px; font-size: 13px; line-height: 1.5; color: #9a9ca1; }\n.foot .right { display: flex; align-items: center; gap: 24px; }\n.foot a { color: #cfcfd2; }\n.foot a:hover { color: var(--white); }\n.foot .fig { display: inline-flex; }\n.foot .fig svg { width: 20px; height: 20px; }\n\n@media (max-width: 1199px) {\n  .hero { grid-template-columns: 1fr 180px; column-gap: 24px; align-items: end; padding-top: 36px; }\n  .title { font-size: 62px; line-height: 1; }\n  .shield { width: 180px; }\n  .ig span { display: none; }\n  .stage { padding-bottom: 90px; }\n  .wm { font-size: 300px; bottom: -60px; }\n  .lower { grid-template-columns: 1fr; gap: 40px; }\n  .intro { padding-top: 0; }\n  .intro p { max-width: none; }\n  .card { padding: 32px; }\n  .it { font-size: 34px; }\n}\n@media (max-width: 767px) {\n  .wrap { padding: 0 20px; }\n  .top { padding-top: 24px; }\n  .logo { height: 48px; }\n  .hero { display: block; padding-top: 28px; padding-bottom: 36px; }\n  .tag { margin-bottom: 128px; font-size: 11px; }\n  .shield { position: absolute; top: 28px; right: 20px; width: 104px; }\n  .title { font-size: min(10.4vw, 72px); }\n  .stage { padding-bottom: 70px; }\n  .wm { font-size: 190px; left: 2%; bottom: -40px; }\n  .intro p { margin-bottom: 24px; font-size: 16px; }\n  .bullets li { font-size: 14px; }\n  .card { padding: 22px 20px; gap: 16px; }\n  .card-title { font-size: 22px; }\n  .it { font-size: 26px; margin-right: 20px; }\n  .dia { margin-right: 20px; width: 9px; height: 9px; }\n  .band { padding: 12px 0; }\n  .foot { margin-top: 40px; }\n  .foot .wrap { flex-direction: column; align-items: flex-start; }\n}\n@media (max-width: 599px) {\n  .row2 { grid-template-columns: 1fr; }\n}\n\n.socials { display: inline-flex; align-items: center; gap: 22px; }\n.socials-t { margin-right: 4px; font-size: 12px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: var(--white); white-space: nowrap; }\n@media (max-width: 767px) {\n  .socials { gap: 12px; }\n  .socials-t { flex: none; width: 84px; margin-right: 0; font-size: 9px; letter-spacing: .1em; line-height: 1.35; text-align: right; white-space: normal; }\n  .logo { width: 200px; }\n}\n.overlay { position: fixed; inset: 0; z-index: 50; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(0,0,0,.74); }\n.dialog { width: 100%; max-width: 720px; max-height: min(86vh, 820px); display: flex; flex-direction: column; background: var(--white); color: var(--ink);\n  border-top: 4px solid var(--red-500); box-shadow: 0 30px 80px rgba(0,0,0,.6); outline: none; }\n.dhead { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 22px 28px 14px; }\n.dhead h2 { font-size: 22px; font-weight: 900; font-style: italic; font-stretch: 118%; line-height: 1.1; text-transform: uppercase; color: var(--ink); }\n.x { flex: none; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--line); background: var(--white); color: var(--ink); cursor: pointer; }\n.x:hover { background: var(--ink); border-color: var(--ink); color: var(--white); }\n.x:focus-visible { outline: 2px solid var(--red-500); outline-offset: 2px; }\n.x svg { width: 18px; height: 18px; }\n.dbody { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px 28px 20px; font-size: 15px; line-height: 1.6; color: var(--grey-700); }\n.dbody h3 { margin: 22px 0 6px; font-size: 15px; font-weight: 800; line-height: 1.3; color: var(--ink); }\n.dbody p { margin: 0 0 8px; }\n.dbody ul { margin: 0 0 8px; padding-left: 20px; }\n.dbody li { margin-bottom: 4px; }\n.dbody a { color: var(--ink); font-weight: 600; text-underline-offset: 3px; }\n.dbody .upd { font-size: 13px; color: var(--grey-500); }\n.dfoot { flex: none; display: flex; justify-content: flex-end; padding: 16px 28px 22px; border-top: 1px solid var(--line); }\n.dfoot .send { width: 200px; margin: 0; }\n@media (max-width: 767px) {\n  .overlay { padding: 12px; }\n  .dhead { padding: 18px 20px 10px; }\n  .dhead h2 { font-size: 18px; }\n  .dbody { padding: 4px 20px 16px; font-size: 14px; }\n  .dfoot { padding: 14px 20px 18px; }\n  .dfoot .send { width: 100%; }\n}\n`,\n\n  mounted(d2, root) {\n    FONT();\n    if (typeof window !== 'undefined') {\n      window.addEventListener('resize', () => { const b = bucket(); if (d2.state.bk !== b) d2.state.bk = b; });\n    }\n    // Clique delegado: o Button fica dentro de <span data-act=\"send\">\n    d2.on('[data-act]', 'click', (e, el) => {\n      const act = el.getAttribute('data-act');\n      if (act === 'send') { const go = d2.root && d2.root.__tzSend; if (typeof go === 'function') go(); return; }\n      if (act === 'privacy') { e.preventDefault(); openPrivacyOf(d2)(); return; }\n      if (act === 'close') { d2.state.privacy = false; }\n    });\n    d2.on('.overlay', 'click', (e, el) => { if (e.target === el) d2.state.privacy = false; });\n    // Honeypot\n    d2.on('.hp', 'input', (e, el) => { store(d2).hp = el.value; });\n    // Enter envia, menos dentro da caixa de texto e no próprio botão\n    d2.on(root, 'keydown', (e) => {\n      if (e.key === 'Escape' && d2.state.privacy) { d2.state.privacy = false; return; }\n      if (e.key !== 'Enter' || e.shiftKey || e.isComposing) return;\n      const path = typeof e.composedPath === 'function' ? e.composedPath() : [];\n      const t = path[0];\n      if (t && (t.tagName === 'TEXTAREA' || t.tagName === 'BUTTON')) return;\n      const go = d2.root && d2.root.__tzSend;\n      if (typeof go === 'function') { e.preventDefault(); go(); }\n    });\n  },\n\n  updated(d2, root) {\n    const open = !!d2.state.privacy;\n    try { const h = root && root.host; if (h) h.style.overflowY = open ? 'hidden' : ''; } catch (e) { /* sem host */ }\n    if (!d2.root) return;\n    if (open && !d2.root.__tzFocused) { d2.root.__tzFocused = true; const dlg = d2.$('.dialog'); if (dlg) dlg.focus(); }\n    if (!open) d2.root.__tzFocused = false;\n  },\n\n  render(d2) {\n    const p = d2.params || {};\n    const bk = d2.state.bk || bucket();\n    const s = store(d2);\n    const ffRef = refOf(d2.param('formField'));\n    const cbRef = refOf(d2.param('checkbox'));\n    const btRef = refOf(d2.param('button'));\n    const icRef = refOf(d2.param('iconComponent'));\n    const lgRef = refOf(d2.param('logo'));\n    const errs = d2.state.errs || {};\n    const sending = !!d2.state.sending;\n    const igHandle = p.igHandle || '@autosuperbr';\n    const igUrl = p.igUrl ? String(p.igUrl) : IG_URL;\n    const ytUrl = YT_URL;\n    const openPrivacy = openPrivacyOf(d2);\n\n    const clear = (k) => {\n      const cur = d2.state.errs;\n      if (!cur) return;\n      if (!(cur[k] || ((k === 'email' || k === 'phone') && cur.contact) || d2.state.formError)) return;\n      const n = Object.assign({}, cur);\n      n[k] = '';\n      if (k === 'email' || k === 'phone') n.contact = '';\n      d2.state.errs = n;\n      d2.state.formError = '';\n    };\n\n    const submit = async () => {\n      if (d2.state.sending) return;\n      const st = store(d2);\n      const name = String(st.name || '').trim();\n      const email = String(st.email || '').trim();\n      const phone = digits(st.phone);\n      const e = {};\n      if (name.length < 2) e.name = 'Conta pra gente seu nome.';\n      if (!email && !phone) e.contact = 'Deixe pelo menos um contato: e-mail ou WhatsApp.';\n      if (email && !validEmail(email)) e.email = 'Confere o e-mail: parece incompleto.';\n      if (phone && (phone.length < 10 || phone.length > 11)) e.phone = 'Informe o DDD e o número do WhatsApp.';\n      if (!st.consent) e.consent = 'Para receber os avisos, marque a autorização.';\n      d2.state.errs = e;\n      d2.state.formError = '';\n      if (Object.keys(e).length) return;\n      const via = email && phone ? 'por e-mail e WhatsApp' : (email ? 'por e-mail' : 'por WhatsApp');\n      if (String(st.hp || '').trim()) { d2.state.via = via; d2.state.firstName = name.split(' ')[0]; d2.state.done = true; return; }\n      const payload = {\n        nome: name,\n        email: email.toLowerCase(),\n        whatsapp: phone,\n        feedback: String(st.feedback || '').trim(),\n        consentimento: true,\n        consentimentoEm: new Date().toISOString(),\n        origem: 'teaser'\n      };\n      d2.state.sending = true;\n      try {\n        const svc = d2.param('saveService');\n        if (svc) {\n          const r = unwrap(await d2.rush(svc).invoke(payload));\n          if (r.ok === false || r.erro || r.error) throw new Error(String(r.erro || r.error || 'falhou'));\n        }\n        emit(d2, 'onSubmit', payload);\n        d2.state.via = via;\n        d2.state.firstName = name.split(' ')[0];\n        d2.state.done = true;\n      } catch (err) {\n        d2.state.formError = 'Não deu pra enviar agora. Tenta de novo em instantes.';\n      }\n      d2.state.sending = false;\n    };\n    if (d2.root) d2.root.__tzSend = submit;\n\n    // ——— Topo\n    const logo = lgRef ? d2.render(lgRef, { layout: bk === 'desktop' ? 'desktop' : 'tablet', noLink: true, tone: 'dark' }, { key: 'logo-' + bk, style: 'height:' + (bk === 'desktop' ? 56 : 48) + 'px' }) : '';\n    const socials = `<span class=\"socials\"><span class=\"socials-t\">Acesse as nossas redes</span><a class=\"ig\" href=\"${esc(igUrl)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"Instagram\">${IG}</a><a class=\"ig\" href=\"${esc(ytUrl)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"YouTube\">${YT}</a></span>`;\n\n    // ——— Título (as quebras de linha mudam no tablet, como no layout)\n    const lines = bk === 'tablet' ? ['A loja', 'oficial', 'da Auto', 'Super', 'está'] : ['A loja', 'oficial da', 'Auto Super', 'está'];\n    const title = lines.map((l) => `<span>${esc(l)}</span>`).join('') + '<em>chegando</em>';\n\n    // ——— Avisos com check vermelho\n    const check = (i) => (icRef ? d2.render(icRef, { name: 'check', size: 12, iconColor: '#ffffff' }, { key: 'ckb' + i, width: 12, height: 12 }) : '✓');\n    const bullets = ['Aviso no dia em que a loja abrir', 'Lançamentos e novidades antes de todo mundo', 'Você conta o que quer encontrar na loja']\n      .map((t, i) => `<li><span class=\"chk\" aria-hidden=\"true\">${check(i)}</span><span>${esc(t)}</span></li>`).join('');\n\n    // ——— Cartão: formulário ou sucesso\n    let cardInner;\n    if (d2.state.done) {\n      const who = d2.state.firstName ? 'Valeu, ' + esc(d2.state.firstName) + '! ' : 'Valeu! ';\n      cardInner = `<div class=\"done\" role=\"status\" aria-live=\"polite\">\n        <h2 class=\"card-title\">Você está na lista!</h2>\n        <p class=\"done-text\">${who}A gente te avisa ${esc(d2.state.via || '')}, em primeira mão, assim que a loja oficial da Auto Super abrir.</p>\n      </div>`;\n    } else {\n      const field = (key, params, onInput) => {\n        const err = errs[key] || '';\n        return ffRef ? d2.render(ffRef, Object.assign({ layout: bk, iconComponent: icRef, error: err }, params), {\n          key: key + (err ? '-err-' + err.length : ''), style: 'height:auto', onInput\n        }) : '';\n      };\n      const fName = field('name', {\n        label: 'Nome', type: 'text', name: 'nome', autoComplete: 'name', placeholder: 'Como você se chama?', value: s.name\n      }, (v) => { store(d2).name = v; clear('name'); });\n      const fEmail = field('email', {\n        label: 'E-mail', type: 'email', name: 'email', autoComplete: 'email', inputMode: 'email',\n        placeholder: 'seuemail@exemplo.com', value: s.email\n      }, (v) => { store(d2).email = v; clear('email'); });\n      const fPhone = field('phone', {\n        label: 'WhatsApp', type: 'tel', name: 'whatsapp', autoComplete: 'tel-national', mask: 'phone',\n        placeholder: '(11) 91234-5678', value: s.phone\n      }, (v) => { store(d2).phone = v; clear('phone'); });\n      const fFeedback = field('feedback', {\n        label: 'O que eu gostaria de ver na loja da Auto Super', optional: true, multiline: true, maxLength: 1000, counter: true,\n        rows: 4, name: 'feedback', placeholder: 'Produtos, marcas, novidades… o que você sentir falta.', value: s.feedback\n      }, (v) => { store(d2).feedback = v; });\n\n      const consentErr = errs.consent || '';\n      const consent = cbRef ? d2.render(cbRef, {\n        layout: bk, iconComponent: icRef, top: true, name: 'consentimento', checked: !!s.consent, error: consentErr,\n        label: 'Eu autorizo compartilhar meus dados para receber comunicações por e-mail ou WhatsApp. [Política de privacidade](#privacidade)'\n      }, {\n        key: 'consent' + (consentErr ? '-err' : ''), style: 'height:auto',\n        onChange: (v) => { store(d2).consent = !!v; clear('consent'); },\n        onLink: openPrivacy\n      }) : '';\n\n      const btn = btRef ? `<span class=\"send\" data-act=\"send\">${d2.render(btRef, {\n        label: sending ? 'Enviando…' : 'Entrar na lista', variant: 'brand', size: 'lg', fullWidth: 'always', arrow: !sending, isDisabled: sending, layout: bk\n      }, { key: 'send-' + (sending ? 's' : 'i'), height: 52 })}</span>` : '';\n\n      const formError = d2.state.formError ? `<p class=\"err\" role=\"alert\">${esc(d2.state.formError)}</p>` : '';\n      const contactError = errs.contact ? `<p class=\"err\" role=\"alert\">${esc(errs.contact)}</p>` : '';\n\n      cardInner = `<h2 class=\"card-title\">Quero ser avisado em primeira mão</h2>\n        ${fName}\n        <p class=\"group-hint\">Deixe o e-mail, o WhatsApp ou os dois. Fica do jeito que for melhor pra você.</p>\n        <div class=\"row2\">${fEmail}${fPhone}</div>\n        ${contactError}\n        ${fFeedback}\n        <input class=\"hp\" type=\"text\" name=\"website\" tabindex=\"-1\" autocomplete=\"off\" aria-hidden=\"true\" value=\"${esc(s.hp)}\">\n        ${consent}\n        ${formError}\n        ${btn}`;\n    }\n\n    // ——— Faixa vermelha: o mesmo grupo duas vezes, para o movimento fechar sem emenda (translateX de 0 a -50%)\n    const modal = d2.state.privacy ? `<div class=\"overlay\">\n      <div class=\"dialog\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"pp-t\" tabindex=\"-1\">\n        <div class=\"dhead\"><h2 id=\"pp-t\">Política de privacidade</h2><button type=\"button\" class=\"x\" data-act=\"close\" aria-label=\"Fechar\">${X}</button></div>\n        <div class=\"dbody\">${POLICY}</div>\n        <div class=\"dfoot\">${btRef ? `<span class=\"send\" data-act=\"close\">${d2.render(btRef, { label: 'Entendi', variant: 'primary', size: 'md', fullWidth: 'always', layout: bk }, { key: 'pp-ok', height: 44 })}</span>` : ''}</div>\n      </div>\n    </div>` : '';\n    const group = '<span class=\"it\">Loja oficial Auto Super</span><i class=\"dia\"></i><span class=\"it\">Em breve</span><i class=\"dia\"></i>';\n    const half = group + group + group + group;\n\n    return `<main class=\"page\">\n      <div class=\"checker\" aria-hidden=\"true\"></div>\n      <header class=\"wrap top\">\n        <div class=\"logo\">${logo}</div>\n        ${socials}\n      </header>\n      <section class=\"wrap hero\">\n        <div>\n          <span class=\"tag\"><i></i>Em breve</span>\n          <h1 class=\"title\">${title}</h1>\n        </div>\n        <img class=\"shield\" src=\"${SHIELD}\" alt=\"Escudo Auto Super\" draggable=\"false\">\n      </section>\n      <div class=\"stage\">\n        <span class=\"wm\" aria-hidden=\"true\">Shop</span>\n        <section class=\"wrap lower\">\n          <div class=\"intro\">\n            <p>Produtos, lançamentos e novidades da AutoSuper num lugar só. Entre na lista e fique sabendo <strong>antes de todo mundo</strong> quando a loja abrir.</p>\n            <ul class=\"bullets\">${bullets}</ul>\n          </div>\n          <div class=\"card\" role=\"form\" aria-label=\"Quero ser avisado em primeira mão\">${cardInner}</div>\n        </section>\n      </div>\n      <div class=\"band\" data-d2-static aria-hidden=\"true\"><div class=\"track\">${half}${half}</div></div>\n      <footer class=\"foot\">\n        <div class=\"wrap\">\n          <span>© AutoSuper</span>\n          <span class=\"right\"><a href=\"#privacidade\" data-act=\"privacy\">Política de privacidade</a><a class=\"fig\" href=\"${esc(igUrl)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"Instagram\">${IG}</a><a class=\"fig\" href=\"${esc(ytUrl)}\" target=\"_blank\" rel=\"noopener\" aria-label=\"YouTube\">${YT}</a></span>\n        </div>\n      </footer>\n      ${modal}\n    </main>`;\n  }\n};\n"}},"button":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"button"}}}},"checkbox":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"checkbox"}}}},"cnpj":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cnpj"}}}},"formField":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"formField"}}}},"iconComponent":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"iconComponent"}}}},"igHandle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"igHandle"}}}},"igUrl":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"igUrl"}}}},"legalName":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"legalName"}}}},"logo":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"logo"}}}},"privacyHref":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"privacyHref"}}}},"saveService":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"saveService"}}}}}}}}}},{"_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}}}}},{"_id":"5ecd05ded68ce41360f74b43","component":"data2-core-types@Component","params":{"extendable":{"placeholder":"children"},"component":"Block","package":"data2-core-primitives"}},{"_id":"60019e844369e2af95919c94","component":"data2-core-types@Component","params":{"definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"html@iframe","params":{"frameBorder":"0","src":{"component":"data2-core-flow@If","params":{"compare":[{"component":"data2-core-types@Reference","params":{"reference":"builder"}}],"then":"about:blank","else":{"component":"data2-core-types@Reference","params":{"reference":"url"}}}},"allow":"autoplay","allowFullScreen":{"component":"data2-core-types@Reference","params":{"reference":"allowFullScreen"}},"loading":"lazy","style":{"position":"absolute","width":"100%","height":"100%","border":"none","padding":"none","overflow":"auto"}}}}},"component":"IframeBlock","package":"data2-blocks","extends":"data2-core-primitives@Block"}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"component":"data2-core-flow@If","displayName":"management exception never picks the system","params":{"compare":{"component":"data2-core-logical-operators@Or","params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-logical-operators@Not","params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}}}]}},"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"system":{"component":"data2-core-primitives@String","params":{"content":"63611bdff62ab6060b8396ac"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-10-14T16:52:02.312Z","updateMetadata":{"updatedBy":{"user":"5f1609be1ca96def427e1559"}}},{"_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"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6ac7b5a433de4825ef2ee58f","component":"data2-rush@HTTPListener","reference":"leadAvisoSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6f72ee48a29f4b8cb7a43","component":"data2-rush@HTTPListener","reference":"minhaSessaoContato","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6f6fd29b16dbc1687d169","component":"data2-rush@HTTPListener","reference":"mensagemEnviar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac4271778f3730f24cb815d","component":"data2-rush@HTTPListener","reference":"categoriasLoja","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac52d612f4d9a7641b93a1f","component":"data2-rush@HTTPListener","reference":"produtosLoja","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6aceb2c236e69ceacd76f639","component":"data2-rush@HTTPListener","reference":"cupomValidar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac42cd843b15d0ea6b7bdd7","component":"data2-rush@HTTPListener","reference":"gestaoCategoriasListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac428727a27742c857ecb94","component":"data2-rush@HTTPListener","reference":"gestaoCategoriaSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac427231c34e8ef0a953ff1","component":"data2-rush@HTTPListener","reference":"gestaoCategoriaDesativar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac4272a005d4042c2e3254d","component":"data2-rush@HTTPListener","reference":"gestaoCategoriaExcluir","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac43a7e76d49ffb413a091f","component":"data2-rush@HTTPListener","reference":"gestaoCategoriaImagem","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac526507a27742c85806af5","component":"data2-rush@HTTPListener","reference":"gestaoProdutosListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac562ff06e81c2076f3ba1b","component":"data2-rush@HTTPListener","reference":"gestaoDestaquesProdutosSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac4361a7a27742c857eeb94","component":"data2-rush@HTTPListener","reference":"gestaoDestaquesSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac5268b27b4b62e8522a57c","component":"data2-rush@HTTPListener","reference":"gestaoProdutoObter","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac526a02f4d9a7641b92667","component":"data2-rush@HTTPListener","reference":"gestaoProdutoSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac527097a27742c85806d38","component":"data2-rush@HTTPListener","reference":"gestaoProdutoAtivar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac527117cfd4d3426c21cb1","component":"data2-rush@HTTPListener","reference":"gestaoProdutoExcluir","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac527066bb5213aca6176e1","component":"data2-rush@HTTPListener","reference":"gestaoGaleriaRemover","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac52d2380918a4cab959650","component":"data2-rush@HTTPListener","reference":"gestaoParceiroSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac526ec5ae4f7827ef7fc6a","component":"data2-rush@HTTPListener","reference":"gestaoGaleriaEnviar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6f7173cd42e411b47f2b7","component":"data2-rush@HTTPListener","reference":"gestaoMensagensContagem","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6948e29b16dbc1686ff9e","component":"data2-rush@HTTPListener","reference":"opinioesProduto","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac694b65da28ca8d58be8e9","component":"data2-rush@HTTPListener","reference":"opiniaoEnviar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac694c878f3730f24d01169","component":"data2-rush@HTTPListener","reference":"gestaoOpinioesListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac694cd29b16dbc168700ad","component":"data2-rush@HTTPListener","reference":"gestaoOpiniaoExcluir","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69af23cd42e411b473af4","component":"data2-rush@HTTPListener","reference":"gestaoBannersListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69b095da28ca8d58bfd1e","component":"data2-rush@HTTPListener","reference":"gestaoBannerSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69b1278f3730f24d02697","component":"data2-rush@HTTPListener","reference":"gestaoBannerExcluir","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69b1ad0e6a463e356b5e4","component":"data2-rush@HTTPListener","reference":"gestaoBannersOrdenar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69b1fd0e6a463e356b5f0","component":"data2-rush@HTTPListener","reference":"gestaoBannerImagem","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac69ae55da28ca8d58bfcb1","component":"data2-rush@HTTPListener","reference":"bannersHome","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6f70d3cd42e411b47f29d","component":"data2-rush@HTTPListener","reference":"gestaoMensagensListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6ac6f712862cb6202e1d038c","component":"data2-rush@HTTPListener","reference":"gestaoMensagemMarcar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6aceb2e33b0c8ee9370280d1","component":"data2-rush@HTTPListener","reference":"gestaoCuponsListar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6aceb2eea05c951878d01bd6","component":"data2-rush@HTTPListener","reference":"gestaoCupomSalvar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6aceb2f24811f379cb11f77a","component":"data2-rush@HTTPListener","reference":"gestaoCupomAtivar","system":"6ab58b1260cbe7aa719c6d7b","params":{}},{"_id":"6aceb2f87e0f2e58ab6ff53a","component":"data2-rush@HTTPListener","reference":"gestaoCupomExcluir","system":"6ab58b1260cbe7aa719c6d7b","params":{}}]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6ad0f78da1bed74427855f81","component":"data2-core-primitives@Router","system":"6ab58b1260cbe7aa719c6d7b","displayName":"Teaser","params":{"initialPath":{"component":"data2-core-primitives@String","params":{"content":"/"}},"routes":[{"component":"data2-core-primitives@Route","params":{"path":{"component":"data2-core-primitives@String","params":{"content":"/"}},"content":{"params":{"reference":"QU2jUOJ4vsUMWuGld5fb0"},"component":"data2-core-types@ArtboardReference"},"icon":{"component":"data2-core-types@FileComponentUrlReference","params":{"reference":"7JdlktektKrT-sjFK5yjP"}},"name":"Auto Super Shop | Em breve"}}]},"reference":"EAeFfq-astVB7E7VtFIJR"},{"_id":"6ad10028cfd91fc5ed242c8c","component":"data2-core-primitives@Artboard","system":"6ab58b1260cbe7aa719c6d7b","displayName":"Teaser iframe","params":{"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1366,"height":768}},"fill":{"params":{"backgrounds":[{"params":{"color":{"disabled":false,"component":"data2-core-types@Color","params":{"rgba":"rgba(255,255,255,1)"}},"gradient":{"disabled":true,"component":"data2-core-types@ColorGradient","params":{}},"image":{"disabled":true,"component":"data2-core-types@String","params":{}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"}},"reference":"QU2jUOJ4vsUMWuGld5fb0"},{"_id":"6ad1004ccfd91fc5ed242c8d","parent":"6ad10028cfd91fc5ed242c8c","component":"data2-blocks@IframeBlock","system":"6ab58b1260cbe7aa719c6d7b","params":{"super":{"border":{"component":"data2-core-types@Border","params":{"color":"#808080","radius":{},"width":{"left":"0","top":"0","right":"0","bottom":"0"}}},"fill":{"params":{"backgrounds":[{"params":{"color":{"component":"data2-core-types@Color","params":{"rgba":"rgba(5,5,6,1)"}}},"component":"data2-core-types@Background"}]},"component":"data2-core-types@Fill"},"dimensions":{"component":"data2-core-types@Dimensions","params":{"width":1366,"height":768,"exactWidth":false,"exactHeight":false}},"position":{"component":"data2-core-types@Position","params":{"freeTransform":true,"x":0,"y":0,"exactX":false,"exactY":false,"top":true,"right":true,"bottom":true,"growV":false,"left":true,"growH":false}}},"url":{"component":"data2-core-primitives@String","params":{"content":"https://teaser-iframe.babi-lf.next.data2.app/"}}}}]}},{"_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":"6ad0f103143088150287c6d8","displayName":"Teaser","reference":"hg3gfPYn7-BVprehogXzQ","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"teaser"}},"entryPoint":{"params":{"component":"data2-core-primitives@Router","reference":"EAeFfq-astVB7E7VtFIJR"},"component":"data2-core-types@ComponentInstanceReference"}},"system":"6ab58b1260cbe7aa719c6d7b","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[{"_id":"6abd7aa38dad80a86684a089","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"produtos","schema":[{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"slug","title":"Slug","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"sku","title":"Código (SKU)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"categoria","title":"Categoria","component":{"component":"data2-core-types@Lookup","params":{"table":"categorias"}}}},{"component":"data2-core-types@Field","params":{"key":"subcategoria","title":"Subcategoria","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":"origem","title":"Origem (proprio ou parceiro)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"preco","title":"Preço (cartão)","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"precoAntigo","title":"Preço \"de\" (riscado)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"parcelas","title":"Parcelas sem juros","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"estoque","title":"Estoque","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"vendas","title":"Vendas (para \"Mais vendidos\")","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"imagem","title":"Foto principal (URL)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"destaque","title":"Destaque na home","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"ativo","title":"Ativo (pausa rápida)","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"descricao","title":"Descrição","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"beneficios","title":"Benefícios","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"ficha_tecnica","title":"Ficha técnica","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"instrucoes_uso","title":"Instruções de uso","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"social_video","title":"Vídeo (YouTube/Instagram)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"desconto_pix","title":"Desconto Pix (%)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"parceiro","title":"Parceiro","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"posicao_destaque","title":"Posição no carrossel de destaques (1 a 12)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"faq","title":"Perguntas frequentes (lista de pergunta e resposta)","component":"data2-core-primitives@Array"}}]},"reference":"produtos"},{"_id":"6ac7e9f48304dd1773f6ef94","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"pedidos","schema":[{"component":"data2-core-types@Field","params":{"key":"numero","title":"Número","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cliente_id","title":"Cliente","component":"data2-core-primitives@ObjectId","required":true}},{"component":"data2-core-types@Field","params":{"key":"pagador_nome","title":"Pagador — nome","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"pagador_email","title":"Pagador — e-mail","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"pagador_whatsapp","title":"Pagador — WhatsApp","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"pagador_tipo_documento","title":"Pagador — tipo do documento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"pagador_documento","title":"Pagador — documento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"itens","title":"Itens","component":"data2-core-primitives@Array","required":true}},{"component":"data2-core-types@Field","params":{"key":"entrega","title":"Endereço de entrega","component":"data2-core-primitives@Object"}},{"component":"data2-core-types@Field","params":{"key":"envio_por","title":"Envio por (lucas ou parceiro)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"codigo_rastreio","title":"Código de rastreio","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"status_envio","title":"Status do envio","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"subtotal","title":"Subtotal","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"frete","title":"Frete","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"desconto","title":"Desconto","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"total","title":"Total","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"pagamento_forma","title":"Forma de pagamento (pix ou cartao)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"parcelas","title":"Parcelas","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"pagamento_status","title":"Status do pagamento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_order_id","title":"Mercado Pago — id do pedido","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_payment_id","title":"Mercado Pago — id do pagamento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"status","title":"Status do pedido","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"estoque_baixado","title":"Estoque baixado","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"revisao_estoque","title":"Revisão de estoque","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"termos_versao","title":"Termos — versão aceita","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"termos_aceite_em","title":"Termos — aceite em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"criado_em","title":"Criado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"pago_em","title":"Pago em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"enviado_em","title":"Enviado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"entregue_em","title":"Entregue em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"cupom","title":"Cupom aplicado (código)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"cupom_desconto","title":"Cupom — valor descontado (R$)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"mp_checkout_url","title":"Mercado Pago — URL do checkout","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_status","title":"Mercado Pago — status da order","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_status_detail","title":"Mercado Pago — detalhe do status","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_tentativas","title":"Mercado Pago — tentativas de criar order","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"mp_expira_em","title":"Mercado Pago — order válida até","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"mp_atualizado_em","title":"Mercado Pago — última atualização","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"mp_modo","title":"Mercado Pago — modo (teste ou producao)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_total_pago","title":"Mercado Pago — total pago (com juros)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"pagamento_metodo","title":"Meio de pagamento usado (ex.: pix, visa)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"revisao_pagamento","title":"Revisão de pagamento (valor divergente)","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"revisao_estoque_itens","title":"Revisão de estoque — itens sem saldo","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"cancelado_em","title":"Cancelado/expirado em","component":"data2-core-primitives@Date"}},{"component":"data2-core-types@Field","params":{"key":"cancelado_motivo","title":"Motivo do cancelamento (expirado, estoque, cliente, pagamento)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"emails_enviados","title":"E-mails já enviados (feito, pago, cancelado)","component":"data2-core-primitives@Array"}},{"component":"data2-core-types@Field","params":{"key":"frete_servico","title":"Frete — serviço","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"frete_prazo_dias","title":"Frete — prazo (dias úteis)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"frete_cep","title":"Frete — CEP de destino","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"parcelas_max","title":"Máximo de parcelas permitido (menor entre os itens)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"baixa_claim","title":"Controle interno da baixa de estoque","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mp_order_ids","title":"Mercado Pago — todas as orders já criadas para o pedido","component":"data2-core-primitives@Array"}}]},"reference":"pedidos"},{"_id":"6ac554f706e81c2076f394fe","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"enderecos","schema":[{"component":"data2-core-types@Field","params":{"key":"cliente_id","title":"Cliente","component":"data2-core-primitives@ObjectId","required":true}},{"component":"data2-core-types@Field","params":{"key":"apelido","title":"Apelido","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"destinatario","title":"Destinatário","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cep","title":"CEP","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"logradouro","title":"Logradouro","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"numero","title":"Número","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"complemento","title":"Complemento","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"bairro","title":"Bairro","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cidade","title":"Cidade","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"estado","title":"Estado","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"principal","title":"Principal","component":"data2-core-primitives@Boolean","required":true}}]},"reference":"enderecos"},{"_id":"6ac425f627b4b62e8520ffa3","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"categorias","schema":[{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"slug","title":"Slug","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"ativa","title":"Ativa","component":"data2-core-primitives@Boolean","required":true}},{"component":"data2-core-types@Field","params":{"key":"destaque_home","title":"Destaque na home","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"destaque_menu","title":"Destaque no menu","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"ordem","title":"Ordem (menor aparece primeiro)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"legenda","title":"Legenda do card da home","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"posicao_home","title":"Vaga na home (1 a 4, 0 = fora)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"posicao_menu","title":"Vaga no menu (1 a 6, 0 = fora)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"imagem","title":"Imagem do card (URL)","component":"data2-core-primitives@String"}}]},"reference":"categorias"},{"_id":"6ac6947f5da28ca8d58be826","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"opinioes","schema":[{"component":"data2-core-types@Field","params":{"key":"produto","title":"Produto","component":"data2-core-primitives@ObjectId","required":true}},{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Cliente","component":"data2-core-primitives@ObjectId","required":true}},{"component":"data2-core-types@Field","params":{"key":"nota","title":"Nota (1 a 5)","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"titulo","title":"Título","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"texto","title":"Opinião","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"imagem","title":"Imagem (opcional)","component":"data2-core-types@File","folder":{"component":"data2-core-types@DriveReference","params":{"reference":"opinioesimagens"}}}}]},"reference":"opinioes"},{"_id":"6aceb2a4a05c951878d01b05","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"cupons","schema":[{"component":"data2-core-types@Field","params":{"key":"codigo","title":"Código (maiúsculo, único)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"tipo","title":"Tipo (percentual ou valor)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"valor","title":"Valor (% ou R$)","component":"data2-core-primitives@Number","required":true}},{"component":"data2-core-types@Field","params":{"key":"ativo","title":"Ativo","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"inicio","title":"Válido a partir de (AAAA-MM-DD, vazio = já)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"fim","title":"Válido até (AAAA-MM-DD, vazio = sem fim)","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"valor_minimo","title":"Pedido mínimo em R$ (0 = sem mínimo)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"limite_total","title":"Limite total de usos (0 = sem limite)","component":"data2-core-primitives@Number"}},{"component":"data2-core-types@Field","params":{"key":"um_por_cliente","title":"Um uso por cliente","component":"data2-core-primitives@Boolean"}}]},"reference":"cupons"},{"_id":"6ac6f6efd0e6a463e35765bc","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"mensagens","schema":[{"component":"data2-core-types@Field","params":{"key":"assunto","title":"Assunto (código)","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"assunto_rotulo","title":"Assunto","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"email","title":"E-mail","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"whatsapp","title":"WhatsApp","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"empresa","title":"Empresa ou veículo","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"tipo_proposta","title":"Tipo de proposta","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"mensagem","title":"Mensagem","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"cliente","title":"Cliente (se estava logado)","component":"data2-core-primitives@ObjectId"}},{"component":"data2-core-types@Field","params":{"key":"lida","title":"Lida","component":"data2-core-primitives@Boolean"}}]},"reference":"mensagens"},{"_id":"6ad0f603895f0d008530115d","component":"data2-core@Data2TableDataSource","system":"6ab58b1260cbe7aa719c6d7b","params":{"table":"teaser","schema":[{"component":"data2-core-types@Field","params":{"key":"nome","title":"Nome","component":"data2-core-primitives@String","required":true}},{"component":"data2-core-types@Field","params":{"key":"email","title":"E-mail","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"whatsapp","title":"WhatsApp","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"feedback","title":"O que gostaria de ver na loja","component":"data2-core-primitives@String"}},{"component":"data2-core-types@Field","params":{"key":"consentimento","title":"Autorizou comunicações","component":"data2-core-primitives@Boolean"}},{"component":"data2-core-types@Field","params":{"key":"origem","title":"Origem","component":"data2-core-primitives@String"}}]},"reference":"teaser"}]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}