{"context":{"domain":"gustavo-playground"},"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":"684302e0f220c5f37f7a25d1","name":"Gustavo's Playground","domain":"gustavo-playground","visibility":"public","settings":{"defaultAccessPolicy":"private"},"updatedAt":"2026-08-14T19:26:29.927Z"}]}},{"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":"6a46ad605b52bb5fa5323a38","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"n0j6CJSazCIkBa9u_JXG-","package":"gustavo-playground","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    .shell { display:flex; flex-direction:column; width:100%; height:100%; box-sizing:border-box; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; background:linear-gradient(180deg,#0f172a 0%,#1e293b 100%); color:#f8fafc; }\n    .header { display:flex; align-items:center; justify-content:space-between; padding:20px 40px; border-bottom:1px solid rgba(255,255,255,0.08); }\n    .brand { display:flex; align-items:center; gap:10px; font-weight:700; font-size:18px; letter-spacing:0.2px; }\n    .brand .dot { width:10px; height:10px; border-radius:50%; background:#7c4dff; }\n    .nav { display:flex; gap:28px; font-size:14px; color:rgba(248,250,252,0.7); }\n    .hero { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:40px; gap:18px; }\n    .eyebrow { font-size:13px; letter-spacing:1.5px; text-transform:uppercase; color:#7c4dff; font-weight:600; }\n    .hero h1 { margin:0; font-size:44px; font-weight:800; max-width:720px; line-height:1.15; }\n    .hero p { margin:0; font-size:17px; color:rgba(248,250,252,0.7); max-width:520px; line-height:1.5; }\n    .cta { margin-top:12px; padding:14px 28px; border-radius:10px; border:none; background:#7c4dff; color:#fff; font-size:15px; font-weight:600; cursor:pointer; transition:transform 0.15s ease, background 0.15s ease; }\n    .cta:hover { background:#6a3ff0; transform:translateY(-1px); }\n    .footer { padding:18px 40px; text-align:center; font-size:12px; color:rgba(248,250,252,0.4); border-top:1px solid rgba(255,255,255,0.08); }\n  `,\n  render(d2) {\n    const title = d2.param('title', 'Bem-vindo ao seu novo projeto')\n    const subtitle = d2.param('subtitle', 'Esta e a tela inicial padrao do projeto. Substitua este componente pelo primeiro fluxo do seu produto.')\n    const ctaLabel = d2.param('ctaLabel', 'Comecar')\n    const brand = d2.param('brand', 'gustavo-playground')\n    return `\n      <div class=\"shell\">\n        <div class=\"header\">\n          <div class=\"brand\"><span class=\"dot\"></span>${brand}</div>\n          <div class=\"nav\"><span>Inicio</span><span>Recursos</span><span>Sobre</span></div>\n        </div>\n        <div class=\"hero\">\n          <div class=\"eyebrow\">Novo projeto</div>\n          <h1>${title}</h1>\n          <p>${subtitle}</p>\n          <button class=\"cta\">${ctaLabel}</button>\n        </div>\n        <div class=\"footer\">Construido com Data2</div>\n      </div>\n    `\n  },\n  mounted(d2) {\n    d2.on('.cta', 'click', () => {\n      d2.emit('onCta', { source: 'hero' })\n    })\n  },\n}"}},"title":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"title"}}}},"subtitle":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"subtitle"}}}},"ctaLabel":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"ctaLabel"}}}},"brand":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"brand"}}}},"onCta":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"onCta"}}}}}}}}}},{"_id":"6a46b122c53088741c5363ed","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"pixel-monkey-sword","package":"gustavo-playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{ position:relative; width:100%; height:100%; display:flex; align-items:center; justify-content:center; gap:28px; font-family:'Segoe UI', sans-serif; background:transparent; box-sizing:border-box; }\n    canvas.monkey-canvas{ image-rendering: pixelated; image-rendering: crisp-edges; }\n    .action-btn{\n      padding:14px 30px; font-size:16px; font-weight:700; letter-spacing:1px; text-transform:uppercase;\n      background:#E07B2A; color:#2A1206; border:3px solid #2A1206; border-radius:8px; cursor:pointer;\n      box-shadow:0 4px 0 #7a3f10; user-select:none; transition:transform 0.05s linear;\n    }\n    .action-btn:active{ transform:translateY(4px); box-shadow:0 0 0 #7a3f10; }\n  `,\n  render(d2) {\n    return `\n      <div class=\"wrap\">\n        <canvas class=\"monkey-canvas\" width=\"280\" height=\"340\"></canvas>\n        <button class=\"action-btn\" type=\"button\">Action</button>\n      </div>\n    `\n  },\n  mounted(d2, root) {\n    const canvas = d2.$('.monkey-canvas')\n    const ctx = canvas.getContext('2d')\n    const dpr = window.devicePixelRatio || 1\n    const cssW = 280, cssH = 340\n    canvas.width = cssW * dpr\n    canvas.height = cssH * dpr\n    canvas.style.width = cssW + 'px'\n    canvas.style.height = cssH + 'px'\n    ctx.scale(dpr, dpr)\n    ctx.imageSmoothingEnabled = false\n\n    const R = 8 // px per pixel-art unit\n\n    // ---------- shape helpers (local bone space) ----------\n    function ellipseCells(cx, cy, rx, ry) {\n      const cells = []\n      for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {\n        for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {\n          const nx = (x - cx) / rx, ny = (y - cy) / ry\n          if (nx * nx + ny * ny <= 1) cells.push([x, y])\n        }\n      }\n      return cells\n    }\n    function rectCells(x0, y0, x1, y1) {\n      const cells = []\n      const xa = Math.round(Math.min(x0, x1)), xb = Math.round(Math.max(x0, x1))\n      const ya = Math.round(Math.min(y0, y1)), yb = Math.round(Math.max(y0, y1))\n      for (let y = ya; y <= yb; y++) for (let x = xa; x <= xb; x++) cells.push([x, y])\n      return cells\n    }\n    function lineCells(x0, y0, x1, y1, thickness) {\n      const cells = []\n      const dx = x1 - x0, dy = y1 - y0\n      const steps = Math.max(Math.abs(dx), Math.abs(dy)) * 2 || 1\n      const half = Math.floor(thickness / 2)\n      for (let i = 0; i <= steps; i++) {\n        const tt = i / steps\n        const x = Math.round(x0 + dx * tt), y = Math.round(y0 + dy * tt)\n        for (let ox = -half; ox <= thickness - 1 - half; ox++) {\n          for (let oy = -half; oy <= thickness - 1 - half; oy++) {\n            cells.push([x + ox, y + oy])\n          }\n        }\n      }\n      return cells\n    }\n    function tag(cellsArr, color) { return cellsArr.map(([x, y]) => [x, y, color]) }\n    function buildPart(taggedCells) {\n      const map = new Map()\n      for (const [x, y, c] of taggedCells) map.set(x + ',' + y, c)\n      const outline = new Set()\n      const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]]\n      for (const k of map.keys()) {\n        const [x, y] = k.split(',').map(Number)\n        for (const [dx, dy] of dirs) {\n          const nk = (x + dx) + ',' + (y + dy)\n          if (!map.has(nk)) outline.add(nk)\n        }\n      }\n      return { cells: map, outline }\n    }\n\n    // ---------- palette (mustard/copper monkey, matching reference art) ----------\n    const palette = {\n      Y: '#E07B2A', // main fur (copper/orange)\n      D: '#A34E12', // shade / far-arm / feet / tail tip\n      F: '#F7DFB0', // cream belly & muzzle patch\n      K: '#20120A', // eye\n      N: '#20120A', // nose\n      S: '#D9D9E3', // sword blade\n      G: '#C9A227', // sword guard\n      H: '#5C3418', // sword hilt\n      O: '#2A1206', // outline\n    }\n\n    // ---------- bone shapes (each defined around its own pivot at (0,0)) ----------\n    const torsoShape = buildPart([\n      ...tag(ellipseCells(0, -7, 5.5, 7), 'Y'),\n      ...tag(ellipseCells(0.6, -6, 3, 4.5), 'F'),\n    ])\n    const headShape = buildPart([\n      ...tag(ellipseCells(0, -5, 4.3, 4.6), 'Y'),\n      ...tag(ellipseCells(-3, -8.2, 1.6, 1.6), 'Y'),\n      ...tag(ellipseCells(2.8, -8.4, 1.7, 1.7), 'Y'),\n      ...tag(ellipseCells(1.6, -4, 2.6, 2.2), 'F'),\n      ...tag([[2.4, -5.3]], 'K'),\n      ...tag([[4.2, -4.0]], 'N'),\n    ])\n    const armFarShape = buildPart([\n      ...tag(lineCells(0, 0, 0, 7, 3), 'D'),\n      ...tag(ellipseCells(0, 7, 1.6, 1.6), 'D'),\n    ])\n    const armNearShape = buildPart([\n      ...tag(lineCells(0, 0, 0, 7, 3), 'Y'),\n      ...tag(ellipseCells(0, 7, 1.8, 1.8), 'Y'),\n    ])\n    const swordShape = buildPart([\n      ...tag(rectCells(-0.5, -1, 0.5, 0), 'H'),\n      ...tag(rectCells(-1.5, 0, 1.5, 1), 'G'),\n      ...tag(rectCells(-0.7, 1, 0.7, 7), 'S'),\n    ])\n    const thighShape = buildPart(tag(lineCells(0, 0, 0, 5, 3), 'Y'))\n    const shinShape = buildPart([\n      ...tag(lineCells(0, 0, 0, 5, 2), 'Y'),\n      ...tag(rectCells(0, 4, 3, 5.5), 'D'),\n    ])\n    const tailBaseShape = buildPart(tag(lineCells(0, 0, 0, -4.5, 3), 'Y'))\n    const tailMidShape = buildPart(tag(lineCells(0, 0, 0, -4, 2), 'Y'))\n    const tailTipShape = buildPart(tag(lineCells(0, 0, 0, -3.4, 2), 'D'))\n\n    function drawPart(part) {\n      ctx.fillStyle = palette.O\n      for (const k of part.outline) {\n        const [x, y] = k.split(',').map(Number)\n        ctx.fillRect(x * R, y * R, R, R)\n      }\n      for (const [k, color] of part.cells) {\n        const [x, y] = k.split(',').map(Number)\n        ctx.fillStyle = palette[color] || '#000'\n        ctx.fillRect(x * R, y * R, R, R)\n      }\n    }\n\n    // ---------- keyframe sampler (smoothstep interpolation) ----------\n    function sample(kf, p) {\n      if (p <= kf[0][0]) return kf[0][1]\n      for (let i = 0; i < kf.length - 1; i++) {\n        const [t0, v0] = kf[i], [t1, v1] = kf[i + 1]\n        if (p >= t0 && p <= t1) {\n          const local = (t1 === t0) ? 0 : (p - t0) / (t1 - t0)\n          const eased = local * local * (3 - 2 * local)\n          return v0 + (v1 - v0) * eased\n        }\n      }\n      return kf[kf.length - 1][1]\n    }\n\n    // ---------- rest pose + jump keyframes (anticipation -> stretch launch -> air -> land) ----------\n    const REST_THIGH = 0.12, REST_SHIN = 0.18\n    const REST_TAIL_0 = -0.3, REST_TAIL_1 = -1.1, REST_TAIL_2 = -0.9\n\n    const THIGH_KF = [[0, REST_THIGH], [0.15, REST_THIGH + 0.85], [0.42, -0.05], [0.72, -0.05], [0.88, REST_THIGH + 0.7], [1, REST_THIGH]]\n    const SHIN_KF = [[0, REST_SHIN], [0.15, REST_SHIN + 1.0], [0.42, 0], [0.72, 0], [0.88, REST_SHIN + 0.9], [1, REST_SHIN]]\n    const TORSO_Y_KF = [[0, 0], [0.15, 3], [0.42, -1.5], [0.72, -1], [0.88, 2.5], [1, 0]]\n    const TORSO_SCALEY_KF = [[0, 1], [0.15, 0.82], [0.42, 1.18], [0.72, 1.08], [0.88, 0.84], [1, 1]]\n    const TORSO_ANGLE_KF = [[0, 0], [0.15, 0.05], [0.42, -0.08], [0.72, 0], [0.88, 0.06], [1, 0]]\n    const ARM_NEAR_KF = [[0, 0], [0.15, -0.3], [0.42, 0.75], [0.6, 0.95], [0.88, 0.1], [1, 0]]\n    const ARM_FAR_KF = [[0, 0], [0.15, 0.3], [0.42, -0.6], [0.6, -0.75], [0.88, -0.05], [1, 0]]\n    const HEAD_KF = [[0, 0], [0.15, -0.05], [0.42, 0.15], [0.6, 0.22], [0.88, -0.05], [1, 0]]\n    const TAIL_EXTRA_KF = [[0, 0], [0.15, -0.35], [0.42, 0.45], [0.72, 0.3], [1, 0]]\n\n    let jumping = false\n    let jumpStart = 0\n    const JUMP_DURATION = 780 // ms\n    const JUMP_HEIGHT = 65 // px\n\n    function computePose(t) {\n      const pose = {}\n      if (jumping) {\n        let p = (t - jumpStart) / JUMP_DURATION\n        if (p >= 1) { p = 1; jumping = false }\n        pose.bodyY = -JUMP_HEIGHT * 4 * p * (1 - p)\n        pose.thigh = sample(THIGH_KF, p)\n        pose.shin = sample(SHIN_KF, p)\n        pose.torsoLocalY = sample(TORSO_Y_KF, p)\n        pose.torsoScaleY = sample(TORSO_SCALEY_KF, p)\n        pose.torsoAngle = sample(TORSO_ANGLE_KF, p)\n        pose.armNear = sample(ARM_NEAR_KF, p)\n        pose.armFar = sample(ARM_FAR_KF, p)\n        pose.headAngle = sample(HEAD_KF, p)\n        pose.tailExtra = sample(TAIL_EXTRA_KF, p)\n      } else {\n        pose.bodyY = 0\n        pose.thigh = REST_THIGH + Math.sin(t / 1600) * 0.02\n        pose.shin = REST_SHIN + Math.sin(t / 1600 + 0.4) * 0.02\n        pose.torsoLocalY = Math.sin(t / 450) * 1.2\n        pose.torsoScaleY = 1 + Math.sin(t / 900) * 0.015\n        pose.torsoAngle = Math.sin(t / 1500) * 0.015\n        pose.armNear = Math.sin(t / 900 + 1.0) * 0.06\n        pose.armFar = Math.sin(t / 900 + 1.0 + Math.PI) * 0.06\n        pose.headAngle = Math.sin(t / 900 + 0.3) * 0.04\n        pose.tailExtra = 0\n      }\n      return pose\n    }\n\n    const ROOT_X = 130, ROOT_Y = 225\n    const FEET_Y = ROOT_Y + 11 * R\n\n    function drawShadow(pose) {\n      const t = Math.min(Math.abs(pose.bodyY) / JUMP_HEIGHT, 1)\n      const scale = 1 - t * 0.4\n      ctx.save()\n      ctx.globalAlpha = Math.max(0.32 - t * 0.18, 0.08)\n      ctx.fillStyle = '#000'\n      ctx.beginPath()\n      ctx.ellipse(ROOT_X, FEET_Y, 6 * R * scale, 1.3 * R, 0, 0, Math.PI * 2)\n      ctx.fill()\n      ctx.restore()\n    }\n\n    function drawFigure(pose) {\n      const scaleX = 1 + (1 - pose.torsoScaleY) * 0.6\n      ctx.save()\n      ctx.translate(ROOT_X, ROOT_Y + pose.bodyY)\n\n      ctx.save() // torso group: everything below shares torso squash/stretch + sway\n      ctx.translate(0, pose.torsoLocalY * R)\n      ctx.rotate(pose.torsoAngle)\n      ctx.scale(scaleX, pose.torsoScaleY)\n\n      // tail chain (behind)\n      ctx.save()\n      ctx.translate(-4 * R, -8 * R)\n      ctx.rotate(REST_TAIL_0 + pose.tailExtra * 1.0)\n      drawPart(tailBaseShape)\n      ctx.translate(0, -4.5 * R)\n      ctx.rotate(REST_TAIL_1 + pose.tailExtra * 1.4)\n      drawPart(tailMidShape)\n      ctx.translate(0, -4 * R)\n      ctx.rotate(REST_TAIL_2 + pose.tailExtra * 1.8)\n      drawPart(tailTipShape)\n      ctx.restore()\n\n      // far arm (behind torso)\n      ctx.save()\n      ctx.translate(-4 * R, -11 * R)\n      ctx.rotate(pose.armFar)\n      drawPart(armFarShape)\n      ctx.restore()\n\n      // torso\n      drawPart(torsoShape)\n\n      // legs (thigh -> shin, straighten fully on jump launch = \"pernas esticadas\")\n      ;[-2.5, 2.5].forEach((sx) => {\n        ctx.save()\n        ctx.translate(sx * R, -1 * R)\n        ctx.rotate(pose.thigh)\n        drawPart(thighShape)\n        ctx.translate(0, 5 * R)\n        ctx.rotate(pose.shin)\n        drawPart(shinShape)\n        ctx.restore()\n      })\n\n      // near arm + sword (rigid child of the arm bone)\n      ctx.save()\n      ctx.translate(4 * R, -11 * R)\n      ctx.rotate(pose.armNear)\n      drawPart(armNearShape)\n      ctx.save()\n      ctx.translate(0, 7 * R)\n      drawPart(swordShape)\n      ctx.restore()\n      ctx.restore()\n\n      // head\n      ctx.save()\n      ctx.translate(0, -14 * R)\n      ctx.rotate(pose.headAngle)\n      drawPart(headShape)\n      ctx.restore()\n\n      ctx.restore() // end torso group\n      ctx.restore() // end root\n    }\n\n    d2.raf((t) => {\n      const pose = computePose(t)\n      ctx.clearRect(0, 0, cssW, cssH)\n      drawShadow(pose)\n      drawFigure(pose)\n    })\n\n    d2.on('.action-btn', 'click', () => {\n      if (jumping) return\n      jumping = true\n      jumpStart = performance.now()\n    })\n  },\n}"}}}}}}}},{"_id":"6a46b572db0f6c9b1b2a3696","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"pixel-monkey-static","package":"gustavo-playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{ position:relative; width:100%; height:100%; display:flex; align-items:center; justify-content:center; background:transparent; }\n    canvas.monkey-canvas{ image-rendering: pixelated; image-rendering: crisp-edges; }\n  `,\n  render(d2) {\n    return `<div class=\"wrap\"><canvas class=\"monkey-canvas\" width=\"300\" height=\"300\"></canvas></div>`\n  },\n  mounted(d2, root) {\n    const canvas = d2.$('.monkey-canvas')\n    const ctx = canvas.getContext('2d')\n    const dpr = window.devicePixelRatio || 1\n    const cssW = 300, cssH = 300\n    canvas.width = cssW * dpr\n    canvas.height = cssH * dpr\n    canvas.style.width = cssW + 'px'\n    canvas.style.height = cssH + 'px'\n    ctx.scale(dpr, dpr)\n    ctx.imageSmoothingEnabled = false\n\n    const R = 10\n\n    function ellipseCells(cx, cy, rx, ry) {\n      const cells = []\n      for (let y = Math.floor(cy - ry); y <= Math.ceil(cy + ry); y++) {\n        for (let x = Math.floor(cx - rx); x <= Math.ceil(cx + rx); x++) {\n          const nx = (x - cx) / rx, ny = (y - cy) / ry\n          if (nx * nx + ny * ny <= 1) cells.push([x, y])\n        }\n      }\n      return cells\n    }\n    function tag(cellsArr, color) { return cellsArr.map(([x, y]) => [x, y, color]) }\n    function shaded(cellsArr, baseColor, shadeColor, cx, cy, k) {\n      return cellsArr.map(([x, y]) => [x, y, ((x - cx) + (y - cy) > k) ? shadeColor : baseColor])\n    }\n    function tailCells(path, radii) {\n      const all = []\n      for (let seg = 0; seg < path.length - 1; seg++) {\n        const [x0, y0] = path[seg], [x1, y1] = path[seg + 1]\n        const r0 = radii[seg], r1 = radii[seg + 1]\n        const steps = 6\n        for (let i = 0; i <= steps; i++) {\n          const t = i / steps\n          const cx = x0 + (x1 - x0) * t\n          const cy = y0 + (y1 - y0) * t\n          const r = r0 + (r1 - r0) * t\n          for (const [dx, dy] of ellipseCells(0, 0, r, r)) all.push([Math.round(cx + dx), Math.round(cy + dy)])\n        }\n      }\n      return all\n    }\n\n    const palette = {\n      Y: '#E07B2A',\n      D: '#A34E12',\n      F: '#F7DFB0',\n      K: '#20120A',\n      N: '#20120A',\n      O: '#2A1206',\n    }\n\n    const layers = []\n\n    // tail (behind body), curling from the lower back up and over to the left\n    layers.push(...tag(\n      tailCells(\n        [[19, 21], [22, 18], [23, 13], [20, 7], [15, 3], [9, 2], [5, 4], [3, 8]],\n        [3.2, 3.0, 2.8, 2.6, 2.4, 2.2, 2.0, 1.8]\n      ), 'Y'\n    ))\n\n    // sitting body\n    layers.push(...shaded(ellipseCells(13, 18, 8, 6.5), 'Y', 'D', 13, 18, 4))\n\n    // feet peeking from under the body\n    layers.push(...tag(ellipseCells(8.5, 24, 2.3, 1.7), 'D'))\n    layers.push(...tag(ellipseCells(17, 25, 2.4, 1.8), 'D'))\n\n    // hands resting together in front\n    layers.push(...shaded(ellipseCells(15, 23, 3.4, 2.3), 'Y', 'D', 15, 23, 2))\n\n    // head\n    layers.push(...shaded(ellipseCells(17, 9, 5.3, 5), 'Y', 'D', 17, 9, 3))\n\n    // ears\n    layers.push(...tag(ellipseCells(13, 4.2, 1.7, 1.7), 'Y'))\n    layers.push(...tag(ellipseCells(20.2, 4.0, 1.9, 1.9), 'Y'))\n\n    // muzzle / face patch\n    layers.push(...tag(ellipseCells(19.5, 11, 4.2, 3.6), 'F'))\n\n    // eye\n    layers.push(...tag(ellipseCells(20.3, 8.3, 1.2, 1.5), 'K'))\n\n    // nose\n    layers.push(...tag(ellipseCells(22.5, 11, 0.8, 0.7), 'N'))\n\n    const master = new Map()\n    for (const [x, y, color] of layers) master.set(x + ',' + y, color)\n\n    const outline = new Set()\n    const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]]\n    for (const k of master.keys()) {\n      const [x, y] = k.split(',').map(Number)\n      for (const [dx, dy] of dirs) {\n        const nk = (x + dx) + ',' + (y + dy)\n        if (!master.has(nk)) outline.add(nk)\n      }\n    }\n\n    ctx.fillStyle = palette.O\n    for (const k of outline) {\n      const [x, y] = k.split(',').map(Number)\n      ctx.fillRect(x * R, y * R, R, R)\n    }\n    for (const [k, color] of master) {\n      const [x, y] = k.split(',').map(Number)\n      ctx.fillStyle = palette[color] || '#000'\n      ctx.fillRect(x * R, y * R, R, R)\n    }\n  },\n}"}}}}}}}},{"_id":"6a46bbb05b52bb5fa5328de4","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"pixel-panda-static","package":"gustavo-playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{ position:relative; width:100%; height:100%; display:flex; align-items:center; justify-content:center; background:transparent; }\n    canvas.panda-canvas{ image-rendering: pixelated; image-rendering: crisp-edges; }\n  `,\n  render(d2) {\n    return `<div class=\"wrap\"><canvas class=\"panda-canvas\" width=\"480\" height=\"648\"></canvas></div>`\n  },\n  mounted(d2, root) {\n    const canvas = d2.$('.panda-canvas')\n    const ctx = canvas.getContext('2d')\n    const dpr = window.devicePixelRatio || 1\n    const cssW = 480, cssH = 648\n    canvas.width = cssW * dpr\n    canvas.height = cssH * dpr\n    canvas.style.width = cssW + 'px'\n    canvas.style.height = cssH + 'px'\n    ctx.scale(dpr, dpr)\n    ctx.imageSmoothingEnabled = false\n\n    const R = 8\n\n    // exact palette extracted from the reference photo - unchanged, no new colors\n    const palette = {\n      f: '#FDF4E9', // cream fur (belly)\n      s: '#C4BDB4', // shadow on cream fur\n      y: '#39424B', // dark fur (base tone)\n      d: '#222B33', // darker fur shadow (rim shading + feet)\n      o: '#0C1014', // outline / pupils\n    }\n\n    // head rows (0-31): exact downsampled-from-photo data, unchanged.\n    // body rows (32+): redesigned from scratch around the composition rules that make\n    // the reference images read well as \"panda\" - flat 2-tone color (no noise/texture),\n    // one smooth symmetric silhouette (no lumps), a dominant oversized belly (not a small\n    // oval), frontal/symmetric stance for the standing mascot look, minimal rim shading,\n    // and legs fused-with-a-seam so they read as one connected figure, not floating\n    // circles. Verified via headless dry_render: 100% flood-fill connected, and the\n    // topmost body row is wider than the head's bottom row (no neck pinch).\n    const GRID = [\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"............................................................\",\n      \"...................yoooy............oood....................\",\n      \"..................yodyddy.ddddddd..odyyyd...................\",\n      \".................yoddodddysssssss.yddyyyo...................\",\n      \".................yooooddysfffffffsssydyyo...................\",\n      \".................yoooooysfffffffffffsydd....................\",\n      \"..................dooodsfffffffffffffsyd....................\",\n      \"..................ydodysfffffffffffffffy....................\",\n      \"...................ydyssfffsssffffffsfffo...................\",\n      \"...................yyssffffyyysffffsyyffo...................\",\n      \"..................yoyssffsyyyyysfffsyyyffo..................\",\n      \"..................yyssffsyyyooysfffsddyyfo..................\",\n      \"..................ysssffsyyyooysfffsdoyyfo..................\",\n      \"..................ysssffsyyyddsfffffsdyyfo..................\",\n      \"..................ysssfffsyyysffsyyyssysfo..................\",\n      \"..................yysssfffsssfffsyooyfsffo..................\",\n      \"..................ydyssffffffffffsddsfffsy..................\",\n      \".................yodysssfffffffsssyysfffo...................\",\n      \".................doddysssffffffyddodyffsd...................\",\n      \"................dodddddysssfffffffffffsd....................\",\n      \"................ddddydyyyysssssssssssddd....................\",\n      \"..............ooyyyyyyyyyyyyyyyyyyyyyyyyyyyoo...............\",\n      \".............oyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo..............\",\n      \"............oyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo.............\",\n      \"............oyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo.............\",\n      \"...........oyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyyyyyyyyfyyyyyyyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyyyyfffffffffyyyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyyyfffffffffffyyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyyfffffffffffffyyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyyfffffffffffffffyyyyyyyyyyo............\",\n      \"...........oyyyyyyyyyfffffffffffffffffyyyyyyyyyo............\",\n      \"...........oyyyyyyyyfffffffffffffffffffyyyyyyyyo............\",\n      \"...........oyyyyyyyyfffffffffffffffffffyyyyyyyyo............\",\n      \"...........oyyyyyyyyfffffffffffffffffffyyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"..........oyyyyyyyyfffffffffffffffffffffyyyyyyyyo...........\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyfffffffffffffffffffffffyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oyyyyyyyfffffffffffffffffffffyyyyyyyo............\",\n      \"...........oddddddyfffffffffffffffffffffyddddddo............\",\n      \"...........odddddddyssfffffffffffffffssydddddddo............\",\n      \"...........oddddddddsssfffffffffffffsssddddddddo............\",\n      \"...........oddddddddssysfffffffffffsyssddddddddo............\",\n      \"...........odddddddyyyyyyyfffffffyyyyyyydddddddo............\",\n      \"...........oddddddyyyyyyyyyfffffyyyyyyyyyddddddo............\",\n      \"............oddddyyyyyyyyyyysosyyyyyyyyyyyddddo.............\",\n      \"............odddyyyyyyyyyyyyyoyyyyyyyyyyyyydddo.............\",\n      \".............oddyyyyyyyyyyyyyoyyyyyyyyyyyyyddo..............\",\n      \"..............ooyyyyyyyyyyyyyoyyyyyyyyyyyyyoo...............\",\n      \"...............oyyyyyyyyyyyyyoyyyyyyyyyyyyyo................\",\n      \"..............oyyyyyyyyyyyyyyoyyyyyyyyyyyyyyo...............\",\n      \"...............oyyyyyyyyyyyyyoyyyyyyyyyyyyyo................\",\n      \"...............oyyyyyyyyyyyyyoyyyyyyyyyyyyyo................\",\n      \"...............oyyyyyyyyyyyyyoyyyyyyyyyyyyyo................\",\n      \"...............oddyyyyyyyyyddoddyyyyyyyyyddo................\",\n      \"................oddyyydyyyddo.oddyyydyyyddo.................\",\n      \"................odddddddddddo.odddddddddddo.................\",\n      \"...............odddddddddddddodddddddddddddo................\",\n      \"..............oddddddddddddddoddddddddddddddo...............\",\n      \"...............odddddddddddddodddddddddddddo................\",\n      \"................odddddddddddo.odddddddddddo.................\",\n      \".................ooooodooooo...ooooodooooo..................\",\n      \"............................................................\",\n    ]\n\n    for (let y = 0; y < GRID.length; y++) {\n      const row = GRID[y]\n      for (let x = 0; x < row.length; x++) {\n        const c = row[x]\n        if (c === '.') continue\n        ctx.fillStyle = palette[c] || '#FF00FF'\n        ctx.fillRect(x * R, y * R, R, R)\n      }\n    }\n  },\n}"}}}}}}}},{"_id":"6a47043ad135f62a64ec8d48","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"image-pixelator","package":"gustavo-playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"export default {\n  css: `\n    .wrap{ width:100%; height:100%; box-sizing:border-box; padding:16px; font-family:'Segoe UI', sans-serif; display:flex; flex-direction:column; gap:14px; overflow:auto; }\n    .controls{ display:flex; flex-wrap:wrap; align-items:center; gap:14px; }\n    .file-label{ position:relative; padding:10px 16px; background:#2A2A2A; color:#fff; border-radius:6px; cursor:pointer; font-size:13px; font-weight:600; white-space:nowrap; }\n    .file-label input[type=file]{ position:absolute; inset:0; opacity:0; cursor:pointer; }\n    .row{ display:flex; gap:8px; }\n    .url-input{ width:220px; padding:9px 10px; border:1px solid #ccc; border-radius:6px; font-size:13px; box-sizing:border-box; }\n    .load-btn, .apply-btn{ padding:9px 14px; background:#3B82F6; color:#fff; border:none; border-radius:6px; font-weight:600; cursor:pointer; font-size:13px; }\n    .apply-btn:disabled{ background:#9CB8E8; cursor:not-allowed; }\n    .field{ display:flex; flex-direction:column; gap:2px; font-size:12px; color:#333; }\n    .field input{ width:80px; padding:6px 8px; border:1px solid #ccc; border-radius:6px; font-size:13px; }\n    .status{ font-size:13px; color:#444; }\n    .status.error{ color:#C0392B; }\n    .canvases{ display:flex; flex-wrap:wrap; gap:20px; }\n    .canvas-box{ display:flex; flex-direction:column; gap:6px; align-items:flex-start; }\n    .canvas-box .label{ font-size:11px; text-transform:uppercase; letter-spacing:0.5px; color:#777; font-weight:700; }\n    canvas{ image-rendering: pixelated; image-rendering: crisp-edges; border:1px solid #e0e0e0; background:repeating-conic-gradient(#f2f2f2 0% 25%, #ffffff 0% 50%) 50% / 16px 16px; }\n    .download-link{ align-self:flex-start; padding:9px 16px; background:#16a34a; color:#fff; border-radius:6px; font-weight:600; font-size:13px; text-decoration:none; }\n  `,\n  render(d2) {\n    const st = d2.state\n    const statusText =\n      st.status === 'loading' ? 'Carregando imagem...' :\n      st.status === 'error' ? (st.error || 'Erro ao processar a imagem.') :\n      st.status === 'done' ? `Original ${st.srcW}x${st.srcH} -> grade ${st.cols}x${st.rows}` :\n      'Selecione um arquivo ou cole a URL de uma imagem.'\n    return `\n      <div class=\"wrap\">\n        <div class=\"controls\">\n          <label class=\"file-label\">\n            Escolher imagem\n            <input type=\"file\" class=\"file-input\" accept=\"image/*\" />\n          </label>\n          <div class=\"row\">\n            <input type=\"text\" class=\"url-input\" placeholder=\"ou cole a URL de uma imagem...\" />\n            <button type=\"button\" class=\"load-btn\">Carregar</button>\n          </div>\n          <label class=\"field\">Colunas (largura da grade)\n            <input type=\"number\" class=\"cols-input\" min=\"4\" max=\"200\" value=\"${st.cols}\" />\n          </label>\n          <label class=\"field\">Cores (0 = originais)\n            <input type=\"number\" class=\"colors-input\" min=\"0\" max=\"64\" value=\"${st.colors}\" />\n          </label>\n          <button type=\"button\" class=\"apply-btn\" ${st.hasImage ? '' : 'disabled'}>Pixelar de novo</button>\n        </div>\n        <div class=\"status ${st.status === 'error' ? 'error' : ''}\">${statusText}</div>\n        <div class=\"canvases\">\n          <div class=\"canvas-box\">\n            <div class=\"label\">Original</div>\n            <canvas class=\"orig-canvas\"></canvas>\n          </div>\n          <div class=\"canvas-box\">\n            <div class=\"label\">Pixelado</div>\n            <canvas class=\"pixel-canvas\"></canvas>\n          </div>\n        </div>\n        <a class=\"download-link\" style=\"display:${st.hasImage ? 'inline-block' : 'none'}\" download=\"pixelado.png\">Baixar PNG</a>\n      </div>\n    `\n  },\n  mounted(d2, root) {\n    d2.state.status = 'idle'\n    d2.state.hasImage = false\n    d2.state.cols = 48\n    d2.state.colors = 16\n\n    let currentImg = null\n\n    function setupCanvas(canvas, cssW, cssH) {\n      const dpr = window.devicePixelRatio || 1\n      canvas.width = cssW * dpr\n      canvas.height = cssH * dpr\n      canvas.style.width = cssW + 'px'\n      canvas.style.height = cssH + 'px'\n      const ctx = canvas.getContext('2d')\n      ctx.setTransform(dpr, 0, 0, dpr, 0, 0)\n      return ctx\n    }\n\n    // k-means color quantization - deterministic seeding (no Math.random), few iterations\n    function quantize(imageData, k) {\n      const data = imageData.data\n      const n = data.length / 4\n      if (n === 0 || k <= 0) return imageData\n      let centroids = []\n      for (let c = 0; c < k; c++) {\n        const idx = Math.floor((c / k) * n) * 4\n        centroids.push([data[idx], data[idx + 1], data[idx + 2]])\n      }\n      for (let iter = 0; iter < 6; iter++) {\n        const sums = Array.from({ length: k }, () => [0, 0, 0, 0])\n        for (let i = 0; i < data.length; i += 4) {\n          let best = 0, bestD = Infinity\n          for (let c = 0; c < k; c++) {\n            const dr = data[i] - centroids[c][0], dg = data[i + 1] - centroids[c][1], db = data[i + 2] - centroids[c][2]\n            const d = dr * dr + dg * dg + db * db\n            if (d < bestD) { bestD = d; best = c }\n          }\n          sums[best][0] += data[i]; sums[best][1] += data[i + 1]; sums[best][2] += data[i + 2]; sums[best][3]++\n        }\n        centroids = centroids.map((c, i) => sums[i][3] > 0 ? [sums[i][0] / sums[i][3], sums[i][1] / sums[i][3], sums[i][2] / sums[i][3]] : c)\n      }\n      for (let i = 0; i < data.length; i += 4) {\n        let best = 0, bestD = Infinity\n        for (let c = 0; c < k; c++) {\n          const dr = data[i] - centroids[c][0], dg = data[i + 1] - centroids[c][1], db = data[i + 2] - centroids[c][2]\n          const d = dr * dr + dg * dg + db * db\n          if (d < bestD) { bestD = d; best = c }\n        }\n        data[i] = Math.round(centroids[best][0])\n        data[i + 1] = Math.round(centroids[best][1])\n        data[i + 2] = Math.round(centroids[best][2])\n      }\n      return imageData\n    }\n\n    function drawOriginalPreview(img) {\n      const maxW = 260\n      const scale = Math.min(1, maxW / img.naturalWidth)\n      const w = Math.round(img.naturalWidth * scale)\n      const h = Math.round(img.naturalHeight * scale)\n      const canvas = d2.$('.orig-canvas')\n      const ctx = setupCanvas(canvas, w, h)\n      ctx.imageSmoothingEnabled = true\n      ctx.drawImage(img, 0, 0, w, h)\n    }\n\n    function pixelate() {\n      if (!currentImg) return\n      const colsInput = d2.$('.cols-input')\n      const colorsInput = d2.$('.colors-input')\n      const cols = Math.max(4, Math.min(200, parseInt(colsInput.value, 10) || 48))\n      const colors = Math.max(0, Math.min(64, parseInt(colorsInput.value, 10) || 0))\n      d2.state.cols = cols\n      d2.state.colors = colors\n\n      // aspect ratio comes ONLY from the source image - rows is derived, never picked\n      // independently, so the output can never be stretched/squashed relative to the original\n      const aspect = currentImg.naturalHeight / currentImg.naturalWidth\n      const rows = Math.max(1, Math.round(cols * aspect))\n      d2.state.rows = rows\n\n      const small = document.createElement('canvas')\n      small.width = cols\n      small.height = rows\n      const sctx = small.getContext('2d')\n      sctx.imageSmoothingEnabled = true\n      sctx.drawImage(currentImg, 0, 0, cols, rows)\n\n      if (colors > 0) {\n        let imgData = sctx.getImageData(0, 0, cols, rows)\n        imgData = quantize(imgData, colors)\n        sctx.putImageData(imgData, 0, 0)\n      }\n\n      // upscale with an integer factor derived from the same aspect ratio - still no distortion\n      const maxDisplayW = 380\n      const scale = Math.max(1, Math.floor(maxDisplayW / cols))\n      const dispW = cols * scale\n      const dispH = rows * scale\n      const canvas = d2.$('.pixel-canvas')\n      const ctx = setupCanvas(canvas, dispW, dispH)\n      ctx.imageSmoothingEnabled = false\n      ctx.drawImage(small, 0, 0, dispW, dispH)\n\n      const link = d2.$('.download-link')\n      link.href = canvas.toDataURL('image/png')\n    }\n\n    function loadImage(src) {\n      d2.state.status = 'loading'\n      d2.state.error = null\n      const img = new Image()\n      img.crossOrigin = 'anonymous'\n      img.onload = () => {\n        currentImg = img\n        d2.state.hasImage = true\n        d2.state.srcW = img.naturalWidth\n        d2.state.srcH = img.naturalHeight\n        drawOriginalPreview(img)\n        try {\n          pixelate()\n          d2.state.status = 'done'\n        } catch (err) {\n          d2.state.status = 'error'\n          d2.state.error = 'Nao foi possivel ler os pixels (bloqueio de CORS). Use o seletor de arquivo local ou uma URL com CORS liberado.'\n        }\n      }\n      img.onerror = () => {\n        d2.state.status = 'error'\n        d2.state.error = 'Falha ao carregar a imagem a partir da URL informada.'\n      }\n      img.src = src\n    }\n\n    d2.on('.file-input', 'change', (e, el) => {\n      const file = el.files && el.files[0]\n      if (!file) return\n      const reader = new FileReader()\n      reader.onload = () => loadImage(reader.result)\n      reader.readAsDataURL(file)\n    })\n\n    d2.on('.load-btn', 'click', () => {\n      const url = d2.$('.url-input').value.trim()\n      if (url) loadImage(url)\n    })\n\n    d2.on('.apply-btn', 'click', () => {\n      try {\n        pixelate()\n      } catch (err) {\n        d2.state.status = 'error'\n        d2.state.error = 'Nao foi possivel reprocessar a imagem.'\n      }\n    })\n  },\n}"}}}}}}}},{"_id":"6a473a21ad9ec4c39b05ecff","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"pixelCharacterWalk","package":"gustavo-playground","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    .app{display:flex;flex-direction:column;height:100%;width:100%;box-sizing:border-box;font-family:sans-serif;background:#eef1f5}\n    .bar{display:flex;gap:6px;flex-wrap:wrap;padding:8px;box-sizing:border-box;background:#dfe4ea}\n    .grp{display:flex;gap:4px;align-items:center}\n    .lbl{font-size:11px;color:#6b7280;text-transform:uppercase;letter-spacing:.04em;margin-right:2px}\n    .btn{border:1px solid #c7cdd6;background:#fff;color:#333;font-size:12px;padding:5px 10px;border-radius:6px;cursor:pointer}\n    .btn[data-active]{background:#3b82c4;border-color:#3b82c4;color:#fff}\n    .stage{flex:1;position:relative;min-height:0}\n    .cv{display:block;width:100%;height:100%}\n  `,\n  render(d2) {\n    const view = d2.state.view || 'front'\n    const anim = d2.state.anim || 'idle'\n    const views = [['front', 'Frente'], ['side', 'Lado']]\n    const anims = [['idle', 'Parado'], ['walk', 'Andar'], ['run', 'Correr'], ['jump', 'Pular']]\n    return `<div class=\"app\">\n      <div class=\"bar\">\n        <div class=\"grp\"><span class=\"lbl\">Vista</span>${views.map(([k, l]) =>\n          `<button class=\"btn\" data-view=\"${k}\" ${view === k ? 'data-active' : ''}>${l}</button>`).join('')}</div>\n        <div class=\"grp\"><span class=\"lbl\">Animação</span>${anims.map(([k, l]) =>\n          `<button class=\"btn\" data-anim=\"${k}\" ${anim === k ? 'data-active' : ''}>${l}</button>`).join('')}</div>\n      </div>\n      <div class=\"stage\"><canvas class=\"cv\" data-d2-static></canvas></div>\n    </div>`\n  },\n  mounted(d2, root) {\n    const PALETTE = { k: '#140a16', w: '#ffffff' }\n\n    // Downsampled from a real reference photo (panda mascot), palette-snapped\n    // to k=black fur/outline, w=white fur. The gaming controller the panda\n    // holds in the source photo was masked out before downsampling.\n    const STATIC_BODY = [\n      \"................................\",\n      \"................................\",\n      \"................................\",\n      \"................................\",\n      \"..........kkk...........kkk.....\",\n      \".........kkkkkkkkkkkkkkkkkk.....\",\n      \".........kkkkk..wwwww..kkkk.....\",\n      \".........kkkk.wwwwwwwww.kkk.....\",\n      \".........kk.wwwwwwwwwwwwww......\",\n      \".........k.wwwwwwwwwwwwwwww.....\",\n      \".........k.wwww.k.wwwww..ww.....\",\n      \"..........wwww.kkk.www.kk.w.....\",\n      \"........k.ww.kkk.k.wwwk.kkk.....\",\n      \"........kwww.kkkkk.www.kkkk.....\",\n      \"........k.ww.kkkk.wwwwwkkkk.....\",\n      \"........k.www.....wkkkk.k.......\",\n      \"........k..wwwww..wwkk...ww.....\",\n      \"............wwwwk.ww..wk.ww.....\",\n      \"............wwww.k..kk..www.....\",\n      \"........kkkk.....k..ww.ww.k.....\",\n      \"........kkkk......k.kkk..kk.....\",\n      \".......kkkkkkkk........kkkk.....\",\n      \"......kkkkkkkkkkkkkkkkkkkkk.....\",\n      \".....kkkkkkkkkkkkkkkkkkkkkk.....\",\n      \".....kkkkkkkkkkk...wwww..kk.....\",\n      \".....kkkkkkkkk.wwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \".....kkkkkkk.wwwwwwwwwwwwww.....\",\n      \"......kkkkkk..wwwwwwwwwwwwwk....\",\n      \".......kkkkk...wwwwwwwwww..k....\",\n      \".........kkkk...wwwwwww.........\",\n    ]\n    const LEFT_FOOT = [\".kkkkk......\", \"..kkkkkkkkk.\", \"..kkkkkkkkk.\", \"...kkkkkkkk.\", \"...kkkkkkk..\"]\n    const RIGHT_FOOT = [\"......kk.....\", \"..kkkkkk.....\", \".kkkkkkk.....\", \".kkkkkkk.....\", \"..kkkkkk.....\"]\n\n    const W = 32, H = 44\n    const FEET_GY = 35, LEFT_GX = 8, RIGHT_GX = 19\n\n    // No profile photo of the panda exists, so \"Lado\" reuses the same master\n    // art (only the feet animate) rather than inventing a turned silhouette.\n    const ANIMATIONS = {\n      idle: { stepMs: 450, frames: [{}, { left: { dx: 1 } }, {}, { right: { dx: -1 } }] },\n      walk: { stepMs: 150, frames: [{ left: { dy: -2 } }, {}, { right: { dy: -2 } }, {}] },\n      run: { stepMs: 90, frames: [{ left: { dy: -3, dx: -1 } }, {}, { right: { dy: -3, dx: 1 } }, {}] },\n      jump: { stepMs: 180, frames: [{}, { left: { dy: -4 }, right: { dy: -4 } }, { left: { dy: -2 }, right: { dy: -2 } }] },\n    }\n\n    function composite(animKey, frameIdx) {\n      const anim = ANIMATIONS[animKey]\n      const frame = anim.frames[frameIdx % anim.frames.length]\n      const out = []\n      for (let y = 0; y < H; y++) out.push(new Array(W).fill('.'))\n      const put = (grid, gx, gyBase, dx, dy) => {\n        for (let ry = 0; ry < grid.length; ry++) {\n          const row = grid[ry]\n          for (let rx = 0; rx < row.length; rx++) {\n            const ch = row[rx]\n            if (ch === '.') continue\n            const px = gx + rx + dx, py = gyBase + ry + dy\n            if (px < 0 || px >= W || py < 0 || py >= H) continue\n            out[py][px] = ch\n          }\n        }\n      }\n      const l = frame.left || {}, r = frame.right || {}\n      put(LEFT_FOOT, LEFT_GX, FEET_GY, l.dx || 0, l.dy || 0)\n      put(RIGHT_FOOT, RIGHT_GX, FEET_GY, r.dx || 0, r.dy || 0)\n      put(STATIC_BODY, 0, 0, 0, 0)\n      return out\n    }\n\n    let cv = null, ctx = null\n    function ensureCanvas() {\n      const el = d2.$('.cv')\n      if (el && el !== cv) {\n        cv = el\n        ctx = cv.getContext('2d')\n        cv.width = 0\n      }\n      return cv\n    }\n    function size() {\n      if (!ensureCanvas()) return\n      const dpr = window.devicePixelRatio || 1\n      cv.width = Math.round(cv.clientWidth * dpr)\n      cv.height = Math.round(cv.clientHeight * dpr)\n      ctx.setTransform(1, 0, 0, 1, 0, 0)\n      ctx.scale(dpr, dpr)\n    }\n    size()\n    if (typeof ResizeObserver !== 'undefined') {\n      const ro = new ResizeObserver(size); ro.observe(root); d2.onUnmount(() => ro.disconnect())\n    }\n\n    d2.on('[data-view]', 'click', (e, el) => { d2.state.view = el.dataset.view })\n    d2.on('[data-anim]', 'click', (e, el) => { d2.state.anim = el.dataset.anim })\n\n    let frameIdx = 0, acc = 0, prev = null, lastKey = null\n\n    d2.raf((t) => {\n      if (!ensureCanvas()) return\n      const dpr = window.devicePixelRatio || 1\n      const wantW = Math.round(cv.clientWidth * dpr)\n      const wantH = Math.round(cv.clientHeight * dpr)\n      if (cv.width !== wantW || cv.height !== wantH) size()\n\n      if (prev == null) prev = t\n      const dt = Math.min(t - prev, 1000 / 15)\n      prev = t\n\n      const view = d2.state.view || 'front'\n      const animKey = d2.state.anim || 'idle'\n      const key = view + ':' + animKey\n      if (key !== lastKey) { lastKey = key; frameIdx = 0; acc = 0 }\n\n      const stepMs = ANIMATIONS[animKey].stepMs\n      acc += dt\n      if (acc >= stepMs) { acc = 0; frameIdx = frameIdx + 1 }\n\n      const grid = composite(animKey, frameIdx)\n      const w = cv.clientWidth, h = cv.clientHeight\n      const scale = Math.max(1, Math.floor(Math.min(w / W, h / H)))\n      ctx.imageSmoothingEnabled = false\n      ctx.clearRect(0, 0, w, h)\n      const offX = Math.floor((w - W * scale) / 2)\n      const offY = Math.floor((h - H * scale) / 2)\n      for (let y = 0; y < H; y++) {\n        for (let x = 0; x < W; x++) {\n          const ch = grid[y][x]\n          if (ch === '.') continue\n          ctx.fillStyle = PALETTE[ch]\n          ctx.fillRect(offX + x * scale, offY + y * scale, scale, scale)\n        }\n      }\n    })\n  },\n}\n"}}}}}}}},{"_id":"6a4dbb0fcbfceedf15eaf843","component":"data2-core-types@Component","system":"684302e0f220c5f37f7a25d1","params":{"extends":"data2-core-primitives@Block","component":"mtgMonolitoApp","package":"gustavo-playground","definition":{"preventEvaluation":true,"component":"data2-core-types@Definition","params":{"def":{"component":"data2-code-components@CodeHost","params":{"code":{"component":"data2-core-primitives@String","params":{"content":"let commanderSearchTimer = null\nlet commanderSearchSeq = 0\nlet cardSearchTimer = null\nlet cardSearchSeq = 0\n\nconst WUBRG = ['W', 'U', 'B', 'R', 'G']\nconst COLOR_HEX = { W: '#f8f6d8', U: '#0e68ab', B: '#150b00', R: '#d3202a', G: '#00733e' }\nconst COLOR_LABEL = { W: 'Branco', U: 'Azul', B: 'Preto', R: 'Vermelho', G: 'Verde', C: 'Incolor' }\nconst BRACKET_LABELS = { 1: 'Exhibition', 2: 'Core', 3: 'Upgraded', 4: 'Optimized', 5: 'cEDH' }\nconst BRACKET_COLORS = { 1: '#16a34a', 2: '#2563eb', 3: '#7c3aed', 4: '#ea580c', 5: '#dc2626' }\nconst SORT_LABELS = { name: 'nome', color: 'cor', bracket: 'bracket', updated: 'atualização' }\nconst TYPE_ORDER = ['Creatures', 'Planeswalkers', 'Battles', 'Instants', 'Sorceries', 'Artifacts', 'Enchantments', 'Lands', 'Outros']\nconst COLOR_GROUP_ORDER = ['Branco', 'Azul', 'Preto', 'Vermelho', 'Verde', 'Multicolor', 'Incolor']\n\nfunction esc(s) {\n  return String(s == null ? '' : s).replace(/[&<>\"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]))\n}\n\nfunction colorPips(colors) {\n  if (!colors || !colors.length) return `<span class=\"pip pip-c\" title=\"Incolor\">C</span>`\n  return WUBRG.filter(c => colors.indexOf(c) !== -1)\n    .map(c => `<span class=\"pip\" style=\"background:${COLOR_HEX[c]}\" title=\"${COLOR_LABEL[c]}\"></span>`).join('')\n}\n\nfunction bracketBadge(bracket) {\n  if (!bracket) return ''\n  const label = BRACKET_LABELS[bracket] || ''\n  const color = BRACKET_COLORS[bracket] || '#6b7280'\n  return `<span class=\"bracket-badge\" style=\"background:${color}22;color:${color}\">Bracket ${bracket} · ${label}</span>`\n}\n\nfunction relativeTime(iso) {\n  if (!iso) return '—'\n  const then = new Date(iso).getTime()\n  if (isNaN(then)) return '—'\n  const diffMs = Date.now() - then\n  const min = Math.floor(diffMs / 60000)\n  if (min < 1) return 'agora mesmo'\n  if (min < 60) return `há ${min} min`\n  const hr = Math.floor(min / 60)\n  if (hr < 24) return `há ${hr}h`\n  const day = Math.floor(hr / 24)\n  if (day === 1) return 'há 1 dia'\n  if (day < 7) return `há ${day} dias`\n  const week = Math.floor(day / 7)\n  if (day < 30) return week === 1 ? 'há 1 semana' : `há ${week} semanas`\n  const month = Math.floor(day / 30)\n  if (day < 365) return month === 1 ? 'há 1 mês' : `há ${month} meses`\n  const year = Math.floor(day / 365)\n  return year === 1 ? 'há 1 ano' : `há ${year} anos`\n}\n\nfunction iconStar(filled) {\n  const color = filled ? '#f59e0b' : '#c1c7d0'\n  return `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"${filled ? color : 'none'}\" stroke=\"${color}\" stroke-width=\"2\" stroke-linejoin=\"round\"><polygon points=\"12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2\"/></svg>`\n}\n\nfunction iconGrid() {\n  return `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"/><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"/><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"/><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"/></svg>`\n}\n\nfunction iconList() {\n  return `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"><line x1=\"4\" y1=\"6\" x2=\"20\" y2=\"6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/><line x1=\"4\" y1=\"18\" x2=\"20\" y2=\"18\"/></svg>`\n}\n\nfunction iconFilter() {\n  return `<svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"4 4 20 4 14 12 14 19 10 21 10 12 4 4\"/></svg>`\n}\n\nfunction sortCaretIcon(column, sortBy, sortDir) {\n  const active = sortBy === column\n  const upColor = active && sortDir === 'asc' ? '#333333' : '#c1c7d0'\n  const downColor = active && sortDir === 'desc' ? '#333333' : '#c1c7d0'\n  return `<svg class=\"sort-caret-icon\" width=\"9\" height=\"11\" viewBox=\"0 0 10 12\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M5 0L9 4.5H1z\" fill=\"${upColor}\"/><path d=\"M5 12L1 7.5H9z\" fill=\"${downColor}\"/></svg>`\n}\n\nfunction openDeck(d2, id) {\n  const deck = d2.data('decks').records().find(x => x._id === id)\n  d2.state.currentDeckId = id\n  d2.state.currentDeckName = deck ? deck.name : ''\n  d2.state.previewCard = null\n  d2.state.showMoreMenu = false\n  d2.state.showExportModal = false\n  d2.state.showImportModal = false\n  d2.state.cardSearchResults = []\n  d2.state.screen = 'detail'\n}\n\nfunction activeFilterCount(d2) {\n  let n = 0\n  if (d2.state.search) n++\n  if ((d2.state.filterColors || []).length) n++\n  if (d2.state.filterBracket) n++\n  if (d2.state.filterCommander) n++\n  return n\n}\n\nfunction filteredSortedDecks(d2, decks, cardRows) {\n  const search = (d2.state.search || '').trim()\n  let regex = null\n  if (search) {\n    try { regex = new RegExp(search, 'i') } catch (e) { regex = null }\n  }\n\n  const filterColors = d2.state.filterColors || []\n  const filterBracket = d2.state.filterBracket\n  const filterCommander = (d2.state.filterCommander || '').trim().toLowerCase()\n\n  let rows = decks.map(r => {\n    const cards = cardRows.filter(c => c.deckId === r._id)\n    const colors = r.colorIdentity || []\n    const total = cards.reduce((s, c) => s + (Number(c.quantity) || 0), 0)\n    const fallbackCover = (cards.find(c => c.imageUrl) || {}).imageUrl\n    const cover = r.commanderImageUrl || fallbackCover\n    return { deck: r, cards, colors, total, cover }\n  })\n\n  if (regex) {\n    rows = rows.filter(x => regex.test(x.deck.name))\n  } else if (search) {\n    const needle = search.toLowerCase()\n    rows = rows.filter(x => x.deck.name.toLowerCase().indexOf(needle) !== -1)\n  }\n\n  if (filterColors.length) {\n    const wubrgSelected = filterColors.filter(c => c !== 'C')\n    const wantColorless = filterColors.indexOf('C') !== -1\n    rows = rows.filter(x => {\n      if (wubrgSelected.length) return wubrgSelected.every(c => x.colors.indexOf(c) !== -1)\n      if (wantColorless) return x.colors.length === 0\n      return true\n    })\n  }\n\n  if (filterBracket) {\n    rows = rows.filter(x => String(x.deck.bracket || '') === String(filterBracket))\n  }\n\n  if (filterCommander) {\n    rows = rows.filter(x => (x.deck.commanderName || '').toLowerCase().indexOf(filterCommander) !== -1)\n  }\n\n  const sortBy = d2.state.sortBy || 'name'\n  const dir = d2.state.sortDir === 'desc' ? -1 : 1\n\n  rows.sort((a, b) => {\n    if (sortBy === 'color') {\n      const diff = (a.colors.length - b.colors.length) * dir\n      if (diff !== 0) return diff\n      return a.deck.name.localeCompare(b.deck.name)\n    }\n    if (sortBy === 'bracket') {\n      const diff = ((a.deck.bracket || 0) - (b.deck.bracket || 0)) * dir\n      if (diff !== 0) return diff\n      return a.deck.name.localeCompare(b.deck.name)\n    }\n    if (sortBy === 'updated') {\n      const at = a.deck.updatedAt ? new Date(a.deck.updatedAt).getTime() : 0\n      const bt = b.deck.updatedAt ? new Date(b.deck.updatedAt).getTime() : 0\n      const diff = (at - bt) * dir\n      if (diff !== 0) return diff\n      return a.deck.name.localeCompare(b.deck.name)\n    }\n    return a.deck.name.localeCompare(b.deck.name) * dir\n  })\n\n  return rows\n}\n\nfunction buildResultsSummary(d2, total, shown) {\n  const parts = []\n  if (d2.state.search) parts.push(`nome contendo \"${d2.state.search}\"`)\n  if (d2.state.filterCommander) parts.push(`commander contendo \"${d2.state.filterCommander}\"`)\n  if (d2.state.filterBracket) parts.push(`bracket ${d2.state.filterBracket}`)\n  if ((d2.state.filterColors || []).length) parts.push(`cor ${d2.state.filterColors.join('/')}`)\n  const sortBy = d2.state.sortBy || 'name'\n  const dirLabel = (d2.state.sortDir || 'asc') === 'asc' ? 'crescente' : 'decrescente'\n  let text = `Mostrando ${shown} de ${total} deck${total === 1 ? '' : 's'}`\n  if (parts.length) text += ` com ${parts.join(', ')}`\n  text += `, ordenado por ${SORT_LABELS[sortBy] || 'nome'} em ordem ${dirLabel}`\n  return text\n}\n\n// ---- MTG type/color/subtype grouping helpers ----\nfunction cardMainType(typeLine) {\n  const t = typeLine || ''\n  if (t.indexOf('Creature') !== -1) return 'Creatures'\n  if (t.indexOf('Planeswalker') !== -1) return 'Planeswalkers'\n  if (t.indexOf('Battle') !== -1) return 'Battles'\n  if (t.indexOf('Instant') !== -1) return 'Instants'\n  if (t.indexOf('Sorcery') !== -1) return 'Sorceries'\n  if (t.indexOf('Artifact') !== -1) return 'Artifacts'\n  if (t.indexOf('Enchantment') !== -1) return 'Enchantments'\n  if (t.indexOf('Land') !== -1) return 'Lands'\n  return 'Outros'\n}\n\nfunction cardColorGroup(colors) {\n  const cs = colors || []\n  if (cs.length === 0) return 'Incolor'\n  if (cs.length > 1) return 'Multicolor'\n  return COLOR_LABEL[cs[0]] || 'Outros'\n}\n\nfunction cardSubtypes(typeLine) {\n  const t = typeLine || ''\n  const idx = t.indexOf('—')\n  if (idx === -1) return ['Outros']\n  const sub = t.slice(idx + 1).trim()\n  const tokens = sub.split(/\\s+/).filter(Boolean)\n  return tokens.length ? tokens : ['Outros']\n}\n\nfunction groupCards(cards, groupBy) {\n  const groups = {}\n  cards.forEach(c => {\n    let keys\n    if (groupBy === 'color') keys = [cardColorGroup(c.colors)]\n    else if (groupBy === 'subtype') keys = cardSubtypes(c.typeLine)\n    else keys = [cardMainType(c.typeLine)]\n    keys.forEach(k => {\n      if (!groups[k]) groups[k] = []\n      groups[k].push(c)\n    })\n  })\n  return groups\n}\n\nfunction orderedGroupKeys(groups, groupBy) {\n  if (groupBy === 'type') return TYPE_ORDER.filter(k => groups[k])\n  if (groupBy === 'color') return COLOR_GROUP_ORDER.filter(k => groups[k])\n  return Object.keys(groups).sort()\n}\n\nfunction sortCards(cards, sortBy, sortDir) {\n  const dir = sortDir === 'desc' ? -1 : 1\n  return cards.slice().sort((a, b) => {\n    if (sortBy === 'cmc') {\n      const diff = ((a.cmc || 0) - (b.cmc || 0)) * dir\n      if (diff !== 0) return diff\n      return String(a.cardName).localeCompare(String(b.cardName))\n    }\n    return String(a.cardName).localeCompare(String(b.cardName)) * dir\n  })\n}\n\n// ---- List screen ----\nfunction renderSortMenu(sortBy, sortDir) {\n  const opts = [['name', 'Nome'], ['color', 'Cor'], ['bracket', 'Bracket'], ['updated', 'Atualizado']]\n  return `\n    <div class=\"dropdown-menu sort-menu\">\n      <div class=\"sort-section-label\">Ordenar por</div>\n      ${opts.map(([v, label]) => `<label class=\"sort-option\"><input type=\"radio\" name=\"sortBy\" class=\"sort-by-radio\" value=\"${v}\" ${sortBy === v ? 'checked' : ''} /> ${label}</label>`).join('')}\n      <div class=\"sort-section-label\">Direção</div>\n      <label class=\"sort-option\"><input type=\"radio\" name=\"sortDir\" class=\"sort-dir-radio\" value=\"asc\" ${sortDir === 'asc' ? 'checked' : ''} /> Crescente</label>\n      <label class=\"sort-option\"><input type=\"radio\" name=\"sortDir\" class=\"sort-dir-radio\" value=\"desc\" ${sortDir === 'desc' ? 'checked' : ''} /> Decrescente</label>\n    </div>\n  `\n}\n\nfunction renderListTopbar(d2) {\n  const sortBy = d2.state.sortBy || 'name'\n  const sortDir = d2.state.sortDir || 'asc'\n  const viewMode = d2.state.viewMode || 'grid'\n  const filterCount = activeFilterCount(d2)\n\n  return `\n    <div class=\"list-topbar\">\n      <div class=\"topbar-row1\">\n        <h1>Meus Decks</h1>\n      </div>\n      <div class=\"topbar-row2\">\n        <button class=\"new-btn\">+ Novo Deck</button>\n        <div class=\"topbar-actions\">\n          <input class=\"search-input\" placeholder=\"Pesquisar decks...\" value=\"${esc(d2.state.search || '')}\" />\n          <button class=\"filter-btn ${filterCount ? 'active' : ''}\">Filtros${filterCount ? ` (${filterCount})` : ''}</button>\n          <div class=\"dropdown-wrap\">\n            <button class=\"sort-btn\"><span class=\"sort-icon\">${sortDir === 'asc' ? '↑' : '↓'}</span> Ordenar</button>\n            ${d2.state.showSortMenu ? renderSortMenu(sortBy, sortDir) : ''}\n          </div>\n          <div class=\"view-toggle\">\n            <button class=\"view-btn ${viewMode === 'grid' ? 'active' : ''}\" data-view=\"grid\" title=\"Grade\">${iconGrid()}</button>\n            <button class=\"view-btn ${viewMode === 'list' ? 'active' : ''}\" data-view=\"list\" title=\"Lista\">${iconList()}</button>\n          </div>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderGridDecks(rows) {\n  return `\n    <div class=\"deck-grid\">\n      ${rows.map(x => `\n        <div class=\"deck-card\" data-id=\"${x.deck._id}\" data-key=\"${x.deck._id}\">\n          <button class=\"fav-btn\" data-id=\"${x.deck._id}\" title=\"Favoritar\">${iconStar(x.deck.favorite)}</button>\n          ${x.cover ? `<img class=\"deck-cover\" src=\"${x.cover}\" alt=\"\" />` : `<div class=\"deck-cover placeholder\"></div>`}\n          <div class=\"deck-card-name\">${esc(x.deck.name)}</div>\n          <div class=\"deck-card-meta\">\n            <span class=\"pip-row\">${colorPips(x.colors)}</span>\n            <span class=\"muted\">${x.total} carta${x.total === 1 ? '' : 's'}</span>\n          </div>\n          ${x.deck.bracket ? `<div class=\"deck-card-bracket\">${bracketBadge(x.deck.bracket)}</div>` : ''}\n        </div>\n      `).join('')}\n    </div>\n  `\n}\n\nfunction renderTableDecks(rows, sortBy, sortDir) {\n  return `\n    <table class=\"deck-table\">\n      <thead>\n        <tr>\n          <th></th>\n          <th class=\"sortable-th\" data-sort=\"name\">Nome ${sortCaretIcon('name', sortBy, sortDir)}</th>\n          <th class=\"sortable-th\" data-sort=\"color\">Cores ${sortCaretIcon('color', sortBy, sortDir)}</th>\n          <th class=\"sortable-th\" data-sort=\"bracket\">Bracket ${sortCaretIcon('bracket', sortBy, sortDir)}</th>\n          <th class=\"sortable-th\" data-sort=\"updated\">Atualizado ${sortCaretIcon('updated', sortBy, sortDir)}</th>\n        </tr>\n      </thead>\n      <tbody>\n        ${rows.map(x => `\n          <tr class=\"deck-row\" data-id=\"${x.deck._id}\" data-key=\"${x.deck._id}\">\n            <td><button class=\"fav-btn\" data-id=\"${x.deck._id}\" title=\"Favoritar\">${iconStar(x.deck.favorite)}</button></td>\n            <td class=\"deck-row-name\">${esc(x.deck.name)}</td>\n            <td><span class=\"pip-row\">${colorPips(x.colors)}</span></td>\n            <td>${bracketBadge(x.deck.bracket)}</td>\n            <td class=\"muted\">${relativeTime(x.deck.updatedAt)}</td>\n          </tr>\n        `).join('')}\n      </tbody>\n    </table>\n  `\n}\n\nfunction renderNewDeckModal(d2) {\n  const query = d2.state.commanderQuery || ''\n  const results = d2.state.commanderResults || []\n  const selected = d2.state.selectedCommander\n  const name = d2.state.newDeckNameDraft || ''\n  const canCreate = !!(name.trim() && selected) && !d2.state.modalCreating\n\n  return `\n    <div class=\"modal-overlay\">\n      <div class=\"modal\">\n        <div class=\"modal-header\">\n          <h2>Novo Deck</h2>\n          <button class=\"modal-close\">&times;</button>\n        </div>\n        <div class=\"modal-body\">\n          <div>\n            <div class=\"field-label\">Nome</div>\n            <input class=\"modal-deck-name\" placeholder=\"Nome do deck\" value=\"${esc(name)}\" />\n          </div>\n          <div>\n            <div class=\"field-label\">Commander</div>\n            ${selected ? `\n              <div class=\"selected-commander\">\n                ${selected.image ? `<img src=\"${selected.image}\" alt=\"\" />` : ''}\n                <span>${esc(selected.name)}</span>\n                <button class=\"clear-commander-btn\" title=\"Remover\">&times;</button>\n              </div>\n            ` : `\n              <div class=\"commander-search-wrap\">\n                <input class=\"modal-commander-input\" placeholder=\"Buscar commander...\" autocomplete=\"off\" value=\"${esc(query)}\" />\n                ${results.length ? `\n                  <div class=\"commander-results\">\n                    ${results.map((c, i) => `\n                      <div class=\"commander-result\" data-idx=\"${i}\">\n                        ${c.thumb ? `<img src=\"${c.thumb}\" alt=\"\" />` : ''}\n                        <span>${esc(c.name)}</span>\n                      </div>\n                    `).join('')}\n                  </div>\n                ` : ''}\n              </div>\n            `}\n          </div>\n          ${d2.state.modalError ? `<p class=\"modal-error\">${esc(d2.state.modalError)}</p>` : ''}\n        </div>\n        <div class=\"modal-footer\">\n          <button class=\"modal-cancel-btn\">Cancelar</button>\n          <button class=\"modal-create-btn\" ${canCreate ? '' : 'disabled'}>${d2.state.modalCreating ? 'Criando...' : 'Criar Deck'}</button>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderFilterModal(d2) {\n  const draft = d2.state.filterDraft || {}\n  const colors = draft.colors || []\n  const opts = WUBRG.concat(['C'])\n\n  return `\n    <div class=\"modal-overlay filter-modal-overlay\">\n      <div class=\"modal filter-modal\">\n        <div class=\"modal-header\">\n          <h2>Filtros</h2>\n          <button class=\"modal-close filter-modal-close\">&times;</button>\n        </div>\n        <div class=\"modal-body\">\n          <div class=\"form-row\">\n            <div class=\"field-label\">Nome do Deck</div>\n            <input class=\"filter-name-input\" placeholder=\"Nome do deck\" value=\"${esc(draft.name || '')}\" />\n          </div>\n          <div class=\"form-row\">\n            <div class=\"field-label\">Bracket</div>\n            <select class=\"filter-bracket-select\">\n              <option value=\"\">Qualquer Bracket</option>\n              <option value=\"1\" ${draft.bracket === '1' ? 'selected' : ''}>Bracket 1 — Exhibition</option>\n              <option value=\"2\" ${draft.bracket === '2' ? 'selected' : ''}>Bracket 2 — Core</option>\n              <option value=\"3\" ${draft.bracket === '3' ? 'selected' : ''}>Bracket 3 — Upgraded</option>\n              <option value=\"4\" ${draft.bracket === '4' ? 'selected' : ''}>Bracket 4 — Optimized</option>\n              <option value=\"5\" ${draft.bracket === '5' ? 'selected' : ''}>Bracket 5 — cEDH</option>\n            </select>\n          </div>\n          <div class=\"form-row\">\n            <div class=\"field-label\">Cores</div>\n            <div class=\"color-swatch-row\">\n              ${opts.map(c => `\n                <div class=\"color-swatch-item\">\n                  <button type=\"button\" class=\"color-swatch ${colors.indexOf(c) !== -1 ? 'selected' : ''}\" data-color=\"${c}\" style=\"background:${c !== 'C' ? COLOR_HEX[c] : '#e5e7eb'}\"></button>\n                  <span class=\"color-swatch-label\">${COLOR_LABEL[c]}</span>\n                </div>\n              `).join('')}\n            </div>\n          </div>\n          <div class=\"form-row\">\n            <div class=\"field-label\">Commander</div>\n            <input class=\"filter-commander-input\" placeholder=\"Nome do commander\" value=\"${esc(draft.commander || '')}\" />\n          </div>\n        </div>\n        <div class=\"modal-footer\">\n          <button class=\"filter-reset-btn\">Reset</button>\n          <button class=\"filter-save-btn\">Salvar Filtros</button>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderList(d2) {\n  const decks = d2.data('decks')\n  if (decks.loading) return `<div class=\"page\"><p class=\"muted\">Carregando...</p></div>`\n\n  const allDecks = decks.records()\n  const rows = filteredSortedDecks(d2, allDecks, d2.data('cards').records())\n  const viewMode = d2.state.viewMode || 'grid'\n  const sortBy = d2.state.sortBy || 'name'\n  const sortDir = d2.state.sortDir || 'asc'\n\n  let body\n  if (allDecks.length === 0) {\n    body = `<p class=\"muted\">Nenhum deck ainda. Clique em \"+ Novo Deck\" pra criar o primeiro.</p>`\n  } else {\n    body = `\n      <hr class=\"list-divider\" />\n      <div class=\"results-summary\">\n        <span class=\"results-summary-icon\">${iconFilter()}</span>\n        ${esc(buildResultsSummary(d2, allDecks.length, rows.length))}.\n        ${activeFilterCount(d2) ? `<button class=\"reset-filters-btn\">Reset Filters</button>` : ''}\n      </div>\n      ${rows.length === 0 ? `<p class=\"muted\">Nenhum deck encontrado.</p>` : (viewMode === 'grid' ? renderGridDecks(rows) : renderTableDecks(rows, sortBy, sortDir))}\n    `\n  }\n\n  return `\n    <div class=\"page\">\n      <div class=\"page-inner\">\n        ${renderListTopbar(d2)}\n        ${body}\n      </div>\n    </div>\n    ${d2.state.showNewDeckModal ? renderNewDeckModal(d2) : ''}\n    ${d2.state.showFilterModal ? renderFilterModal(d2) : ''}\n  `\n}\n\n// ---- Detail screen ----\nfunction renderMoreMenu() {\n  return `\n    <div class=\"dropdown-menu more-menu\">\n      <button class=\"more-import-btn\">Import</button>\n      <button class=\"more-export-btn\">Export</button>\n      <button class=\"more-duplicate-btn\">Duplicar</button>\n      <button class=\"more-delete-btn danger\">Deletar</button>\n    </div>\n  `\n}\n\nfunction renderPreviewPanel(preview) {\n  const purchase = preview.purchase || {}\n  return `\n    <div class=\"preview-panel\">\n      ${preview.image\n        ? `<img src=\"${preview.image}\" alt=\"${esc(preview.name || '')}\" />`\n        : `<div class=\"preview-placeholder\">${esc(preview.name || 'Sem imagem')}</div>`}\n      ${preview.price ? `<div class=\"preview-price\">$${esc(preview.price)}</div>` : ''}\n      ${(purchase.tcgplayer || purchase.cardmarket) ? `\n        <div class=\"preview-buy-links\">\n          ${purchase.tcgplayer ? `<a class=\"buy-link\" href=\"${esc(purchase.tcgplayer)}\" target=\"_blank\" rel=\"noopener\">Comprar @ TCGplayer</a>` : ''}\n          ${purchase.cardmarket ? `<a class=\"buy-link\" href=\"${esc(purchase.cardmarket)}\" target=\"_blank\" rel=\"noopener\">Comprar @ Cardmarket</a>` : ''}\n        </div>\n      ` : ''}\n    </div>\n  `\n}\n\nfunction renderDeckToolbar(d2) {\n  const view = d2.state.deckView || 'text'\n  const groupBy = d2.state.deckGroupBy || 'type'\n  const sortBy = d2.state.deckSortBy || 'name'\n  const sortDir = d2.state.deckSortDir || 'asc'\n  const results = d2.state.cardSearchResults || []\n\n  return `\n    <div class=\"deck-toolbar\">\n      <div class=\"card-search-wrap\">\n        <input class=\"card-search-input\" placeholder=\"Buscar carta pra adicionar...\" autocomplete=\"off\" />\n        ${results.length ? `\n          <div class=\"commander-results card-search-results\">\n            ${results.map((c, i) => `\n              <div class=\"commander-result card-search-result\" data-idx=\"${i}\">\n                ${c.thumb ? `<img src=\"${c.thumb}\" alt=\"\" />` : ''}\n                <span>${esc(c.name)}</span>\n              </div>\n            `).join('')}\n          </div>\n        ` : ''}\n      </div>\n      <div class=\"deck-toolbar-controls\">\n        <label class=\"toolbar-select-label\">View\n          <select class=\"view-select\">\n            <option value=\"text\" ${view === 'text' ? 'selected' : ''}>Text</option>\n            <option value=\"grid\" ${view === 'grid' ? 'selected' : ''}>Visual Grid</option>\n            <option value=\"spoiler\" ${view === 'spoiler' ? 'selected' : ''}>Visual Spoiler</option>\n          </select>\n        </label>\n        <label class=\"toolbar-select-label\">Group\n          <select class=\"group-select\">\n            <option value=\"type\" ${groupBy === 'type' ? 'selected' : ''}>Tipo</option>\n            <option value=\"color\" ${groupBy === 'color' ? 'selected' : ''}>Cor</option>\n            <option value=\"subtype\" ${groupBy === 'subtype' ? 'selected' : ''}>Subtipo</option>\n          </select>\n        </label>\n        <label class=\"toolbar-select-label\">Sort\n          <select class=\"deck-sort-select\">\n            <option value=\"name\" ${sortBy === 'name' ? 'selected' : ''}>Nome</option>\n            <option value=\"cmc\" ${sortBy === 'cmc' ? 'selected' : ''}>Mana Value</option>\n          </select>\n        </label>\n        <button class=\"deck-sort-dir-btn\" title=\"Direção\">${sortDir === 'asc' ? '↑' : '↓'}</button>\n      </div>\n    </div>\n  `\n}\n\nfunction cardDataAttrs(c) {\n  return `data-key=\"${c._id}\" data-name=\"${esc(c.cardName)}\" data-image=\"${esc(c.imageUrl || '')}\" data-price=\"${esc(c.priceUsd || '')}\" data-purchase=\"${esc(JSON.stringify(c.purchaseUris || {}))}\"`\n}\n\nfunction renderTextView(groups, orderedKeys) {\n  return `\n    <div class=\"text-view\">\n      ${orderedKeys.map(k => `\n        <div class=\"group-section\">\n          <div class=\"group-heading\">${esc(k)} (${groups[k].reduce((s, c) => s + (Number(c.quantity) || 0), 0)})</div>\n          ${groups[k].map(c => `\n            <div class=\"text-row\" ${cardDataAttrs(c)}>\n              <span>${c.quantity} ${esc(c.cardName)}</span>\n              <button class=\"remove-card-btn\" data-id=\"${c._id}\" title=\"Remover\">&times;</button>\n            </div>\n          `).join('')}\n        </div>\n      `).join('')}\n    </div>\n  `\n}\n\nfunction renderTileView(groups, orderedKeys, big) {\n  return `\n    <div class=\"${big ? 'spoiler-view' : 'tile-view'}\">\n      ${orderedKeys.map(k => `\n        <div class=\"group-section\">\n          <div class=\"group-heading\">${esc(k)} (${groups[k].reduce((s, c) => s + (Number(c.quantity) || 0), 0)})</div>\n          <div class=\"${big ? 'spoiler-grid' : 'card-grid'}\">\n            ${groups[k].map(c => `\n              <div class=\"card-item\" ${cardDataAttrs(c)}>\n                ${c.imageUrl\n                  ? `<img src=\"${c.imageUrl}\" alt=\"${esc(c.cardName)}\" />`\n                  : `<div class=\"card-placeholder\">${esc(c.cardName)}</div>`}\n                <div class=\"card-qty\">x${c.quantity}</div>\n                <button class=\"remove-card-btn overlay\" data-id=\"${c._id}\" title=\"Remover\">&times;</button>\n              </div>\n            `).join('')}\n          </div>\n        </div>\n      `).join('')}\n    </div>\n  `\n}\n\nfunction renderFanView(groups, orderedKeys) {\n  return `\n    <div class=\"fan-container\">\n      ${orderedKeys.map(k => `\n        <div class=\"group-section\">\n          <div class=\"group-heading\">${esc(k)} (${groups[k].reduce((s, c) => s + (Number(c.quantity) || 0), 0)})</div>\n          <div class=\"fan-row\">\n            ${groups[k].map((c, i) => `\n              <div class=\"card-item fan-card\" style=\"z-index:${i}\" ${cardDataAttrs(c)}>\n                ${c.imageUrl\n                  ? `<img src=\"${c.imageUrl}\" alt=\"${esc(c.cardName)}\" />`\n                  : `<div class=\"card-placeholder\">${esc(c.cardName)}</div>`}\n                <div class=\"card-qty\">x${c.quantity}</div>\n                <button class=\"remove-card-btn overlay\" data-id=\"${c._id}\" title=\"Remover\">&times;</button>\n              </div>\n            `).join('')}\n          </div>\n        </div>\n      `).join('')}\n    </div>\n  `\n}\n\nfunction renderExportModal(deck, cards) {\n  const lines = cards.map(c => `${c.quantity} ${c.cardName}`).join('\\n')\n  const text = (deck.commanderName ? `1 ${deck.commanderName}\\n\\n` : '') + lines\n  return `\n    <div class=\"modal-overlay export-modal-overlay\">\n      <div class=\"modal export-modal\">\n        <div class=\"modal-header\"><h2>Exportar Deck</h2><button class=\"modal-close export-modal-close\">&times;</button></div>\n        <div class=\"modal-body\">\n          <textarea class=\"export-textarea\" readonly>${esc(text)}</textarea>\n        </div>\n        <div class=\"modal-footer\">\n          <button class=\"modal-cancel-btn export-modal-close\">Fechar</button>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderImportModal(d2) {\n  return `\n    <div class=\"modal-overlay import-modal-overlay\">\n      <div class=\"modal import-modal\">\n        <div class=\"modal-header\"><h2>Importar Cartas</h2><button class=\"modal-close import-modal-close\">&times;</button></div>\n        <div class=\"modal-body\">\n          <textarea class=\"import-modal-text\" placeholder=\"Cole as cartas, ex:&#10;4 Lightning Bolt&#10;2 Counterspell\" ${d2.state.importingCards ? 'disabled' : ''}></textarea>\n          ${d2.state.importModalStatus ? `<p class=\"muted\">${esc(d2.state.importModalStatus)}</p>` : ''}\n        </div>\n        <div class=\"modal-footer\">\n          <button class=\"modal-cancel-btn import-modal-close\">Cancelar</button>\n          <button class=\"import-modal-submit-btn\" ${d2.state.importingCards ? 'disabled' : ''}>${d2.state.importingCards ? 'Importando...' : 'Importar'}</button>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderDeleteConfirmModal(deck) {\n  return `\n    <div class=\"modal-overlay delete-modal-overlay\">\n      <div class=\"modal delete-modal\">\n        <div class=\"modal-header\"><h2>Deletar Deck</h2><button class=\"modal-close delete-modal-close\">&times;</button></div>\n        <div class=\"modal-body\">\n          <p>Tem certeza que quer deletar <strong>${esc(deck.name || '')}</strong>? Essa ação não pode ser desfeita.</p>\n        </div>\n        <div class=\"modal-footer\">\n          <button class=\"modal-cancel-btn delete-modal-close\">Cancelar</button>\n          <button class=\"delete-confirm-btn\">Deletar</button>\n        </div>\n      </div>\n    </div>\n  `\n}\n\nfunction renderDetail(d2) {\n  const deckId = d2.state.currentDeckId\n  const deck = d2.data('decks').records().find(x => x._id === deckId) || {}\n  const allCards = d2.data('cards').records()\n  const cards = allCards.filter(c => c.deckId === deckId)\n  const deckName = d2.state.currentDeckName || deck.name || ''\n  const totalCount = cards.reduce((s, c) => s + (Number(c.quantity) || 0), 0)\n\n  const preview = d2.state.previewCard || {\n    name: deck.commanderName, image: deck.commanderImageUrl,\n    price: deck.commanderPriceUsd, purchase: deck.commanderPurchaseUris || {}\n  }\n\n  const view = d2.state.deckView || 'text'\n  const groupBy = d2.state.deckGroupBy || 'type'\n  const sortBy = d2.state.deckSortBy || 'name'\n  const sortDir = d2.state.deckSortDir || 'asc'\n  const sortedCards = sortCards(cards, sortBy, sortDir)\n  const groups = groupCards(sortedCards, groupBy)\n  const orderedKeys = orderedGroupKeys(groups, groupBy)\n\n  let decklistHtml\n  if (cards.length === 0) {\n    decklistHtml = `<p class=\"muted\">Nenhuma carta ainda. Use a busca acima ou \"More &gt; Import\" pra adicionar.</p>`\n  } else if (view === 'grid') {\n    decklistHtml = renderFanView(groups, orderedKeys)\n  } else if (view === 'spoiler') {\n    decklistHtml = renderTileView(groups, orderedKeys, false)\n  } else {\n    decklistHtml = renderTextView(groups, orderedKeys)\n  }\n\n  return `\n    <div class=\"page\">\n      <div class=\"page-inner\">\n        <div class=\"topbar\">\n          <button class=\"back-btn\">&larr; Voltar</button>\n          <h1>${esc(deckName)}</h1>\n          <span class=\"pip-row\">${colorPips(deck.colorIdentity)}</span>\n          <span class=\"muted\">${totalCount} cartas</span>\n          <div class=\"dropdown-wrap\">\n            <button class=\"more-btn\">⋮ More</button>\n            ${d2.state.showMoreMenu ? renderMoreMenu() : ''}\n          </div>\n        </div>\n        <div class=\"detail-layout\">\n          ${renderPreviewPanel(preview)}\n          <div class=\"detail-main\">\n            ${deck.commanderName ? `\n              <div class=\"commander-chip\" data-name=\"${esc(deck.commanderName)}\" data-image=\"${esc(deck.commanderImageUrl || '')}\" data-price=\"${esc(deck.commanderPriceUsd || '')}\" data-purchase=\"${esc(JSON.stringify(deck.commanderPurchaseUris || {}))}\">\n                <div class=\"commander-chip-body\">\n                  <div class=\"commander-label muted\">COMMANDER</div>\n                  <div class=\"commander-name-row\">\n                    <span class=\"commander-name\">${esc(deck.commanderName)}</span>\n                    ${bracketBadge(deck.bracket)}\n                  </div>\n                </div>\n              </div>\n            ` : ''}\n            ${renderDeckToolbar(d2)}\n            ${decklistHtml}\n          </div>\n        </div>\n      </div>\n    </div>\n    ${d2.state.showExportModal ? renderExportModal(deck, cards) : ''}\n    ${d2.state.showImportModal ? renderImportModal(d2) : ''}\n    ${d2.state.showDeleteConfirm ? renderDeleteConfirmModal(deck) : ''}\n  `\n}\n\nexport default {\n  css: `\n    .page{width:100%;height:100%;box-sizing:border-box;padding:80px 24px 24px;overflow-y:auto;background:#eef0f2;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;color:#1a1a1a;font-size:14px}\n    .page-inner{max-width:1280px;margin:0 auto}\n    .topbar{display:flex;align-items:center;gap:14px;margin-bottom:24px;flex-wrap:wrap}\n    .topbar h1{font-size:20px;margin:0;flex:1}\n    .back-btn{background:none;border:none;color:#2563eb;font-size:14px;cursor:pointer;padding:6px 0}\n    .muted{color:#6b7280;font-size:12px}\n\n    .new-btn{background:none;border:none;color:#7c3aed;font-weight:600;font-size:14px;cursor:pointer;padding:0;white-space:nowrap}\n    .new-btn:hover{text-decoration:underline}\n\n    .list-topbar{margin-bottom:20px}\n    .topbar-row1 h1{font-size:32px;margin:0;font-weight:700}\n    .topbar-row2{display:flex;align-items:center;gap:14px;margin-top:14px;flex-wrap:wrap}\n    .topbar-actions{display:flex;align-items:center;gap:8px;margin-left:auto;flex-wrap:wrap}\n\n    .search-input{padding:7px 10px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;width:180px;box-sizing:border-box}\n\n    .dropdown-wrap{position:relative}\n    .filter-btn,.sort-btn,.more-btn{background:#fff;border:1px solid #7c3aed;color:#7c3aed;padding:7px 12px;border-radius:6px;font-size:13px;cursor:pointer;display:flex;align-items:center;gap:6px}\n    .filter-btn.active{background:#7c3aed;color:#fff}\n    .sort-icon{font-weight:700}\n\n    .view-toggle{display:flex;border:1px solid #d1d5db;border-radius:6px;overflow:hidden}\n    .view-btn{background:#fff;border:none;padding:7px 9px;cursor:pointer;color:#6b7280;font-size:13px;line-height:1}\n    .view-btn.active{background:#2563eb;color:#fff}\n\n    .dropdown-menu{position:absolute;top:calc(100% + 6px);right:0;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,0.12);padding:12px;min-width:190px;z-index:20}\n    .filter-option,.sort-option{display:flex;align-items:center;gap:8px;padding:5px 0;font-size:13px;cursor:pointer;white-space:nowrap}\n    .sort-section-label{font-size:11px;text-transform:uppercase;color:#9ca3af;margin:8px 0 4px;font-weight:600}\n    .sort-section-label:first-child{margin-top:0}\n\n    .more-menu{padding:6px;min-width:150px}\n    .more-menu button{display:block;width:100%;text-align:left;background:none;border:none;padding:8px 10px;font-size:13px;cursor:pointer;border-radius:6px;color:#374151}\n    .more-menu button:hover{background:#f3f4f6}\n    .more-menu button.danger{color:#dc2626}\n\n    .results-summary{font-size:13px;color:#6b7280;margin-bottom:14px}\n    .reset-filters-btn{margin-left:6px;background:#7c3aed;color:#fff;border:none;padding:4px 12px;border-radius:999px;font-size:12px;cursor:pointer}\n    .list-divider{border:none;border-top:1px solid rgba(0,0,0,0.06);margin:0 0 20px}\n\n    .pip{display:inline-block;width:15px;height:15px;border-radius:50%;border:1px solid rgba(0,0,0,0.2);vertical-align:middle;margin-right:2px}\n    .pip-c{background:#e5e7eb;color:#4b5563;font-size:8px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;border:1px solid rgba(0,0,0,0.2);margin-right:2px}\n    .pip-row{display:inline-flex;align-items:center}\n\n    .bracket-badge{display:inline-block;padding:3px 9px;border-radius:999px;font-size:11px;font-weight:600;white-space:nowrap}\n\n    .fav-btn{background:none;border:none;font-size:16px;line-height:1;cursor:pointer;color:#f59e0b;padding:2px;display:inline-flex;align-items:center;justify-content:center}\n\n    .deck-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:16px}\n    .deck-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,0.1);overflow:hidden;cursor:pointer;transition:transform .15s,box-shadow .15s}\n    .deck-card:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,0.12)}\n    .deck-card .fav-btn{position:absolute;top:8px;right:8px;background:rgba(255,255,255,0.85);border-radius:50%;width:26px;height:26px;display:flex;align-items:center;justify-content:center}\n    .deck-cover{width:100%;height:100px;object-fit:cover;object-position:top;display:block;background:#e5e7eb}\n    .deck-cover.placeholder{background:linear-gradient(135deg,#dbeafe,#bfdbfe)}\n    .deck-card-name{padding:10px 10px 4px;font-weight:600;font-size:14px;color:#7c3aed}\n    .deck-card:hover .deck-card-name{text-decoration:underline}\n    .deck-card-meta{display:flex;align-items:center;justify-content:space-between;padding:4px 10px 10px}\n    .deck-card-bracket{padding:0 10px 10px}\n\n    .deck-table{width:100%;border-collapse:collapse}\n    .deck-table th{text-align:left;font-size:13px;color:#333333;font-weight:700;padding:8px 10px;border-bottom:1px solid #e5e7eb}\n    .sortable-th{cursor:pointer;user-select:none}\n    .sort-caret-icon{margin-left:4px;vertical-align:middle}\n    .deck-table td{padding:9px 10px;border-bottom:1px solid #f0f1f3;font-size:13px}\n    .deck-row{cursor:pointer}\n    .deck-row:hover{background:#f3f4f6}\n    .deck-row-name{font-weight:600;color:#7c3aed}\n    .deck-row:hover .deck-row-name{text-decoration:underline}\n\n    .modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:100}\n    .modal{background:#fff;border-radius:10px;width:420px;max-width:90vw;box-shadow:0 20px 60px rgba(0,0,0,0.3);overflow:visible}\n    .modal.export-modal,.modal.import-modal{width:520px}\n    .modal-header{display:flex;align-items:center;justify-content:space-between;padding:16px 20px;border-bottom:1px solid #e5e7eb;border-top-left-radius:10px;border-top-right-radius:10px}\n    .modal-header h2{margin:0;font-size:17px}\n    .modal-close{background:none;border:none;font-size:20px;cursor:pointer;color:#6b7280;line-height:1}\n    .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n    .form-row{display:flex;flex-direction:column;gap:6px}\n    .field-label{font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:0.03em;margin-bottom:6px}\n    .modal-deck-name,.filter-name-input,.filter-commander-input{width:100%;padding:9px 11px;border:1px solid #d1d5db;border-radius:6px;font-size:14px;box-sizing:border-box}\n    .filter-bracket-select{width:100%;padding:9px 11px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;background:#fff;box-sizing:border-box}\n\n    .color-swatch-row{display:flex;gap:18px;flex-wrap:wrap}\n    .color-swatch-item{display:flex;flex-direction:column;align-items:center;gap:5px}\n    .color-swatch{width:32px;height:32px;border-radius:50%;border:2px solid rgba(0,0,0,0.15);cursor:pointer;padding:0;box-shadow:0 1px 2px rgba(0,0,0,0.1);transition:transform .1s}\n    .color-swatch.selected{border-color:#7c3aed;box-shadow:0 0 0 3px #7c3aed33;transform:scale(1.08)}\n    .color-swatch-label{font-size:11px;color:#6b7280}\n\n    .commander-search-wrap{position:relative}\n    .modal-commander-input,.card-search-input{width:100%;padding:9px 11px;border:1px solid #d1d5db;border-radius:6px;font-size:14px;box-sizing:border-box}\n    .commander-results{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1px solid #e5e7eb;border-radius:6px;box-shadow:0 8px 24px rgba(0,0,0,0.12);max-height:230px;overflow-y:auto;z-index:10}\n    .commander-result{display:flex;align-items:center;gap:10px;padding:7px 10px;cursor:pointer;font-size:13px}\n    .commander-result:hover{background:#f3f4f6}\n    .commander-result img{width:30px;border-radius:4px;display:block}\n    .selected-commander{display:flex;align-items:center;gap:10px;background:#f7f8fa;border-radius:6px;padding:8px 10px}\n    .selected-commander img{width:38px;border-radius:4px;display:block}\n    .selected-commander span{flex:1;font-size:13px;font-weight:600}\n    .clear-commander-btn{background:none;border:none;font-size:16px;cursor:pointer;color:#6b7280}\n    .modal-error{color:#dc2626;font-size:12px;margin:0}\n    .modal-footer{display:flex;justify-content:flex-end;gap:10px;padding:14px 20px;border-top:1px solid #e5e7eb;border-bottom-left-radius:10px;border-bottom-right-radius:10px}\n    .modal-cancel-btn,.filter-reset-btn{background:#f3f4f6;border:none;padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer;color:#374151}\n    .delete-confirm-btn{background:#dc2626;color:#fff;border:none;padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer}\n    .delete-confirm-btn:hover{background:#b91c1c}\n    .delete-modal .modal-body p{margin:0;font-size:14px;line-height:1.5;color:#374151}\n    .modal-create-btn{background:#2563eb;color:#fff;border:none;padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer}\n    .modal-create-btn:disabled{background:#93c5fd;cursor:default}\n    .filter-save-btn{background:#7c3aed;color:#fff;border:none;padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer}\n    .import-modal-submit-btn{background:#2563eb;color:#fff;border:none;padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer}\n    .import-modal-submit-btn:disabled{background:#93c5fd;cursor:default}\n    .export-textarea{width:100%;min-height:260px;padding:10px;border:1px solid #d1d5db;border-radius:6px;font-family:monospace;font-size:13px;box-sizing:border-box}\n    .import-modal-text{width:100%;min-height:200px;padding:10px;border:1px solid #d1d5db;border-radius:6px;font-family:monospace;font-size:13px;box-sizing:border-box;resize:vertical}\n\n    .detail-layout{display:flex;gap:22px;align-items:flex-start}\n    .preview-panel{position:sticky;top:0;width:240px;flex-shrink:0}\n    .preview-panel img{width:100%;border-radius:8px;box-shadow:0 4px 16px rgba(0,0,0,0.15);display:block}\n    .preview-placeholder{width:100%;aspect-ratio:5/7;background:#e5e7eb;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#6b7280;font-size:12px;text-align:center;padding:12px;box-sizing:border-box}\n    .preview-price{font-size:15px;font-weight:700;margin-top:10px;text-align:center;color:#1a1a1a}\n    .preview-buy-links{display:flex;flex-direction:column;gap:6px;margin-top:8px}\n    .buy-link{display:block;text-align:center;background:#fff;border:1px solid #7c3aed;color:#7c3aed;padding:7px;border-radius:6px;font-size:12px;text-decoration:none}\n    .buy-link:hover{background:#7c3aed;color:#fff}\n    .detail-main{flex:1;min-width:0}\n\n    .commander-chip{display:flex;align-items:center;background:#fff;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,0.1);padding:12px 14px;margin-bottom:16px;max-width:420px;cursor:pointer}\n    .commander-chip-body{display:flex;flex-direction:column;gap:6px}\n    .commander-label{font-size:10px;letter-spacing:0.05em;font-weight:700}\n    .commander-name-row{display:flex;align-items:center;gap:10px;flex-wrap:wrap}\n    .commander-name{font-size:15px;font-weight:700}\n\n    .deck-toolbar{display:flex;align-items:flex-end;justify-content:space-between;gap:16px;margin-bottom:18px;flex-wrap:wrap}\n    .card-search-wrap{position:relative;flex:1;min-width:220px;max-width:340px}\n    .deck-toolbar-controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}\n    .toolbar-select-label{display:flex;flex-direction:column;font-size:11px;color:#6b7280;gap:3px;font-weight:600}\n    .toolbar-select-label select{padding:6px 8px;border:1px solid #d1d5db;border-radius:6px;font-size:13px;background:#fff}\n    .deck-sort-dir-btn{background:#fff;border:1px solid #d1d5db;border-radius:6px;padding:6px 10px;cursor:pointer;font-size:13px;align-self:flex-end}\n\n    .group-section{margin-bottom:20px;break-inside:avoid}\n    .group-heading{font-weight:700;font-size:13px;color:#333333;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid #e5e7eb}\n    .text-view{column-count:3;column-gap:28px}\n    .text-row{display:flex;align-items:center;justify-content:space-between;font-size:13px;padding:3px 0}\n    .text-row .remove-card-btn{opacity:0;background:none;border:none;color:#9ca3af;cursor:pointer;font-size:14px}\n    .text-row:hover .remove-card-btn{opacity:1}\n\n    .card-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:14px}\n    .spoiler-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px}\n    .card-item{position:relative;border-radius:6px;overflow:hidden;background:#e5e7eb}\n    .card-item img{width:100%;display:block;border-radius:6px}\n    .card-placeholder{padding:14px;font-size:12px;text-align:center;color:#374151;aspect-ratio:5/7;display:flex;align-items:center;justify-content:center;background:#e5e7eb;box-sizing:border-box}\n    .card-qty{position:absolute;top:6px;left:6px;background:rgba(0,0,0,0.75);color:#fff;font-size:11px;font-weight:600;padding:2px 7px;border-radius:999px}\n    .remove-card-btn.overlay{position:absolute;top:6px;right:6px;background:rgba(0,0,0,0.7);color:#fff;border:none;border-radius:50%;width:20px;height:20px;font-size:13px;cursor:pointer;line-height:1;opacity:0}\n    .card-item:hover .remove-card-btn.overlay{opacity:1}\n\n    .fan-row{display:flex;flex-wrap:wrap;padding:16px 0 20px 30px}\n    .fan-card{width:120px;margin-left:-30px;flex-shrink:0;transition:transform .15s ease,box-shadow .15s ease;box-shadow:0 1px 4px rgba(0,0,0,0.25)}\n    .fan-card:hover{transform:translateY(-16px) scale(1.3) !important;z-index:999 !important;box-shadow:0 10px 24px rgba(0,0,0,0.4)}\n\n    .results-summary{display:flex;align-items:center;gap:6px}\n    .results-summary-icon{color:#9ca3af;display:inline-flex}\n\n    .modal-overlay{backdrop-filter:blur(3px)}\n    .modal{box-shadow:0 24px 64px rgba(15,23,42,0.28),0 2px 8px rgba(15,23,42,0.08)}\n    .deck-card{border:1px solid rgba(0,0,0,0.05)}\n    .filter-btn,.sort-btn,.more-btn,.new-btn,.view-btn,.fav-btn,.color-swatch,.buy-link,.modal-cancel-btn,.modal-create-btn,.filter-save-btn,.filter-reset-btn,.import-modal-submit-btn,.reset-filters-btn{transition:all .15s ease}\n    .filter-btn:hover,.sort-btn:hover,.more-btn:hover{background:#f5f3ff}\n    .filter-btn.active:hover{background:#6d28d9}\n    .modal-cancel-btn:hover,.filter-reset-btn:hover{background:#e5e7eb}\n    .modal-create-btn:hover{background:#1d4ed8}\n    .filter-save-btn:hover{background:#6d28d9}\n    .import-modal-submit-btn:hover{background:#1d4ed8}\n    .reset-filters-btn:hover{background:#6d28d9}\n    .color-swatch:hover{transform:scale(1.06)}\n    .search-input:focus,.modal-deck-name:focus,.filter-name-input:focus,.filter-commander-input:focus,.modal-commander-input:focus,.card-search-input:focus,.filter-bracket-select:focus,.import-modal-text:focus,.export-textarea:focus{outline:none;border-color:#7c3aed;box-shadow:0 0 0 3px rgba(124,58,237,0.15)}\n  `,\n\n  render(d2) {\n    const screen = d2.state.screen || 'list'\n    if (screen === 'detail') return renderDetail(d2)\n    return renderList(d2)\n  },\n\n  mounted(d2, root) {\n    d2.data('cards').loadAll()\n\n    d2.on('.back-btn', 'click', () => {\n      d2.state.currentDeckId = null\n      d2.state.screen = 'list'\n    })\n\n    d2.on('.deck-card', 'click', (e, el) => {\n      if (e.target.closest('.fav-btn')) return\n      openDeck(d2, el.dataset.id)\n    })\n\n    d2.on('.deck-row', 'click', (e, el) => {\n      if (e.target.closest('.fav-btn')) return\n      openDeck(d2, el.dataset.id)\n    })\n\n    d2.on('.fav-btn', 'click', async (e, el) => {\n      e.stopPropagation()\n      const id = el.dataset.id\n      const deck = d2.data('decks').records().find(x => x._id === id)\n      if (!deck) return\n      await d2.data('decks').update({ _id: id, favorite: !deck.favorite })\n    })\n\n    d2.on('.search-input', 'input', (e, el) => { d2.state.search = el.value })\n\n    d2.on('.sort-btn', 'click', (e) => {\n      e.stopPropagation()\n      d2.state.showSortMenu = !d2.state.showSortMenu\n    })\n\n    d2.on('.sortable-th', 'click', (e, el) => {\n      const col = el.dataset.sort\n      if ((d2.state.sortBy || 'name') === col) {\n        d2.state.sortDir = d2.state.sortDir === 'asc' ? 'desc' : 'asc'\n      } else {\n        d2.state.sortBy = col\n        d2.state.sortDir = 'asc'\n      }\n    })\n\n    d2.on('.sort-by-radio', 'change', (e, el) => { d2.state.sortBy = el.value })\n    d2.on('.sort-dir-radio', 'change', (e, el) => { d2.state.sortDir = el.value })\n    d2.on('.view-btn', 'click', (e, el) => { d2.state.viewMode = el.dataset.view })\n\n    d2.on(document, 'click', (e) => {\n      if (!e.target.closest('.dropdown-wrap')) {\n        d2.state.showSortMenu = false\n        d2.state.showMoreMenu = false\n      }\n    })\n\n    d2.on('.reset-filters-btn', 'click', () => {\n      d2.state.search = ''\n      d2.state.filterColors = []\n      d2.state.filterBracket = ''\n      d2.state.filterCommander = ''\n    })\n\n    // ---- Filter modal ----\n    d2.on('.filter-btn', 'click', () => {\n      d2.state.filterDraft = {\n        name: d2.state.search || '',\n        bracket: d2.state.filterBracket ? String(d2.state.filterBracket) : '',\n        colors: (d2.state.filterColors || []).slice(),\n        commander: d2.state.filterCommander || ''\n      }\n      d2.state.showFilterModal = true\n    })\n\n    d2.on('.filter-modal-overlay', 'click', (e, el) => { if (e.target === el) d2.state.showFilterModal = false })\n    d2.on('.filter-modal-close', 'click', () => { d2.state.showFilterModal = false })\n    d2.on('.filter-name-input', 'input', (e, el) => {\n      d2.state.filterDraft = Object.assign({}, d2.state.filterDraft, { name: el.value })\n    })\n    d2.on('.filter-bracket-select', 'change', (e, el) => {\n      d2.state.filterDraft = Object.assign({}, d2.state.filterDraft, { bracket: el.value })\n    })\n    d2.on('.color-swatch', 'click', (e, el) => {\n      const color = el.dataset.color\n      const current = (d2.state.filterDraft && d2.state.filterDraft.colors) || []\n      const next = current.indexOf(color) !== -1 ? current.filter(c => c !== color) : current.concat([color])\n      d2.state.filterDraft = Object.assign({}, d2.state.filterDraft, { colors: next })\n    })\n    d2.on('.filter-commander-input', 'input', (e, el) => {\n      d2.state.filterDraft = Object.assign({}, d2.state.filterDraft, { commander: el.value })\n    })\n    d2.on('.filter-reset-btn', 'click', () => {\n      d2.state.search = ''\n      d2.state.filterColors = []\n      d2.state.filterBracket = ''\n      d2.state.filterCommander = ''\n      d2.state.showFilterModal = false\n    })\n    d2.on('.filter-save-btn', 'click', () => {\n      const draft = d2.state.filterDraft || {}\n      d2.state.search = draft.name || ''\n      d2.state.filterColors = draft.colors || []\n      d2.state.filterBracket = draft.bracket || ''\n      d2.state.filterCommander = draft.commander || ''\n      d2.state.showFilterModal = false\n    })\n\n    // ---- New Deck modal ----\n    d2.on('.new-btn', 'click', () => {\n      d2.state.showNewDeckModal = true\n      d2.state.newDeckNameDraft = ''\n      d2.state.selectedCommander = null\n      d2.state.commanderQuery = ''\n      d2.state.commanderResults = []\n      d2.state.modalError = ''\n    })\n    d2.on('.modal-overlay', 'click', (e, el) => { if (e.target === el) d2.state.showNewDeckModal = false })\n    d2.on('.modal-close', 'click', () => { d2.state.showNewDeckModal = false })\n    d2.on('.modal-cancel-btn', 'click', () => { d2.state.showNewDeckModal = false })\n    d2.on('.modal-deck-name', 'input', (e, el) => { d2.state.newDeckNameDraft = el.value })\n\n    d2.on('.modal-commander-input', 'input', (e, el) => {\n      const q = el.value\n      d2.state.commanderQuery = q\n      const trimmed = q.trim()\n      if (commanderSearchTimer) clearTimeout(commanderSearchTimer)\n      if (trimmed.length < 2) { d2.state.commanderResults = []; return }\n      const seq = ++commanderSearchSeq\n      commanderSearchTimer = setTimeout(async () => {\n        try {\n          const res = await fetch('https://api.scryfall.com/cards/search?q=' + encodeURIComponent('is:commander ' + trimmed) + '&order=name')\n          if (seq !== commanderSearchSeq) return\n          if (res.ok) {\n            const data = await res.json()\n            d2.state.commanderResults = (data.data || []).slice(0, 8).map(c => {\n              const imgs = c.image_uris || (c.card_faces && c.card_faces[0] && c.card_faces[0].image_uris) || {}\n              return {\n                name: c.name, thumb: imgs.small || '', image: imgs.normal || imgs.large || imgs.small || '',\n                manaCost: c.mana_cost || '', colorIdentity: c.color_identity || [],\n                cmc: c.cmc || 0, typeLine: c.type_line || '', colors: c.colors || [],\n                priceUsd: (c.prices && c.prices.usd) || '', purchaseUris: c.purchase_uris || {}\n              }\n            })\n          } else d2.state.commanderResults = []\n        } catch (err) { if (seq === commanderSearchSeq) d2.state.commanderResults = [] }\n      }, 300)\n    })\n\n    d2.on('.commander-result', 'click', (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const c = (d2.state.commanderResults || [])[idx]\n      if (!c) return\n      d2.state.selectedCommander = c\n      d2.state.commanderResults = []\n      d2.state.commanderQuery = ''\n    })\n\n    d2.on('.clear-commander-btn', 'click', (e) => { e.stopPropagation(); d2.state.selectedCommander = null })\n\n    d2.on('.modal-create-btn', 'click', async () => {\n      const name = (d2.state.newDeckNameDraft || '').trim()\n      const commander = d2.state.selectedCommander\n      if (!name || !commander) {\n        d2.state.modalError = 'Preencha o nome e escolha um commander válido.'\n        return\n      }\n      d2.state.modalCreating = true\n      d2.state.modalError = ''\n      try {\n        const now = new Date().toISOString()\n        const deck = await d2.data('decks').create({\n          name, favorite: false, bracket: 2,\n          commanderName: commander.name, commanderImageUrl: commander.image, commanderManaCost: commander.manaCost,\n          colorIdentity: commander.colorIdentity, commanderCmc: commander.cmc, commanderTypeLine: commander.typeLine,\n          commanderColors: commander.colors, commanderPriceUsd: commander.priceUsd, commanderPurchaseUris: commander.purchaseUris,\n          createdAt: now, updatedAt: now\n        })\n        const deckId = deck && (deck._id || deck.id)\n        d2.state.modalCreating = false\n        d2.state.showNewDeckModal = false\n        openDeck(d2, deckId)\n        d2.state.currentDeckName = name\n      } catch (err) {\n        d2.state.modalCreating = false\n        d2.state.modalError = 'Erro ao criar: ' + (err && err.message ? err.message : 'tente novamente.')\n      }\n    })\n\n    // ---- Detail: hover preview ----\n    d2.on('.card-item, .commander-chip, .text-row', 'mouseover', (e, el) => {\n      d2.state.previewCard = {\n        name: el.dataset.name, image: el.dataset.image,\n        price: el.dataset.price || '', purchase: el.dataset.purchase ? JSON.parse(el.dataset.purchase) : {}\n      }\n    })\n\n    d2.on('.remove-card-btn', 'click', async (e, el) => {\n      e.stopPropagation()\n      const id = el.dataset.id\n      await d2.data('cards').remove(id)\n      await d2.data('cards').loadAll()\n      await d2.data('decks').update({ _id: d2.state.currentDeckId, updatedAt: new Date().toISOString() })\n    })\n\n    // ---- Deck toolbar: view/group/sort ----\n    d2.on('.view-select', 'change', (e, el) => { d2.state.deckView = el.value })\n    d2.on('.group-select', 'change', (e, el) => { d2.state.deckGroupBy = el.value })\n    d2.on('.deck-sort-select', 'change', (e, el) => { d2.state.deckSortBy = el.value })\n    d2.on('.deck-sort-dir-btn', 'click', () => { d2.state.deckSortDir = d2.state.deckSortDir === 'desc' ? 'asc' : 'desc' })\n\n    // ---- Card search + add (legal:commander only) ----\n    d2.on('.card-search-input', 'input', (e, el) => {\n      const q = el.value\n      const trimmed = q.trim()\n      if (cardSearchTimer) clearTimeout(cardSearchTimer)\n      if (trimmed.length < 2) { d2.state.cardSearchResults = []; return }\n      const seq = ++cardSearchSeq\n      cardSearchTimer = setTimeout(async () => {\n        try {\n          const res = await fetch('https://api.scryfall.com/cards/search?q=' + encodeURIComponent('legal:commander ' + trimmed) + '&order=name')\n          if (seq !== cardSearchSeq) return\n          if (res.ok) {\n            const data = await res.json()\n            d2.state.cardSearchResults = (data.data || []).slice(0, 8).map(c => {\n              const imgs = c.image_uris || (c.card_faces && c.card_faces[0] && c.card_faces[0].image_uris) || {}\n              return {\n                name: c.name, thumb: imgs.small || '', image: imgs.normal || imgs.large || imgs.small || '',\n                manaCost: c.mana_cost || '', cmc: c.cmc || 0, typeLine: c.type_line || '',\n                colors: c.colors || [], priceUsd: (c.prices && c.prices.usd) || '', purchaseUris: c.purchase_uris || {}\n              }\n            })\n          } else d2.state.cardSearchResults = []\n        } catch (err) { if (seq === cardSearchSeq) d2.state.cardSearchResults = [] }\n      }, 300)\n    })\n\n    d2.on('.card-search-result', 'click', async (e, el) => {\n      const idx = Number(el.dataset.idx)\n      const c = (d2.state.cardSearchResults || [])[idx]\n      if (!c) return\n      d2.state.cardSearchResults = []\n      const searchInput = d2.$('.card-search-input')\n      if (searchInput) searchInput.value = ''\n\n      const deckId = d2.state.currentDeckId\n      const existing = d2.data('cards').records().find(x => x.deckId === deckId && x.cardName.toLowerCase() === c.name.toLowerCase())\n      if (existing) {\n        await d2.data('cards').update({ _id: existing._id, quantity: (Number(existing.quantity) || 0) + 1 })\n      } else {\n        await d2.data('cards').create({\n          deckId, cardName: c.name, quantity: 1, imageUrl: c.image, manaCost: c.manaCost,\n          cmc: c.cmc, typeLine: c.typeLine, colors: c.colors, priceUsd: c.priceUsd, purchaseUris: c.purchaseUris\n        })\n      }\n      await d2.data('cards').loadAll()\n      await d2.data('decks').update({ _id: deckId, updatedAt: new Date().toISOString() })\n    })\n\n    // ---- More menu: Import / Export / Duplicar / Deletar ----\n    d2.on('.more-btn', 'click', (e) => { e.stopPropagation(); d2.state.showMoreMenu = !d2.state.showMoreMenu })\n\n    d2.on('.more-import-btn', 'click', () => {\n      d2.state.showMoreMenu = false\n      d2.state.showImportModal = true\n      d2.state.importModalStatus = ''\n    })\n    d2.on('.import-modal-close', 'click', () => { d2.state.showImportModal = false })\n    d2.on('.import-modal-overlay', 'click', (e, el) => { if (e.target === el) d2.state.showImportModal = false })\n    d2.on('.import-modal-submit-btn', 'click', async () => {\n      const textInput = d2.$('.import-modal-text')\n      const text = (textInput && textInput.value || '').trim()\n      if (!text) return\n      const deckId = d2.state.currentDeckId\n      d2.state.importingCards = true\n      d2.state.importModalStatus = 'Buscando cartas no Scryfall...'\n      try {\n        const lines = text.split('\\n').map(l => l.trim()).filter(Boolean)\n        const parsed = lines.map(line => {\n          const m = line.match(/^(\\d+)x?\\s+(.+)$/)\n          return { quantity: m ? parseInt(m[1], 10) : 1, cardName: m ? m[2].trim() : line }\n        })\n        const cardByName = {}\n        for (let i = 0; i < parsed.length; i += 75) {\n          const chunk = parsed.slice(i, i + 75)\n          const res = await fetch('https://api.scryfall.com/cards/collection', {\n            method: 'POST', headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify({ identifiers: chunk.map(c => ({ name: c.cardName })) })\n          })\n          if (res.ok) {\n            const data = await res.json()\n            ;(data.data || []).forEach(card => { cardByName[card.name.toLowerCase()] = card })\n          }\n        }\n        d2.state.importModalStatus = 'Salvando cartas...'\n        await Promise.all(parsed.map(p => {\n          const card = cardByName[p.cardName.toLowerCase()]\n          const imgs = card ? (card.image_uris || (card.card_faces && card.card_faces[0] && card.card_faces[0].image_uris) || {}) : {}\n          return d2.data('cards').create({\n            deckId, cardName: p.cardName, quantity: p.quantity,\n            imageUrl: imgs.normal || imgs.large || '',\n            manaCost: card ? (card.mana_cost || '') : '',\n            cmc: card ? (card.cmc || 0) : 0,\n            typeLine: card ? (card.type_line || '') : '',\n            colors: card ? (card.colors || []) : [],\n            priceUsd: card ? ((card.prices && card.prices.usd) || '') : '',\n            purchaseUris: card ? (card.purchase_uris || {}) : {}\n          })\n        }))\n        await d2.data('cards').loadAll()\n        await d2.data('decks').update({ _id: deckId, updatedAt: new Date().toISOString() })\n        d2.state.importingCards = false\n        d2.state.importModalStatus = ''\n        d2.state.showImportModal = false\n      } catch (err) {\n        d2.state.importingCards = false\n        d2.state.importModalStatus = 'Erro: ' + (err && err.message ? err.message : 'tente novamente.')\n      }\n    })\n\n    d2.on('.more-export-btn', 'click', () => {\n      d2.state.showMoreMenu = false\n      d2.state.showExportModal = true\n    })\n    d2.on('.export-modal-close', 'click', () => { d2.state.showExportModal = false })\n    d2.on('.export-modal-overlay', 'click', (e, el) => { if (e.target === el) d2.state.showExportModal = false })\n\n    d2.on('.more-duplicate-btn', 'click', async () => {\n      d2.state.showMoreMenu = false\n      const deck = d2.data('decks').records().find(x => x._id === d2.state.currentDeckId)\n      if (!deck) return\n      const now = new Date().toISOString()\n      const newDeck = await d2.data('decks').create({\n        name: deck.name + ' (Cópia)', favorite: false, bracket: deck.bracket,\n        commanderName: deck.commanderName, commanderImageUrl: deck.commanderImageUrl, commanderManaCost: deck.commanderManaCost,\n        colorIdentity: deck.colorIdentity, commanderCmc: deck.commanderCmc, commanderTypeLine: deck.commanderTypeLine,\n        commanderColors: deck.commanderColors, commanderPriceUsd: deck.commanderPriceUsd, commanderPurchaseUris: deck.commanderPurchaseUris,\n        createdAt: now, updatedAt: now\n      })\n      const newDeckId = newDeck && (newDeck._id || newDeck.id)\n      const myCards = d2.data('cards').records().filter(c => c.deckId === d2.state.currentDeckId)\n      await Promise.all(myCards.map(c => d2.data('cards').create({\n        deckId: newDeckId, cardName: c.cardName, quantity: c.quantity, imageUrl: c.imageUrl, manaCost: c.manaCost,\n        cmc: c.cmc, typeLine: c.typeLine, colors: c.colors, priceUsd: c.priceUsd, purchaseUris: c.purchaseUris\n      })))\n      await d2.data('cards').loadAll()\n      openDeck(d2, newDeckId)\n      d2.state.currentDeckName = deck.name + ' (Cópia)'\n    })\n\n    d2.on('.more-delete-btn', 'click', () => {\n      d2.state.showMoreMenu = false\n      d2.state.showDeleteConfirm = true\n    })\n\n    d2.on('.delete-modal-close', 'click', () => { d2.state.showDeleteConfirm = false })\n    d2.on('.delete-modal-overlay', 'click', (e, el) => { if (e.target === el) d2.state.showDeleteConfirm = false })\n\n    d2.on('.delete-confirm-btn', 'click', async () => {\n      const deckId = d2.state.currentDeckId\n      try {\n        const myCards = d2.data('cards').records().filter(c => c.deckId === deckId)\n        await Promise.all(myCards.map(c => d2.data('cards').remove(c._id)))\n        await d2.data('decks').remove(deckId)\n        await d2.data('cards').loadAll()\n      } catch (err) {}\n      d2.state.showDeleteConfirm = false\n      d2.state.currentDeckId = null\n      d2.state.screen = 'list'\n    })\n  }\n}"}},"decks":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"decks"}}}},"cards":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"cards"}}}}}}}}}}]}},{"reference":"ExternalComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_id":"5f060d9b1ca96def42f3c40a","displayName":"Builder AuthStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"credentials":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"verifier":{"component":"data2-core-types@DataSetReference","params":{"reference":"UsersDataSet"}},"sessionShared":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},"email":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"nR6Q9d7qdaPtZhJ28Q8Dy"},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true,"reference":"yPCJBTKo8q1Mw0B_3Kpwl"},"component":"data2-core-types@ArtboardReference"}}},"session":[{"component":"data2-core-types@DataSetColumnReference","params":{"column":"name"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"email"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"password"}},{"component":"data2-core-types@DataSetColumnReference","params":{"column":"status"}}],"sessionMetadata":{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},{"params":{"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"active"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.status"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is active user"}]},"component":"data2-core-logical-operators@And","displayName":"is active and has _id"}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $user"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-primitives@String","params":{"content":"@data2.app"}},"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user.email"}}}}},"component":"data2-core-functions@Includes","displayName":"isData2User"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}},"component":"data2-core-flow@Step"},{"params":{"action":{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.d2_app"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"management"}}]},"component":"data2-core-functions@Includes","displayName":"isData2App Exception"},"resultName":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}},"component":"data2-core-flow@Step"},{"params":{"resultName":{"component":"data2-core-primitives@String","params":{"content":"has_access"}},"action":{"params":{"compare":{"params":{"components":[{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_user"}}}},{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"is_data2_app_exception"}}}}]},"component":"data2-core-logical-operators@Or"},"else":{"params":{"actions":[{"params":{"actions":[{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"component":"data2-core-logical-operators@Not"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check $metadata.system"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":"filter","args":[{"_id":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"compare":{"params":{"subject":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records.length"}}}}},"component":"data2-core-logical-operators@Not","displayName":"systems not found"},"then":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"}},"component":"data2-core-flow@If","displayName":"check system found"},{"params":{"actions":[{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"organization":{"component":"data2-core-types@Getter","params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"cursor.current.organization._id"}}}}}]},"component":"data2-core-types@Interaction"},{"params":{"module":{"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":"filter","args":[{"user":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$user._id"}}}},"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}}]},"component":"data2-core-types@Interaction"}]},"component":"data2-core-flow@Parallel"}]},"component":"data2-core-flow@Series","displayName":"common setup"},{"params":{"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"},"else":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"compare":{"params":{"components":{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"WuUa-VKHhW9uYU0E3-mJ8"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"mapper":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"system"}}],"body":{"params":{"expression":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.visibility"}}}},"defaultCase":{"params":{"content":false},"component":"data2-core-primitives@Boolean"},"cases":[{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"public"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"billing-owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}},{"component":"data2-core-primitives@String","params":{"content":"member"}}]},"component":"data2-core-functions@Includes","displayName":"has role with access"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"comparisonSubject":{"component":"data2-core-primitives@String","params":{"content":"collaborator"}}},"component":"data2-core-comparison-operators@Equal","displayName":"is collaborator"}]},"component":"data2-core-logical-operators@And","displayName":"is org member and collaborator"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is collaborator"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is collaborator and has system access"}]},"component":"data2-core-logical-operators@Or"}}},{"component":"data2-core-flow@Case","params":{"expression":{"component":"data2-core-primitives@String","params":{"content":"private"}},"value":{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"components":[{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal","displayName":"is org member"},{"params":{"pattern":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.role"}}}},"subject":[{"component":"data2-core-primitives@String","params":{"content":"owner"}},{"component":"data2-core-primitives@String","params":{"content":"admin"}}]},"component":"data2-core-functions@Includes","displayName":"is owner/admin"}]},"component":"data2-core-logical-operators@And","displayName":"has org member role with access"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is owner/admin"},{"params":{"components":[{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"Ok6Tsu5XkS7r8PyW_7TyW"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"member"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"member.organization._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system.organization._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"is org member"},{"params":{"array":{"params":{"module":{"disabled":false,"component":"data2-core-types@InteractiveModuleReference","params":{"reference":"NqKAY34n2ffFL64zamp7t"}},"prop":{"component":"data2-core-primitives@String","params":{"content":"data.records"}}},"component":"data2-core-types@Getter"},"filter":{"disabled":false,"component":"data2-core-functions@Function","params":{"parameters":[{"component":"data2-core-primitives@String","params":{"content":"access"}}],"body":{"params":{"subject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"access.system._id"}}}},"comparisonSubject":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"system._id"}}}}},"component":"data2-core-comparison-operators@Equal"}}}},"component":"data2-core-functions@ArrayFind","displayName":"has system access"}]},"component":"data2-core-logical-operators@And","displayName":"is member and has system access"}]},"component":"data2-core-logical-operators@Or","displayName":"is owner/admin or has system access"}}}]},"component":"data2-core-flow@Switch"}}}},"component":"data2-core-functions@ArrayMap"}},"component":"data2-core-logical-operators@And","displayName":"has org member for all systems in request"}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"then":{"params":{"content":true},"component":"data2-core-primitives@Boolean"}},"component":"data2-core-flow@If"}},"component":"data2-core-flow@Step"},{"params":{"then":{"system":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"$metadata.system"}}}}},"else":{"params":{"message":{"component":"data2-core-primitives@String","params":{"content":"Invalid Credentials"}},"code":{"component":"data2-core-primitives@String","params":{"content":"401"}}},"component":"data2-core-types@Error"},"compare":{"component":"data2-core-types@Reference","params":{"reference":{"component":"data2-core-primitives@String","params":{"content":"has_access"}}}}},"component":"data2-core-flow@If"}]},"component":"data2-core-flow@Series"},"sessionExpiresIn":{"component":"data2-core-primitives@Number","params":{"content":604800}},"sessionRenewal":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"identityProviders":[{"provider":{"component":"data2-core-primitives@String","params":{"content":"GOOGLE"}},"clientId":{"component":"data2-core-primitives@String","params":{"content":"55470861603-22pvfc1t49lp8cjnbfk71s5ct9hp1q1v.apps.googleusercontent.com"}}}]},"reference":"Data2BuilderAuthentication","updatedAt":"2026-08-06T19:31:23.557Z"},{"_id":"5f060ee51ca96def42f3fbd1","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"UsersTableDataSource"}},"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":1}}}},"reference":"UsersDataSet","updatedAt":"2026-03-04T21:16:40.291Z"},{"_id":"68f938cf5fb002e33752a43d","displayName":"InternalApplicationsAuthenticationStrategy","component":"data2-core@AuthenticationStrategy","system":"5e3b796d6228227ca637d0f4","params":{"resetPassword":{"resetEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}},"resetSuccessEmail":{"artboard":{"params":{"preventEvaluation":true},"component":"data2-core-types@ArtboardReference"}}},"verifier":{"disabled":false,"component":"data2-core-types@DataSetReference","params":{"reference":"InternalApplicationsVerifierDataSet"}},"password":{"disabled":false,"component":"data2-core-types@DataSetColumnReference","params":{"column":"secret"}},"credentials":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}],"session":[{"component":"data2-core-types@FieldReference","params":{"key":"application"}},{"component":"data2-core-types@FieldReference","params":{"key":"secret"}},{"component":"data2-core-types@FieldReference","params":{"key":"status"}}]},"reference":"InternalApplicationsAuthenticationStrategy","updatedAt":"2025-10-22T20:08:54.442Z","createdAt":"2025-10-22T20:04:31.198Z"},{"component":"data2-core@DataSet","reference":"SessionsDataSet","params":{"requiresConfirmation":false,"pk":"key","datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"data2sessionsdatasource"}}}},{"reference":"UsersTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"component":"data2-core@ServerSessionsDataSource","reference":"data2sessionsdatasource","params":{}},{"_id":"5f1600b70528b92601890f75","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ServiceComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ServiceComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:20.208Z","createdAt":"2020-07-20T20:38:15.436Z"},{"_id":"5f1602190528b92601890f7d","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":40000},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ComponentInstancesTableDataSource"}},"sort":{"_id":1},"pk":"_id","autoEdit":true,"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"ComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:07.089Z","createdAt":"2020-07-20T20:44:09.350Z"},{"_id":"5f16017f0528b92601890f7a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"FileComponentInstancesDataSet","updatedAt":"2023-08-03T16:08:18.193Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"5f1601990528b92601890f7b","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"ApplicationsTableDataSource"}},"pk":"_id","autoEdit":{"component":"data2-core-primitives@Boolean","params":{"content":true}},"master":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"params":{"column":"_id"},"component":"data2-core-types@DataSetColumnReference"}},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"ApplicationsDataSet","updatedAt":"2022-12-13T21:51:13.995Z","createdAt":"2020-07-20T20:42:01.657Z"},{"_id":"5f16172a0528b92601890f85","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"pagination":{"size":{"component":"data2-core-primitives@Number","params":{"content":10000}}},"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"DataComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}},"policies":{"params":{"allowGet":{"component":"data2-core-primitives@Boolean","params":{"content":true}}},"component":"data2@hkXy-UhMjhyfptdE6_fpW"}},"reference":"DataComponentInstancesDataSet","updatedAt":"2025-11-06T21:07:12.831Z","createdAt":"2020-07-20T22:14:02.097Z"},{"_id":"60ddc1bbda3861ca65407c5a","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"domain":{"component":"data2-core-types@Reference","params":{"reference":"clientApplicationDomain"}}},"pagination":{"size":1},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"}},"reference":"OneFileComponentInstancesDataSet","updatedAt":"2022-02-09T19:44:38.036Z","createdAt":"2020-07-20T20:41:35.797Z"},{"_id":"63ee3d38b8c9838c62fb05fb","displayName":"DriveFileComponentInstancesDataSet","component":"data2-core@DataSet","system":"5e3b796d6228227ca637d0f4","params":{"datasource":{"component":"data2-core-types@DataSourceReference","params":{"reference":"FileComponentInstancesTableDataSource"}},"pk":"_id","autoEdit":true,"filter":{"component":{"component":"data2-core-primitives@String","params":{"content":"data2-core@Drive"}}},"master":{"params":{"dataset":{"component":"data2-core-types@DataSetReference","params":{"reference":"SystemsDataSet"}},"localField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"system"}},"masterField":{"component":"data2-core-types@DataSetColumnReference","params":{"column":"_id"}}},"component":"data2-core-types@DataSetMaster"},"autoSelect":{"component":"data2-core-primitives@Boolean","params":{"content":false}}},"reference":"DriveFileComponentInstancesDataSet","updatedAt":"2025-12-04T21:58:32.367Z","createdAt":"2023-02-16T14:27:07.797Z"},{"reference":"ServiceComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"ComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"_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":"6a4c76c4df6d868fb52e58c1","displayName":"Teste Navegação","reference":"g4EkxYfAJc4fJ2pv4G5yc","params":{"domain":{"component":"data2-core-primitives@String","params":{"content":"testenav"}},"entryPoint":{"component":"data2-core-types@ComponentInstanceReference","params":{"component":"data2-core-primitives@Artboard","reference":"qE2OwgmC6d_u7EjGGh3qV"}}},"system":"684302e0f220c5f37f7a25d1","component":"data2-core-primitives@Application"}]}},{"reference":"DataComponentInstancesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}},{"reference":"BundlesTableDataSource","component":"data2-core@MemoryDataSource","params":{"initialData":[]}}],"processesParams":{}}}}