PageSourceSearch

https://www.comopedir.com.br/assets/js/catalog-journeys.js?v=20260914-session-codigos

js comopedir.com.br collected 2026-10-06 04:45:03 UTC 444,223 bytes, 3,767 lines download raw bytes

1// catalog-journeys.js — dados de jornadas/conceitos/telas/patterns
2// (extraído de app.js em 2026-09-02, Fase 2 do split).
3// Só é carregado nas páginas que renderizam catálogo/jornada.
4// Consts globais (script clássico), como antes do split.
5
6const patternExamples = [
7    {
8        slug: 'landing-page',
9        label: 'Landing page',
10        title: 'Hero + CTA',
11        accent: '#1D3557',
12        summary: 'Header, Hero, Button, Card',
13        description: 'Boa para apresentar uma oferta, explicar valor e conduzir a pessoa para uma ação principal.',
14        components: ['Header', 'Hero', 'CTA', 'Card'],
15        prompt: 'Crie uma landing page com Header, Hero com promessa clara, CTA principal e cards de benefícios abaixo.',
16        visual: () => `<div class="pattern-example pattern-landing"><header><strong>Como Pedir</strong><nav>Recursos · Planos · Entrar</nav></header><section><p>Treinamento prático</p><h3>Aprenda a pedir interfaces melhores.</h3><button>Começar agora</button></section><div><article>Clareza</article><article>Exemplos</article><article>Prática</article></div></div>`
17    },
18    {
19        slug: 'dashboard',
20        label: 'Dashboard',
21        title: 'Sidebar + Cards + Chart',
22        accent: '#E63946',
23        summary: 'Sidebar, Badge, Chart, Filter',
24        description: 'Serve para acompanhar métricas, navegar por áreas internas e comparar indicadores com rapidez.',
25        components: ['Sidebar', 'Filter', 'Chart'],
26        prompt: 'Crie um dashboard com sidebar lateral, filtros no topo, três cards de KPI e um gráfico principal de desempenho.',
27        visual: () => `<div class="pattern-example pattern-dashboard"><aside><span>Home</span><span>Vendas</span><span>Alunos</span></aside><main><header><strong>Visão geral</strong><button>Filtrar</button></header><section><article><span>Alunos</span><b>248</b></article><article><span>Receita</span><b>R$ 12k</b></article><article><span>Ativos</span><b>91%</b></article></section><div class="fake-chart"><span></span><span></span><span></span><span></span></div></main></div>`
28    },
29    {
30        slug: 'formulario',
31        label: 'Formulário',
32        title: 'Form + Input + Button',
33        accent: '#1D3557',
34        summary: 'Label, Input, Select, Alert',
35        description: 'Ideal quando o usuário precisa informar dados, escolher opções e receber feedback antes de avançar.',
36        components: ['Label', 'Input', 'Alert'],
37        prompt: 'Crie um formulário com labels claros, campos de nome e e-mail, select de interesse, mensagem de ajuda e botão de envio.',
38        visual: () => `<div class="pattern-example pattern-form"><form><label>Nome<input placeholder="Maria Souza"></label><label>E-mail<input placeholder="[email protected]"></label><label>Interesse<select><option>Primeiros Passos</option></select></label><p>Revise os dados antes de continuar.</p><button>Enviar cadastro</button></form></div>`
39    },
40    {
41        slug: 'onboarding',
42        label: 'Onboarding',
43        title: 'Progress + Form + Tooltip',
44        accent: '#E17BB8',
45        summary: 'Progress, Tooltip, Button',
46        description: 'Ajuda uma pessoa nova a passar por etapas curtas sem se perder no começo do produto.',
47        components: ['Progress', 'Tooltip', 'Button'],
48        prompt: 'Crie um onboarding em três passos com barra de progresso, card de orientação, tooltip contextual e botões de avançar/voltar.',
49        visual: () => `<div class="pattern-example pattern-onboarding"><div class="progress"><span></span><span></span><span></span></div><article><small>Passo 02 de 03</small><h3>Configure seu primeiro pedido</h3><p>Use uma frase simples e escolha o tipo de tela.</p><em>Dica: comece pelo objetivo.</em></article><footer><button>Voltar</button><button>Avançar</button></footer></div>`
50    },
51    {
52        slug: 'galeria-midia',
53        label: 'Galeria de mídia',
54        title: 'Gallery + Modal + Image',
55        accent: '#6A58E8',
56        summary: 'Gallery, Modal, Aspect Ratio',
57        description: 'Boa para mostrar imagens, produtos, aulas, portfólio ou materiais visuais em uma grade organizada.',
58        components: ['Gallery', 'Modal', 'Aspect Ratio'],
59        prompt: 'Crie uma galeria com imagem principal grande, miniaturas laterais, título da mídia e botão para abrir preview em modal.',
60        visual: () => `<div class="pattern-example pattern-gallery"><div class="hero-image">Preview</div><div class="thumbs"><span></span><span></span><span></span></div><article><h3>Aula visual</h3><p>Abra a imagem para ver detalhes.</p><button>Ver preview</button></article></div>`
61    },
62    {
63        slug: 'busca-avancada',
64        label: 'Busca avançada',
65        title: 'Search + Filter + Pagination',
66        accent: '#26896E',
67        summary: 'Search, Filter, Pagination',
68        description: 'Ajuda a explorar listas grandes com busca, filtros, resultados escaneáveis e paginação.',
69        components: ['Search', 'Filter', 'Pagination'],
70        prompt: 'Crie uma busca avançada com campo de pesquisa, filtros por categoria e status, lista de resultados e paginação.',
71        visual: () => `<div class="pattern-example pattern-search"><header><input placeholder="Buscar componente"><button>
71Buscar</button></header><div class="filters"><span>Categoria</span><span>Status</span><span>Nível</span></div><section><article>Modal</article><article>Tabs</article><article>Dropdown</article></section><footer><button>1</button><button>2</button><button>3</button></footer></div>`
72    }
73];
74
75const fullScreens = [
76    {
77        slug: 'login',
78        name: 'Login',
79        label: 'Acesso',
80        audience: 'Entrada rápida e segura para produtos SaaS, áreas de membros e painéis internos.',
81        description: 'Uma tela curta, objetiva e focada em reduzir fricção. Ideal para destacar credenciais, recuperação de senha e caminhos alternativos como SSO.',
82        components: ['Header', 'Card', 'Form', 'Input', 'Button', 'Alert'],
83        contexts: ['SaaS', 'Área logada', 'Mobile first'],
84        steps: [
85            'Apresente marca, valor e contexto em um bloco simples acima do formulário.',
86            'Agrupe email, senha e ação principal em um card com hierarquia clara.',
87            'Reserve espaço para erro, loading e recuperação de acesso sem quebrar a composição.'
88        ],
89        blueprint: loginBlueprint
90    },
91    {
92        slug: 'dashboard',
93        name: 'Dashboard',
94        label: 'Operação',
95        audience: 'Painéis com dados, métricas e ações frequentes para times de produto, vendas ou operações.',
96        description: 'Combina navegação lateral, filtros, cards de KPI e área analítica. A meta é permitir leitura rápida e decisões sem sobrecarga visual.',
97        components: ['Sidebar', 'Header', 'Filter', 'Card', 'Chart', 'Badge'],
98        contexts: ['Analytics', 'Admin', 'B2B'],
99        steps: [
100            'Separe navegação, contexto da página e conteúdo analítico em zonas visuais distintas.',
101            'Use cards-resumo para leitura imediata antes de gráficos e tabelas mais densas.',
102            'Mantenha filtros próximos das métricas para reforçar causa e efeito na exploração.'
103        ],
104        blueprint: dashboardBlueprint
105    },
106    {
107        slug: 'checkout',
108        name: 'Checkout',
109        label: 'Conversão',
110        audience: 'Fluxos de compra que precisam gerar confiança, mostrar resumo e evitar abandono.',
111        description: 'Organiza entrega, pagamento e revisão em uma progressão clara. A tela precisa equilibrar informação, confiança e foco na ação principal.',
112        components: ['Progress Bar', 'Form', 'Input', 'Select', 'Radio Button', 'Button'],
113        contexts: ['E-commerce', 'Pagamento', 'Fluxo guiado'],
114        steps: [
115            'Mostre progresso, etapa atual e resumo do pedido desde o início.',
116            'Divida o formulário em blocos curtos para reduzir sensação de esforço.',
117            'Deixe total, política e CTA final sempre visíveis na coluna de apoio ou no rodapé fixo.'
118        ],
119        blueprint: checkoutBlueprint
120    },
121    {
122        slug: 'profile',
123        name: 'Perfil',
124        label: 'Conta',
125        audience: 'Áreas de conta com dados pessoais, preferências e histórico de uso.',
126        description: 'Mistura leitura e edição com segurança. Boa para planos, avatar, configurações e dados persistentes do usuário.',
127        components: ['Avatar', 'Tabs', 'Form', 'Input', 'Switch', 'Button'],
128        contexts: ['Configurações', 'Conta', 'Preferências'],
129        steps: [
130            'Crie um resumo superior com identidade, cargo, plano ou status atual.',
131            'Separe dados pessoais, segurança e notificações em abas ou seções independentes.',
132            'Confirme mudanças com feedback persistente, não apenas toast passageiro.'
133        ],
134        blueprint: profileBlueprint
135    },
136    {
137        slug: 'product',
138        name: 'Página de produto',
139        label: 'Catálogo',
140        audience: 'Páginas de detalhe para e-commerce, cursos, templates e produtos digitais.',
141        description: 'Precisa vender o item e responder dúvidas ao mesmo tempo. Imagem, preço, variações, prova social e CTA trabalham em conjunto.',
142        components: ['Gallery', 'Aspect Ratio', 'Badge', 'Accordion', 'Button', 'Tabs'],
143        contexts: ['E-commerce', 'Marketplace', 'Landing transacional'],
144        steps: [
145            'Destaque mídia e preço logo no primeiro bloco da dobra.',
146            'Use badges e microtextos para confiança, estoque e benefício principal.',
147            'Empilhe detalhes técnicos, políticas e perguntas frequentes abaixo da decisão de compra.'
148        ],
149        blueprint: productBlueprint
150    },
151    {
152        slug: 'notifications',
153        name: 'Central de notificações',
154        label: 'Engajamento',
155        audience: 'Feeds de avisos, atividades e atualizações em produtos com colaboração ou automação.',
156        description: 'Organiza volume e prioridade. Deve permitir leitura escaneável, filtros rápidos e estados de lido/não lido sem virar ruído.',
157        components: ['Header', 'Search Bar', 'Filter', 'Badge', 'Tabs', 'Scroll Area'],
158        contexts: ['Colaboração', 'Sistema', 'Activity feed'],
159        steps: [
160            'Mostre filtros por tipo, prioridade e status de leitura logo no topo.',
161            'Use badges e tipografia para diferenciar novidade, urgência e agrupamentos.',
162            'Combine lista densa com detalhes progressivos para não interromper o fluxo de leitura.'
163        ],
164        blueprint: notificationsBlueprint
165    }
166];
167
168const learningJourney = [
169    {
170        stage: 'Etapa 01',
171        title: 'Entenda blocos básicos',
172        goal: 'Comece por peças fáceis de reconhecer em quase toda interface: estrutura, ação e leitura rápida.',
173        components: ['Header', 'Section', 'Card', 'Button'],
174        markers: ['Header', 'Section', 'Card', 'Card', 'Button'],
175        focus: 'Comece lendo a tela de cima para baixo: topo, destaque, conteúdo e ação.',
176        practice: 'Nomeie quatro blocos de qualquer página e transforme isso em uma frase de pedido.',
177        lessons: [
178            'Aprenda a diferença entre estrutura da página e conteúdo dentro dela.',
179            'Observe como títulos, textos curtos e ações principais se organizam em um mesmo bloco.',
180            'Compare componentes que aparecem em marketing, dashboards e páginas institucionais.'
181        ],
182        visual: () => `<div class="training-wireframe grid grid-cols-1 gap-3"><div class="training-target" data-marker="1" data-label="Header"><div class="h-10 border-2 border-mondrian-black bg-white"></div></div><div class="training-target" data-marker="2" data-label="Section"><div class="h-24 border-2 border-mondrian-black bg-forge-blue"></div></div><div class="grid grid-cols-2 gap-3"><div class="training-target" data-marker="3" data-label="Card"><div class="h-20 border-2 border-mondrian-black bg-white"></div></div><div class="training-target" data-marker="4" data-label="Card"><div class="h-20 border-2 border-mondrian-black bg-white"></div></div></div><div class="training-target training-target-inline" data-marker="5" data-label="Button"><div class="h-10 w-32 bg-mondrian-black"></div></div></div>`
183    },
184    {
185        stage: 'Etapa 02',
186        title: 'Domine entrada e decisão',
187        goal: 'Depois dos blocos, foque em como o usuário informa dados, confirma ações e recebe orientação.',
188        components: ['Form', 'Input', 'Select', 'Checkbox', 'Label'],
189        markers: ['Label', 'Input', 'Mensagem', 'Select', 'Opção', 'CTA'],
190        focus: 'Procure primeiro o label, depois o campo e por último a ação de confirmação.',
191        practice: 'Pegue um formulário real e escreva quais dados entram, quais escolhas existem e qual botão finaliza.',
192        lessons: [
193            'Veja como labels, campos e mensagens de ajuda formam um conjunto.',
194            'Pratique a diferença entre selecionar uma opção, marcar várias ou apenas digitar.',
195            'Leia os estados e erros esperados antes de copiar qualquer snippet.'
196        ],
197        visual: () => `<div class="training-wireframe space-y-3"><div class="training-target training-target-inline" data-marker="1" data-label="Label"><div class="h-9 w-24 bg-mondrian-black"></div></div><div class="training-target" data-marker="2" data-label="Input"><div class="h-11 border-2 border-mondrian-black bg-white"></div></div><div class="training-target" data-marker="3" data-label="Mensagem"><div class="h-11 border-2 border-mondrian-black bg-white"></div></div><div class="grid grid-cols-2 gap-3"><div class="training-target" data-marker="4" data-label="Select"><div class="h-11 border-2 border-mondrian-black bg-white"></div></div><div class="training-target" data-marker="5" data-label="Opção"><div class="h-11 border-2 border-mondrian-black bg-white"></div></div></div><div class="training-target" data-marker="6" data-label="CTA"><div class="h-10 bg-forge-lime border-2 border-mondrian-black"></div></div></div>`
198    },
199    {
200        stage: 'Etapa 03',
201        title: 'Navegue sem confusão',
202        goal: 'Agora que os componentes básicos fazem sentido, avance para orientação, menus e descoberta de conteúdo.',
203        components: ['Navbar/Navigation', 'Tabs', 'Dropdown', 'Breadcrumb', 'Pagination'],
204        markers: ['Navbar', 'Menu', 'Tab', 'Aba ativa', 'Conteúdo', 'Página'],
205        focus: 'Identifique o que orienta localização, o que troca conteúdo e o que abre novas escolhas.',
206        practice: 'Descreva uma navegação usando nomes reais: Navbar, Tabs, Dropdown, Breadcrumb e Pagination.',
207        lessons: [
208            'Entenda quando cada padrão organiza escolhas, contexto ou caminhos.',
209            'Observe o que fica sempre visível e o que aparece apenas sob demanda.',
210            'Teste no catálogo quais padrões servem para poucas opções e quais lidam com listas maiores.'
211        ],
212        visual: () => `<div class="training-wireframe space-y-3"><div class="grid grid-cols-[minmax(0,1fr)_72px] gap-3"><div class="training-target" data-marker="1" data-label="Navbar"><div class="h-10 bg-mondrian-black"></div></div><div class="training-target" data-marker="2" data-label="Menu"><div class="h-10 border-2 border-mondrian-black bg-white"></div></div></div><div class="grid grid-cols-3 gap-2"><div class="training-target" data-marker="3" data-label="Tab"><div class="h-9 bg-white border-2 border-mondrian-black"></div></div><div class="training-target" data-marker="4" data-label="Ativa"><div class="h-9 bg-forge-pink border-2 border-mondrian-black"></div></div><div class="h-9 bg-white border-2 border-mondrian-black"></div></div><div class="training-target" data-marker="5" data-label="Conteúdo"><div class="h-20 border-2 border-mondrian-black bg-white"></div></div><div class="grid grid-cols-[42px_72px_42px] gap-3"><div class="h-8 border-2 border-mondrian-black bg-white"></div><div class="training-target" data-marker="6" data-label="Página"><div class="h-8 bg-mondrian-black"></div></div><div class="h-8 border-2 border-mondrian-black bg-white"></div></div></div>`
213    },
214    {
215        stage: 'Etapa 04',
216        title: 'Monte fluxos reais',
217        goal: 'Feche a trilha conectando componentes em telas completas, estados e interações mais avançadas.',
218        components: ['Modal', 'Drawer', 'Toast', 'Chart', 'Dashboard'],
219        markers: ['Sidebar', 'Header', 'Chart', 'Card', 'Feedback'],
220        focus: 'Veja a tela como fluxo: navegação, conteúdo, dado importante e feedback de ação.',
221        practice: 'Escolha uma tela completa e escreva quais componentes aparecem, em que ordem e por quê.',
222        lessons: [
223            'Abra uma tela completa e tente reconhecer cada bloco do fluxo.',
224            'Estude feedback, overlays e componentes ricos como parte da experiência, não como itens soltos.',
225            'Volte ao catálogo com uma pergunta prática: “o que preciso para montar minha próxima tela?”'
226        ],
227        visual: () => `<div class="training-wireframe grid grid-cols-[70px_1fr] gap-3"><div class="training-target" data-marker="1" data-label="Sidebar"><div class="bg-mondrian-black min-h-[150px]"></div></div><div class="grid gap-3"><div class="training-target" data-marker="2" data-label="Header"><div class="h-10 border-2 border-mondrian-black bg-white"></div></div><div class="grid grid-cols-2 gap-3"><div class="training-target" data-marker="3" data-label="Chart"><div class="h-24 border-2 border-mondrian-black bg-forge-blue"></div></div><div class="training-target" data-marker="4" data-label="Card"><div class="h-24 border-2 border-mondrian-black bg-forge-pink"></div></div></div><div class="training-target" data-marker="5" data-label="Feedback"><div class="h-12 border-2 border-mondrian-black bg-white"></div></div></div></div>`
228    }
229];
230
231const primeirosPassosCodigos = {
232    '01': [
233        { codigo: '/anatomy' },
234        { codigo: '/layout' },
235        { codigo: '/component' }
236    ],
237    '02': [
238        { codigo: '/form' },
239        { codigo: '/feedback' }
240    ],
241    '03': [
242        { codigo: '/navigation' }
243    ],
244    '04': [
245        { codigo: '/flow' },
246        { codigo: '/empty' },
247        { codigo: '/loading' },
248        { codigo: '/error' }
249    ],
250    '05': [
251        { codigo: '/pattern' },
252        { codigo: '/composition' }
253    ],
254    '06': [
255        { codigo: '/screen' },
256        { codigo: '/responsive' }
257    ],
258    '07': [
259        { codigo: '/prompt' },
260        { codigo: '/context' }
261    ]
262};
263
264
265// Slug do plano/curso "Regras e Funcionamento" — precisa bater exatamente
266// com o slug real criado no painel gestor (Cursos -> Novo curso), gerado
267// automaticamente a partir do nome do curso na criação (ver
268// slugifyPlanName em functions/_shared/plans.js) e que não muda depois.
269const REGRAS_FUNCIONAMENTO_PLAN_SLUG = 'regras_e_funcionamentos';
270
271const conceitoJourney = [
272    {
273        stage: 'Aula 01',
274        estimatedMinutes: 35,
275        layout: 'grid',
276        title: 'Estado e decisão',
277        goal: 'Aprenda a reconhecer as regras que definem quando algo pode acontecer, em que situação um item está e até onde ele pode ir — a base de qualquer pedido de funcionamento, não só de visual.',
278        focus: 'Antes de pedir uma tela ou uma funcionalidade, pergunte: isso depende de alguma condição? Em qual status esse item está? Precisa de aprovação? Pode ser cancelado? O que acontece se der errado? Existe um limite?',
279        practice: 'Escolha um fluxo que você já usa (um app de delivery, um sistema de tarefas) e escreva a condição, o status, quem aprova, se cancela, o que é exceção e qual é o limite dele.',
280        exerciseFeedback: 'Não existe resposta única aqui — o que importa é cada frase estar específica: a condição diz exatamente quando algo acontece, o status tem nomes claros, fica definido quem aprova, até quando dá pra cancelar, o que conta como exceção e qual é o limite numérico. Releia sua resposta e veja se um dev ou uma IA entenderiam sem precisar perguntar nada de volta.',
281        lessons: [
282            'Aprenda a diferença entre uma condição (regra de decisão) e um status (estado atual do item).',
283            'Veja como aprovação e cancelamento definem quem controla cada etapa de um fluxo.',
284            'Pratique como descrever exceções e limites sem deixar brechas no pedido.'
285        ],
286        conceitos: [
287            {
288                nome: 'Condição',
289                oQueE: 'Uma regra que diz o que precisa ser verdade pra alguma coisa acontecer. É o "se isso, então aquilo" por trás de qualquer comportamento condicional na tela ou no sistema.',
290                quandoUsar: 'Use quando o comportamento depende de um valor, escolha ou situação específica — por exemplo, um botão que só aparece se o carrinho tiver itens, ou um desconto que só vale acima de certo valor.',
291                pedidoConfuso: 'O botão de finalizar deveria sumir às vezes.',
292                pedidoClaro: 'O botão "Finalizar compra" só deve aparecer quando o carrinho tiver pelo menos 1 item. Se estiver vazio, mostrar o botão desabilitado com a mensagem "Adicione um item para continuar".',
293                termoTecnico: 'Condição (if/condition)',
294                termosRelacionados: ['regra condicional', 'if/else', 'critério', 'requisito']
295            },
296            {
297                nome: 'Status',
298                oQueE: 'O estado atual de um registro dentro de um fluxo — mostra em qual etapa ou situação aquele item está agora (pendente, ativo, concluído, etc.).',
299                quandoUsar: 'Use sempre que um item passa por fases diferentes ao longo do tempo — um pedido, um cadastro, uma tarefa — e você precisa deixar claro em qual fase ele está.',
300                pedidoConfuso: 'Mostra lá se o pedido já foi ou não.',
301                pedidoClaro: 'Cada pedido deve ter um status visível: "Pendente", "Em preparo", "Enviado" ou "Entregue". O status muda automaticamente conforme o pedido avança no fluxo.',
302                termoTecnico: 'Status (state)',
303                termosRelacionados: ['estado', 'etapa do fluxo', 'situação atual']
304            },
305            {
306                nome: 'Aprovação',
307                oQueE: 'Uma etapa em que alguém (ou algum critério automático) precisa confirmar que uma ação pode seguir em frente antes dela valer de fato.',
308                quandoUsar: 'Use quando uma ação tem impacto sério o suficiente pra exigir confirmação de outra pessoa antes de ser executada — reembolsos, publicações, mudanças de permissão.',
309                pedidoConfuso: 'Alguém precisa dar OK antes de liberar.',
310                pedidoClaro: 'Toda solicitação de reembolso acima de R$ 500 precisa de aprovação de um gestor antes de ser processada. Enquanto isso, o status fica "Aguardando aprovação".',
311                termoTecnico: 'Aprovação (approval workflow)',
312                termosRelacionados: ['fluxo de aprovação', 'confirmação', 'revisão']
313            },
314            {
315                nome: 'Cancelamento',
316                oQueE: 'A ação de interromper algo que já estava em andamento ou programado, antes que ele se complete.',
317                quandoUsar: 'Use quando o usuário precisa poder desistir de uma ação já iniciada — um pedido, um agendamento, uma assinatura — e você precisa definir até quando isso é permitido e o que acontece depois.',
318                pedidoConfuso: 'Precisa ter como cancelar.',
319                pedidoClaro: 'O usuário pode cancelar o pedido enquanto o status for "Pendente" ou "Em preparo". Depois que o status virar "Enviado", o cancelamento fica bloqueado e mostra a mensagem "Este pedido já está a caminho".',
320                termoTecnico: 'Cancelamento (cancel action)',
321                termosRelacionados: ['desistência', 'estorno', 'revogação']
322            },
323            {
324                nome: 'Exceção',
325                oQueE: 'Uma situação fora do caminho normal esperado — um erro, uma falha ou um caso especial que o fluxo principal não previu.',
326                quandoUsar: 'Use quando você precisa descrever o que acontece quando algo dá errado ou foge da regra geral — falha de pagamento, dado inválido, item fora de estoque.',
327                pedidoConfuso: 'Trata os erros direitinho.',
328                pedidoClaro: 'Se o pagamento falhar, mostrar a mensagem "Não conseguimos processar seu pagamento" e manter os dados do carrinho salvos para nova tentativa, sem cancelar o pedido automaticamente.',
329                termoTecnico: 'Exceção (exception/edge case)',
330                termosRelacionados: ['caso extremo', 'erro', 'falha', 'cenário fora do padrão']
331            },
332            {
333                nome: 'Limite',
334                oQueE: 'Um teto ou piso definido pra quantidade, valor, tempo ou tentativas permitidas antes que o sistema bloqueie ou avise.',
335                quandoUsar: 'Use quando existe um número máximo (ou mínimo) que não pode ser ultrapassado — tentativas de login, itens no carrinho, caracteres num campo, tempo de sessão.',
336                pedidoConfuso: 'Não deixa exagerar na quantidade.',
337                pedidoClaro: 'O campo "Quantidade" aceita no máximo 10 unidades por pedido. Ao tentar digitar mais, mostrar a mensagem "Limite de 10 unidades por pedido" e travar o valor em 10.',
338                termoTecnico: 'Limite (limit/threshold)',
339                termosRelacionados: ['teto', 'máximo', 'mínimo', 'quota']
340            }
341        ],
342        codigos: [
343            { codigo: '/conditions' },
344            { codigo: '/status' },
345            { codigo: '/decision' },
346            { codigo: '/exceptions' }
347        ]
348    },
349    {
350        stage: 'Aula 02',
351        estimatedMinutes: 35,
352        layout: 'timeline',
353        sequenceCount: 3,
354        connectors: ['dispara', 'avisa'],
355        title: 'Automação e tempo: o que o sistema faz sozinho',
356        goal: 'Aprenda a descrever o que acontece sozinho no sistema — prazos que vencem, ações que disparam automaticamente, avisos que chegam sem pedir e telas que se atualizam na hora.',
357        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: isso tem prazo? O sistema precisa fazer algo sozinho quando esse prazo bate? Alguém precisa ser avisado automaticamente? É um fluxo com etapas obrigatórias? A tela precisa se atualizar sozinha?',
358        practice: 'Pegue o mesmo fluxo que você usou na Aula 01 e acrescente: um prazo, uma ação automática ligada a esse prazo, uma notificação automática, se esse fluxo tem etapas obrigatórias e o que precisa atualizar em tempo real.',
359        exerciseFeedback: 'Não existe resposta única aqui — o que importa é a cadeia ficar específica: qual é o prazo exato, o que dispara quando ele bate, quem é avisado e como, se existem etapas que não podem ser puladas, e o que na tela precisa mudar sozinho sem o usuário atualizar a página. Releia e veja se cada peça da cadeia está clara, não genérica.',
360        lessons: [
361            'Aprenda a diferença entre um prazo (limite de tempo) e uma ação automática (o que acontece quando o prazo bate).',
362            'Veja como notificações automáticas mantêm o usuário informado sem ele precisar ficar checando a tela.',
363            'Pratique como descrever fluxos por etapas e telas que se atualizam em tempo real sem ambiguidade.'
364        ],
365        conceitos: [
366            {
367                nome: 'Prazo',
368                when: 'Ponto de partida',
369                icon: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/>',
370                oQueE: 'Uma data ou horário limite até quando algo precisa acontecer, depois do qual o comportamento muda (expira, atrasa, bloqueia).',
371                quandoUsar: 'Use quando uma ação só é válida até um certo momento — pagamento, cupom, convite, resposta a uma solicitação.',
372                pedidoConfuso: 'Bota um prazo pro cupom.',
373                pedidoClaro: 'O cupom "BEMVINDO10" fica válido por 7 dias a partir do cadastro. Depois de expirar, o campo mostra "Cupom expirado" e não aplica mais o desconto.',
374                termoTecnico: 'Prazo (deadline)',
375                termosRelacionados: ['contagem regressiva', 'expiração', 'vencimento', 'data limite']
376            },
377            {
378                nome: 'Ação automática',
379                when: 'Quando o prazo bate',
380                icon: '<path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"/>',
381                oQueE: 'Algo que o sistema faz sozinho, sem o usuário clicar em nada, quando uma condição ou um tempo é atingido.',
382                quandoUsar: 'Use quando o comportamento deve acontecer sem depender de um clique — cancelamento automático, renovação, arquivamento.',
383                pedidoConfuso: 'Faz isso sozinho depois de um tempo.',
384                pedidoClaro: 'Se o pedido ficar "Aguardando pagamento" por mais de 24 horas, o sistema cancela automaticamente e muda o status pra "Cancelado por falta de pagamento" — sem o usuário precisar fazer nada.',
385                termoTecnico: 'Ação automática (automated trigger)',
386                termosRelacionados: ['gatilho', 'trigger', 'job agendado', 'tarefa em segundo plano']
387            },
388            {
389                nome: 'Notificação automática',
390                when: 'Logo depois da ação',
391                icon: '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/>',
392                oQueE: 'Um aviso (e-mail, push, mensagem na tela) que o sistema dispara sozinho quando algo relevante acontece, sem precisar que alguém peça.',
393                quandoUsar: 'Use quando o usuário precisa saber de uma mudança sem estar olhando a tela naquele momento — pedido enviado, prazo próximo do fim, aprovação concluída.',
394                pedidoConfuso: 'Manda um aviso pro usuário.',
395                pedidoClaro: 'Quando o pedido mudar pra "Enviado", mandar um e-mail automático com o código de rastreio, e também mostrar um sininho de notificação no ícone do perfil.',
396                termoTecnico: 'Notificação automática (automated notification)',
397                termosRelacionados: ['alerta', 'push', 'e-mail transacional', 'aviso em tempo real']
398            },
399            {
400                nome: 'Fluxo por etapas',
401                when: 'Durante todo o processo',
402                icon: '<path d="M4 7l8-4 8 4-8 4-8-4z"/><path d="M4 12l8 4 8-4"/><path d="M4 17l8 4 8-4"/>',
403                oQueE: 'Um processo dividido em passos numerados que precisam acontecer em ordem, um depois do outro, sem pular etapas.',
404                quandoUsar: 'Use quando uma ação só pode avançar depois que a anterior foi concluída — checkout, onboarding, aprovação em cadeia.',
405                pedidoConfuso: 'Faz um passo a passo.',
406                pedidoClaro: 'O cadastro tem 3 etapas obrigatórias em ordem: dados pessoais, endereço e pagamento. O usuário só acessa a etapa 2 depois de preencher a etapa 1 corretamente — não dá pra pular direto pro pagamento.',
407                termoTecnico: 'Fluxo por etapas (step flow / wizard)',
408                termosRelacionados: ['wizard', 'etapas sequenciais', 'checklist de progresso', 'passo a passo']
409            },
410            {
411                nome: 'Atualização em tempo real',
412                when: 'A qualquer momento',
413                icon: '<path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/>',
414                oQueE: 'Quando a tela muda sozinha, na hora, refletindo uma mudança que aconteceu no sistema, sem o usuário precisar atualizar a página.',
415                quandoUsar: 'Use quando a informação muda com frequência e o usuário precisa ver isso instantaneamente — status de entrega, chat, painel de acompanhamento.',
416                pedidoConfuso: 'Atualiza sozinho.',
417                pedidoClaro: 'O status do pedido na tela de acompanhamento muda de "Em preparo" pra "Saiu para entrega" automaticamente, sem o usuário precisar apertar F5 ou sair e voltar na página.',
418                termoTecnico: 'Atualização em tempo real (real-time update)',
419                termosRelacionados: ['live update', 'polling', 'websocket', 'sincronização automática']
420            }
421        ],
422        codigos: [
423            { codigo: '/deadline' },
424            { codigo: '/automation' },
425            { codigo: '/notifications' },
426            { codigo: '/workflow' }
427        ]
428    },
429    {
430        stage: 'Aula 03',
431        estimatedMinutes: 45,
432        layout: 'diagram',
433        diagramCount: 3,
434        title: 'Integração entre sistemas: quando dois sistemas precisam se falar',
435        goal: 'Aprenda a descrever como o seu sistema troca dados com sistemas externos, e as regras internas que controlam a ordem e as condições dessas trocas.',
436        focus: 'Antes de pedir uma integração, pergunte: com qual sistema externo isso conversa? Quem avisa quem quando algo muda? Os dados precisam ficar sincronizados? Existe uma ordem obrigatória entre as ações? Alguma regra deveria bloquear isso em certos casos?',
437        practice: 'Pegue um fluxo que envolva um sistema externo que você conhece (pagamento, e-mail, login social) e descreva: como os dois sistemas se conectam, quem avisa quem, o que precisa ficar sincronizado, e quais regras internas dependem disso.',
438        exerciseFeedback: 'Não existe resposta única — o que importa é distinguir os papéis: qual sistema manda o webhook e qual recebe, o que exatamente precisa ficar sincronizado (e com que frequência), qual ação depende da outra pra acontecer, e qual regra bloquearia isso se uma condição não fosse atendida. Releia e veja se cada peça está específica, não genérica.',
439        lessons: [
440            'Aprenda a diferença entre integração (a conexão em si), webhook (o aviso automático) e sincronização (manter os dados iguais).',
441            'Veja como dependência entre ações define a ordem obrigatória de um processo.',
442            'Pratique como descrever um bloqueio por regra de negócio sem confundir com erro técnico.'
443        ],
444        conceitos: [
445            {
446                nome: 'Integração com sistema externo',
447                icon: '<path d="M4 12h4M16 12h4M9 6l-2 6 2 6M15 6l2 6-2 6"/>',
448                oQueE: 'A conexão entre o seu sistema e outro sistema de fora (de outra empresa ou outro serviço), pra trocar dados ou disparar ações automaticamente.',
449                quandoUsar: 'Use quando o sistema precisa conversar com uma ferramenta externa — gateway de pagamento, serviço de e-mail, CRM, transportadora.',
450                comoFunciona: [
451                    'Você identifica qual sistema externo precisa participar do fluxo (ex.: gateway de pagamento).',
452                    'O sistema externo disponibiliza uma forma de conexão (geralmente uma API ou um webhook).',
453                    'Seu sistema se conecta usando uma credencial de acesso fornecida por esse sistema externo.',
454                    'A partir daí, os dois sistemas trocam informações conforme o fluxo definido.'
455                ],
456                pedidoConfuso: 'Conecta com o sistema deles.',
457                pedidoClaro: 'Quando o pagamento for aprovado no Asaas (sistema externo), nosso sistema recebe essa confirmação automaticamente e libera o acesso do aluno.',
458                segundoExemplo: 'Em vez de cadastrar o endereço manualmente, o checkout usa a integração com o serviço dos Correios (ViaCEP) pra preencher rua, bairro e cidade automaticamente assim que o CEP é digitado.',
459                errosComuns: [
460                    'Não prever o que acontece se o sistema externo estiver fora do ar.',
461                    'Guardar a credencial de acesso em local inseguro ou visível no código.',
462                    'Achar que a integração funciona pra sempre sem monitorar se ela continua ativa.'
463                ],
464                termoTecnico: 'Integração (external integration)',
465                termosRelacionados: ['API', 'sistema terceiro', 'conector', 'integração nativa']
466            },
467            {
468                nome: 'Webhook',
469                icon: '<path d="M12 3v7"/><path d="m8 7 4 3 4-3"/><rect x="4" y="13" width="16" height="7" rx="1"/>',
470                oQueE: 'Um aviso automático que um sistema externo manda pro seu sistema assim que algo acontece do lado dele, sem você precisar ficar perguntando.',
471                quandoUsar: 'Use quando você precisa reagir a um evento que acontece em outro sistema — pagamento confirmado, e-mail entregue, formulário externo enviado.',
472                comoFunciona: [
473                    'Algo acontece no sistema externo (ex.: um pagamento é confirmado).',
474                    'O sistema externo monta um pacote de dados descrevendo esse evento.',
475                    'Ele envia esse pacote pro endereço (endpoint) que você configurou com antecedência.',
476                    'Seu sistema recebe, confere se é confiável, e reage (ex.: libera o acesso do aluno).'
477                ],
478                pedidoConfuso: 'Recebe o aviso deles.',
479                pedidoClaro: 'O Asaas manda um webhook pro nosso sistema assim que um Pix é pago. Assim que recebemos esse aviso, o sistema libera o acesso do aluno automaticamente, sem esperar alguém verificar manualmente.',
480                segundoExemplo: 'Quando um cliente responde "PARE" num SMS, o provedor de SMS manda um webhook avisando, e o sistema remove esse número da lista de envios automaticamente.',
481                errosComuns: [
482                    'Não validar se o webhook realmente veio do sistema externo (brecha de segurança).',
483                    'Não tratar o caso do mesmo webhook chegar duas vezes (duplicidade).',
484                    'Deixar o endpoint lento, fazendo o sistema externo desistir de entregar o aviso.'
485                ],
486                termoTecnico: 'Webhook (callback event)',
487                termosRelacionados: ['callback', 'evento externo', 'notificação de sistema', 'endpoint receptor']
488            },
489            {
490                nome: 'Sincronização',
491                icon: '<path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/>',
492                oQueE: 'Manter os mesmos dados atualizados e iguais em dois lugares diferentes, sem um ficar desatualizado em relação ao outro.',
493                quandoUsar: 'Use quando a mesma informação existe em dois sistemas e precisa estar sempre igual nos dois — estoque, cadastro de cliente, status de pedido.',
494                comoFunciona: [
495                    'Você define qual sistema é a fonte da verdade (quem manda no dado).',
496                    'Toda vez que esse dado muda na fonte, o outro sistema é avisado ou consultado.',
497                    'O outro sistema atualiza sua cópia pra ficar igual.',
498                    'Em caso de conflito (os dois mudam ao mesmo tempo), uma regra decide quem vence.'
499                ],
500                pedidoConfuso: 'Deixa tudo igual nos dois sistemas.',
501                pedidoClaro: 'Quando o estoque muda no sistema de vendas, o número de unidades disponíveis no site precisa atualizar em até 1 minuto, pra não vender um produto que já acabou.',
502                segundoExemplo: 'O nome do aluno cadastrado no sistema de pagamento sincroniza com o cadastro do curs
502o, então se ele corrige o nome no pagamento, o certificado já sai com o nome certo.',
503                errosComuns: [
504                    'Não definir qual sistema é a fonte da verdade, gerando dados conflitantes.',
505                    'Sincronizar com atraso maior do que o negócio realmente precisa.',
506                    'Não ter um jeito de perceber quando a sincronização parou de funcionar.'
507                ],
508                termoTecnico: 'Sincronização (data sync)',
509                termosRelacionados: ['fonte da verdade', 'dessincronização', 'atualização bidirecional', 'replicação']
510            },
511            {
512                nome: 'Dependência entre ações',
513                icon: '<circle cx="6" cy="6" r="2"/><circle cx="6" cy="18" r="2"/><path d="M6 8v8"/><path d="M6 8a6 6 0 0 0 6 6h4"/>',
514                oQueE: 'Quando uma ação só pode acontecer depois que outra ação específica já foi concluída — existe uma ordem obrigatória entre elas.',
515                quandoUsar: 'Use quando uma etapa depende do resultado de outra pra fazer sentido — emitir nota fiscal depois do pagamento, enviar certificado depois da conclusão do curso.',
516                comoFunciona: [
517                    'Você mapeia a ordem natural das ações (o que precisa vir antes).',
518                    'O sistema verifica se a ação anterior já foi concluída antes de liberar a próxima.',
519                    'Se não foi, a ação seguinte fica bloqueada ou escondida.',
520                    'Assim que a condição é atendida, a próxima ação libera automaticamente.'
521                ],
522                pedidoConfuso: 'Isso só pode acontecer depois daquilo.',
523                pedidoClaro: 'O certificado só pode ser emitido depois que todas as 7 aulas estiverem marcadas como concluídas. Se faltar alguma, o botão "Emitir certificado" fica desabilitado com a mensagem "Termine todas as aulas primeiro".',
524                segundoExemplo: 'Só é possível avaliar um produto depois que o pedido está marcado como "Entregue" — antes disso, o botão "Avaliar" nem aparece na tela.',
525                errosComuns: [
526                    'Bloquear a ação sem explicar pro usuário por que ela está bloqueada.',
527                    'Criar dependências circulares (A depende de B que depende de A).',
528                    'Esquecer de liberar a ação automaticamente, exigindo recarregar a página.'
529                ],
530                termoTecnico: 'Dependência entre ações (action dependency)',
531                termosRelacionados: ['pré-requisito', 'ordem de execução', 'bloqueio condicional', 'encadeamento']
532            },
533            {
534                nome: 'Bloqueio por regra',
535                icon: '<rect x="3" y="11" width="18" height="10" rx="1"/><path d="M7 11V8a5 5 0 0 1 10 0v3"/>',
536                oQueE: 'Quando o sistema impede uma ação de acontecer porque uma regra de negócio não foi atendida — não é erro técnico, é uma decisão deliberada.',
537                quandoUsar: 'Use quando existe uma regra de negócio que precisa impedir uma ação específica em vez de deixar ela acontecer e dar errado depois.',
538                comoFunciona: [
539                    'Você define a regra de negócio que precisa ser respeitada.',
540                    'Antes de executar a ação, o sistema checa se a regra está sendo cumprida.',
541                    'Se não estiver, a ação é impedida e o motivo é explicado.',
542                    'Se estiver, a ação segue normalmente.'
543                ],
544                pedidoConfuso: 'Não deixa fazer se não puder.',
545                pedidoClaro: 'Um aluno não pode se matricular num curso avançado sem ter concluído o curso básico primeiro. Se tentar, o sistema mostra a mensagem "Conclua o curso Primeiros Passos antes de acessar este curso" em vez de permitir a matrícula.',
546                segundoExemplo: 'Um cupom de primeira compra é bloqueado automaticamente se o CPF já tiver um pedido anterior no sistema, mesmo com e-mail diferente.',
547                errosComuns: [
548                    'Bloquear sem dizer o motivo, deixando o usuário sem saber o que fazer.',
549                    'Confundir bloqueio por regra com erro técnico — um é intencional, o outro é falha.',
550                    'Regra de bloqueio desatualizada que passa a impedir casos que deveriam ser permitidos.'
551                ],
552                termoTecnico: 'Bloqueio por regra (business rule block)',
553                termosRelacionados: ['regra de negócio', 'validação de acesso', 'restrição', 'pré-condição']
554            }
555        ],
556        codigos: [
557            { codigo: '/integration' },
558            { codigo: '/dependencies' },
559            { codigo: '/rules' }
560        ]
561    },
562    {
563        stage: 'Aula 04',
564        estimatedMinutes: 45,
565        layout: 'groups',
566        title: 'Concorrência e resiliência: o que fazer quando algo repete ou trava',
567        goal: 'Aprenda a descrever como o sistema se protege de ações repetidas, se recupera de falhas temporárias, e lida com tarefas demoradas ou disputadas por mais de uma pessoa ao mesmo tempo.',
568        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: essa ação pode ser disparada duas vezes por acidente? Se falhar, deveria tentar de novo sozinha? Ela demora tempo suficiente pra não travar a tela? Duas pessoas podem disputar a mesma coisa ao mesmo tempo?',
569        practice: 'Pegue uma ação importante de um sistema que você usa (finalizar uma compra, reservar um horário) e descreva: como ela evita duplicidade, o que acontece se falhar, se ela roda em segundo plano, e como o sistema decide quando duas pessoas disputam a mesma coisa.',
570        exerciseFeedback: 'Não existe resposta única — o que importa é cada peça estar concreta: qual identificador evita a duplicidade, quantas vezes e com que intervalo o sistema tenta de novo, o que o usuário vê enquanto o processo roda em segundo plano, e qual mensagem específica aparece pra quem "perde" a disputa por um recurso concorrido. Releia e veja se um dev entenderia sem perguntar nada de volta.',
571        lessons: [
572            'Aprenda a diferença entre evitar uma ação duplicada e simplesmente impedir o usuário de tentar de novo.',
573            'Veja como timeout, novas tentativas e interruptor de circuito trabalham juntos quando algo falha.',
574            'Entenda quando um processo deveria rodar em segundo plano em vez de travar a tela.',
575            'Pratique como descrever o que acontece quando duas ações disputam o mesmo recurso ao mesmo tempo.'
576        ],
577        groups: [
578            { title: 'Lidando com falhas', desc: 'O que fazer quando uma ação falha, demora demais, ou pode ser disparada mais de uma vez.' },
579            {
580                title: 'Simultaneidade',
581                desc: 'O que fazer quando mais de uma coisa acontece — ou é disputada — ao mesmo tempo.',
582                note: 'Os dois trabalham juntos com frequência: uma tarefa demorada rodando em segundo plano ainda pode ser disputada por duas pessoas ao mesmo tempo — por isso o cenário completo abaixo usa os dois no mesmo passo.'
583            }
584        ],
585        scenario: {
586            title: 'Cenário completo: comprando o último ingresso de um evento',
587            intro: 'Um único fluxo real, mostrando onde cada conceito desta aula aparece.',
588            steps: [
589                { text: 'Você clica em "Comprar" — o pedido usa uma chave de idempotência, então clicar duas vezes não gera duas cobranças.', tag: 'Evitar ação duplicada' },
590                { text: 'O sistema chama o gateway de pagamento com um limite de 8 segundos de espera — se não responder a tempo, desiste e avisa.', tag: 'Timeout' },
591                { text: 'Se a chamada falhar por instabilidade, o sistema tenta de novo automaticamente até 3 vezes, com intervalo crescente.', tag: 'Tentar novamente após erro' },
592                { text: 'Se o gateway falhar repetidamente em pouco tempo, o circuito abre e o sistema para de tentar por 2 minutos, mostrando "pagamento temporariamente indisponível" direto.', tag: 'Interruptor de circuito' },
593                { text: 'Enquanto isso, o envio do ingresso por e-mail e a geração do PDF rodam em segundo plano, sem travar a tela de confirmação.', tag: 'Processo em segundo plano' },
594                { text: 'Se outra pessoa tentar comprar o mesmo último ingresso no mesmo segundo, só uma das duas compras é confirmada — a outra recebe "esse ingresso acabou de ser vendido".', tag: 'Operações simultâneas' }
595            ]
596        },
597        conceitos: [
598            {
599                nome: 'Evitar ação duplicada',
600                grupo: 0,
601                icon: '<path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/>',
602                oQueE: 'Garantir que uma mesma ação, mesmo se disparada mais de uma vez sem querer (duplo clique, conexão instável, nova tentativa automática), só produza um resultado — não repete o efeito.',
603                quandoUsar: 'Use quando uma ação tem consequência real que não pode acontecer duas vezes — cobrar um pagamento, enviar um e-mail, criar um pedido.',
604                quandoNaoUsar: 'Não é necessário quando a ação já é naturalmente segura de repetir, como marcar uma notificação como lida — idempotência importa mais quanto maior a consequência de repetir.',
605                comparacao: 'Diferente de "tentar novamente", que é sobre reagir a falhas — idempotência é o que torna seguro tentar de novo sem duplicar o efeito.',
606                comoFunciona: [
607                    'Cada ação recebe um identificador único no momento em que é disparada.',
608                    'Antes de executar, o sistema confere se já existe um resultado registrado pra esse identificador.',
609                    'Se já existe, o sistema devolve o resultado anterior em vez de repetir a ação.',
610                    'Se não existe, executa normalmente e guarda o resultado vinculado a esse identificador.'
611                ],
612                pedidoConfuso: 'Não deixa cobrar duas vezes.',
613                pedidoClaro: 'Se o usuário clicar em "Finalizar compra" duas vezes seguidas (ou a internet cair e o app tentar de novo), o sistema usa o mesmo identificador de pedido e cobra só uma vez — a segunda tentativa devolve o mesmo pedido já criado, sem duplicar.',
614                segundoExemplo: 'Um formulário de cadastro usa o e-mail como identificador — se o usuário reenviar o mesmo formulário, o sistema não cria uma segunda conta, atualiza a existente.',
615                errosComuns: [
616                    'Usar só o botão desabilitado como proteção — não impede duplicidade se a nova tentativa vier de outro lugar.',
617                    'Não guardar o identificador de forma que sobreviva a uma queda do sistema no meio da ação.',
618                    'Confundir "evitar duplicado" com "impedir o usuário de tentar de novo" — a ideia é permitir a tentativa, só não duplicar o efeito.'
619                ],
620                termoTecnico: 'Idempotência (idempotency)',
621                termosRelacionados: ['chave de idempotência', 'ação repetível', 'deduplicação', 'identificador único de operação']
622            },
623            {
624                nome: 'Tentar novamente após erro',
625                grupo: 0,
626                icon: '<path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v6h-6"/>',
627                oQueE: 'Quando uma ação falha por um motivo temporário, o sistema tenta executá-la de novo automaticamente, em vez de desistir na primeira falha.',
628                quandoUsar: 'Use quando a falha pode ser passageira — uma conexão instável, um sistema externo temporariamente fora do ar, um tempo limite excedido.',
629                quandoNaoUsar: 'Não use quando o erro é definitivo, como um cartão recusado por saldo insuficiente — tentar de novo nesses casos só atrasa mostrar o problema real pro usuário.',
630                comparacao: 'Só é seguro fazer isso se a ação for idempotente — senão, tentar de novo pode duplicar o efeito da primeira tentativa.',
631                comoFunciona: [
632                    'A ação falha por um motivo que pode ser temporário.',
633                    'O sistema espera um tempo antes de tentar de novo, em vez de tentar imediatamente sem parar.',
634                    'Tenta executar a ação novamente.',
635                    'Se continuar falhando, aumenta o tempo de espera a cada tentativa, até desistir depois de um número máximo.'
636                ],
637                pedidoConfuso: 'Tenta de novo se der erro.',
638                pedidoClaro: 'Se o envio do webhook de pagamento falhar, o sistema tenta de novo em 1 minuto, depois em 5 minutos, depois em 15 minutos — até 3 tentativas. Se todas falharem, marca o pedido como "Pendente de confirmação" e avisa o suporte.',
639                segundoExemplo: 'Ao fazer upload de uma foto de perfil e a conexão cair no meio, o app tenta reenviar automaticamente até 2 vezes antes de mostrar a mensagem "Não foi possível enviar a foto".',
640                errosComuns: [
641                    'Tentar de novo infinitamente sem limite, deixando o problema mascarado.',
642                    'Tentar de novo imediatamente e sem intervalo crescente, sobrecarregando um sistema que já está com problema.',
643                    'Repetir uma ação sem garantir também que ela não duplique o efeito (por isso anda junto com idempotência).'
644                ],
645                termoTecnico: 'Nova tentativa (retry / backoff)',
646                termosRelacionados: ['backoff exponencial', 'número máximo de tentativas', 'falha temporária', 'fila de reprocessamento']
647            },
648            {
649                nome: 'Timeout',
650                grupo: 0,
651                icon: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/>',
652                oQueE: 'Um limite de tempo que o sistema espera por uma resposta antes de desistir e tratar como falha, em vez de ficar esperando indefinidamente.',
653                quandoUsar: 'Use quando uma ação depende de uma resposta externa que pode nunca chegar — uma chamada pra outro sistema, uma consulta ao banco, um upload.',
654                quandoNaoUsar: 'Não faz sentido quando a ação é instantânea por natureza, como validar um campo localmente na tela — timeout é pra esperas que dependem de algo externo.',
655                comparacao: 'Trabalha junto com "tentar novamente": o timeout decide QUANDO desistir de esperar; o retry decide o que fazer depois disso.',
656                comoFunciona: [
657                    'Antes de disparar a ação, você define quanto tempo é razoável esperar.',
658                    'O sistema inicia a ação e começa a contar esse tempo.',
659                    'Se a resposta chegar dentro do prazo, segue normalmente.',
660                    'Se o tempo esgotar sem resposta, o sistema desiste, trata como erro e libera pra seguir.'
661                ],
662                pedidoConfuso: 'Não deixa travar esperando pra sempre.',
663                pedidoClaro: 'Se a consulta de frete não responder em 5 segundos, o sistema desiste, mostra "Não foi possível calcular o frete agora" e libera o checkout com frete a combinar, em vez de travar a tela esperando indefinidamente.',
664                segundoExemplo: 'Uma chamada de vídeo desliga sozinha se a outra pessoa não atender em 45 segundos, em vez de ficar chamando pra sempre.',
665                errosComuns: [
666                    'Não definir timeout nenhum, deixando o sistema vulnerável a travar esperando um sistema externo lento.',
667                    'Definir um timeout curto demais, cancelando ações que só estavam um pouco lentas.',
668                    'Tratar o timeout só como "deu erro" sem considerar que a ação pode ter sido concluída do outro lado mesmo sem resposta a tempo.'
669                ],
670                termoTecnico: 'Timeout (tempo limite de espera)',
671                termosRelacionados: ['tempo limite', 'cancelamento por inatividade', 'resposta lenta', 'prazo de resposta']
672            },
673            {
674                nome: 'Interruptor de circuito',
675                grupo: 0,
676                icon: '<path d="M12 2v6"/><path d="M18.4 6.6a9 9 0 1 1-12.8 0"/>',
677                oQueE: 'Um mecanismo que percebe quando um sistema externo está falhando repetidamente e para de chamá-lo por um tempo, em vez de continuar tentando e piorando o problema.',
678                quandoUsar: 'Use quando repetidas falhas de um sistema externo podem sobrecarregar ainda mais esse sistema ou travar o seu próprio sistema esperando por ele.',
679                quandoNaoUsar: 'Não vale a pena pra chamadas muito raras ou de baixíssimo risco — o custo de implementar e manter o circuito não compensa nesses casos.',
680                comparacao: 'Enquanto o timeout limita UMA chamada, o interruptor de circuito olha o HISTÓRICO de várias chamadas pra decidir se vale a pena nem tentar.',
681                comoFunciona: [
682                    'O sistema monitora quantas vezes seguidas uma chamada a um serviço externo falhou.',
683                    'Se as falhas passarem de um limite, o circuito "abre" — o sistema para de tentar chamar esse serviço por um tempo.',
684                    'Enquanto o circuito está aberto, o sistema responde imediatamente com uma alternativa, sem nem tentar.',
685                    'Depois de um tempo, o sistema tenta de novo com cautela; se funcionar, volta ao normal.'
686                ],
687                pedidoConfuso: 'Para de tentar se tiver dando muito erro.',
688                pedidoClaro: 'Se o serviço de recomendação de produtos falhar 5 vezes seguidas, o sistema para de chamá-lo por 2 minutos e mostra uma lista padrão de produtos populares nesse período, em vez de deixar a página inteira lenta esperando um serviço que já está com problema.',
689                segundoExemplo: 'Um app de transporte para de tentar buscar a localização em tempo real de um motorista se o serviço de mapas falhar repetidamente, e mostra a última localização conhecida com um aviso, em vez de travar o app tentando de novo sem parar.',
690                errosComuns: [
691                    'Não ter um plano B pra quando o circuito está aberto, deixando a experiência tão ruim quanto o erro original.',
692                    'Deixar o circuito aberto pra sempre, sem nunca testar se o sistema externo já voltou a funcionar.',
693                    'Confundir com retry — um evita insistir, o outro insiste com intervalo; geralmente os dois trabalham juntos.'
694                ],
695                termoTecnico: 'Interruptor de circuito (circuit breaker)',
696                termosRelacionados: ['circuito aberto', 'plano B', 'degradação graciosa', 'disjuntor']
697            },
698            {
699                nome: 'Processo em segundo plano',
700                grupo: 1,
701                icon: '<path d="M12 2 2 7l10 5 10-5-10-5Z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>',
702                oQueE: 'Uma tarefa que roda por conta própria, sem travar a tela nem exigir que o usuário fique esperando ela terminar.',
703                quandoUsar: 'Use quando a tarefa demora mais do que o usuário deveria esperar olhando pra tela parada — gerar um relatório grande, processar um vídeo, enviar um lote de e-mails.',
704                quandoNaoUsar: 'Não use quando o usuário precisa da confirmação imediata pra continuar, como saber se o pagamento passou antes de fechar a tela — nesses casos, a espera é necessária.',
705                comparacao: 'Diferente de operações simultâneas, que é sobre DISPUTA por um recurso — processo em segundo plano é sobre NÃO travar a tela enquanto algo demorado roda.',
706                comoFunciona: [
707                    'O usuário dispara a ação, e o sistema confirma que recebeu o pedido na hora.',
708                    'A tarefa pesada é colocada numa fila separada, fora do caminho principal da tela.',
709                    'O sistema processa essa fila no seu próprio ritmo, sem travar mais nada.',
710                    'Quando termina, o usuário é avisado (notificação, e-mail, ou a tela atualiza sozinha).'
711                ],
712                pedidoConfuso: 'Processa isso sem travar a tela.',
713                pedidoClaro: 'Ao clicar em "Exportar relatório completo", a tela mostra "Preparando seu relatório..." na hora e libera o usuário pra continuar usando o sistema. Quando o relatório fica pronto, chega uma notificação com o link pra baixar.',
714                segundoExemplo: 'Depois que um vídeo é enviado pra plataforma, ele aparece como "Processando" na lista — o usuário já pode sair da tela, e o vídeo fica disponível assim que o processamento em segundo plano termina.',
715                errosComuns: [
716                    'Deixar o usuário sem nenhum feedback de que a tarefa está rodando (parece que travou).',
717                    'Não avisar quando o processo termina, obrigando o usuário a ficar checando manualmente.',
718                    'Rodar em segundo plano uma ação que na verdade precisa de confirmação imediata do usuário.'
719                ],
720                termoTecnico: 'Processamento assíncrono (background job)',
721                termosRelacionados: ['fila de tarefas', 'job assíncrono', 'processamento em lote', 'worker']
722            },
723            {
724                nome: 'Operações simultâneas',
725                grupo: 1,
726                icon: '<circle cx="8" cy="8" r="3"/><circle cx="16" cy="16" r="3"/><path d="M11 8h5M8 11v5"/>',
727                oQueE: 'Quando mais de uma ação tenta acontecer ao mesmo tempo sobre o mesmo dado, e o sistema precisa decidir como lidar com isso sem gerar inconsistência.',
728                quandoUsar: 'Use quando existe risco real de duas pessoas (ou dois processos) mexerem na mesma coisa ao mesmo tempo — a última vaga de um evento, o mesmo cupom, o mesmo horário sendo agendado.',
729                quandoNaoUsar: 'Não é preciso se o recurso nunca é disputado por mais de uma pessoa ao mesmo tempo — a complexidade de travar só vale a pena quando existe risco real de conflito.',
730                comparacao: 'Diferente de processo em segundo plano, que é sobre demora — aqui o problema é DISPUTA, mesmo que a ação seja rápida.',
731                comoFunciona: [
732                    'Duas ações chegam quase ao mesmo tempo, mirando no mesmo recurso (ex.: a última vaga de um curso).',
733                    'O sistema trava esse recurso brevemente pra processar uma ação de cada vez, nunca as duas juntas.',
734                    'A primeira ação a chegar é processada e o recurso é atualizado (ex.: vaga ocupada).',
735                    'A segunda ação, ao ser processada, já encontra o recurso indisponível e recebe uma resposta clara.'
736                ],
737                pedidoConfuso: 'Não deixa dar erro se dois mexerem ao mesmo tempo.',
738                pedidoClaro: 'Se dois alunos tentarem comprar a última vaga de uma turma no mesmo segundo, o sistema processa um pedido primeiro, marca a vaga como ocupada, e mostra pro segundo aluno "Essa vaga acabou de ser preenchida" em vez de deixar os dois pagarem pela mesma vaga.',
739                segundoExemplo: 'Dois atendentes tentam responder o mesmo chamado de suporte ao mesmo tempo — o sistema atribui o chamado a quem clicou primeiro e avisa o segundo que o chamado já foi assumido.',
740                errosComuns: [
741                    'Não travar o recurso durante o processamento, permitindo as duas ações passarem (overselling).',
742                    'Travar por tempo demais, fazendo outras ações legítimas esperarem sem necessidade.',
743                    'Mostrar um erro técnico genérico em vez de explicar que outra pessoa já pegou aquilo primeiro.'
744                ],
745                termoTecnico: 'Concorrência (concurrency / race condition)',
746                termosRelacionados: ['condição de corrida', 'travamento otimista', 'travamento pessimista', 'conflito de escrita']
747            }
748        ],
749        codigos: [
750            { codigo: '/idempotency' },
751            { codigo: '/retry' },
752            { codigo: '/edgecases' }
753        ]
754    },
755    {
756        stage: 'Aula 05',
757        estimatedMinutes: 50,
758        layout: 'groups',
759        title: 'Permissões e controle de acesso: quem pode fazer o quê',
760        goal: 'Aprenda a descrever quem tem autorização pra ver ou fazer algo no sistema, e até onde esse acesso se espalha.',
761        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: quem pode fazer essa ação? Existe um responsável específico por esse item? Quem consegue nem ver que ele existe? Se der acesso a um grupo, isso libera tudo dentro dele automaticamente?',
762        practice: 'Pegue uma área do seu sistema que envolve mais de uma pessoa (um documento, uma pasta, um projeto) e descreva: quais papéis existem, quais permissões cada um tem, quem é o dono, quem consegue ver e o que a herança de acesso libera automaticamente.',
763        exerciseFeedback: 'Não existe resposta única — o que importa é distinguir claramente: o papel (o pacote de permissões), a permissão específica (a ação isolada), quem é o responsável pelo item, quem consegue nem ver que ele existe, e o que se prop
763aga automaticamente pros itens de dentro. Releia e veja se um dev entenderia sem perguntar nada de volta.',
764        lessons: [
765            'Aprenda a diferença entre papel (um pacote de permissões) e permissão (uma autorização isolada).',
766            'Veja como o dono de um recurso se diferencia de quem só tem acesso a ele.',
767            'Pratique como descrever visibilidade e herança de permissão sem misturar os dois conceitos.'
768        ],
769        groups: [
770            { title: 'Quem decide o que pode ser feito', desc: 'Como o sistema determina se alguém tem autorização pra uma ação específica.' },
771            {
772                title: 'Até onde o acesso alcança',
773                desc: 'Quando uma autorização vale só pra um item, ou se estende pros itens dentro dele.',
774                note: 'Os dois grupos andam juntos: decidir quem pode fazer algo não resolve sozinho até onde esse alguém enxerga — por isso o cenário completo abaixo usa os dois no mesmo fluxo.'
775            }
776        ],
777        scenario: {
778            title: 'Cenário completo: compartilhando uma pasta de projeto com o time',
779            intro: 'Um único fluxo real, mostrando onde cada conceito desta aula aparece.',
780            steps: [
781                { text: 'Maria cria a pasta "Contratos 2026" — ela vira automaticamente a dona do recurso.', tag: 'Dono do recurso' },
782                { text: 'Ela convida João como "Editor", um papel que já vem com um pacote pronto de permissões (ver, editar, comentar — não excluir).', tag: 'Papel' },
783                { text: 'Internamente, o sistema checa a permissão "pode_editar" antes de deixar João salvar qualquer alteração.', tag: 'Permissão' },
784                { text: 'A pasta está marcada como "Restrita ao time Jurídico" — quem está fora desse grupo nem encontra a pasta na busca.', tag: 'Visibilidade' },
785                { text: 'Como a pasta tem 12 arquivos dentro, ninguém precisa configurar acesso arquivo por arquivo — o acesso à pasta já libera os 12 automaticamente.', tag: 'Herança de permissão' },
786                { text: 'Quando Maria sai da empresa, seu acesso some, mas a pasta continua existindo — uma regra decide quem vira o novo dono, pra ela não ficar órfã.', tag: 'Dono do recurso' }
787            ]
788        },
789        conceitos: [
790            {
791                nome: 'Papel',
792                grupo: 0,
793                icon: '<circle cx="12" cy="8" r="4"/><path d="M4 21v-2a6 6 0 0 1 12 0v2"/><path d="M17 10a3 3 0 1 0 0-6"/><path d="M20 21v-2a4 4 0 0 0-3-3.87"/>',
794                oQueE: 'Um perfil predefinido que já vem com um conjunto fixo de permissões — em vez de configurar autorização pessoa por pessoa, você atribui um papel e todas as permissões dele valem de uma vez.',
795                quandoUsar: 'Use quando várias pessoas exercem a mesma função e devem ter exatamente as mesmas autorizações — editor, revisor, administrador, convidado.',
796                quandoNaoUsar: 'Não crie um papel novo pra uma autorização que só uma pessoa específica vai ter — nesse caso, dê a permissão direto pra ela, sem inventar um papel exclusivo.',
797                comparacao: 'Diferente de permissão, que é uma autorização isolada — papel é um pacote pronto de várias permissões, pensado pra não repetir a mesma configuração pra cada pessoa.',
798                comoFunciona: [
799                    'Você define os papéis que existem no sistema (ex.: Admin, Editor, Visitante).',
800                    'Cada papel recebe um conjunto fixo de permissões vinculadas a ele.',
801                    'Uma pessoa é associada a um ou mais papéis.',
802                    'O sistema soma as permissões de todos os papéis dela pra decidir o que ela pode fazer.'
803                ],
804                pedidoConfuso: 'Cria um nível de acesso pra cada pessoa.',
805                pedidoClaro: 'Crie três papéis: "Admin" (acesso total), "Editor" (pode criar e editar, não pode excluir nem convidar gente) e "Visitante" (só visualiza). Cada membro do time recebe um desses três papéis.',
806                segundoExemplo: 'Num fórum, o papel "Moderador" já vem com permissão de apagar comentários e banir usuários, sem precisar marcar essas duas permissões manualmente pra cada moderador novo.',
807                errosComuns: [
808                    'Criar um papel novo pra cada exceção, até virar impossível saber quem pode o quê.',
809                    'Dar poderes de mais a um papel só porque "vai ser mais prático", sem revisar se é realmente necessário.',
810                    'Esquecer de remover o papel de alguém que trocou de função dentro do time.'
811                ],
812                termoTecnico: 'Papel (role)',
813                termosRelacionados: ['role-based access', 'perfil de acesso', 'grupo de permissões', 'nível de acesso']
814            },
815            {
816                nome: 'Permissão',
817                grupo: 0,
818                icon: '<rect x="3" y="11" width="18" height="10" rx="1"/><path d="M7 11V8a5 5 0 0 1 10 0v3"/><circle cx="12" cy="16" r="1.5"/>',
819                oQueE: 'Uma autorização específica pra executar uma ação sobre um recurso — não é "o que a pessoa é", é "o que ela pode fazer" (editar, excluir, aprovar, ver).',
820                quandoUsar: 'Use quando uma ação precisa de checagem individual antes de ser liberada, independentemente de papel — inclusive pra dar uma exceção pontual a alguém.',
821                quandoNaoUsar: 'Não use permissão avulsa pra algo que várias pessoas do mesmo grupo sempre vão precisar — nesse caso, agrupe num papel em vez de repetir a mesma configuração pessoa por pessoa.',
822                comparacao: 'Permissão é o item individual; papel é o pacote. Um papel "Editor" é, por baixo dos panos, só uma lista de permissões marcadas como concedidas.',
823                comoFunciona: [
824                    'Cada ação sensível do sistema é identificada por um nome de permissão (ex.: "excluir_pedido").',
825                    'Antes de executar essa ação, o sistema verifica se a pessoa tem essa permissão — direto ou via um papel.',
826                    'Se tiver, a ação roda normalmente.',
827                    'Se não tiver, a ação é bloqueada e a interface nem mostra o botão, ou mostra desabilitado.'
828                ],
829                pedidoConfuso: 'Só quem pode, faz.',
830                pedidoClaro: 'A ação "Excluir pedido" só fica disponível pra quem tem a permissão "excluir_pedido". Sem essa permissão, o botão nem aparece na tela — não é só desabilitado, é removido.',
831                segundoExemplo: 'Um estagiário pode ter a permissão "ver_relatorio_financeiro" liberada como exceção pontual, sem ganhar o papel completo de "Gestor financeiro".',
832                errosComuns: [
833                    'Checar a permissão só no frontend (esconder o botão) sem validar de novo no backend — qualquer um pode chamar a API direto.',
834                    'Nomear permissões de forma vaga ("acesso especial") em vez de descrever a ação exata que ela libera.',
835                    'Deixar uma permissão perigosa (ex.: excluir dados) sem exigir confirmação extra antes de executar.'
836                ],
837                termoTecnico: 'Permissão (permission)',
838                termosRelacionados: ['autorização', 'ação permitida', 'controle de acesso granular', 'permission grant']
839            },
840            {
841                nome: 'Dono do recurso',
842                grupo: 0,
843                icon: '<path d="M12 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z"/><path d="M12 15v6"/><path d="m9 18 3 3 3-3"/>',
844                oQueE: 'A pessoa (ou conta) responsável por um item específico — geralmente quem criou, mas pode ser transferido — e que costuma ganhar automaticamente permissões totais sobre aquele item.',
845                quandoUsar: 'Use quando um item precisa de um responsável claro, mesmo que outras pessoas tenham acesso a ele — quem decide excluir, transferir ou mudar a visibilidade.',
846                quandoNaoUsar: 'Não force um "dono" em recursos que são genuinamente coletivos desde o início (ex.: um documento criado em conjunto por um time) — nesse caso, o time inteiro pode ser o responsável, sem um dono individual.',
847                comoFunciona: [
848                    'Quando um item é criado, quem criou vira automaticamente o dono.',
849                    'O dono recebe todas as permissões sobre aquele item, mesmo sem estar em nenhum papel especial.',
850                    'O dono pode transferir a titularidade pra outra pessoa, se o sistema permitir.',
851                    'Se o dono deixa de existir no sistema (conta removida), uma regra decide o que acontece — ficar órfão não é uma opção segura.'
852                ],
853                pedidoConfuso: 'Quem criou pode mexer em tudo.',
854                pedidoClaro: 'Quem cria uma pasta de projeto vira o dono dela automaticamente, com permissão total (editar, excluir, convidar, mudar visibilidade). Se o dono sair da empresa, o gestor direto assume automaticamente a titularidade.',
855                segundoExemplo: 'Num app de notas, cada nota tem um único dono, mesmo que ela seja compartilhada com permissão de edição pra outras pessoas.',
856                errosComuns: [
857                    'Não definir o que acontece quando o dono é removido do sistema, deixando o item sem responsável.',
858                    'Confundir "quem criou" com "quem pode decidir sobre o item" — às vezes a titularidade precisa ser transferível.',
859                    'Dar ao dono permissões implícitas demais, sem deixar claro na interface o que exatamente isso inclui.'
860                ],
861                termoTecnico: 'Dono do recurso (resource owner)',
862                termosRelacionados: ['titularidade', 'responsável pelo item', 'ownership', 'transferência de posse']
863            },
864            {
865                nome: 'Visibilidade',
866                grupo: 1,
867                icon: '<path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/>',
868                oQueE: 'A regra que define quem consegue enxergar (não necessariamente editar) um item — público, privado, ou restrito a um grupo específico.',
869                quandoUsar: 'Use quando existe diferença entre "poder editar" e "poder simplesmente ver que aquilo existe" — nem tudo que é editável deveria ser visível a qualquer um, e vice-versa.',
870                quandoNaoUsar: 'Não confunda visibilidade com permissão de edição — um item pode ser visível a todo mundo (público) e ainda assim só o dono poder editá-lo.',
871                comparacao: 'Visibilidade decide quem enxerga; permissão decide o que quem enxerga pode fazer. Um item pode ser visível e ainda assim só permitir leitura.',
872                comoFunciona: [
873                    'Cada item recebe um nível de visibilidade: público, privado ou restrito a um grupo.',
874                    'Ao montar uma busca ou listagem, o sistema filtra o que aparece de acordo com quem está pedindo.',
875                    'Itens fora da visibilidade da pessoa nem aparecem — não é só bloqueado ao clicar, é omitido da lista.',
876                    'O dono (ou um administrador) pode mudar o nível de visibilidade a qualquer momento.'
877                ],
878                pedidoConfuso: 'Deixa privado quem não pode ver.',
879                pedidoClaro: 'A pasta "Contratos 2026" tem visibilidade "Restrita ao time Jurídico" — pessoas fora desse grupo não veem a pasta nem na busca, e não só recebem "acesso negado" ao tentar abrir.',
880                segundoExemplo: 'Um post de rascunho num blog tem visibilidade "Privada" (só o autor vê) até ser publicado, quando passa a "Pública".',
881                errosComuns: [
882                    'Confiar só na interface pra esconder algo, sem bloquear de verdade a consulta no backend.',
883                    'Deixar um item "privado por padrão" trocar sem querer pra "público" numa edição, sem confirmação extra.',
884                    'Misturar visibilidade com status de publicação (rascunho/publicado), quando são regras diferentes.'
885                ],
886                termoTecnico: 'Visibilidade (visibility scope)',
887                termosRelacionados: ['escopo de acesso', 'público/privado', 'acesso restrito', 'nível de exposição']
888            },
889            {
890                nome: 'Herança de permissão',
891                grupo: 1,
892                icon: '<circle cx="6" cy="6" r="2"/><circle cx="6" cy="14" r="2"/><circle cx="6" cy="18" r="2"/><path d="M6 8v4"/><path d="M6 10h6"/>',
893                oQueE: 'Quando o acesso concedido a um item "pai" se aplica automaticamente aos itens "filhos" dentro dele, sem precisar configurar cada um separadamente.',
894                quandoUsar: 'Use quando os itens filhos fazem parte naturalmente do item pai e não faz sentido configurar acesso peça por peça — arquivos dentro de uma pasta, aulas dentro de um curso.',
895                quandoNaoUsar: 'Não use quando um item filho específico precisa de uma regra diferente do resto — nesses casos, uma exceção pontual deve poder sobrescrever a herança, não ser impedida por ela.',
896                comparacao: 'Enquanto visibilidade decide o alcance de um item isolado, herança decide se esse alcance se prop
896aga pros itens dentro dele.',
897                comoFunciona: [
898                    'Um item pai (ex.: pasta) recebe uma permissão ou visibilidade.',
899                    'Todo item criado dentro dele herda essa mesma configuração automaticamente.',
900                    'Se alguém muda a configuração do pai, os filhos sem exceção própria acompanham a mudança.',
901                    'Um filho pode receber uma exceção específica, que passa a valer só pra ele, sobrescrevendo a herança.'
902                ],
903                pedidoConfuso: 'O acesso da pasta vale pra tudo dentro.',
904                pedidoClaro: 'Dar acesso de leitura a uma pasta libera automaticamente a leitura de todos os arquivos dentro dela — exceto o arquivo "Confidencial.pdf", que tem uma exceção própria de acesso restrito só ao gestor.',
905                segundoExemplo: 'Num curso com módulos e aulas, liberar acesso ao módulo "Fundamentos" já libera automaticamente as 5 aulas dentro dele, sem precisar marcar aula por aula.',
906                errosComuns: [
907                    'Herdar permissão em cadeias longas demais sem visibilidade clara de onde a regra "nasceu".',
908                    'Não permitir nenhuma exceção pontual, forçando tudo a seguir exatamente a regra do pai.',
909                    'Mudar a permissão do pai sem avisar que isso vai afetar dezenas de itens filhos de uma vez.'
910                ],
911                termoTecnico: 'Herança de permissão (permission inheritance)',
912                termosRelacionados: ['acesso em cascata', 'propagação de permissão', 'exceção pontual', 'permissão sobrescrita']
913            }
914        ],
915        codigos: [
916            { codigo: '/roles' },
917            { codigo: '/permissions' },
918            { codigo: '/visibility' }
919        ]
920    },
921    {
922        stage: 'Aula 06',
923        estimatedMinutes: 45,
924        layout: 'timeline',
925        sequenceCount: 3,
926        connectors: ['fica registrada em', 'pode ser desfeita com'],
927        title: 'Histórico, auditoria e reversão: o que aconteceu e como desfazer',
928        goal: 'Aprenda a descrever como o sistema guarda o que mudou ao longo do tempo, quando isso é obrigatório, e como permitir desfazer algo com segurança.',
929        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: essa mudança precisa ficar registrada por obrigação? Alguém vai precisar consultar o que já foi diferente antes? Dá pra desfazer um erro sem refazer tudo na mão? Faz sentido ter uma versão em edição separada da versão que está valendo?',
930        practice: 'Pegue uma ação importante de um sistema que você usa (mudar um preço, editar um cadastro) e descreva: o que precisa ficar registrado por obrigação, onde esse histórico fica visível, como alguém desfaria um erro, e se existe uma etapa de rascunho antes de valer de verdade.',
931        exerciseFeedback: 'Não existe resposta única — o que importa é cada peça estar concreta: o que exatamente vira registro obrigatório (e por quê), onde e como o histórico fica consultável, o que acontece quando alguém reverte uma mudança, e se o conteúdo passa por um estágio de rascunho antes de valer publicamente. Releia e veja se um dev entenderia sem perguntar nada de volta.',
932        lessons: [
933            'Aprenda a diferença entre trilha de auditoria (obrigação de registrar) e histórico de alterações (o registro em si, consultável).',
934            'Veja como a reversão depende do histórico existir pra ter o que restaurar.',
935            'Entenda quando vale separar uma versão em edição (rascunho) da versão que está valendo (publicada).',
936            'Pratique a diferença entre guardar mudança por mudança e guardar uma foto completa do estado (snapsh
936ot).'
937        ],
938        conceitos: [
939            {
940                nome: 'Trilha de auditoria em ações sensíveis',
941                when: 'Quando a ação acontece',
942                icon: '<path d="M12 2 3 6v6c0 5 3.8 9.4 9 11 5.2-1.6 9-6 9-11V6l-9-4Z"/>',
943                oQueE: 'A obrigação de registrar quem fez o quê, e quando, sempre que a ação envolve dados sensíveis (financeiros, pessoais, de acesso) — não é opcional, é rastreabilidade exigida.',
944                quandoUsar: 'Use em ações com consequência real ou exigência legal — mudar um valor cobrado, alterar dado de outra pessoa, conceder ou revogar acesso.',
945                quandoNaoUsar: 'Não é necessário auditar toda ação trivial do sistema (ex.: abrir uma página) — isso só gera ruído; reserve pra ações que alguém pode legitimamente perguntar "quem fez isso e por quê".',
946                comparacao: 'Trilha de auditoria decide o QUE precisa virar registro obrigatório; histórico de alterações é ONDE e COMO esse registro fica guardado e consultável.',
947                comoFunciona: [
948                    'Você identifica quais ações são sensíveis o bastante pra exigir registro obrigatório.',
949                    'Toda vez que uma dessas ações roda, o sistema captura quem fez, o quê, e quando, antes de considerar a ação concluída.',
950                    'Esse registro não pode ser editado nem apagado depois — inclusive por quem tem acesso de administrador.',
951                    'Em caso de dúvida ou investigação, esse registro é a fonte confiável do que realmente aconteceu.'
952                ],
953                pedidoConfuso: 'Guarda um log disso.',
954                pedidoClaro: 'Toda mudança no valor cobrado de um plano precisa registrar quem alterou, o valor antigo, o valor novo e a data/hora — esse registro não pode ser editado nem apagado depois, nem por admin.',
955                segundoExemplo: 'Alterar o e-mail de contato de outro usuário (ação feita por um admin do sistema) gera automaticamente um registro de auditoria, mesmo que o admin tenha autorização total pra fazer isso.',
956                errosComuns: [
957                    'Registrar só "usuário X editou algo", sem guardar o valor antes/depois — isso não serve de nada numa investigação.',
958                    'Deixar o próprio registro de auditoria editável, o que anula a confiança nele.',
959                    'Auditar tudo indiscriminadamente, gerando tanto volume de log que ninguém consegue achar o que importa.'
960                ],
961                termoTecnico: 'Trilha de auditoria (audit trail)',
962                termosRelacionados: ['log de auditoria', 'rastreabilidade', 'compliance', 'registro imutável']
963            },
964            {
965                nome: 'Histórico de alterações',
966                when: 'Logo depois, já registrado',
967                icon: '<path d="M3 3v18h18"/><path d="m7 14 4-4 3 3 5-6"/>',
968                oQueE: 'O registro consultável de tudo que mudou num item ao longo do tempo — quem mudou, o que era antes, o que passou a ser, e quando.',
969                quandoUsar: 'Use quando alguém pode legitimamente perguntar "isso sempre foi assim?" ou "quem mudou esse valor?" — dados que evoluem e cuja evolução importa.',
970                comoFunciona: [
971                    'Toda alteração relevante num item gera uma entrada no histórico, em vez de simplesmente sobrescrever o valor antigo.',
972                    'Cada entrada guarda o valor anterior, o valor novo, quem fez e quando.',
973                    'O histórico fica disponível pra consulta, geralmente numa aba ou seção separada do item.',
974                    'Diferente da trilha de auditoria (que é sobre obrigação legal), o histórico pode existir só por utilidade — entender a evolução de um dado.'
975                ],
976                pedidoConfuso: 'Mostra o que mudou.',
977                pedidoClaro: 'A tela de detalhe do aluno tem uma aba "Histórico" listando cada mudança de plano ou status: data, quem mudou (gestor ou automação), valor antigo e valor novo.',
978                segundoExemplo: 'Um documento colaborativo mostra "Editado por Maria há 2 horas, por João há 1 dia" — um resumo do histórico sem precisar abrir o log completo.',
979                errosComuns: [
980                    'Guardar histórico só de alguns campos e não de outros, sem critério claro de por que uns importam e outros não.',
981                    'Não indicar quem fez a mudança quando ela foi automática (cron, webhook) — "sistema" também é uma origem válida a registrar.',
982                    'Deixar o histórico crescer sem paginação, travando a tela quando o item tem muitas mudanças acumuladas.'
983                ],
984                termoTecnico: 'Histórico de alterações (change log)',
985                termosRelacionados: ['log de mudanças', 'linha do tempo do item', 'change history', 'registro de edição']
986            },
987            {
988                nome: 'Reversão',
989                when: 'Quando alguém precisa desfazer',
990                icon: '<path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v6h6"/>',
991                oQueE: 'A capacidade de voltar um item pro estado anterior a uma mudança específica, usando o histórico como base — sem precisar refazer tudo manualmente.',
992                quandoUsar: 'Use quando um erro (humano ou automático) precisa ser desfeito rapidamente, ou quando alguém quer comparar/restaurar uma versão anterior de algo.',
993                quandoNaoUsar: 'Não ofereça reversão em ações que já dis
993pararam efeito colateral externo irreversível (ex.: um pagamento já processado no banco) — nesses casos, a correção precisa de um fluxo próprio, não um simples "desfazer".',
994                comparacao: 'Reversão depende do histórico existir — sem um registro do estado anterior, não tem o que restaurar.',
995                comoFunciona: [
996                    'O sistema identifica qual versão anterior (do histórico) deve ser restaurada.',
997                    'Confirma que restaurar aquele estado não quebra nenhuma outra regra vigente hoje (ex.: um preço antigo que já não é permitido).',
998                    'Aplica os valores da versão anterior sobre o item atual.',
999                    'Registra a própria reversão como uma nova entrada no histórico — desfazer também é uma mudança, e também fica registrado.'
1000                ],
1001                pedidoConfuso: 'Deixa voltar atrás se errar.',
1002                pedidoClaro: 'No editor de curso, o botão "Reverter" ao lado de cada entrada do histórico restaura o conteúdo daquele momento específico, e essa reversão vira uma nova entrada no histórico (não apaga as anteriores).',
1003                segundoExemplo: 'Um gestor que mudou o preço de um plano por engano usa "Reverter última alteração" pra voltar ao valor correto, sem precisar digitar o valor de novo manualmente.',
1004                errosComuns: [
1005                    'Permitir reversão sem registrar que ela aconteceu, apagando o rastro do que foi desfeito.',
1006                    'Reverter sem checar se o estado antigo ainda é válido pelas regras atuais do sistema.',
1007                    'Confundir reversão com exclusão — desfazer uma mudança não deveria apagar a existência do item inteiro.'
1008                ],
1009                termoTecnico: 'Reversão (undo / rollback)',
1010                termosRelacionados: ['desfazer', 'restaurar versão anterior', 'rollback', 'correção retroativa']
1011            },
1012            {
1013                nome: 'Rascunho vs. publicado',
1014                icon: '<rect x="4" y="4" width="16" height="16" rx="2"/><path d="M8 10h8M8 14h5"/>',
1015                oQueE: 'Dois estados paralelos de um mesmo conteúdo: uma versão em edição (rascunho), que ninguém de fora vê, e uma ver
1015são que está valendo de fato (publicado).',
1016                quandoUsar: 'Use quando alguém precisa editar algo com calma, errar, revisar, sem que isso afete quem já está vendo a versão atual — posts, cursos, páginas, textos legais.',
1017                quandoNaoUsar: 'Não force esse modelo em dados que sempre precisam refletir a realidade imediatamente (ex.: status de um pedido) — lá, a mudança precisa valer na hora, não esperar uma "publicação".',
1018                comoFunciona: [
1019                    'O conteúdo existe em duas versões: a publicada (visível a todos) e o rascunho (visível só a quem edita).',
1020                    'Mudanças no rascunho não afetam a versão publicada até uma ação explícita de "publicar".',
1021                    'Ao publicar, o conteúdo do rascunho substitui o publicado, e um novo rascunho pode começar a partir dali.',
1022                    'Enquanto o rascunho existe sem publicar, ele pode ser descartado sem deixar rastro na versão pública.'
1023                ],
1024                pedidoConfuso: 'Deixa editar sem mudar o que já está no ar.',
1025                pedidoClaro: 'O artigo do blog tem um botão "Salvar rascunho" (só o autor vê essa versão) e um botão separado "Publicar" (só aí a mudança aparece pros leitores).',
1026                segundoExemplo: 'Um certificado com erro de digitação pode ser corrigido em rascunho e revisado antes de "republicar" a versão oficial, sem que o aluno veja a versão errada durante a correção.',
1027                errosComuns: [
1028                    'Publicar automaticamente sem uma ação explícita de confirmação, perdendo a proteção que o rascunho deveria dar.',
1029                    'Não deixar claro na interface qual versão (rascunho ou publicada) a pessoa está vendo no momento.',
1030                    'Perder o rascunho ao sair da tela sem salvar, sem nenhum aviso antes.'
1031                ],
1032                termoTecnico: 'Rascunho vs. publicado (draft / published state)',
1033                termosRelacionados: ['estado de publicação', 'versão de trabalho', 'conteúdo ao vivo', 'preview antes de publicar']
1034            },
1035            {
1036                nome: 'Versão/snapshot',
1037                icon: '<circle cx="12" cy="12" r="3"/><path d="M12 2v4M12 18v4M4.9 4.9l2.8 2.8M16.3 16.3l2.8 2.8M2 12h4M18 12h4M4.9 19.1l2.8-2.8M16.3 7.7l2.8-2.8"/>',
1038                oQueE: 'Uma cópia completa do estado de algo num momento específico, guardada pra permitir comparação ou restauração depois, independente do histórico de campo a campo.',
1039                quandoUsar: 'Use quando faz sentido guardar o item inteiro de uma vez (não só o que mudou) — antes de uma operação arriscada, ou em marcos importantes (ex.: versão enviada pro cliente).',
1040                quandoNaoUsar: 'Não crie um snapshot pra toda mudança pequena — isso pesa em armazenamento; reserve pra momentos que realmente merecem uma foto completa do estado.',
1041                comparacao: 'Diferente de histórico de alterações (que registra mudanças campo a campo), snapshot guarda o item inteiro de uma vez, como uma foto — mais pesado, mas mais simples de restaurar de uma vez só.',
1042                comoFunciona: [
1043                    'Em um momento definido (manual ou automático), o sistema copia o estado completo do item.',
1044                    'Essa cópia fica guardada com uma identificação (data, versão, motivo).',
1045                    'A pessoa pode comparar duas versões lado a lado, ou restaurar o item inteiro pra aquele estado.',
1046                    'Snapshots antigos e sem uso podem ser descartados depois de um tempo, pra não acumular indefinidamente.'
1047                ],
1048                pedidoConfuso: 'Guarda uma cópia de segurança.',
1049                pedidoClaro: 'Antes de rodar a migração que reorganiza os dados dos alunos, o sistema tira um snapshot completo da tabela, identificado com data e motivo, guardado por 30 dias.',
1050                segundoExemplo: 'Um editor de design salva "Versão 3 — aprovada pelo cliente" como snapshot, podendo comparar com a "Versão 5 — em revisão" atual a qualquer momento.',
1051                errosComuns: [
1052                    'Tirar snapshot de tudo o tempo todo, sem critério, lotando o armazenamento sem necessidade real.',
1053                    'Não guardar o motivo/contexto do snapshot, deixando uma lista de versões sem significado nenhum meses depois.',
1054                    'Confundir snapshot com backup de segurança do sistema inteiro — são propósitos diferentes, mesmo que pareçam parecidos.'
1055                ],
1056                termoTecnico: 'Versão/snapshot (state snapshot)',
1057                termosRelacionados: ['ponto de restauração', 'cópia de estado', 'versionamento', 'comparação de versões']
1058            }
1059        ],
1060        codigos: [
1061            { codigo: '/audit' },
1062            { codigo: '/history' },
1063            { codigo: '/revert' },
1064            { codigo: '/draft' }
1065        ],
1066        scenario: {
1067            title: 'Cenário completo: corrigindo um erro no plano de um aluno',
1068            intro: 'Um único fluxo real, mostrando onde cada conceito desta aula aparece.',
1069            steps: [
1070                { text: 'O gestor muda por engano o preço do plano "Equipe" de R$ 199 pra R$ 19 — como é uma ação sensível (valor cobrado), o sistema registra automaticamente quem fez e quando.', tag: 'Trilha de auditoria' },
1071                { text: 'Essa mudança aparece na aba "Histórico" do plano, mostrando o valor antigo (R$ 199) e o novo (R$ 19).', tag: 'Histórico de alterações' },
1072                { text: 'Um segundo gestor percebe o erro e clica em "Reverter última alteração" — o preço volta pra R$ 199, e essa correção também vira uma nova entrada no histórico.', tag: 'Reversão' },
1073                { text: 'Antes de qualquer uma dessas mudanças valer pros alunos, ela passa por um estágio de rascunho revisável, evitando que um erro de digitação chegue direto ao público.', tag: 'Rascunho vs. publicado' },
1074                { text: 'Como essa era uma mudança importante de política de preço, o sistema também guardou um snapshot completo do plano antes da alteração, pra restaurar tudo de uma vez se precisasse.', tag: 'Versão/snapshot' }
1075            ]
1076        }
1077    },
1078    {
1079        stage: 'Aula 07',
1080        estimatedMinutes: 30,
1081        layout: 'grid',
1082        title: 'Códigos Secretos: a régua do curso inteiro',
1083        goal: 'Aula bônus. Aqui a gente recapitula, aula por aula, o que o curso ensinou — e logo abaixo de cada recap traz os Códigos Secretos daquela aula: os comandos curtos que transformam o conteúdo em pedido.',
1084        focus: 'Antes de mandar um pedido de sistema pra IA ou pro dev, passe a régua da aula certa: o comportamento tem condição? tem status? algo dispara sozinho? depende de outro sistema? o que acontece se repetir ou travar? quem pode fazer? fica registrado?',
1085        practice: 'Pegue um pedido de funcionalidade que você precisa fazer esta semana. Rode pelo menos 4 códigos desta lista sobre ele — copie os prompts, preencha os [colchetes] com o seu caso, e junte tudo num pedido só.',
1086        exerciseFeedback: 'Não existe resposta única. O que importa: o pedido final não deixa buraco. Cada código que você aplicou virou uma frase específica — condição exata, status nomeados, gatilho de automação claro, o que acontece no erro, quem tem permissão. Releia: um dev ou uma IA conseguiria implementar sem te perguntar nada de volta?',
1087        lessons: [
1088            'Reconheça, num pedido pronto, quais códigos já foram respondidos e quais ficaram de fora.',
1089            'Monte a régua de revisão que você vai usar em todo pedido de sistema daqui pra frente.',
1090            'Combine vários códigos num pedido único, sem repetir nem se contradizer.'
1091        ],
1092        conceitos: [],
1093        bonus: true
1094    }
1095
1096];
1097
1098// Curso "Do Pedido ao Sistema" (Primeiros Passos + Regras e Funcionamento
1099// combinados). Só as 2 aulas de ponte moram aqui — as outras 13 aulas do
1100// curso já existem em `aprender.html`/`combinacoes.html`/`telas.html`/
1101// `prompt-ia.html` (curso 1) e `regras-e-funcionamento.html` (curso 2), e
1102// continuam sendo servidas por lá; o painel do aluno já sabe listar aulas
1103// de páginas diferentes dentro do mesmo curso.
1104const pedidoCompletoJourney = [
1105    {
1106        stage: 'Aula 01',
1107        estimatedMinutes: 35,
1108        layout: 'grid',
1109        title: 'Onde interface e regra se encontram',
1110        goal: 'Aprenda a descrever, num pedido só, o comportamento de tela e a regra do sistema que c
1110ausa esse comportamento — a ponte entre os dois vocabulários que você já aprendeu separados.',
1111        focus: 'Antes de pedir uma tela com comportamento dinâmico, pergunte: o que faz esse botão/campo/selo mudar? Existe uma condição, uma automação, uma permissão ou um histórico por trás? Se a tela muda, alguma coisa no sistema decidiu isso.',
1112        practice: 'Escolha uma tela que você usa com frequência e ache 2 comportamentos de interface (botão que some, campo bloqueado, selo que aparece) e descreva a regra do sistema que causa cada um.',
1113        exerciseFeedback: 'Não existe resposta única — o que importa é conseguir apontar exatamente qual regra (condição, automação, permissão, histórico) explica o comportamento visual, em vez de descrever só o que aparece na tela.',
1114        lessons: [
1115            'Aprenda a reconhecer quando um comportamento "só visual" na verdade é uma regra de sistema se manifestando.',
1116            'Veja 5 pares comuns de interface↔regra, prontos pra reconhecer no seu próprio trabalho.',
1117            'Pratique escrever um pedido que já nasce cobrindo os dois lados de uma vez.'
1118        ],
1119        conceitos: [
1120            {
1121                nome: 'Botão condicional',
1122                icon: '<rect x="3" y="9" width="18" height="6" rx="3"/><path d="M8 12h.01M12 12h.01M16 12h.01"/>',
1123                oQueE: 'Um botão (ou ação) que aparece, some ou fica desabilitado dependendo de uma condição do sistema — a tela não decide sozinha, ela reflete uma regra.',
1124                quandoUsar: 'Toda vez que uma ação só deveria estar disponível sob certas circunstâncias — carrinho vazio, formulário incompleto, prazo vencido.',
1125                comoFunciona: [
1126                    'Você define a condição no backend.',
1127                    'A tela consulta esse estado antes de renderizar o botão.',
1128                    'O botão nasce habilitado, desabilitado ou oculto conforme a condição.',
1129                    'Qualquer mudança na condição deveria atualizar o botão, não só no carregamento inicial.'
1130                ],
1131                pedidoConfuso: 'O botão de finalizar some às vezes.',
1132                pedidoClaro: 'O botão "Finalizar compra" só fica habilitado quando o carrinho tem pelo menos 1 item e a forma de pagamento foi selecionada — a condição vem do backend, a tela só reflete.',
1133                segundoExemplo: 'Um botão "Aprovar" que só aparece pra quem tem a permissão certa — a mesma lógica de condição, aplicada a controle de acesso em vez de estado do pedido.',
1134                errosComuns: [
1135                    'Descrever só o visual ("deixa cinza") sem dizer qual regra decide isso.',
1136                    'Esquecer de dizer o que acontece quando a condição muda depois da tela já carregada.'
1137                ],
1138                termoTecnico: 'Renderização condicional (conditional rendering)',
1139                termosRelacionados: ['regra condicional', 'condição', 'estado da interface']
1140            },
1141            {
1142                nome: 'Formulário bloqueado por regra',
1143                icon: '<rect x="4" y="4" width="16" height="16" rx="2"/><path d="M8 10h8M8 14h5"/><path d="M15 15l3 3M18 15l-3 3"/>',
1144                oQueE: 'Um envio de formulário recusado não por erro de preenchimento, mas por uma regra de negócio — a tela precisa explicar isso diferente de um erro de validação comum.',
1145                quandoUsar: 'Quando existe uma regra que pode impedir a ação mesmo com todos os campos corretos — limite atingido, cadastro duplicado, janela de tempo fechada.',
1146                comoFunciona: [
1147                    'O formulário valida os campos normalmente primeiro.',
1148                    'Ao enviar, o backend checa a regra de negócio.',
1149                    'Se a regra bloquear, devolve um motivo específico.',
1150                    'A tela mostra esse motivo, diferente da mensagem genérica de "campo inválido".'
1151                ],
1152                pedidoConfuso: 'Não deixa cadastrar se não puder.',
1153                pedidoClaro: 'Ao tentar se inscrever numa turma lotada, o formulário passa na validação normal, mas o backend recusa com a regra "vagas esgotadas" — a tela mostra "Esta turma não tem mais vagas" em vez do erro genérico de formulário.',
1154                segundoExemplo: 'Tentar aplicar um cupom de primeira compra numa conta que já comprou antes — os campos estão certos, a regra de negócio que barra.',
1155                errosComuns: [
1156                    'Misturar a mensagem de bloqueio por regra com a de erro de validação, confundindo o usuário sobre o que ele precisa corrigir.'
1157                ],
1158                termoTecnico: 'Validação de regra de negócio (business rule validation)',
1159                termosRelacionados: ['bloqueio por regra', 'regra de negócio', 'validação de acesso']
1160            },
1161            {
1162                nome: 'Feedback de ação automática',
1163                icon: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/>',
1164                oQueE: 'O que a tela mostra enquanto (e depois que) algo acontece sozinho no backend — sem isso, o usuário fica sem saber se algo está rodando, travou, ou já terminou.',
1165                quandoUsar: 'Sempre que um pedido de tela envolve algo que o sistema faz sozinho (automação, processamento, integração) — é preciso pedir também o que a tela mostra durante esse tempo.',
1166                comoFunciona: [
1167                    'A ação automática começa no backend.',
1168                    'A tela entra num estado de "processando" (loading, mensagem, ícone).',
1169                    'Quando termina, a tela atualiza pro resultado (sucesso, erro, ou novo estado).',
1170                    'Se demorar muito, a tela deveria avisar isso também.'
1171                ],
1172                pedidoConfuso: 'Mostra que tá carregando.',
1173                pedidoClaro: 'Enquanto o pagamento é processado automaticamente, a tela mostra "Confirmando pagamento..." com um spinner; se passar de 10 segundos, troca pra "Isso está demorando mais que o normal" sem travar a navegação.',
1174                segundoExemplo: 'Um upload que dispara processamento em segundo plano — a tela mostra "Em processamento" e atualiza sozinha pra "Pronto" quando o backend termina.',
1175                errosComuns: [
1176                    'Pedir a automação no backend mas esquecer de pedir o que a tela mostra enquanto ela roda.',
1177                    'Não dizer o que a tela faz se a automação falhar.'
1178                ],
1179                termoTecnico: 'Estado de carregamento assíncrono (async loading state)',
1180                termosRelacionados: ['ação automática', 'notificação automática', 'processo em segundo plano']
1181            },
1182            {
1183                nome: 'Tela dependente de permissão',
1184                icon: '<path d="M12 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z"/><path d="M5 21c0-3.9 3.1-7 7-7s7 3.1 7 7"/>',
1185                oQueE: 'Quando o próprio layout da tela muda — não só o que é clicável, mas o que existe na tela — dependendo do papel ou permissão de quem está vendo.',
1186                quandoUsar: 'Painéis com mais de um tipo de usuário (admin, aluno, convidado) onde cada um deveria ver uma versão diferente da mesma tela, não a mesma tela com coisas desabilitadas.',
1187                comoFunciona: [
1188                    'A tela identifica o papel/permissão de quem está logado.',
1189                    'Monta os blocos que essa pessoa tem permissão de ver.',
1190                    'Blocos sem permissão nem chegam a ser enviados pro navegador, não é só escondidos via CSS.',
1191                    'A mesma rota de tela serve conteúdo diferente por pessoa.'
1192                ],
1193                pedidoConfuso: 'Esconde as coisas de admin pra quem não é admin.',
1194                pedidoClaro: 'O dashboard mostra o card "Receita do mês" só pra quem tem o papel Financeiro — pra outros papéis, esse card nem é enviado na resposta da tela, não é só ocultado visualmente.',
1195                segundoExemplo: 'Uma barra lateral de navegação onde os itens de menu que aparecem mudam conforme o papel de quem logou.',
1196                errosComuns: [
1197                    'Esconder algo só no CSS/JS (qualquer um inspeciona e vê), quando deveria ser bloqueado também na origem do dado.'
1198                ],
1199                termoTecnico: 'Renderização baseada em permissão (permission-based rendering)',
1200                termosRelacionados: ['papel', 'permissão', 'visibilidade']
1201            },
1202            {
1203                nome: 'Estado visual sincronizado com histórico',
1204                icon: '<path d="M3 3v18h18"/><path d="m7 14 4-4 3 3 5-6"/>',
1205                oQueE: 'Uma indicação na tela (selo, texto, ícone) que reflete que algo mudou e ficou registrado — a tela vira uma janela pro histórico, não só pro estado atual.',
1206                quandoUsar: 'Quando é importante que quem vê a tela saiba que algo não é o valor original — foi editado, revertido, ou está em rascunho.',
1207                comoFunciona: [
1208                    'Toda mudança relevante gera uma entrada no histórico.',
1209                    'A tela consulta se o item tem histórico recente.',
1210                    'Mostra uma indicação visual discreta ("Editado há 2h").',
1211                    'Clicar nessa indicação pode abrir o histórico completo.'
1212                ],
1213                pedidoConfuso: 'Mostra se foi editado.',
1214                pedidoClaro: 'Ao lado do preço do plano, um selo discreto "Editado há 2h por Maria" aparece sempre que houver uma entrada de histórico nas últimas 24h; passado esse prazo, o selo some.',
1215                segundoExemplo: 'Um documento colaborativo mostrando "Você está vendo uma versão restaurada" quando o conteúdo atual veio de uma reversão.',
1216                errosComuns: [
1217                    'Pedir o histórico só no backend e esquecer de pedir a pista visual — sem isso, ninguém sabe que aquele dado tem uma história.'
1218                ],
1219                termoTecnico: 'Indicador de estado auditável (audit-aware UI)',
1220                termosRelacionados: ['histórico de alterações', 'trilha de auditoria', 'rascunho vs. publicado']
1221            }
1222        ]
1223    },
1224    {
1225        stage: 'Aula 02',
1226        layout: 'timeline',
1227        sequenceCount: 4,
1228        connectors: ['leva ao próximo passo', 'leva ao próximo passo', 'fecha com'],
1229        title: 'Cenário completo: pedido de ponta a ponta',
1230        goal: 'Aprenda o framework de 4 passos pra estruturar qualquer pedido completo — tela e regra juntas — e aplique num cenário real inteiro.',
1231        focus: 'Antes de escrever um pedido de ponta a ponta, siga a ordem: primeiro a tela (interface pura), depois o estado inicial (a regra que decide o que ela mostra), depois o que é automático (o que o sistema faz sozinho), e por último quem vê o quê (permissão e histórico).',
1232        practice: 'Escolha um sistema que você usa (reserva de restaurante, biblioteca de livros, agendamento) e escreva um pedido completo cobrindo os 4 passos desta aula: a tela, o estado inicial, o que é automático e quem vê o quê.',
1233        exerciseFeedback: 'Não existe resposta única — o que importa é os 4 passos aparecerem claramente separados, sem misturar regra com visual no passo errado, e o pedido final ser algo que um dev, designer ou IA conseguiria implementar sem perguntar nada de volta.',
1234        lessons: [
1235            'Aprenda a ordem certa de um pedido completo: interface antes de regra, estado antes de automação, automação antes de permissão.',
1236            'Veja o framework de 4 passos aplicado a um cenário real inteiro, do início ao fim.',
1237            'Feche o curso escrevendo seu próprio pedido de ponta a ponta.'
1238        ],
1239        conceitos: [
1240            {
1241                nome: 'Descreva a tela',
1242                when: 'Primeiro passo',
1243                icon: '<rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18"/>',
1244                oQueE: 'O primeiro bloco de qualquer pedido completo: qual tela é, qual o objetivo dela, quais componentes aparecem e qual é a ação principal — o vocabulário de interface puro, sem regra ainda.',
1245                quandoUsar: 'Sempre no início de um pedido de ponta a ponta — antes de falar de regra, quem vai construir precisa saber que tela está sendo pedida.',
1246                comoFunciona: [
1247                    'Nomeie a tela e o objetivo dela em uma frase.',
1248                    'Liste os componentes principais que aparecem.',
1249                    'Diga qual é a ação principal que a tela existe pra permitir.',
1250                    'Deixe pra descrever regra e comportamento nos próximos passos — aqui é só interface.'
1251                ],
1252                pedidoConfuso: 'Faz uma tela de inscrição.',
1253                pedidoClaro: 'Crie a tela "Inscrição no workshop": um card com nome do evento, data, vagas restantes, um formulário com nome e e-mail, e um botão "Confirmar inscrição" como ação principal.',
1254                segundoExemplo: 'Descrever a tela de checkout como "formulário de entrega + resumo do pedido + botão Finalizar compra", antes de falar de qualquer regra de pagamento.',
1255                errosComuns: [
1256                    'Já misturar regra de negócio nesse primeiro passo, perdendo a separação que ajuda quem vai construir.',
1257                    'Esquecer de nomear a ação principal, deixando a tela sem objetivo claro.'
1258                ],
1259                termoTecnico: 'Especificação de interface (UI spec)',
1260                termosRelacionados: ['objetivo da tela', 'componente principal', 'ação primária']
1261            },
1262            {
1263                nome: 'Descreva o estado inicial',
1264                when: 'Depois de definir a tela',
1265                icon: '<circle cx="12" cy="12" r="9"/><path d="M9 12l2 2 4-4"/>',
1266                oQueE: 'O que decide o que a tela mostra ou permite antes de qualquer ação do usuário — a condição e o status por trás do que parece "só visual".',
1267                quandoUsar: 'Sempre que o que a tela mostra depende de alguma regra — vagas disponíveis, prazo aberto, permissão de quem está vendo.',
1268                comoFunciona: [
1269                    'Pergunte: o que precisa ser verdade pra essa tela funcionar do jeito esperado?',
1270                    'Descreva a condição usando o vocabulário de Estado e decisão.',
1271                    'Diga o que a tela mostra quando a condição NÃO é atendida.',
1272                    'Amarre o componente visual à condição: qual botão/mensagem muda quando o estado muda.'
1273                ],
1274                pedidoConfuso: 'Mostra as vagas certinho.',
1275                pedidoClaro: 'O card mostra "12 vagas restantes" enquanto o limite de 30 não for atingido; ao chegar em 0, o botão "Confirmar inscrição" vira desabilitado com o texto "Vagas esgotadas".',
1276                segundoExemplo: 'Um badge "Convidado" só aparece se o status do usuário for "pendente de confirmação" — a condição decide, o badge só reflete.',
1277                errosComuns: [
1278                    'Descrever só o estado "feliz" (vagas disponíveis) e esquecer o que a tela faz quando a condição falha.',
1279                    'Confundir estado inicial com automação — estado é o que já é verdade antes de qualquer ação; automação é o que muda sozinho depois.'
1280                ],
1281                termoTecnico: 'Estado inicial da tela (initial UI state)',
1282                termosRelacionados: ['condição', 'status', 'limite']
1283            },
1284            {
1285                nome: 'Descreva o que é automático',
1286                when: 'Enquanto o aluno usa a tela',
1287                icon: '<path d="M13 2 3 14h7l-1 8 10-12h-7l1-8z"/>',
1288                oQueE: 'O que o sistema faz sozinho depois que o usuário age na tela — e o que a tela mostra enquanto isso acontece, pra ninguém ficar no escuro.',
1289                quandoUsar: 'Sempre que uma ação do usuário dispara algo que não é instantâneo — confirmação por e-mail, processamento, integração com outro sistema.',
1290                comoFunciona: [
1291                    'Diga qual ação do usuário dispara o comportamento automático.',
1292                    'Descreva o que o sistema faz sozinho.',
1293                    'Diga o que a tela mostra enquanto isso roda.',
1294                    'Descreva o que muda na tela quando o processo termina.'
1295                ],
1296                pedidoConfuso: 'Manda o e-mail de confirmação.',
1297                pedidoClaro: 'Ao clicar em "Confirmar inscrição", o sistema dispara automaticamente um e-mail de confirmação; a tela mostra "Confirmando sua vaga..." por até 3 segundos, e depois troca pra "Vaga confirmada! Verifique seu e-mail."',
1298                segundoExemplo: 'Um upload de comprovante que dispara verificação automática em segundo plano, com a tela mostrando "Em análise" até o sistema aprovar ou recusar sozinho.',
1299                errosComuns: [
1300                    'Pedir a automação sem pedir o feedback visual dela, deixando a tela travada sem explicação.',
1301                    'Não dizer o que acontece se a automação falhar.'
1302                ],
1303                termoTecnico: 'Comportamento pós-ação (post-action behavior)',
1304                termosRelacionados: ['ação automática', 'notificação automática', 'feedback de ação automática']
1305            },
1306            {
1307                nome: 'Descreva quem vê o quê',
1308                when: 'Fechando o pedido',
1309                icon: '<path d="M12 15a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z"/><path d="M5 21c0-3.9 3.1-7 7-7s7 3.1 7 7"/>',
1310                oQueE: 'O fechamento do pedido: quem tem permissão de ver ou fazer o quê nessa tela, e se alguma parte precisa ficar registrada por ser sensível.',
1311                quandoUsar: 'Sempre que a tela é usada por mais de um tipo de pessoa (aluno vs. organizador, cliente vs. admin) ou envolve dado que precisa de rastro.',
1312                comoFunciona: [
1313                    'Liste quem usa essa tela (papéis diferentes).',
1314                    'Diga o que cada papel pode ver ou fazer de diferente.',
1315                    'Se a ação for sensível, diga se precisa virar um registro de histórico/auditoria.',
1316                    'Feche o pedido conferindo se os 4 passos desta aula foram cobertos.'
1317                ],
1318                pedidoConfuso: 'Só o organizador vê tudo.',
1319                pedidoClaro: 'A tela "Lista de inscritos" mostra nome e e-mail só pra quem tem o papel Organizador; participantes comuns veem só a contagem total. Toda remoção manual de um inscrito por um organizador fica registrada no histórico com quem removeu e quando.',
1320                segundoExemplo: 'Um painel financeiro visível só pro papel Financeiro, com toda alteração de valor virando entrada de auditoria.',
1321                errosComuns: [
1322                    'Esquecer de checar se algum dado ali é sensível o bastante pra precisar de histórico obrigatório.',
1323                    'Fechar o pedido sem revisar se os 4 passos da aula foram realmente cobertos.'
1324                ],
1325                termoTecnico: 'Controle de acesso e rastreabilidade (access control & traceability)',
1326                termosRelacionados: ['permissão', 'visibilidade', 'trilha de auditoria']
1327            }
1328        ],
1329        scenario: {
1330            title: 'Cenário completo: inscrição num workshop com vagas limitadas',
1331            intro: 'Um único fluxo real, amarrando os 4 passos desta aula — e até conceitos de aulas anteriores dos dois cursos.',
1332            steps: [
1333                { text: 'Tela "Inscrição no workshop": card com nome do evento, vagas restantes, formulário (nome, e-mail) e botão "Confirmar inscrição".', tag: 'Descreva a tela' },
1334                { text: 'Enquanto houver menos de 30 inscritos, o botão fica ativo e mostra "X vagas restantes"; ao bater 30, o botão desabilita e mostra "Vagas esgotadas".', tag: 'Descreva o estado inicial' },
1335                { text: 'Ao confirmar, o sistema dispara um e-mail de confirmação automaticamente; a tela mostra "Confirmando sua vaga..." até o processo terminar, depois "Vaga confirmada!".', tag: 'Descreva o que é automático' },
1336                { text: 'Se dois inscritos confirmarem a última vaga no mesmo segundo, só um consegue — o outro vê "Essa vaga acabou de ser preenchida".', tag: 'Operações simultâneas (curso Regras e Funcionamento)' },
1337                { text: 'A lista de inscritos completa (nome, e-mail) só aparece pra quem tem o papel Organizador; participantes veem só o total.', tag: 'Descreva quem vê o quê' },
1338                { text: 'Se um organizador remove um inscrito manualmente, essa ação vira uma entrada no histórico, com quem removeu e quando.', tag: 'Trilha de auditoria (curso Regras e Funcionamento)' }
1339            ]
1340        }
1341    }
1342];
1343
1344// Slug do plano/curso "Dados e Informações" — precisa bater exatamente com
1345// o slug real criado no painel gestor (gerado automaticamente a partir do
1346// nome do curso, ver slugifyPlanName em functions/_shared/plans.js).
1347const DADOS_INFORMACOES_PLAN_SLUG = 'dados_e_informacoes';
1348const APIS_QUE_FUNCIONAM_PLAN_SLUG = 'apis_que_funcionam';
1349const SEGURANCA_APLICACOES_WEB_PLAN_SLUG = 'seguranca_aplicacoes_web';
1350const PROTOTIPANDO_COM_IA_PLAN_SLUG = 'prototipando_com_ia';
1351const UI_DESCOMPLICADA_PLAN_SLUG = 'ui_descomplicada';
1352
1353function apiConceito(nome, oQueE, quandoUsar, pedidoConfuso, pedidoClaro, termoTecnico, termosRelacionados, extras = {}) {
1354    return {
1355        nome,
1356        oQueE,
1357        quandoUsar,
1358        pedidoConfuso,
1359        pedidoClaro,
1360        termoTecnico,
1361        termosRelacionados,
1362        ...extras
1363    };
1364}
1365
1366const apisQueFuncionamJourney = [
1367    {
1368        stage: 'Aula 01',
1369        layout: 'grid',
1370        title: 'O que é uma API e quando pedir uma',
1371        goal: 'Entenda API como uma conversa organizada entre sistemas, sem precisar programar, para saber quando uma integração é realmente necessária.',
1372        focus: 'Antes de pedir uma API, pergunte: qual sistema precisa falar com qual outro, qual evento inicia a conversa e que resultado deve acontecer depois?',
1373        practice: 'Escolha um fluxo real, como pagamento aprovado, cadastro enviado ou certificado liberado, e descreva quem avisa quem, com quais dad
1373os e para qual ação.',
1374        exerciseFeedback: 'A resposta boa não usa apenas "integrar tudo". Ela mostra origem, destino, informação enviada e ação esperada.',
1375        lessons: [
1376            'API é um jeito combinado de um sistema conversar com outro.',
1377            'Nem toda necessidade de tela ou relatório precisa virar uma API.',
1378            'Um pedido claro explica evento, sistema de origem, sistema de destino e resultado esperado.'
1379        ],
1380        conceitos: [
1381            apiConceito('API', 'Um canal organizado para sistemas trocarem dados ou dispararem ações entre si.', 'Use quando uma informação precisa sair de um sistema e provocar algo em outro.', 'Preciso de uma API para integrar tudo.', 'Quando o pagamento for aprovado no Asaas, a plataforma deve receber o aviso, liberar o curso comprado e enviar o e-mail de acesso.', 'API (Application Programming Interface)', ['integração', 'sistema externo', 'comunicação entre sistemas']),
1382            apiConceito('Integração', 'A conexão prática entre dois ou mais sistemas para que um fluxo aconteça sem trabalho manual.', 'Use quando repetir a mesma ação manualmente gera atraso, erro ou retrabalho.', 'Integra com o pagamento.', 'Ao confirmar o pagamento, o gateway envia os dados do comprador e do curso para a plataforma liberar o acesso.', 'Integração de sistemas', ['automação', 'sincronização', 'gateway']),
1383            apiConceito('Sistema de origem e destino', 'Origem é quem envia a informação; destino é quem recebe e age a partir dela.', 'Use para evitar confusão sobre quem dispara o processo e quem deve mudar estado.', 'Um sistema manda para o outro.', 'O Asaas é a origem do evento de pagamento; a plataforma Como Pedir é o destino que libera o curso.', 'Source and target systems', ['origem', 'destino', 'fonte da verdade'])
1384        ],
1385        codigos: [
1386            { codigo: '/evento-e-resultado' },
1387            { codigo: '/nao-vira-api' }
1388        ]
1389    },
1390    {
1391        stage: 'Aula 02',
1392        estimatedMinutes: 35,
1393        layout: 'grid',
1394        title: 'Métodos, rotas, endpoints e contratos',
1395        goal: 'Aprenda a nomear as peças de uma API para pedir consultas, cadastros e atualizações sem deixar o dev adivinhar a intenção.',
1396        focus: 'Antes de validar um endpoint, confira: método, rota, dados recebidos, resposta esperada e erros previstos.',
1397        practice: 'Transforme "preciso salvar progresso do aluno" em método, rota, payload, resposta e regras importantes.',
1398        exerciseFeedback: 'O pedido fica bom quando a ação principal aparece no método e a rota aponta para o recurso certo.',
1399        lessons: [
1400            'Rota é o caminho do recurso dentro da API.',
1401            'Endpoint junta método e rota para indicar uma ação específica.',
1402            'Contrato define o combinado de entrada, saída, permissões e erros.'
1403        ],
1404        conceitos: [
1405            apiConceito('Método HTTP', 'A ação que indica se a API vai consultar, criar, alterar ou remover algo.', 'Use para deixar clara a intenção do pedido: GET consulta, POST cria, PATCH atualiza parte, DELETE remove.', 'Faz uma rota de aluno.', 'GET /alunos/{id}/acesso consulta se o aluno pode entrar na plataforma.', 'HTTP method', ['GET', 'POST', 'PATCH']),
1406            apiConceito('Rota e endpoint', 'Rota é o caminho; endpoint é o método somado a esse caminho.', 'Use quando precisa especificar exatamente onde a chamada chega e o que ela faz.', 'Cria um endpoint aí.', 'POST /alunos cria um aluno; GET /alunos/{id} consulta um aluno específico.', 'Endpoint', ['rota', 'recurso', 'URL']),
1407            apiConceito('Contrato da API', 'O combinado que define dados enviados, resposta, status e erros esperados.', 'Use antes de desenvolver, testar ou contratar uma integração.', 'Retorna os dados do aluno.', 'A resposta deve trazer aluno_id, nome, curso, acesso_ativo e pagamento_confirmado.', 'API contract', ['payload', 'resposta', 'status HTTP'])
1408        ],
1409        codigos: [
1410            { codigo: '/metodo-pela-intencao' },
1411            { codigo: '/contrato-completo' }
1412        ]
1413    },
1414    {
1415        stage: 'Aula 03',
1416        layout: 'grid',
1417        title: 'Parâmetros, payloads, headers e status HTTP',
1418        goal: 'Entenda onde cada informação entra na chamada da API e como interpretar o resultado que volta.',
1419        focus: 'Separe identificadores na rota, filtros na busca, dados principais no payload, credenciais nos headers e resultado no status HTTP.',
1420        practice: 'Especifique uma consulta de pagamentos confirmados de um aluno, indicando rota, filtros, headers, payload e resposta.',
1421        exerciseFeedback: 'Uma boa resposta separa bem: rota identifica o aluno, query filtra status, payload não é necessário em consulta.',
1422        lessons: [
1423            'Parâmetros de rota identificam um item específico.',
1424            'Payload carrega os dados principais de criação ou atualização.',
1425            'Status HTTP ajuda a saber se deu certo, faltou dado, não há permissão ou algo falhou.'
1426        ],
1427        conceitos: [
1428            apiConceito('Parâmetros', 'Informações usadas para identificar ou filtrar o que a API deve buscar.', 'Use rota para IDs e busca para filtros, ordenação e paginação.', 'Lista os pagamentos.', 'GET /alunos/{aluno_id}/pagamentos?status=confirmado lista pagamentos confirmados daquele aluno.', 'Parameters', ['path parameter', 'query string', 'filtro']),
1429            apiConceito('Payload', 'O corpo da requisição com os dados principais enviados para criar ou atualizar algo.', 'Use em POST, PATCH ou PUT quando a ação precisa carregar informações.', 'Manda os dad
1429os do aluno.', 'POST /alunos recebe nome, email e curso_id no corpo da requisição.', 'Request payload', ['body', 'JSON', 'dados enviados']),
1430            apiConceito('Headers e status', 'Headers levam instruções técnicas; status informa o resultado da chamada.', 'Use para autenticação, formato e leitura clara de sucesso ou erro.', 'Se der erro, avisa.', 'Authorization vai no header; 401 indica token ausente, 403 sem permissão e 404 recurso não encontrado.', 'Headers and HTTP status', ['Authorization', 'Content-Type', '401'])
1431        ],
1432        codigos: [
1433            { codigo: '/separa-entrada' },
1434            { codigo: '/status-por-cenario' }
1435        ]
1436    },
1437    {
1438        stage: 'Aula 04',
1439        layout: 'groups',
1440        title: 'Autenticação em APIs: tokens, sessões e chaves',
1441        goal: 'Entenda como uma API identifica quem está chamando e como separa aluno, gestor e sistema externo.',
1442        focus: 'Antes de proteger uma API, defina quem chama, como prova a identidade, quanto tempo vale o acesso e quais ações são permitidas.',
1443        practice: 'Descreva como um gestor autenticado poderia consultar alunos ativos sem permitir que um aluno comum use o mesmo endpoint.',
1444        exerciseFeedback: 'A resposta certa separa autenticação de autorização: primeiro quem é, depois o que pode fazer.',
1445        lessons: [
1446            'Autenticação responde quem está fazendo a chamada.',
1447            'Autorização responde o que essa pessoa ou sistema pode fazer.',
1448            'Tokens, sessões e chaves resolvem cenários diferentes.'
1449        ],
1450        groups: [
1451            { title: 'Identidade', desc: 'Como a API reconhece quem está chamando.' },
1452            { title: 'Permissão', desc: 'O que aquela identidade pode executar.' }
1453        ],
1454        conceitos: [
1455            apiConceito('Token', 'Credencial temporária enviada geralmente no header Authorization.', 'Use quando uma aplicação precisa chamar a API em nome de um usuário ou acesso temporário.', 'Protege com login.', 'Os endpoints da área do aluno exigem Authorization: Bearer token e só retornam dados do próprio aluno.', 'Bearer token', ['JWT', 'access token', 'Authorization'], { grupo: 0 }),
1456            apiConceito('Sessão', 'Estado de login mantido pelo navegador, normalmente por cookie seguro.', 'Use em painéis web, área do aluno e área administrativa.', 'Mantém o usuário logado.', 'Após login, a sessão do gestor permite abrir o painel sem reenviar senha a cada requisição.', 'Session authentication', ['cookie', 'login', 'sessão segura'], { grupo: 0 }),
1457            apiConceito('Chave de API', 'Credencial usada por uma integração ou sistema externo.', 'Use para chamadas servidor para servidor, com permissões limitadas e rotação.', 'Usa uma chave geral.', 'A integração de pagamento usa uma chave própria que só pode enviar eventos autorizados.', 'API key', ['x-api-key', 'credencial', 'integração externa'], { grupo: 1 })
1458        ],
1459        codigos: [
1460            { codigo: '/quem-esta-chamando' },
1461            { codigo: '/credencial-por-caso' }
1462        ]
1463    },
1464    {
1465        stage: 'Aula 05',
1466        layout: 'grid',
1467        title: 'Validação, erros e respostas bem desenhadas',
1468        goal: 'Aprenda a pedir APIs que conferem dados antes de agir e devolvem mensagens úteis quando algo dá errado.',
1469        focus: 'Toda ação deve prever campos obrigatórios, formato, regra de negócio, permissão, status HTTP e mensagem clara.',
1470        practice: 'Descreva a resposta de API para um aluno tentando criar senha com link expirado.',
1471        exerciseFeedback: 'Uma resposta boa informa o problema sem vazar token, hash, banco de dados ou detalhes internos.',
1472        lessons: [
1473            'Validação evita salvar dado incompleto, duplicado ou sem sentido.',
1474            'Erro bom explica o problema, o campo afetado e como corrigir.',
1475            'Resposta de sucesso confirma o que aconteceu e devolve dados úteis.'
1476        ],
1477        conceitos: [
1478            apiConceito('Validação', 'Checagem feita antes de aceitar ou processar uma informação.', 'Use para campos obrigatórios, formato, duplicidade e regras de negócio.', 'Confere os dados.', 'O campo email é obrigatório, deve ter formato válido e não pode repetir um cadastro existente.', 'Validation', ['campo obrigatório', 'formato', 'regra de negócio']),
1479            apiConceito('Erro desenhado', 'Resposta de erro pensada para orientar pessoa, tela e suporte.', 'Use para evitar mensagens genéricas como "falha" ou "tente novamente".', 'Se der erro mostra uma mensagem.', 'Retornar 409 com código EMAIL_JA_CADASTRADO e mensagem "Este e-mail já está cadastrado".', 'Error response design', ['código de erro', 'mensagem', 'status HTTP']),
1480            apiConceito('Não vazar detalhes internos', 'Evitar expor banco, token, stack trace ou regra sensível na resposta pública.', 'Use em toda API que responde para navegador, app ou sistema externo.', 'Mostra o erro real.', 'Responder "Este e-mail já está cadastrado" em vez de mostrar SQLITE_CONSTRAINT.', 'Safe error handling', ['stack trace', 'dados sensíveis', 'log interno'])
1481        ],
1482        codigos: [
1483            { codigo: '/valida-antes-de-agir' },
1484            { codigo: '/erro-com-codigo' }
1485        ]
1486    },
1487    {
1488        stage: 'Aula 06',
1489        layout: 'timeline',
1490        title: 'Webhooks: quando usar, como receber e como reprocessar',
1491        goal: 'Entenda webhook como aviso automático entre sistemas e aprenda a receber, registrar e reprocessar eventos sem perder controle.',
1492        focus: 'Defina sistema que envia, endpoint que recebe, tipos de evento, validação de origem, resposta rápida e reprocessamento.',
1493        practice: 'Descreva o webhook de um boleto confirmado dois dias depois da compra.',
1494        exerciseFeedback: 'O desenho fica completo quando inclui evento_id, validação de origem, ação esperada e plano para falha parcial.',
1495        lessons: [
1496            'Webhook é o outro sistema avisando que algo aconteceu.',
1497            'A API deve receber, validar, registrar e responder rapidamente.',
1498            'Reprocessamento evita que uma falha temporária vire perda definitiva.'
1499        ],
1500        sequenceCount: 3,
1501        connectors: ['avisa', 'valida', 'registra'],
1502        conceitos: [
1503            apiConceito('Webhook', 'Aviso automático enviado por um sistema quando um evento acontece.', 'Use quando sua plataforma precisa reagir a pagamento, cancelamento, formulário, entrega ou outro evento externo.', 'Quando o pagamento confirmar, libera o aluno.', 'POST /webhooks/pagamentos recebe pagamento.confirmado e inicia a liberação do curs
1503o.', 'Webhook', ['evento', 'callback', 'notificação']),
1504            apiConceito('Evento', 'O fato específico que aconteceu e precisa ser tratado pela plataforma.', 'Use para diferenciar pagamento.confirmado, pagamento.recusado, assinatura.cancelada e outros cenários.', 'Recebe status do pagamento.', 'Se tipo = pagamento.confirmado, libera acesso; se tipo = pagamento.estornado, bloqueia ou sinaliza revisão.', 'Event type', ['pagamento.confirmado', 'payload', 'status']),
1505            apiConceito('Reprocessamento', 'Capacidade de tentar tratar novamente um evento que falhou ou ficou incompleto.', 'Use quando enviar e-mail, atualizar acesso ou chamar outro serviço pode falhar temporariamente.', 'Tenta de novo se falhar.', 'Se o e-mail falhar, o evento fica registrado como falha parcial e pode ser reenviado pelo gestor.', 'Retry / reprocessing', ['fila', 'tentativa', 'falha parcial'])
1506        ],
1507        codigos: [
1508            { codigo: '/webhook-por-tipo' },
1509            { codigo: '/plano-de-reprocessamento' }
1510        ]
1511    },
1512    {
1513        stage: 'Aula 07',
1514        estimatedMinutes: 45,
1515        layout: 'grid',
1516        title: 'Segurança e idempotência em integrações',
1517        goal: 'Aprenda a proteger chamadas externas e impedir que eventos repetidos criem matrículas, cobranças ou e-mails duplicados.',
1518        focus: 'Toda integração deve validar origem, registrar identificador único e saber o que fazer quando recebe a mesma chamada outra vez.',
1519        practice: 'Descreva como tratar um webhook de pagamento aprovado que pode chegar duas vezes.',
1520        exerciseFeedback: 'O melhor desenho retorna sucesso para evento repetido, mas não executa de novo a ação principal.',
1521        lessons: [
1522            'Segurança protege a entrada da integração.',
1523            'Idempotência protege o resultado contra repetição.',
1524            'Logs ajudam suporte e auditoria a entender o que aconteceu.'
1525        ],
1526        conceitos: [
1527            apiConceito('Idempotência', 'Garantia de que repetir a mesma chamada não duplica o resultado.', 'Use em pagamentos, matrícula, envio de e-mail, criação de pedido e qualquer ação crítica.', 'Não deixa duplicar.', 'Se evento_id já foi processado, responder sucesso e não liberar o curso nem reenviar e-mail outra vez.', 'Idempotency', ['evento_id', 'duplicidade', 'reprocessamento']),
1528            apiConceito('Validação de origem', 'Checagem que confirma se a chamada veio de uma fonte confiável.', 'Use em webhooks, integrações externas e endpoints sensíveis.', 'Confia no que chegar.', 'Validar assinatura do webhook antes de processar pagamento confirmado.', 'Webhook signature validation', ['assinatura', 'token', 'chave secreta']),
1529            apiConceito('Registro de integração', 'Histórico do que chegou, quando chegou e qual foi o resultado.', 'Use para suporte, auditoria, reprocessamento e investigação de falhas.', 'Salva um log.', 'Registrar evento_id, pagamento_id, aluno, curso, status, resultado e mensagem de erro se houver.', 'Integration log', ['auditoria', 'histórico', 'observabilidade'])
1530        ],
1531        codigos: [
1532            { codigo: '/idempotente-de-verdade' },
1533            { codigo: '/valida-a-origem' }
1534        ]
1535    },
1536    {
1537        stage: 'Aula 08',
1538        estimatedMinutes: 45,
1539        layout: 'grid',
1540        title: 'Projeto final: desenhar uma API com webhook para um fluxo real',
1541        goal: 'Aplique o curso inteiro criando uma especificação clara de integração para liberar acesso após pagamento confirmado.',
1542        focus: 'Junte objetivo, sistemas envolvidos, endpoint, payload, headers, validação, erros, idempotência, logs e fluxo final.',
1543        practice: 'Monte uma especificação completa para compra de curso, confirmação de pagamento e liberação automática de acesso.',
1544        exerciseFeedback: 'O projeto está pronto quando outra pessoa consegue entender o fluxo sem perguntar "quem envia?", "quem recebe?" ou "o que acontece se falhar?".',
1545        lessons: [
1546            'Projeto final não é programar: é saber pedir e revisar a integração.',
1547            'O fluxo precisa prever sucesso, erro, duplicidade e reprocessamento.',
1548            'Uma especificação clara reduz retrabalho entre negócio, design e desenvolvimento.'
1549        ],
1550        conceitos: [
1551            apiConceito('Especificação de integração', 'Documento curto que explica como sistemas conversam em um fluxo real.', 'Use antes de desenvolver, contratar, estimar ou revisar uma API.', 'Faz integração com pagamento.', 'Gateway envia POST /webhooks/pagamentos; plataforma valida assinatura, libera curso, envia e-mail e registra evento.', 'Integration specification', ['contrato', 'fluxo', 'endpoint']),
1552            apiConceito('Fluxo ponta a ponta', 'Sequência completa desde o evento inicial até o resultado visível para o usuário.', 'Use para não esquecer etapas invisíveis como e-mail, log, falha parcial e suporte.', 'Quando comprar libera.', 'Aluno compra, gateway confirma, webhook chega, plataforma valida, matrícula é 
1552criada, e-mail é enviado e progresso aparece no painel.', 'End-to-end flow', ['jornada', 'processo', 'automação']),
1553            apiConceito('Checklist de entrega', 'Lista final que confirma se a integração está pronta para ser implementada ou revisada.', 'Use no fechamento de briefing, aprovação de escopo e testes.', 'Confere se está ok.', 'Validar endpoint, payload, autenticação, status, erros, idempotência, logs e reprocessamento antes de publicar.', 'Delivery checklist', ['QA', 'aceite', 'teste'])
1554        ],
1555        codigos: [
1556            { codigo: '/especifica-a-integracao' },
1557            { codigo: '/fluxo-ponta-a-ponta' }
1558        ]
1559    },
1560    {
1561        stage: 'Aula 09',
1562        estimatedMinutes: 30,
1563        layout: 'grid',
1564        title: 'Códigos Secretos: a régua de integrações do curso inteiro',
1565        goal: 'Aula bônus. Recapitula as 8 aulas e reúne os 16 comandos que cobrem quando integrar, contrato, parâmetros, autenticação, validação, webhook, idempotência e especificação.',
1566        focus: 'Antes de pedir ou revisar uma integração, passe a régua: dá pra nomear evento, origem e destino? o contrato tem entrada, saída e erros? a credencial combina com quem chama? o webhook trata cada tipo de evento e resiste à repetição?',
1567        practice: 'Pegue uma integração real que você precisa pedir ou revisar esta semana. Rode pelo menos 4 códigos desta lista sobre ela e junte tudo numa especificação só.',
1568        exerciseFeedback: 'Não existe resposta única. O que importa: a especificação final não deixa buraco — evento, sistemas, contrato, autenticação e comportamento de falha ficam explícitos, não implícitos.',
1569        lessons: [
1570            'Reconheça, numa integração pronta, quais decisões já foram tomadas e quais ficaram em aberto.',
1571            'Monte a régua de revisão que você vai usar em qualquer API ou webhook daqui pra frente.',
1572            'Combine vários códigos numa especificação única, sem contradição.'
1573        ],
1574        conceitos: [],
1575        bonus: true
1576    }
1577];
1578
1579const prototipandoComIaJourney = [
1580    { stage: 'Aula 01', layout: 'grid', title: 'O que é vibecoding e onde ele ajuda de verdade', goal: 'Use IA para explorar e prototipar ideias sem confundir velocidade com produto pronto.', focus: 'Comece pelo problema, público e resultado; a ferramenta vem depois.', practice: 'Descreva uma ideia com problema, público, resultado e limite da primeira versão.', exerciseFeedback: 'Uma boa resposta define para quem é, qual dor resolve e o que ficará fora.', lessons: ['IA acelera exploração, mas não decide o produto.', 'Protótipo serve para aprender antes de construir tudo.', 'Limites controlam o escopo.'], codigos: [{ codigo: '/problema' }, { codigo: '/limite' }], conceitos: [apiConceito('Vibecoding', 'Uso de linguagem natural e IA para explorar, prototipar e construir software.', 'Use para transformar uma ideia em algo visual ou funcional que possa ser revisado.', 'A IA faz o sistema inteiro.', 'Use IA para criar um protótipo navegável com dados fictícios e limite explícito.', 'Vibe coding', ['protótipo', 'IA', 'escopo']), apiConceito('Protótipo', 'Versão simplificada para testar ideia e fluxo antes da construção completa.', 'Use para validar direção com produto, design e usuários.', 'Faz a versão final rapidinho.', 'Demonstre cadastro e conclusão de tarefas, sem integração real.', 'Prototype', ['MVP', 'fluxo', 'validação'])] },
1581    { stage: 'Aula 02', layout: 'grid', title: 'Como transformar uma ideia em escopo claro', goal: 'Quebre uma ideia ampla em objetivo, público, fluxo, dados e critérios de sucesso.', focus: 'Um escopo responde o que será feito, para quem, em qual ordem e como saber que funcionou.', practice: 'Transforme uma agenda de consultas em escopo de primeira versão.', exerciseFeedback: 'O escopo bom tem começo, caminho e resultado; não é uma lista infinita.', lessons: ['Objetivo orienta decisões.', 'Fluxo principal vem antes das exceções.', 'Critério de sucesso evita opinião solta.'], codigos: [{ codigo: '/escopo' }, { codigo: '/criterio' }], conceitos: [apiConceito('Escopo', 'Limite combinado do que entra e do que fica fora.', 'Use antes de pedir telas, código ou protótipo.', 'Faz uma agenda completa.', 'Na primeira versão, o paciente vê horários fictícios e confirma; pagamentos ficam fora.', 'Scope', ['MVP', 'prioridade']), apiConceito('Critério de sucesso', 'Sinal observável de que o protótipo cumpriu seu objetivo.', 'Use para revisar uma saída da IA.', 'Quando estiver bonito está pronto.', 'Cinco pessoas encontram e confirmam um horário sem orientação.', 'Success criterion', ['teste', 'aceite'])] },
1582    { stage: 'Aula 03', layout: 'groups', title: 'Prompts para criar telas, fluxos e componentes', goal: 'Dê contexto, estrutura e restrições para a IA gerar uma interface revisável.', focus: 'Peça objetivo, público, conteúdo, estados, ações e entrega esperada.', practice: 'Escreva um prompt para uma solicitação de orçamento com estados vazio, carregando, erro e sucesso.', exerciseFeedback: 'O prompt completo descreve comportamento e estados, não só estilo.', lessons: ['Contexto reduz respostas genéricas.', 'Estados fazem parte da tela.', 'Componentes têm função e c
1582omportamento.'], codigos: [{ codigo: '/contexto' }, { codigo: '/estados' }], groups: [{ title: 'Contexto', desc: 'Para quem é e qual resultado a tela produz.' }, { title: 'Comportamento', desc: 'O que acontece antes, durante e depois de cada ação.' }], conceitos: [apiConceito('Briefing para IA', 'Contexto organizado sobre problema, público, objetivo e limites.', 'Use antes de pedir uma tela ou fluxo.', 'Faz uma tela bonita.', 'Crie orçamento para pessoas leigas, com dados mínimos e confirmação de recebimento.', 'AI brief', ['contexto', 'escopo']), apiConceito('Estado de interface', 'Condição da tela que comunica o que acontece e qual ação é possível.', 'Use em carregamento, erro, vazio e sucesso.', 'Se der erro avisa.', 'Durante envio, desabilite o botão; em erro, explique e permita tentar novamente.', 'UI state', ['erro', 'sucesso'])] },
1583    { stage: 'Aula 04', layout: 'grid', title: 'Prompts para corrigir bugs e melhorar código', goal: 'Peça investigação e correção sem mascarar o problema nem aceitar mudança arriscada.', focus: 'Descreva sintoma, reprodução, ambiente, esperado e limite da mudança.', practice: 'Peça correção para botão que funciona no desktop e falha no celular.', exerciseFeedback: 'O pedido permite reproduzir e conferir regressões.', lessons: ['Sintoma não é causa.', 'Reprodução orienta investigação.', 'Correção precisa de verificação.'], codigos: [{ codigo: '/reproduzir' }, { codigo: '/nao-quebrar' }], conceitos: [apiConceito('Reprodução de bug', 'Passos que fazem o problema aparecer de forma verificável.', 'Use para investigar erro real.', 'Às vezes não funciona.', 'Em 375px, tocar duas vezes cria duas tentativas de cobrança.', 'Bug reproduction', ['sintoma', 'ambiente']), apiConceito('Regressão', 'Problema novo causado por uma alteração.', 'Use em correções e refatorações.', 'Arruma e vê se continua.', 'Verifique checkout desktop, campos, duplo clique e erro após o ajuste.', 'Regression', ['teste', 'aceite'])] },
1584    { stage: 'Aula 05', layout: 'timeline', title: 'Como revisar o que a IA gerou', goal: 'Revise intenção, conteúdo, comportamento, acessibilidade, segurança e manutenção.', focus: 'A saída da IA é uma proposta, não autoridade sobre produto ou código.', practice: 'Revise um protótipo gerado por IA usando checklist de produto e comportamento.', exerciseFeedback: 'A revisão encontra ao menos um risco ou pergunta não respondida.', lessons: ['Compare com o objetivo.', 'Teste caminhos felizes e problemas.', 'Pergunte o que foi inventado ou omitido.'], codigos: [{ codigo: '/revisar' }, { codigo: '/perguntar' }], sequenceCount: 3, connectors: ['entenda', 'teste', 'decida'], conceitos: [apiConceito('Revisão crítica', 'Avaliação da saída da IA contra objetivo, regras e riscos.', 'Use antes de aprovar ou publicar.', 'A IA fez, está certo.', 'Compare com critérios do briefing e registre correções.', 'Critical review', ['aceite', 'risco']), apiConceito('Lacuna de especificação', 'Decisão importante que não foi descrita.', 'Use para encontrar comportamento inventado pela IA.', 'Depois a gente decide.', 'Defina o comportamento sem conexão antes de aprovar o fluxo.', 'Specification gap', ['regra', 'exceção'])] },
1585    { stage: 'Aula 06', layout: 'groups', title: 'Como organizar arquivos, versões e entregas', goal: 'Organize prompts, arquivos, versões, decisões e revisões para o projeto continuar.', focus: 'Nomes, versão atual, decisões e próximos passos precisam ficar visíveis.', practice: 'Descreva uma estrutura de entrega para protótipo com dados fictícios e pendências.', exerciseFeedback: 'Outra pessoa localiza a versão atual e entende o que mudou.', lessons: ['Nomes claros reduzem procura.', 'Versão atual precisa ser identificável.', 'Entrega separa pronto, pendente e descartado.'], codigos: [{ codigo: '/estrutura' }, { codigo: '/versao' }], groups: [{ title: 'Encontrar', desc: 'Pastas e nomes para localizar a entrega.' }, { title: 'Entender', desc: 'Decisões, versão atual e pendências.' }], conceitos: [apiConceito('Entrega reproduzível', 'Pacote que outra pessoa consegue abrir e continuar.', 'Use em protótipos compartilhados.', 'Mande os arquivos.', 'Entregue README, instruções, dados fictícios e limitações.', 'Reproducible handoff', ['documentação', 'handoff']), apiConceito('Registro de decisão', 'Nota que explica escolha, motivo e impacto.', 'Use para decisões que serão revisadas.', 'Escolhe o melhor jeito.', 'Registre alternativas descartadas e risco restante.', 'Decision record', ['histórico', 'trade-off'])] },
1586    { stage: 'Aula 07', layout: 'grid', title: 'Limites, riscos e segurança ao usar IA para programar', goal: 'Use IA com responsabilidade, protegendo dados, credenciais, código e usuários.', focus: 'Nunca entregue segredo ou dado pessoal sem entender o risco.', practice: 'Crie regras para usar IA em projeto com dados de clientes e serviços externos.', exerciseFeedback: 'Separe dados compartilháveis, dados a remover e decisões que exigem revisão humana.', lessons: ['Não cole segredos em prompts.', 'Saídas podem ter falhas.', 'Decisões sensíveis exigem revisão.'], codigos: [{ codigo: '/sem-segredo' }, { codigo: '/humano' }], conceitos: [apiConceito('Dado sensível no prompt', 'Informação que pode causar exposição ou dano quando envi
1586ada a ferramenta externa.', 'Use para decidir o que anonimizar ou remover.', 'Cola o banco inteiro.', 'Envie exemplo fictício e remova tokens, senhas e dados pessoais.', 'Sensitive data handling', ['privacidade', 'segredo']), apiConceito('Revisão humana', 'Conferência antes de aceitar saída de IA em contexto de risco.', 'Use em autenticação, pagamentos e permissões.', 'Se compilou pode subir.', 'Revise permissões, dependências e erros antes de publicar.', 'Human review', ['aprovação', 'segurança'])] },
1587    { stage: 'Aula 08', layout: 'grid', title: 'Projeto final: criar um protótipo funcional guiado por IA', goal: 'Junte problema, escopo, prompts, revisão, organização e segurança em um protótipo pequeno.', focus: 'O projeto deve ser pequeno o bastante para terminar e claro o bastante para revisar.', practice: 'Escolha uma ideia, escreva briefing, gere, revise e registre a entrega.', exerciseFeedback: 'O projeto apresenta problema, fluxo, limites, estados, revisão, estrutura e riscos.', lessons: ['Um protótipo mostra uma decisão.', 'Documentação faz parte da entrega.', 'Limitações explícitas aumentam confiança.'], codigos: [{ codigo: '/prototipo' }, { codigo: '/entrega' }], conceitos: [apiConceito('Protótipo funcional', 'Versão pequena que permite interagir e aprender.', 'Use antes de investir na construção final.', 'Faz um sistema real.', 'Crie fluxo navegável com dados fictícios e limites visíveis.', 'Functional prototype', ['MVP', 'fluxo']), apiConceito('Handoff', 'Transferência organizada para quem revisa ou desenvolve.', 'Use ao compartilhar resultado.', 'Está pronto, olha aí.', 'Entregue instruções, critérios, limitações e pendências.', 'Handoff', ['entrega', 'documentação'])] },
1588    { stage: 'Aula 09', layout: 'grid', title: 'Códigos Secretos: a régua do protótipo com IA', bonus: true, goal: 'Recapitule as oito aulas e use os códigos para transformar ideia solta em protótipo revisável e seguro.', focus: 'Passe pela régua: problema, escopo, contexto, estados, revisão, entrega, segurança e protótipo.', practice: 'Use pelo menos quatro códigos em um pedido e explique por que cada um entrou.', exerciseFeedback: 'Conecte cada código a uma decisão concreta e reconheça um limite ou risco.', lessons: ['A régua reúne o método.', 'Códigos são atalhos de intenção.', 'A saída ainda precisa de revisão humana.'], conceitos: [] }
1589];
1590
1591const uiDescomplicadaJourney = [
1592    {
1593        stage: 'Aula 01', layout: 'grid', title: 'A anatomia de uma interface',
1594        goal: 'Aprenda a enxergar uma tela como uma composição de blocos, e não como um desenho inteiro impossível de explicar.',
1595        focus: 'Comece pelo propósito da tela e nomeie estrutura, conteúdo, ação e estado antes de discutir cores ou estilo.',
1596        practice: 'Escolha uma tela real e descreva seu propósito, três blocos principais e a ação mais importante.',
1597        exerciseFeedback: 'Uma boa resposta explica o que a tela ajuda a fazer e nomeia blocos observáveis, sem cair em “uma tela bonita”.',
1598        lessons: ['Uma interface organiza informação para apoiar uma ação.', 'Componente é uma peça reutilizável com função e c
1598omportamento.', 'Nomear blocos reduz ruído entre produto, design, desenvolvimento e IA.'],
1599        conceitos: [
1600            apiConceito('Interface', 'A superfície pela qual uma pessoa percebe informações, toma decisões e interage com um sistema.', 'Use quando precisar explicar a experiência de uma tela ou fluxo completo.', 'Faz uma tela moderna.', 'Crie uma interface de cadastro que ajude uma pessoa leiga a preencher dados, corrigir erros e confirmar o envio.', 'User interface (UI)', ['tela', 'interação', 'experiência']),
1601            apiConceito('Componente', 'Peça de interface com estrutura, função e comportamento próprios, como botão, card, campo ou modal.', 'Use para pedir partes específicas que podem se repetir ou ser revisadas isoladamente.', 'Coloca um negócio pra clicar.', 'Use um botão primário “Salvar” com estados normal, carregando, sucesso e erro.', 'UI component', ['elemento', 'bloco', 'padrão']),
1602            apiConceito('Hierarquia de conteúdo', 'Ordem de importância que ajuda a pessoa a saber o que ler, decidir ou fazer primeiro.', 'Use antes de escolher tamanho, posição, contraste ou destaque visual.', 'Deixa o título maior.', 'Destaque o problema no título, explique o contexto em uma frase e deixe “Começar revisão” como ação principal.', 'Content hierarchy', ['prioridade', 'ênfase', 'escaneabilidade'])
1603        ],
1604        exerciseFields: [
1605            { key: 'proposito', label: 'Qual é o propósito da tela?', help: 'Descreva a ação principal que a pessoa precisa realizar.', placeholder: 'Ex.: acompanhar e concluir uma tarefa.' },
1606            { key: 'blocos', label: 'Quais blocos aparecem?', help: 'Nomeie pelo menos três componentes visíveis.', placeholder: 'Ex.: header, card de resumo, lista e botão.' },
1607            { key: 'acao', label: 'Qual é a ação principal?', help: 'Escreva o texto ou resultado esperado do CTA.', placeholder: 'Ex.: começar a tarefa e ver a confirmação.' }
1608        ],
1609        exerciseModelAnswer: 'Propósito: ajudar a pessoa a acompanhar e concluir uma tarefa. Blocos: header com contexto, card de resumo, lista de tarefas e botão primário. Ação principal: selecionar uma tarefa e marcar como concluída, recebendo confirmação.'
1610    },
1611    {
1612        stage: 'Aula 02', layout: 'grid', title: 'Hierarquia visual e decisão',
1613        goal: 'Organize títulos, textos, ações e destaques para que a pessoa entenda a tela sem precisar decifrá-la.',
1614        focus: 'A hierarquia deve refletir a decisão mais importante, não apenas aumentar o que parece bonito.',
1615        practice: 'Reescreva uma tela com título, explicação, ação principal e ação secundária, justificando a ordem.',
1616        exerciseFeedback: 'A resposta fica consistente quando a hierarquia acompanha a intenção: contexto, decisão principal e alternativa segura.',
1617        lessons: ['Peso visual vem de tamanho, contraste, posição, espaço e repetição.', 'CTA primário indica a ação mais importante daquele momento.', 'Texto curto e específico reduz esforço de interpretação.'],
1618        conceitos: [
1619            apiConceito('Peso visual', 'Força com que um elemento chama atenção em comparação com os demais.', 'Use para decidir o que deve ser percebido primeiro numa tela.', 'Deixa tudo chamativo.', 'O título tem maior peso; o botão principal usa contraste; ações secundárias ficam discretas.', 'Visual weight', ['contraste', 'tamanho', 'posição']),
1620            apiConceito('CTA', 'Chamada para ação que indica o próximo passo esperado da pessoa.', 'Use quando uma tela precisa conduzir uma decisão ou avanço.', 'Coloca um botão aqui.', 'O botão “Revisar pedido” é o CTA principal; “Voltar” permanece secundário.', 'Call to action', ['botão', 'conversão', 'próximo passo']),
1621            apiConceito('Escaneabilidade', 'Facilidade de entender uma tela ao percorrê-la rapidamente com os olhos.', 'Use em dashboards, listas, páginas de produto e qualquer tela com muita informação.', 'Deixa fácil de ler.', 'Agrupe informações relacionadas, use títulos descritivos e destaque apenas os dados que orientam decisão.', 'Scannability', ['leitura rápida', 'agrupamento', 'densidade'])
1622        ],
1623        exerciseFields: [
1624            { key: 'decisao', label: 'Qual decisão a tela apoia?', help: 'Diga o que a pessoa precisa decidir ou fazer.', placeholder: 'Ex.: confirmar o horário escolhido.' },
1625            { key: 'ordem', label: 'Qual será a ordem de leitura?', help: 'Liste título, contexto, ação e alternativa.', placeholder: 'Ex.: título → resumo → confirmar → voltar.' },
1626            { key: 'justificativa', label: 'Por que essa hierarquia?', help: 'Explique como o destaque reduz dúv
1626ida ou esforço.', placeholder: 'Ex.: o CTA aparece depois que a pessoa confere o resumo.' }
1627        ]
1628    },
1629    {
1630        stage: 'Aula 03', layout: 'groups', title: 'Layout, espaço e responsividade',
1631        goal: 'Desenhe uma estrutura que se adapta a diferentes telas sem perder ordem, leitura ou ação principal.',
1632        focus: 'Pense em relações e prioridades: o que fica lado a lado, o que empilha e o que pode desaparecer em telas menores.',
1633        practice: 'Descreva como uma tela desktop se transforma no celular, incluindo colunas, espaçamentos e ordem dos blocos.',
1634        exerciseFeedback: 'Uma boa resposta explica o comportamento em pelo menos dois tamanhos, não apenas diz “será responsivo”.',
1635        lessons: ['Layout define relações entre blocos, não só posições fixas.', 'Espaço em branco separa grupos e cria ritmo de leitura.', 'Responsividade adapta conteúdo, interação e prioridade ao contexto.'],
1636        groups: [{ title: 'Estrutura', desc: 'Colunas, alinhamento, largura e espaçamento entre grupos.' }, { title: 'Adaptação', desc: 'O que empilha, reduz, oculta ou muda de posição no mobile.' }],
1637        conceitos: [
1638            apiConceito('Grid', 'Sistema de colunas e linhas que organiza blocos de forma consistente.', 'Use para explicar alinhamento e proporções sem depender de coordenadas soltas.', 'Coloca três coisas lado a lado.', 'Use uma grade de três cards no desktop e uma coluna única no celular, mantendo a ordem de prioridade.', 'Grid layout', ['coluna', 'linha', 'alinhamento']),
1639            apiConceito('Espaçamento', 'Distância intencional entre elementos e grupos de uma interface.', 'Use para separar níveis de conteúdo e evitar que a tela pareça apertada ou fragmentada.', 'Dá um espaço aí.', 'Use 24px entre seções, 16px dentro de cards e 8px entre label e campo.', 'Spacing', ['margem', 'padding', 'ritmo']),
1640            apiConceito('Responsividade', 'Capacidade de uma interface se adaptar a diferentes larguras, dispositivos e formas de interação.', 'Use ao definir o comportamento em desktop, tablet e celular.', 'Faz funcionar no celular também.', 'No celular, a grade empilha, o menu vira botão e o CTA permanece visível sem exigir zoom.', 'Responsive design', ['mobile first', 'breakpoint', 'adaptação'])
1641        ],
1642        exerciseFields: [
1643            { key: 'desktop', label: 'Como fica no desktop?', help: 'Descreva colunas, largura e ordem.', placeholder: 'Ex.: formulário à esquerda e resumo à direita.' },
1644            { key: 'mobile', label: 'Como fica no celular?', help: 'Explique o que empilha, reduz ou muda.', placeholder: 'Ex.: resumo vem depois do formulário em uma coluna.' },
1645            { key: 'prioridade', label: 'O que nunca pode sumir?', help: 'Indique conteúdo ou ação essencial.', placeholder: 'Ex.: título, campo principal e botão continuar.' }
1646        ]
1647    },
1648    {
1649        stage: 'Aula 04', layout: 'grid', title: 'Formulários que orientam',
1650        goal: 'Crie campos que explicam o que precisam, evitam erros e ajudam a pessoa a concluir uma tarefa com confiança.',
1651        focus: 'Um formulário bom reduz perguntas: o label, o formato esperado, o erro e o próximo passo estão claros.',
1652        practice: 'Modele um formulário de cadastro com campos, ajuda, validações, erro e confirmação de sucesso.',
1653        exerciseFeedback: 'O formulário está bem descrito quando cada campo tem propósito, formato e retorno compreensível.',
1654        lessons: ['Label identifica o dado pedido; placeholder não deve substituir o label.', 'Validação deve orientar a correção no lugar certo.', 'Sucesso e erro precisam confirmar o que aconteceu e o que fazer depois.'],
1655        conceitos: [
1656            apiConceito('Label', 'Texto associado a um campo que informa qual dado deve ser preenchido.', 'Use em todo campo de formulário, inclusive quando existe placeholder ou ícone.', 'Coloca um campo de nome.', 'O label “Nome completo” fica visível acima do campo e permanece legível quando o campo recebe foco.', 'Form label', ['campo', 'formulário', 'acessibilidade']),
1657            apiConceito('Validação inline', 'Retorno apresentado próximo ao campo para explicar se o valor está correto ou como corrigir.', 'Use para evitar que a pessoa descubra todos os erros apenas depois de enviar.', 'Mostra os erros no formulário.', 'Se o e-mail estiver inválido, mostre a mensagem abaixo do campo e preserve o valor digitado.', 'Inline validation', ['erro', 'mensagem de ajuda', 'formato']),
1658            apiConceito('Affordance', 'Pista visual ou textual que indica como um elemento pode ser usado.', 'Use em botões, campos, links e controles que precisam ser reconhecidos sem tentativa e erro.', 'Deixa claro o que clicar.', 'O botão tem texto de ação, contraste e estado de foco; o campo tem label e formato esperado.', 'Affordance', ['pista de uso', 'descoberta', 'interação'])
1659        ],
1660        exerciseFields: [
1661            { key: 'campos', label: 'Quais campos existem?', help: 'Liste somente os dados necessários.', placeholder: 'Ex.: nome, e-mail e telefone.' },
1662            { key: 'validacao', label: 'O que pode dar errado?', help: 'Descreva formato ou regra de cada campo.', placeholder: 'Ex.: e-mail obrigatório e com formato válido.' },
1663            { key: 'retorno', label: 'Qual retorno aparece?', help: 'Explique erro, carregamento e sucesso.', placeholder: 'Ex.: mensagem no campo e confirmação após envio.' }
1664        ]
1665    },
1666    {
1667        stage: 'Aula 05', layout: 'timeline', title: 'Estados que comunicam',
1668        goal: 'Faça a interface explicar o que está acontecendo antes, durante e depois de cada ação — inclusive quando algo falha.',
1669        focus: 'Estado não é detalhe visual: ele informa se a pessoa pode agir, esperar, corrigir ou tentar novamente.',
1670        practice: 'Descreva os estados vazio, carregando, sucesso e erro de uma lista de pedidos.',
1671        exerciseFeedback: 'A resposta está completa quando cada estado explica situação, mensagem e ação possível.',
1672        lessons: ['Estado vazio orienta o primeiro passo.', 'Carregamento protege contra dúvida e cliques repetidos.', 'Erro deve oferecer recuperação quando ela for possível.'],
1673        sequenceCount: 4, connectors: ['prepara', 'aguarda', 'confirma'],
1674        conceitos: [
1675            apiConceito('Estado vazio', 'Situação em que ainda não existe conteúdo para mostrar, mas existe um próximo passo possível.', 'Use em primeira utilização, filtros sem resultado e listas após exclusão.', 'Não tem nada aqui.', 'Você ainda não criou pedidos. Comece pelo botão “Novo pedido” ou ajuste o filtro.', 'Empty state', ['primeiro uso', 'lista vazia', 'orientação']),
1676            apiConceito('Estado de carregamento', 'Indicação de que o sistema está processando ou buscando algo.', 'Use quando a resposta não é imediata e a pessoa precisa entender que a ação foi recebida.', 'Coloca um spinner.', 'Enquanto os pedidos carregam, preserve a estrutura da lista e desabilite apenas ações afetadas.', 'Loading state', ['progresso', 'espera', 'feedback']),
1677            apiConceito('Estado de erro', 'Representação de uma falha que explica o impacto e indica como recuperar.', 'Use quando uma operação não pode continuar ou precisa ser repetida.', 'Deu erro.', 'Mostre “Não foi possível carregar os pedidos” com botão “Tentar novamente” e canal de suporte.', 'Error state', ['recuperação', 'mensagem', 'fallback']),
1678            apiConceito('Estado de sucesso', 'Confirmação de que a ação terminou e comunica o resultado alcançado.', 'Use depois de salvar, enviar, concluir ou alterar algo importante.', 'Mostra que deu certo.', 'Após salvar, mostre “Pedido criado” e ofereça “Ver pedido” como próximo passo.', 'Success state', ['confirmação', 'resultado', 'feedback'])
1679        ],
1680        exerciseFields: [
1681            { key: 'vazio', label: 'O que aparece no vazio?', help: 'Dê mensagem e próximo passo.', placeholder: 'Ex.: ainda não há pedidos; botão Criar pedido.' },
1682            { key: 'erro', label: 'Como a pessoa se recupera?', help: 'Explique mensagem e ação disponível.', placeholder: 'Ex.: erro de rede e botão Tentar novamente.' },
1683            { key: 'sucesso', label: 'Como o sucesso é confirmado?', help: 'Mostre resultado e próximo passo.', placeholder: 'Ex.: pedido criado; botão Ver pedido.' }
1684        ]
1685    },
1686    {
1687        stage: 'Aula 06', layout: 'groups', title: 'Navegação e padrões de interação',
1688        goal: 'Escolha o padrão certo para orientar, alternar, revelar ou confirmar conteúdo sem criar caminhos confusos.',
1689        focus: 'O padrão deve combinar com a quantidade de opções, a importância do contexto e a frequência da ação.',
1690        practice: 'Escolha entre navegação, abas, acordeão, modal e menu para cinco necessidades de uma área do aluno.',
1691        exerciseFeedback: 'A escolha fica forte quando você explica por que o padrão ajuda naquele contexto e qual alternativa evitaria.',
1692        lessons: ['Navegação muda de área; abas trocam conteúdo relacionado.', 'Acordeão revela detalhes sem tirar a pessoa do contexto.', 'Modal interrompe o fluxo e deve ser reservado para decisões ou tarefas focadas.'],
1693        groups: [{ title: 'Orientar', desc: 'Navegação, breadcrumbs e abas mostram onde a pessoa está.' }, { title: 'Revelar', desc: 'Menus, acordeões e modais controlam conteúdo progressivo.' }],
1694        conceitos: [
1695            apiConceito('Abas', 'Padrão que alterna entre conteúdos relacionados mantendo o mesmo contexto de tela.', 'Use quando as opções pertencem à mesma área e a pessoa pode trocar entre elas.', 'Coloca umas abas.', 'Use abas “Visão geral”, “Aulas” e “Certificado” para conteúdos do mesmo curso.', 'Tabs', ['navegação local', 'conteúdo relacionado', 'aba ativa']),
1696            apiConceito('Acordeão', 'Controle que expande e recolhe blocos de conteúdo sob demanda.', 'Use para perguntas frequentes, detalhes técnicos ou informações secundárias.', 'Esconde os detalhes.', 'Cada item de política abre seu conteúdo sem fechar automaticamente os demais.', 'Accordion', ['conteúdo progressivo', 'expandir', 'recolher']),
1697            apiConceito('Modal', 'Janela sobreposta que concentra uma tarefa ou decisão sem mudar de página.', 'Use para confirmação, edição curta ou detalhe que precisa de foco temporário.', 'Abre uma janelinha.', 'Antes de excluir o pedido, abra confirmação com consequência, cancelar e excluir definitivamente.', 'Modal dialog', ['overlay', 'confirmação', 'foco'])
1698        ],
1699        exerciseFields: [
1700            { key: 'padroes', label: 'Quais padrões você escolheu?', help: 'Associe cada necessidade a um padrão.', placeholder: 'Ex.: abas para áreas relacionadas; modal para confirmação.' },
1701            { key: 'motivo', label: 'Por que cada padrão?', help: 'Explique contexto, frequência e impacto.', placeholder: 'Ex.: modal concentra decisão sem perder a página.' },
1702            { key: 'risco', label: 'Qual risco de uso?', help: 'Cite um padrão que poderia confundir.', placeholder: 'Ex.: muitas abas podem esconder conteúdo importante.' }
1703        ]
1704    },
1705    {
1706        stage: 'Aula 07', layout: 'grid', title: 'Acessibilidade e mobile como critério',
1707        goal: 'Projete para mais pessoas desde o início, tratando contraste, foco, toque, leitura e diferentes formas de navegação como requisitos.',
1708        focus: 'Acessibilidade não é acabamento: muda conteúdo, estrutura, interação e critério de qualidade.',
1709        practice: 'Revise uma tela para teclado e celular, registrando pelo menos três ajustes verificáveis.',
1710        exerciseFeedback: 'Uma boa revisão descreve teste e resultado, não apenas adjetivos como “mais acessível”.',
1711        lessons: ['Contraste e tamanho influenciam leitura.', 'Foco visível permite navegar sem mouse.', 'Alvos de toque precisam de espaço e não podem depender só de cor.'],
1712        conceitos: [
1713            apiConceito('Contraste', 'Diferença perceptível entre texto, fundo e elementos que ajuda a leitura e a identificação de controles.', 'Use para revisar textos, botões, bordas, estados e conteúdo em diferentes condições de luz.', 'Usa uma cor que combine.', 'O texto principal mantém contraste suficiente com o fundo e o botão continua distinguível sem depender apenas da cor.', 'Color contrast', ['legibilidade', 'cor', 'WCAG']),
1714            apiConceito('Foco visível', 'Indicação clara de qual elemento receberá a próxima ação do teclado ou tecnologia assistiva.', 'Use em links, botões, campos, menus e modais navegáveis por teclado.', 'Dá pra usar no teclado.', 'Ao pressionar Tab, o foco percorre a ordem lógica e aparece com contorno visível em cada controle.', 'Visible focus', ['teclado', 'ordem de foco', 'navegação']),
1715            apiConceito('Alvo de toque', 'Área interativa com tamanho e espaçamento suficientes para ser acionada com precisão em tela sensível ao toque.', 'Use em botões, links, ícones e controles de celular.', 'Deixa os botões maiores.', 'Separe ações de excluir e editar e garanta área de toque confortável sem exigir precisão de um pixel.', 'Touch target', ['mobile', 'gesto', 'precisão'])
1716        ],
1717        exerciseFields: [
1718            { key: 'teste', label: 'Como você vai testar?', help: 'Escolha teclado, celular, zoom ou contraste.', placeholder: 'Ex.: navegar só com Tab em tela de 375px.' },
1719            { key: 'achado', label: 'Qual problema encontrou?', help: 'Descreva algo observável.', placeholder: 'Ex.: foco some no botão e ícones ficam muito próximos.' },
1720            { key: 'ajuste', label: 'Qual ajuste fará?', help: 'Escreva a mudança e o resultado esperado.', placeholder: 'Ex.: criar contorno de foco e separar os alvos.' }
1721        ]
1722    },
1723    {
1724        stage: 'Aula 08', layout: 'grid', title: 'Projeto final: especificar uma tela completa',
1725        goal: 'Conecte propósito, conteúdo, layout, estados, responsividade e acessibilidade em uma especificação que outra pessoa consiga construir.',
1726        focus: 'Uma boa especificação não descreve apenas aparência: ela torna decisões, comportamentos e critérios observáveis.',
1727        practice: 'Escolha uma tela real e entregue propósito, estrutura, componentes, estados, comportamento mobile e critérios de aceite.',
1728        exerciseFeedback: 'O projeto está pronto quando outra pessoa consegue construir e revisar a tela sem depender de adivinhações.',
1729        lessons: ['Especificar é tornar decisões compartilháveis.', 'Critério de aceite transforma opinião em verificação.', 'Handoff bom registra contexto, limites e pendências.'],
1730        conceitos: [
1731            apiConceito('Critério de aceite de UI', 'Condição observável que define quando uma tela ou interação está pronta.', 'Use para alinhar produto, design, desenvolvimento e revisão.', 'A tela precisa ficar boa.', 'Em 375px, a pessoa encontra o CTA, envia o formulário e entende o erro sem rolagem horizontal.', 'UI acceptance criteria', ['teste', 'pronto', 'qualidade']),
1732            apiConceito('Handoff', 'Entrega organizada que permite a outra pessoa entender, construir ou continuar um trabalho.', 'Use ao passar uma tela do planejamento para design, desenvolvimento ou cliente.', 'Está pronto, dá uma olhada.', 'Entregue objetivo, fluxo, estados, medidas, textos, referências, critérios e pendências.', 'Design handoff', ['entrega', 'documentação', 'continuidade']),
1733            apiConceito('Especificação de interface', 'Documento ou briefing que descreve a intenção, estrutura e comportamento de uma tela.', 'Use antes de construir ou pedir uma tela para IA, dev ou designer.', 'Cria uma tela de pedidos.', 'Especifique usuário, objetivo, componentes, estados, dados, responsividade e critérios de aceite.', 'UI specification', ['briefing', 'fluxo', 'comportamento'])
1734        ],
1735        exerciseFields: [
1736            { key: 'tela', label: 'Qual tela você está especificando?', help: 'Dê um nome e contexto de uso.', placeholder: 'Ex.: tela de confirmação de consulta no celular.' },
1737            { key: 'estrutura', label: 'Como ela funciona?', help: 'Liste conteúdo, componentes e estados.', placeholder: 'Ex.: título, resumo, CTA, erro, sucesso e carregamento.' },
1738            { key: 'aceite', label: 'Como saberemos que está pronta?', help: 'Escreva critérios observáveis.', placeholder: 'Ex.: usuário confirma sem rolagem horizontal e entende o resultado.' }
1739        ]
1740    },
1741    {
1742        stage: 'Aula 09', layout: 'grid', title: 'Códigos Secretos da UI', bonus: true,
1743        goal: 'Recapitule as oito aulas e transforme pedidos vagos de interface em instruções claras para IA, design e desenvolvimento.',
1744        focus: 'Passe pela régua: anatomia, hierarquia, layout, formulário, estados, padrões, acessibilidade e aceite.',
1745        practice: 'Use pelo menos quatro códigos em um pedido único e explique a decisão que c
1745ada um torna explícita.',
1746        exerciseFeedback: 'Conecte cada código a uma decisão observável e revise se o pedido ainda deixa alguma lacuna importante.',
1747        lessons: ['Códigos são atalhos de intenção, não substitutos de contexto.', 'A tela precisa ser entendida em diferentes estados e tamanhos.', 'Qualidade aparece quando o resultado pode ser testado.'],
1748        conceitos: []
1749    }
1750];
1751
1752uiDescomplicadaJourney[0].visualMarkup = `
1753    <section class="ui-visual-lab" aria-label="Laboratório visual: anatomia de uma tela">
1754        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual</p><h4>Veja a anatomia antes de descrevê-la</h4><p>Uma tela não é um bloco único: ela combina contexto, conteúdo e ação.</p></div><span class="ui-visual-stamp">01 · observar</span></div>
1755        <div class="ui-visual-lab-grid">
1756            <div class="ui-visual-screen">
1757                <div class="ui-screen-top"><strong>minhas tarefas</strong><span>terça, 10 set.</span><button type="button" aria-label="Abrir menu">•••</button></div>
1758                <div class="ui-screen-hero"><span class="ui-screen-label">CONTEXTO</span><h5>Escolha o que fazer hoje</h5><p>Uma pequena decisão agora deixa o resto do dia mais claro.</p><button type="button">Começar revisão <span>→</span></button></div>
1759                <div class="ui-screen-list"><div class="ui-screen-list-head"><span>PRÓXIMAS TAREFAS</span><b>3 abertas</b></div><div class="ui-screen-task"><i></i><span>Revisar briefing do projeto</span><em>agora</em></div><div class="ui-screen-task"><i></i><span>Enviar retorno para o time</span><em>16:00</em></div></div>
1760            </div>
1761            <div class="ui-visual-legend"><div><span class="ui-visual-index ui-visual-index-blue">01</span><div><strong>Header</strong><p>Contexto: onde estou e o que encontro aqui.</p></div></div><div><span class="ui-visual-index ui-visual-index-pink">02</span><div><strong>Hero</strong><p>Objetivo: explica a decisão que importa agora.</p></div></div><div><span class="ui-visual-index ui-visual-index-lime">03</span><div><strong>Lista / cards</strong><p>Conteúdo: organiza as opções que a pessoa pode revisar.</p></div></div><div><span class="ui-visual-index ui-visual-index-black">04</span><div><strong>CTA</strong><p>Ação: transforma entendimento em próximo passo.</p></div></div></div>
1762        </div>
1763        <p class="ui-visual-caption">Pergunta de designer: se você remover o texto, ainda consegue explicar o papel de cada bloco?</p>
1764    </section>
1765`;
1766
1767uiDescomplicadaJourney[1].visualMarkup = `
1768    <section class="ui-visual-lab ui-visual-lab-contrast" aria-label="Laboratório visual: hierarquia de decisão">
1769        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · compare</p><h4>Uma tela, três níveis de atenção</h4><p>O visual precisa responder: o que eu leio, o que eu decido e onde eu clico?</p></div><span class="ui-visual-stamp">02 · priorizar</span></div>
1770        <div class="ui-visual-lab-grid">
1771            <div class="ui-visual-screen">
1772                <div class="ui-screen-top"><strong>soluções para sua equipe</strong><span>passo 01 de 03</span><button type="button" aria-label="Abrir menu">•••</button></div>
1773                <div class="ui-screen-choice-intro"><span class="ui-screen-label">CONTEXTO</span><h5>O que você precisa organizar?</h5><p>Escolha um caminho para começar. Você poderá revisar antes de enviar.</p></div>
1774                <div class="ui-screen-choice-list"><article class="ui-screen-choice ui-screen-choice-featured"><div><span class="ui-choice-tag">RECOMENDADO</span><strong>Organizar pedidos</strong><p>Centralize solicitações e acompanhe cada etapa.</p></div><button type="button">Começar <span>→</span></button></article><article class="ui-screen-choice"><div><strong>Consultar histórico</strong><p>Veja o que já foi solicitado pela equipe.</p></div><button type="button">Ver detalhes</button></article></div>
1775            </div>
1776            <div class="ui-visual-legend"><div><span class="ui-visual-index ui-visual-index-blue">01</span><div><strong>Contexto</strong><p>O título explica a situação antes de pedir uma decisão.</p></div></div><div><span class="ui-visual-index ui-visual-index-pink">02</span><div><strong>Ênfase</strong><p>O caminho recomendado recebe destaque sem apagar a alternativa.</p></div></div><div><span class="ui-visual-index ui-visual-index-lime">03</span><div><strong>Ação principal</strong><p>O CTA usa verbo e deixa o próximo passo previsível.</p></div></div></div>
1777        </div>
1778        <p class="ui-visual-caption">Teste rápido: cubra os botões com a mão. Ainda fica claro qual opção é recomendada?</p>
1779    </section>
1780`;
1781
1782uiDescomplicadaJourney[2].visualMarkup = `
1783    <section class="ui-visual-lab ui-visual-lab-responsive" aria-label="Laboratório visual: layout responsivo">
1784        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · adapte</p><h4>A mesma tarefa, dois contextos</h4><p>Responsividade não é apenas diminuir: é reorganizar prioridades para cada tela.</p></div><span class="ui-visual-stamp">03 · adaptar</span></div>
1785        <div class="ui-responsive-stage"><div class="ui-device ui-device-desktop"><div class="ui-device-bar"><span></span><strong>desktop · 1280px</strong><i></i></div><div class="ui-device-body"><aside><b>Como Pedir</b><span>Visão geral</span><span class="is-active">Meus pedidos</span><span>Configurações</span></aside><main>
1785<div class="ui-device-heading"><div><small>MEUS PEDIDOS</small><h5>Acompanhe suas solicitações</h5></div><button>+ Novo pedido</button></div><div class="ui-device-cards"><article><small>ABERTOS</small><strong>08</strong><span>precisam de atenção</span></article><article><small>EM REVISÃO</small><strong>03</strong><span>aguardando retorno</span></article><article><small>CONCLUÍDOS</small><strong>21</strong><span>neste mês</span></article></div></main></div></div><div class="ui-device ui-device-mobile"><div class="ui-device-bar"><span></span><strong>mobile · 375px</strong><i></i></div><div class="ui-device-mobile-body"><div class="ui-mobile-header"><b>Meus pedidos</b><button aria-label="Abrir menu">☰</button></div><small>RESUMO DO DIA</small><h5>Acompanhe suas solicitações</h5><button class="ui-mobile-cta">+ Novo pedido</button><div class="ui-mobile-card"><span>ABERTOS</span><strong>08</strong><em>precisam de atenção</em></div><div class="ui-mobile-card"><span>EM REVISÃO</span><strong>03</strong><em>aguardando retorno</em></div></div></div></div>
1786        <div class="ui-responsive-note"><span class="ui-visual-index ui-visual-index-lime">↘</span><p><strong>O que mudou?</strong> A sidebar virou menu, os três cards viraram uma sequência vertical e o CTA continuou visível no contexto certo.</p></div>
1787    </section>
1788`;
1789uiDescomplicadaJourney[2].conceitos[0].grupo = 0;
1790uiDescomplicadaJourney[2].conceitos[1].grupo = 0;
1791uiDescomplicadaJourney[2].conceitos[2].grupo = 1;
1792
1793uiDescomplicadaJourney[3].visualMarkup = `
1794    <section class="ui-visual-lab ui-visual-lab-form" aria-label="Laboratório visual: formulário que orienta">
1795        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · acompanhe o estado</p><h4>O campo também conversa</h4><p>Label, ajuda, erro e sucesso formam uma sequência de orientação.</p></div><span class="ui-visual-stamp">04 · orientar</span></div>
1796        <div class="ui-form-stage"><div class="ui-form-panel"><div class="ui-form-panel-head"><small>FORMULÁRIO DE ORÇAMENTO</small><strong>Conte um pouco sobre o projeto</strong><span>Etapa 1 de 2</span></div><label class="ui-demo-field"><span>Nome completo <b>*</b></span><input value="Marina Costa" readonly><em>Como você gostaria de ser chamada?</em></label><label class="ui-demo-field ui-demo-field-error"><span>E-mail profissional <b>*</b></span><input value="marina@" readonly aria-invalid="true"><em>Digite um e-mail válido, como [email protected].</em></label><button class="ui-demo-submit" type="button">Continuar <span>→</span></button></div><div class="ui-form-panel ui-form-success"><div class="ui-success-icon">✓</div><small>ESTADO DE SUCESSO</small><h5>Pedido recebido</h5><p>Agora o time pode revisar os dados e retornar com os próximos passos.</p><button type="button">Ver resumo</button></div></div>
1797        <div class="ui-form-legend"><span><i class="ui-form-dot ui-form-dot-label"></i><strong>Label</strong> diz o que informar.</span><span><i class="ui-form-dot ui-form-dot-help"></i><strong>Ajuda</strong> reduz dúvida antes do erro.</span><span><i class="ui-form-dot ui-form-dot-error"></i><strong>Erro</strong> explica como corrigir.</span><span><i class="ui-form-dot ui-form-dot-success"></i><strong>Sucesso</strong> confirma o resultado.</span></div>
1798    </section>
1799`;
1800
1801uiDescomplicadaJourney[4].visualMarkup = `
1802    <section class="ui-visual-lab ui-visual-lab-states" aria-label="Laboratório visual: estados de interface">
1803        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · comunique</p><h4>A mesma lista, quatro momentos</h4><p>Um estado responde três perguntas: o que aconteceu, o que posso fazer e o que devo esperar?</p></div><span class="ui-visual-stamp">05 · responder</span></div>
1804        <div class="ui-state-board">
1805            <article class="ui-state-card ui-state-empty"><div class="ui-state-top"><span class="ui-state-dot"></span><small>VAZIO</small><b>01</b></div><div class="ui-state-illustration ui-state-illustration-empty"><i>+</i></div><h5>Nenhum pedido ainda</h5><p>Comece uma solicitação para acompanhar o andamento por aqui.</p><button type="button">Criar pedido <span>→</span></button></article>
1806            <article class="ui-state-card ui-state-loading"><div class="ui-state-top"><span class="ui-state-dot"></span><small>CARREGANDO</small><b>02</b></div><div class="ui-state-skeleton"><i></i><i></i><i></i></div><h5>Buscando seus pedidos</h5><p>
1806O sistema está consultando os dados. Aguarde um instante.</p><div class="ui-state-progress"><span></span></div></article>
1807            <article class="ui-state-card ui-state-error"><div class="ui-state-top"><span class="ui-state-dot"></span><small>ERRO</small><b>03</b></div><div class="ui-state-illustration ui-state-illustration-error"><i>!</i></div><h5>Não foi possível carregar</h5><p>Seus pedidos continuam seguros. Tente novamente ou verifique sua conexão.</p><button type="button">Tentar novamente ↻</button></article>
1808            <article class="ui-state-card ui-state-success"><div class="ui-state-top"><span class="ui-state-dot"></span><small>SUCESSO</small><b>04</b></div><div class="ui-state-success-count"><strong>03</strong><span>pedidos encontrados</span></div><div class="ui-state-mini-row"><i></i><span>Orçamento do projeto</span><b>em análise</b></div><div class="ui-state-mini-row"><i></i><span>Atualização de cadastro</span><b>concluído</b></div><p class="ui-state-note">Resultado visível. Agora a pessoa sabe onde continuar.</p></article>
1809        </div>
1810        <div class="ui-state-legend"><span><b>Vazio</b> orienta o primeiro passo.</span><span><b>Carregando</b> explica a espera.</span><span><b>Erro</b> oferece recuperação.</span><span><b>Sucesso</b> confirma o resultado.</span></div>
1811        <p class="ui-visual-caption">Pergunta de designer: em cada estado, a pessoa entende o que aconteceu sem precisar adivinhar?</p>
1812    </section>
1813`;
1814
1815uiDescomplicadaJourney[5].visualMarkup = `
1816    <section class="ui-visual-lab ui-visual-lab-patterns" aria-label="Laboratório visual: padrões de interação">
1817        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · escolha</p><h4>O padrão muda a conversa</h4><p>Abas preservam contexto, acordeões revelam detalhes e modais concentram decisões.</p></div><span class="ui-visual-stamp">06 · orientar</span></div>
1818        <div class="ui-pattern-stage">
1819            <div class="ui-pattern-window"><div class="ui-pattern-bar"><span>curso · ui descomplicada</span><b>área do aluno</b></div><div class="ui-pattern-content"><small>MEU CURSO</small><h5>UI Descomplicada</h5><div class="ui-tabs"><button class="is-active" type="button">Visão geral</button><button type="button">Aulas</button><button type="button">Certificado</button></div><div class="ui-tab-panel"><strong>Continue de onde parou</strong><p>Aula 06 · Navegação e padrões de interação</p><button type="button">Continuar aula <span>→</span></button></div><div class="ui-accordion"><div><button type="button"><span>Como funciona o projeto final?</span><b>+</b></button></div><div class="is-open"><button type="button"><span>O que vou aprender?</span><b>−</b></button><p>Você aprende a escolher padrões conforme contexto, frequência e impacto da ação.</p></div></div></div></div>
1820            <div class="ui-pattern-decision"><div class="ui-pattern-decision-head"><span class="ui-visual-index ui-visual-index-lime">?</span><div><small>DECISÃO DE PADRÃO</small><strong>Precisa interromper?</strong></div></div><p>Use a menor interrupção possível. Reserve o modal para confirmação ou tarefa que exige foco.</p><div class="ui-pattern-options"><div class="ui-pattern-option"><span class="ui-pattern-symbol">↔</span><div><strong>ABAS</strong><small>Conteúdos relacionados</small></div></div><div class="ui-pattern-option"><span class="ui-pattern-symbol">⌄</span><div><strong>ACORDEÃO</strong><small>Detalhes sob demanda</small></div></div><div class="ui-pattern-option ui-pattern-option-modal"><span class="ui-pattern-symbol">□</span><div><strong>MODAL</strong><small>Decisão concentrada</small></div></div></div></div>
1821        </div>
1822        <div class="ui-pattern-flow"><span>necessidade</span><i>→</i><span>contexto</span><i>→</i><span>padrão</span><i>→</i><span>comportamento esperado</span></div>
1823        <p class="ui-visual-caption">Pergunta de designer: se você trocar o padrão, a pessoa perde contexto, descobre mais rápido ou precisa confirmar uma decisão?</p>
1824    </section>
1825`;
1826
1827uiDescomplicadaJourney[6].visualMarkup = `
1828    <section class="ui-visual-lab ui-visual-lab-accessibility" aria-label="Laboratório visual: acessibilidade e mobile">
1829        <div class="ui-visual-lab-header"><div><p class="ui-visual-kicker">Laboratório visual · teste</p><h4>Uma tela, duas formas de navegar</h4><p>Acessibilidade aparece quando o foco, a leitura e o toque continuam compreensíveis sem depender do mouse ou da cor.</p></div><span class="ui-visual-stamp">07 · incluir</span></div>
1830        <div class="ui-access-stage">
1831            <div class="ui-access-desktop"><div class="ui-access-browser"><span></span><span></span><span></span><b>checkout · revisão do pedido</b></div><div class="ui-access-screen"><small>ETAPA 2 DE 3</small><h5>Revise antes de enviar</h5><p>Confira os dados e escolha como receber o retorno.</p><div class="ui-access-field"><label>Nome completo</label><div>Marina Costa</div></div><div class="ui-access-field ui-access-focus"><label>E-mail</label><div>[email protected]</div><em>Foco visível · próximo com Tab</em></div><div class="ui-access-actions"><button class="ui-access-secondary" type="button">Voltar</button><button type="button">Enviar pedido <span>→</span></button></div><div class="ui-focus-order"><i>01</i><span>
1831Nome</span><i>02</i><span>E-mail</span><i>03</i><span>Enviar</span></div></div></div>
1832            <div class="ui-access-mobile"><div class="ui-access-mobile-top"><span>mobile · 375px</span><b>9:41</b></div><div class="ui-access-mobile-body"><small>REVISÃO</small><h5>Quase lá!</h5><p>Confirme os dados do pedido.</p><button class="ui-touch-target ui-touch-target-active" type="button"><i>✓</i><span>Receber por e-mail</span><b>selecionado</b></button><button class="ui-touch-target" type="button"><i></i><span>Receber por WhatsApp</span><b>toque</b></button><button class="ui-mobile-cta" type="button">Enviar pedido <span>→</span></button><small class="ui-touch-note">Áreas separadas · texto + ícone · sem depender só da cor</small></div></div>
1833        </div>
1834        <div class="ui-access-checks"><span><b>01 · Foco</b> aparece em cada etapa.</span><span><b>02 · Ordem</b> acompanha a lógica da tela.</span><span><b>03 · Toque</b> tem espaço para acertar.</span><span><b>04 · Cor</b> não carrega a informação sozinha.</span></div>
1835        <p class="ui-visual-caption">Pergunta de designer: alguém conseguiria concluir esta tela com teclado, leitor de tela ou toque impreciso?</p>
1836    </section>
1837`;
1838
1839uiDescomplicadaJourney[7].visualMarkup = `
1840    <section class="ui-final-studio" aria-label="Estúdio do projeto final">
1841        <div class="ui-final-studio-header"><div><p class="ui-final-eyebrow">Estúdio de projeto · entrega final</p><h4>Transforme uma necessidade em uma tela que pode ser construída</h4><p>Você vai conectar intenção, estrutura, comportamento, estados, mobile e critérios de aceite em uma única entrega.</p></div><span class="ui-final-badge">08 · entregar</span></div>
1842        <div class="ui-final-canvas"><div class="ui-final-canvas-top"><span>PROJETO · CHECKOUT DE CONSULTA</span><b>RASCUNHO 01</b></div><div class="ui-final-map"><article class="ui-final-map-card ui-final-map-context"><small>01 · CONTEXTO</small><h5>Confirmar uma consulta sem dúvida</h5><p>Para pessoas que precisam agendar pelo celular.</p><span class="ui-final-pin">briefing</span></article><div class="ui-final-arrow">→</div><article class="ui-final-map-card ui-final-map-screen"><small>02 · TELA</small><div class="ui-final-mini-screen"><b>Confirmar horário</b><span>terça, 10 set. · 14:30</span><button type="button">Confirmar consulta</button></div><span class="ui-final-pin">estrutura</span></article><div class="ui-final-arrow">→</div><article class="ui-final-map-card ui-final-map-proof"><small>03 · PROVA</small><div class="ui-final-proof-row"><i>✓</i><span>CTA visível em 375px</span></div><div class="ui-final-proof-row"><i>✓</i><span>Erro explica como corrigir</span></div><div class="ui-final-proof-row"><i>✓</i><span>Sucesso mostra próximo passo</span></div><span class="ui-final-pin">aceite</span></article></div></div>
1843        <div class="ui-final-delivery"><div><p class="ui-final-eyebrow">Pacote de entrega</p><h5>O que outra pessoa precisa para continuar?</h5><p>Um bom handoff reduz adivinhação: contexto, tela, estados e critérios ficam no mesmo lugar.</p></div><div class="ui-final-checklist"><span><i>✓</i>Briefing</span><span><i>✓</i>Blueprint visual</span><span><i>✓</i>Estados</span><span><i>✓</i>Critérios de aceite</span><span><i>+</i>Pendências visíveis</span></div></div>
1844        <div class="ui-final-rubric-strip"><span><b>Clareza</b> a decisão está explícita?</span><span><b>Completude</b> os estados foram descritos?</span><span><b>Continuidade</b> outra pessoa consegue seguir?</span></div>
1845    </section>
1846`;
1847
1848uiDescomplicadaJourney[8].visualMarkup = `
1849    <section class="ui-codes-lab" aria-label="Laboratório visual: Códigos Secretos da UI">
1850        <div class="ui-codes-lab-header"><div><p class="ui-final-eyebrow">Bônus de encerramento · repertório</p><h4>Um código transforma intenção em instrução</h4><p>Use atalhos de linguagem para pedir decisões específicas sem voltar a frases vagas como “deixa bonito” ou “faz responsivo”.</p></div><span class="ui-final-badge">09 · dominar</span></div>
1851        <div class="ui-code-transform"><div class="ui-code-column ui-code-confused"><small>ANTES · PEDIDO VAGO</small><blockquote>“Cria uma tela bonita para meus pedidos.”</blockquote><span class="ui-code-tag ui-code-tag-red">sem contexto</span></div><div class="ui-code-transform-arrow">→</div><div class="ui-code-column ui-code-clear"><small>DEPOIS · PEDIDO COM CÓDIGOS</small><blockquote>“/nomear /priorizar /estados-ui /mobile /aceite-ui”</blockquote><span class="ui-code-tag ui-code-tag-lime">decisões visíveis</span></div></div>
1852        <div class="ui-code-stack"><div class="ui-code-stack-head"><span>MONTE SEU PEDIDO</span><b>combine intenções</b></div><div class="ui-code-chips"><span><b>/nomear</b> blocos</span><span><b>/priorizar</b> decisão</span><span><b>/estados-ui</b> comportamento</span><span><b>/mobile</b> adaptação</span><span><b>/aceite-ui</b> pronto</span></div><div class="ui-code-output"><small>SAÍDA ESPERADA</small><p>Uma especificação que outra pessoa consegue entender, testar e continuar.</p><span>contexto + estrutura + comportamento + critérios</span></div></div>
1853        <div class="ui-codes-rules"><span><b>01</b> Código não substitui contexto.</span><span><b>02</b> Cada código torna uma decisão explícita.</span><span><b>03</b> O resultado precisa ser observável.</span></div>
1854        <p class="ui-visual-caption">Desafio final: escolha cinco códigos e escreva um pedido para uma tela real do seu trabalho.</p>
1855    </section>
1856`;
1857
1858uiDescomplicadaJourney[8].codigos = [
1859    { codigo: '/anatomia' },
1860    { codigo: '/hierarquia' },
1861    { codigo: '/estados-ui' },
1862    { codigo: '/mobile' },
1863    { codigo: '/aceite-ui' }
1864];
1865
1866const uiDescomplicadaCodigos = [
1867    ['/nomear', '/priorizar'],
1868    ['/decisao', '/escaneavel'],
1869    ['/grade', '/adaptar'],
1870    ['/campo', '/orientar'],
1871    ['/vazio', '/recuperar'],
1872    ['/padrao', '/contexto'],
1873    ['/teclado', '/toque'],
1874    ['/aceite', '/handoff-ui']
1875];
1876uiDescomplicadaJourney.slice(0, 8).forEach((item, index) => {
1877    item.codigos = uiDescomplicadaCodigos[index].map(codigo => ({ codigo }));
1878});
1879
1880prototipandoComIaJourney[0].exerciseFields = [
1881    { key: 'problema', label: 'Qual problema existe?', help: 'Descreva a dor, não a solução.', placeholder: 'Ex.: pessoas esquecem tarefas importantes.' },
1882    { key: 'publico', label: 'Quem enfrenta isso?', help: 'Diga para quem o protótipo será útil.', placeholder: 'Ex.: pessoas que precisam organizar o dia.' },
1883    { key: 'resultado', label: 'Qual resultado você espera?', help: 'Explique o que a pessoa conseguirá fazer.', placeholder: 'Ex.: escolher e concluir as tarefas de hoje.' },
1884    { key: 'limite', label: 'O que fica fora agora?', help: 'Defina o limite da primeira versão.', placeholder: 'Ex.: sem colaboração ou notificações nesta versão.' }
1885];
1886
1887prototipandoComIaJourney[1].exerciseFields = [
1888    { key: 'objetivo', label: 'Qual é o objetivo?', help: 'Descreva a mudança que a primeira versão precisa produzir.', placeholder: 'Ex.: permitir que a clínica organize e confirme consultas.' },
1889    { key: 'publico', label: 'Quem vai usar?', help: 'Escolha o usuário principal desta versão.', placeholder: 'Ex.: recepcionistas de uma clínica pequena.' },
1890    { key: 'fluxo', label: 'Qual é o fluxo principal?', help: 'Liste as ações na ordem em que acontecem.', placeholder: 'Ex.: escolher data, ver horário, confirmar consulta.' },
1891    { key: 'dados', label: 'Quais dados são necessários?', help: 'Inclua somente os dados indispensáveis para o fluxo.', placeholder: 'Ex.: nome, especialidade, data e horário.' },
1892    { key: 'sucesso', label: 'Como saberemos que funcionou?', help: 'Defina um resultado que possa ser observado ou testado.', placeholder: 'Ex.: a pessoa confirma um horário sem pedir ajuda.' },
1893    { key: 'fora', label: 'O que fica fora desta versão?', help: 'Liste uma função importante que será deixada para depois.', placeholder: 'Ex.: pagamento e lembretes automáticos ficam fora agora.' }
1894];
1895
1896prototipandoComIaJourney[1].exerciseModelAnswer = 'Objetivo: permitir que a clínica organize e confirme consultas. Público: recepcionistas de uma clínica pequena. Fluxo: escolher especialidade, ver horários disponíveis e confirmar consulta. Dados: nome, especialidade, data e horário. Sucesso: cinco pessoas conseguem confirmar um horário sem orientação. Fora: pagamento e lembretes automáticos nesta primeira versão.';
1897
1898prototipandoComIaJourney[2].exerciseFields = [
1899    { key: 'contexto', label: 'Para quem é a tela?', help: 'Descreva o usuário e a situação em que ele usará o cadastro.', placeholder: 'Ex.: pessoa leiga solicitando um orçamento pelo celular.' },
1900    { key: 'objetivo', label: 'Qual é o objetivo?', help: 'Explique o que a pessoa precisa conseguir fazer.', placeholder: 'Ex.: enviar os dados para receber um orçamento.' },
1901    { key: 'conteudo', label: 'O que precisa aparecer?', help: 'Liste os campos, textos e ações indispensáveis.', placeholder: 'Ex.: nome, e-mail, serviço, mensagem e botão Enviar orçamento.' },
1902    { key: 'estados', label: 'Quais estados devem existir?', help: 'Descreva o que acontece no início, erro, envio e sucesso.', placeholder: 'Ex.: formulário vazio, erro nos campos, enviando e confirmação.' },
1903    { key: 'entrega', label: 'O que você quer receber da IA?', help: 'Defina o formato da resposta ou protótipo esperado.', placeholder: 'Ex.: estrutura da tela, textos dos estados e comportamento de cada ação.' }
1904];
1905
1906prototipandoComIaJourney[2].exerciseModelAnswer = 'Contexto: pessoa leiga solicitando um orçamento pelo celular. Objetivo: enviar os dados para receber um orçamento. Conteúdo: nome, e-mail, serviço, mensagem e botão Enviar orçamento. Estados: formulário vazio, erro nos campos, enviando, erro de envio e confirmação. Entrega: estrutura da tela, textos dos estados e comportamento de cada ação.';
1907
1908// Os dois conceitos da aula ocupam grupos diferentes para tornar visível a
1909// distinção entre explicar a tela e explicar o que acontece nela.
1910prototipandoComIaJourney[2].conceitos[0].grupo = 0;
1911prototipandoComIaJourney[2].conceitos[1].grupo = 1;
1912
1913prototipandoComIaJourney[3].exerciseFields = [
1914    { key: 'sintoma', label: 'Qual é o sintoma?', help: 'Descreva o que a pessoa vê ou sente, sem tentar adivinhar a causa.', placeholder: 'Ex.: o botão Continuar não responde no celular.' },
1915    { key: 'reproducao', label: 'Como reproduzir?', help: 'Liste os passos exatos que fazem o problema aparecer.', placeholder: 'Ex.: abrir checkout, preencher os dados e tocar duas vezes em Continuar.' },
1916    { key: 'ambiente', label: 'Em qual ambiente acontece?', help: 'Informe dispositivo, navegador, tela ou condição relevante.', placeholder: 'Ex.: iPhone, largura de 375px, Safari, tela de checkout.' },
1917    { key: 'esperado', label: 'O que deveria acontecer?', help: 'Descreva o resultado correto para a pessoa.', placeholder: 'Ex.: processar uma tentativa e mostrar a mensagem de andamento.' },
1918    { key: 'limite', label: 'O que precisa ser preservado?', help: 'Defina o limite da correção e os cenários que não podem quebrar.', placeholder: 'Ex.: manter o comportamento no desktop e evitar cobrança duplicada.' }
1919];
1920
1921prototipandoComIaJourney[3].exerciseModelAnswer = 'Sintoma: o botão Continuar não responde no celular. Reprodução: abrir checkout, preencher os dados e tocar duas vezes em Continuar. Ambiente: iPhone, largura de 375px, Safari. Esperado: processar uma tentativa e mostrar a mensagem de andamento. Preservar: comportamento no desktop e proteção contra cobrança duplicada.';
1922
1923prototipandoComIaJourney[3].conceitos[0].comoFunciona = [
1924    'Registre o sintoma com as palavras que a pessoa usaria, sem atribuir c
1924ausa antes da investigação.',
1925    'Liste os passos na ordem exata, incluindo dados de teste, tela, dispositivo e ação realizada.',
1926    'Separe o resultado observado do resultado esperado.',
1927    'Anexe evidência mínima, como mensagem, horário, captura ou resposta técnica relevante.',
1928    'Só depois formule hipóteses e peça à IA uma investigação limitada.'
1929];
1930prototipandoComIaJourney[3].conceitos[0].segundoExemplo = 'Em vez de “o login está ruim”, registre: “no Android 375px, depois de tocar em Entrar com os campos válidos, o botão fica carregando e nenhuma mensagem aparece”.';
1931prototipandoComIaJourney[3].conceitos[0].errosComuns = ['Confundir sintoma com causa.', 'Omitir dispositivo, navegador ou versão.', 'Descrever apenas o que tentou, sem dizer o que esperava ver.'];
1932prototipandoComIaJourney[3].conceitos[1].comoFunciona = [
1933    'Identifique o comportamento que funcionava antes da mudança.',
1934    'Repita o cenário original e o cenário alterado com os mesmos dados.',
1935    'Compare desktop, celular, fluxo feliz, erro e acessibilidade quando forem afetados.',
1936    'Registre qual mudança introduziu o problema e qual proteção deve permanecer.',
1937    'Aprove a correção somente depois de retestar o bug e o caminho legítimo.'
1938];
1939prototipandoComIaJourney[3].conceitos[1].segundoExemplo = 'Adicionar validação ao formulário pode impedir o envio correto se o campo de telefone mudar de formato. A correção precisa proteger o caso inválido sem quebrar o cadastro válido.';
1940prototipandoComIaJourney[3].conceitos[1].comparacao = 'Reprodução de bug descreve como fazer o problema aparecer; regressão verifica se uma mudança criou um problema novo em algo que já funcionava.';
1941prototipandoComIaJourney[3].conceitos[1].errosComuns = ['Testar somente o caso que motivou a mudança.', 'Corrigir o sintoma com uma exceção frágil.', 'Não registrar o cenário preservado nem o resultado do reteste.'];
1942
1943prototipandoComIaJourney[4].sequenceCount = 2;
1944prototipandoComIaJourney[4].connectors = ['compare'];
1945prototipandoComIaJourney[4].exerciseFields = [
1946    { key: 'intencao', label: 'A saída atende à intenção?', help: 'Compare o que foi pedido com o que a IA realmente entregou.', placeholder: 'Ex.: atende ao objetivo de permitir que a pessoa solicite orçamento.' },
1947    { key: 'conteudo', label: 'O conteúdo está correto?', help: 'Aponte informações ausentes, inventadas ou confusas.', placeholder: 'Ex.: falta explicar prazo de retorno e o campo serviço está sem orientação.' },
1948    { key: 'comportamento', label: 'O comportamento foi previsto?', help: 'Verifique ações, estados, erros e retorno após cada interação.', placeholder: 'Ex.: falta estado de envio e mensagem quando a tentativa falha.' },
1949    { key: 'acessibilidade', label: 'O que precisa de acessibilidade?', help: 'Observe leitura, contraste, foco, teclado e clareza dos rótulos.', placeholder: 'Ex.: o erro precisa ser anunciado e o foco deve ir para o primeiro campo inválido.' },
1950    { key: 'risco', label: 'Qual risco ou lacuna existe?', help: 'Registre uma decisão importante que a IA omitiu ou inventou.', placeholder: 'Ex.: não está claro quais dados são armazenados após o envio.' },
1951    { key: 'decisao', label: 'Qual é a próxima decisão?', help: 'Diga o que será aprovado, corrigido, testado ou perguntado.', placeholder: 'Ex.: corrigir os estados antes de aprovar o protótipo.' }
1952];
1953
1954prototipandoComIaJourney[4].exerciseModelAnswer = 'Intenção: atende ao objetivo de permitir que a pessoa solicite orçamento. Conteúdo: falta explicar prazo de retorno e orientar o campo serviço. Comportamento: falta estado de envio e mensagem de falha. Acessibilidade: anunciar erros e mover foco para o primeiro campo inválido. Risco: não está claro quais dados são armazenados. Decisão: corrigir os estados e retestar antes de aprovar.';
1955prototipandoComIaJourney[4].scenario = {
1956    title: 'Revisão guiada: a IA entregou uma tela “pronta”',
1957    intro: 'A aparência está boa, mas a revisão precisa descobrir o que foi atendido, omitido ou inventado.',
1958    steps: [
1959        { text: 'Pedido: criar um cadastro de orçamento para pessoas leigas, com nome, e-mail, serviço e mensagem.', tag: 'Intenção' },
1960        { text: 'Saída: a tela tem campos e botão, mas não explica prazo, não mostra estado de envio e não define o que acontece em caso de erro.', tag: 'Lacuna' },
1961        { text: 'Revisão: conferir conteúdo, comportamento, acessibilidade, segurança e manutenção antes de aprovar.', tag: 'Checklist' },
1962        { text: 'Decisão: pedir correções específicas e retestar o fluxo completo, em vez de aceitar porque a tela está bonita.', tag: 'Próximo passo' }
1963    ]
1964};
1965prototipandoComIaJourney[4].conceitos[0].comoFunciona = ['Releia a intenção e c
1965ompare cada promessa com a saída.', 'Teste o caminho principal e tente provocar erro, vazio e repetição.', 'Confira texto, hierarquia, acessibilidade, dados e permissões.', 'Registre acertos, riscos, lacunas e perguntas abertas.', 'Converta a revisão em decisões para a próxima versão.'];
1966prototipandoComIaJourney[4].conceitos[0].segundoExemplo = 'Uma tela pode estar visualmente consistente e ainda assim não dizer o que acontece depois do envio. A revisão transforma essa impressão em uma pergunta verificável: qual mensagem aparece e como a pessoa tenta novamente?';
1967prototipandoComIaJourney[4].conceitos[0].errosComuns = ['Avaliar apenas estética.', 'Aceitar comportamento que não foi pedido.', 'Listar problemas sem decidir o próximo passo.'];
1968prototipandoComIaJourney[4].conceitos[1].comoFunciona = ['Procure decisões necessárias que não aparecem no briefing nem na interface.', 'Marque o que a IA inventou, como regras, textos, dados ou permissões.', 'Formule perguntas que possam ser respondidas pelo responsável pelo produto.', 'Não implemente uma suposição importante antes de confirmar a regra.', 'Atualize o pedido ou a documentação com a decisão tomada.'];
1969prototipandoComIaJourney[4].conceitos[1].segundoExemplo = 'Se o protótipo mostra “seus dados foram enviados”, ainda falta saber se eles foram armazenados, para quem serão encaminhados e como a pessoa corrige um e-mail errado.';
1970prototipandoComIaJourney[4].conceitos[1].errosComuns = ['Confundir detalhe visual com especificação completa.', 'Deixar perguntas importantes escondidas no código.', 'Tratar uma resposta inventada pela IA como decisão do produto.'];
1971
1972prototipandoComIaJourney[5].conceitos[0].grupo = 0;
1973prototipandoComIaJourney[5].conceitos[1].grupo = 1;
1974prototipandoComIaJourney[5].exerciseFields = [
1975    { key: 'versao', label: 'Qual é a versão atual?', help: 'Dê um nome ou número que permita reconhecer a entrega certa.', placeholder: 'Ex.: Protótipo agenda v0.3 — revisão do fluxo de confirmação.' },
1976    { key: 'estrutura', label: 'Onde cada coisa está?', help: 'Liste as pastas ou arquivos principais e a finalidade de cada um.', placeholder: 'Ex.: /telas, /componentes, /dados-ficticios, /decisoes e README.' },
1977    { key: 'decisao', label: 'Qual decisão foi registrada?', help: 'Explique uma escolha, o motivo e o impacto para o projeto.', placeholder: 'Ex.: usar dados fictícios para testar o fluxo sem integração externa.' },
1978    { key: 'pendencias', label: 'O que ainda está pendente?', help: 'Separe o que está pronto, em revisão e fora desta versão.', placeholder: 'Ex.: pronto: fluxo; revisão: textos; fora: pagamento real.' },
1979    { key: 'continuidade', label: 'Como outra pessoa continua?', help: 'Diga o que ela precisa abrir, testar e saber antes de alterar.', placeholder: 'Ex.: ler o README, abrir a versão v0.3 e seguir os critérios de teste.' }
1980];
1981prototipandoComIaJourney[5].exerciseModelAnswer = 'Versão: Protótipo agenda v0.3 — revisão do fluxo de confirmação. Estrutura: /telas, /componentes, /dados-ficticios, /decisoes e README. Decisão: usar dados fictícios para testar o fluxo sem integração externa. Pendências: fluxo pronto, textos em revisão e pagamento real fora. Continuidade: ler o README, abrir a versão v0.3 e seguir os critérios de teste.';
1982prototipandoComIaJourney[5].conceitos[0].comoFunciona = ['Dê nomes previsíveis para pastas, arquivos e versões.', 'Separe o que é interface, componente, dado fictício, referência e documentação.', 'Inclua um README com como abrir, testar e entender as limitações.', 'Mantenha apenas uma versão claramente identificada como atual.', 'Revise se outra pessoa encontra o que precisa sem depender de explicação oral.'];
1983prototipandoComIaJourney[5].conceitos[0].segundoExemplo = 'Em vez de enviar “a pasta do protótipo”, entregue /prototipo-v0.3 com README, /telas, /dados-ficticios, /decisoes e /pendencias, cada item com uma finalidade clara.';
1984prototipandoComIaJourney[5].conceitos[0].errosComuns = ['Misturar rascunho e versão atual.', 'Usar nomes como final-final-2.', 'Depender de arquivos ou dados que só existem no computador de quem criou.'];
1985prototipandoComIaJourney[5].conceitos[1].comoFunciona = ['Registre a decisão tomada, as alternativas consideradas e o motivo.', 'Anote impacto, risco restante e o que faria a decisão mudar.', 'Diferencie decisão confirmada de pergunta em aberto.', 'Ligue a decisão aos arquivos, telas ou critérios afetados.', 'Atualize o registro quando uma nova evidência mudar o caminho.'];
1986prototipandoComIaJourney[5].conceitos[1].segundoExemplo = '“Usaremos dados fictícios nesta versão porque ainda não há integração com o calendário; quando o fluxo for aprovado, a equipe avaliará autenticação e sincronização.”';
1987prototipandoComIaJourney[5].conceitos[1].errosComuns = ['Registrar apen
1987as o resultado sem o motivo.', 'Esconder decisões importantes no chat.', 'Tratar pendência como falha sem dizer quem precisa decidir e qual é o próximo passo.'];
1988
1989Object.assign(prototipandoComIaJourney[6], {
1990    practice: 'Prepare um pedido para revisar um formulário de orçamento: substitua dados de clientes por exemplos fictícios, remova credenciais e defina o que precisa ser testado antes da publicação.',
1991    exerciseFields: [
1992        { key: 'compartilhar', label: 'O que ajuda a IA a entender?', help: 'Descreva o problema e o comportamento esperado com informações fictícias.', placeholder: 'Descreva o contexto necessário...' },
1993        { key: 'remover', label: 'O que deve sair do pedido?', help: 'Identifique dados pessoais e credenciais que não são necessários à revisão.', placeholder: 'Liste o que você removeria...' },
1994        { key: 'substituir', label: 'Como criar um exemplo seguro?', help: 'Use valores inventados que preservem o formato necessário ao teste.', placeholder: 'Escreva um exemplo fictício...' },
1995        { key: 'revisar', label: 'O que exige revisão humana?', help: 'Indique decisões sobre acesso, armazenamento e envio das informações.', placeholder: 'Explique quem precisa conferir e o quê...' },
1996        { key: 'testar', label: 'Como conferir a entrega?', help: 'Descreva um teste de sucesso e um de falha, usando dados fictícios.', placeholder: 'Registre os resultados esperados...' },
1997        { key: 'prompt', label: 'Monte seu pedido final', help: 'Junte contexto, limites, revisão e testes em uma instrução para a IA.', placeholder: 'Escreva seu pedido aqui...' }
1998    ],
1999    exerciseModelAnswer: 'Compartilhar: estrutura do formulário e descrição do erro. Remover: nomes reais, e-mails de clientes, senhas e chaves. Substituir: Cliente Exemplo e [email protected]. Revisar: quem pode consultar os pedidos e onde serão armazenados. Testar: envio válido confirma recebimento; falha informa o problema e permite tentar novamente. Pedido: revise este formulário com dados fictícios, identifique dúvidas sobre acesso e armazenamento e proponha testes antes de publicar.',
2000    scenario: {
2001        title: 'Antes de enviar: revise o próprio pedido',
2002        intro: 'Caso fictício: uma equipe quer ajuda da IA para corrigir um formulário de orçamento. O material disponível inclui mensagens de clientes e configurações do serviço de e-mail.',
2003        steps: [
2004            { text: 'Descreva o sintoma e o resultado esperado. Compartilhe somente o trecho necessário para entender o problema.', tag: 'Contexto útil' },
2005            { text: 'Troque as mensagens reais por exemplos inventados e remova senhas, tokens e chaves. Apagar apenas o nome pode deixar outras informações identificáveis.', tag: 'Preparar o exemplo' },
2006            { text: 'Peça à IA que registre dúvidas sobre quem recebe e acessa os pedidos. A pessoa responsável pelo produto confirma essas decisões.', tag: 'Revisão humana' },
2007            { text: 'Teste envio, falha e tentativa repetida com dados fictícios. Registre os resultados e as pendências antes da publicação.', tag: 'Verificação' }
2008        ]
2009    }
2010});
2011Object.assign(prototipandoComIaJourney[6].conceitos[0], {
2012    comoFunciona: ['Identifique o contexto mínimo necessário para explicar o problema.', 'Localize dados pessoais e credenciais no texto, código e imagens.', 'Crie exemplos inteiramente fictícios com o mesmo formato.', 'Confira se os exemplos ainda permitem reproduzir o comportamento.', 'Revise o material completo antes de enviar.'],
2013    segundoExemplo: 'Para investigar uma validação de e-mail, use [email protected]. Não é necessário copiar a lista de clientes para explicar um erro de formato.',
2014    errosComuns: ['Remover o nome e manter telefone, endereço ou mensagem identificável.', 'Colar uma chave real dentro de um exemplo de código.', 'Enviar uma base inteira quando um único registro fictício explica o problema.']
2015});
2016Object.assign(prototipandoComIaJourney[6].conceitos[1], {
2017    comoFunciona: ['Compare a entrega com o objetivo e os limites do pedido.', 'Confirme com o responsável quem pode acessar e alterar os dados.', 'Execute os testes previstos e registre resultados observados.', 'Peça revisão técnica quando a mudança envolver autenticação, permissões ou serviços externos.', 'Deixe pendências explícitas e identifique quem decide a publicação.'],
2018    segundoExemplo: 'A IA sugere guardar os pedidos no navegador. A equipe precisa avaliar se isso atende ao uso compartilhado, à persistência e às regras de acesso antes de aceitar.',
2019    errosComuns: ['Considerar a explicação da IA como prova de que o código funciona.', 'Aprovar a publicação sem executar o fluxo.', 'Deixar uma decisão sobre acesso implícita no código.']
2020});
2021
2022Object.assign(prototipandoComIaJourney[7], {
2023    finalStudio: true,
2024    estimatedMinutes: 120,
2025    goal: 'Crie, teste e apresente um protótipo guiado por IA. Sua entrega deve permitir que outra pessoa percorra o fluxo e entenda suas decisões.',
2026    practice: 'Construa um protótipo de solicitação de orçamento com dados fictícios ou adapte uma ideia própria de tamanho equivalente. Execute os testes e registre abaixo as evidências e pendências.',
2027    lessons: ['Retome seu escopo: escolha um público, uma tarefa principal e o que ficará fora.', 'Peça o protótipo à IA, percorra o fluxo e revise a primeira saída.', 'Teste os estados inicial, inválido, enviando, falha e sucesso com dados fictícios.', 'Entregue a versão identificada, instruções para abrir, resultados dos testes e limitações.'],
2028    scenario: {
2029        title: 'Seu projeto: do pedido à demonstração',
2030        intro: 'Exemplo didático: uma pessoa solicita orçamento pelo celular. O envio será simulado e isso deve ficar explícito na tela. Você pode usar outra ideia, preservando um fluxo pequeno e testável.',
2031        steps: [
2032            { text: 'Defina o recorte: nome fictício, e-mail de exemplo, serviço e mensagem. Pagamento, login e envio real ficam fora desta versão.', tag: 'Planejar' },
2033            { text: 'Peça à IA um protótipo navegável com rótulos claros, uso por teclado e estados de erro e sucesso. Informe como deseja abrir e executar a entrega.', tag: 'Construir' },
2034            { text: 'Abra o resultado. Envie campos vazios, corrija os dad
2034os e percorra o fluxo válido. Simule uma falha de envio e confira se é possível tentar novamente.', tag: 'Testar' },
2035            { text: 'Registre um problema observado e peça uma correção específica. Repita o teste que falhou e confira se o caminho válido continua funcionando.', tag: 'Revisar' },
2036            { text: 'Identifique a versão e demonstre o fluxo. Entregue instruções, decisões, testes executados e pendências. Informe claramente que nenhum orçamento real foi enviado.', tag: 'Apresentar' }
2037        ]
2038    },
2039    exerciseFields: [
2040        { key: 'escopo', label: '01 · Qual problema seu projeto resolve?', help: 'Informe público, tarefa principal e limite da versão.', placeholder: 'Descreva seu recorte...' },
2041        { key: 'prompt', label: '02 · Qual pedido você enviou à IA?', help: 'Inclua contexto, campos, estados e formato da entrega.', placeholder: 'Registre o prompt utilizado...' },
2042        { key: 'prototipo', label: '03 · Onde está a versão para revisão?', help: 'Informe nome ou link da versão e explique como abri-la. Não inclua credenciais.', placeholder: 'Identifique sua entrega e as instruções...' },
2043        { key: 'testes', label: '04 · O que você realmente testou?', help: 'Para cada teste, registre ação, resultado esperado e observado. Identifique o que não foi testado.', placeholder: 'Relate os testes e seus resultados...' },
2044        { key: 'revisao', label: '05 · O que mudou após a revisão?', help: 'Descreva uma correção e o resultado do reteste. Se não houve correção, registre como revisou.', placeholder: 'Explique o que observou e decidiu...' },
2045        { key: 'entrega', label: '06 · Quais são as limitações e próximos passos?', help: 'Liste simulações, pendências e decisões necessárias antes de uso real.', placeholder: 'Registre o que outra pessoa precisa saber...' }
2046    ],
2047    exerciseModelAnswer: 'Exemplo ilustrativo — use evidências do seu próprio projeto. Escopo: pessoa solicita orçamento pelo celular; sem envio real. Pedido: formulário com dados fictícios e estados inicial, inválido, enviando, falha e sucesso. Versão: orçamento-v0.1, com instruções no README. Testes: envio vazio mostra erros; envio válido apresenta confirmação de simulação; falha permite tentar novamente. Revisão: corrigido rótulo do e-mail e repetido o teste. Limitações: dados não são enviados a uma empresa; integração e revisão de acesso permanecem pendentes. O preenchimento deste registro, por si só, não comprova que o protótipo funciona.'
2048});
2049Object.assign(prototipandoComIaJourney[7].conceitos[0], {
2050    comoFunciona: ['Escolha uma hipótese pequena que possa ser demonstrada.', 'Defina as telas, ações e estados necessários ao fluxo.', 'Use dados fictícios e identifique cada integração simulada.', 'Percorra o fluxo com outra pessoa e observe onde surgem dúvidas.', 'Registre o que aprendeu e decida o que revisar.'],
2051    segundoExemplo: 'Uma agenda permite selecionar um horário e ver uma confirmação simulada. Isso ajuda a testar a compreensão do fluxo, mas não comprova que reservas reais serão protegidas contra duplicidade.',
2052    errosComuns: ['Apresentar uma simulação como operação real.', 'Adicionar funções que impedem terminar o fluxo principal.', 'Confundir uma tela renderizada com uma jornada testada.']
2053});
2054Object.assign(prototipandoComIaJourney[7].conceitos[1], {
2055    comoFunciona: ['Identifique a versão e como abrir a entrega.', 'Explique objetivo, escopo e decisões tomadas.', 'Liste testes executados com seus resultados.', 'Marque simulações, pendências e riscos conhecidos.', 'Peça que outra pessoa abra e percorra o fluxo seguindo as instruções.'],
2056    segundoExemplo: 'O pacote orçamento-v0.1 inclui protótipo, README e notas de revisão. Uma pessoa consegue abrir, testar um envio fictício e localizar as limitações sem depender do histórico de conversa com a IA.',
2057    errosComuns: ['Enviar somente uma captura da tela.', 'Declarar testes como aprovados sem executá-los.', 'Omitir instruções, arquivos necessários ou limitações.']
2058});
2059
2060Object.assign(prototipandoComIaJourney[8], {
2061    estimatedMinutes: 30,
2062    goal: 'Leve o método para seu próximo projeto. Escolha os códigos que ajudam a explicar sua intenção e transforme cada um em uma instrução concreta.',
2063    focus: 'Os códigos são lembretes do curso. Uma ferramenta de IA só entende sua intenção quando você explica o que cada código significa no pedido.',
2064    practice: 'Retome seu projeto final e escreva um pedido de próxima versão usando pelo menos quatro códigos. Explique a função de cada um, o resultado esperado e como você vai conferir a entrega.',
2065    lessons: [
2066        'Planejar: /problema e /limite definem o recorte; /escopo e /criterio tornam a entrega verificável.',
2067        'Construir: /contexto e /estados descrevem a experiência; /reproduzir e /nao-quebrar orientam correções.',
2068        'Revisar e organizar: /revisar e /perguntar revelam pendências; /estrutura e /versao preparam a continuidade.',
2069        'Entregar: /sem-segredo e /humano orientam cuidados; /prototipo e /entrega reúnem o projeto para revisão.',
2070        'Selecione os códigos pertinentes. Explique suas instruções em linguagem natural e confira o resultado antes de aceitar.'
2071    ],
2072    exerciseFields: [
2073        { key: 'objetivo', label: '01 · Qual é a próxima melhoria?', help: 'Escolha uma mudança pequena para o projeto final.', placeholder: 'Descreva o objetivo e quem se beneficia...' },
2074        { key: 'codigos', label: '02 · Quais códigos ajudam neste pedido?', help: 'Escolha pelo menos quatro e explique a função de cada um.', placeholder: 'Liste os códigos e suas justificativas...' },
2075        { key: 'pedido', label: '03 · Escreva seu pedido completo', help: 'Expanda os códigos em instruções que a IA consiga entender sem conhecer este curso.', placeholder: 'Junte contexto, ações, limites e entrega...' },
2076        { key: 'verificacao', label: '04 · Como você vai conferir?', help: 'Descreva testes com resultado esperado e o que precisa continuar funcionando.', placeholder: 'Registre os cenários de revisão...' },
2077        { key: 'limites', label: '05 · O que fica para uma decisão futura?', help: 'Informe limites, dúvidas e decisões que precisam de confirmação.', placeholder: 'Registre o que ficou fora ou em aberto...' },
2078        { key: 'aprendizado', label: '06 · O que mudou na sua forma de pedir?', help: 'Compare seu primeiro pedido do curso com este. Cite uma melhoria concreta.', placeholder: 'Explique o que você passou a especificar...' }
2079    ],
2080    exerciseModelAnswer: 'Exemplo didático: melhorar a mensagem de falha do orçamento. /contexto: pessoa leiga usa o celular. /estados: explicar enviando, falha e sucesso simulado. /nao-quebrar: preservar os campos preenchidos e o fluxo válido. /entrega: registrar alteração, testes e pendências. Pedido: revise o formulário para esse público; durante o envio mostre andamento; na falha preserve os campos e permita tentar novamente; no sucesso informe que é uma simulação. Entregue a versão identificada e os resultados dos testes. Conferir: simular falha, repetir envio e testar o caminho válido. Fora: envio real de e-mail. Aprendizado: passei a descrever o que acontece após cada ação.'
2081});
2082
2083const segurancaAplicacoesWebJourney = [
2084    {
2085        stage: 'Aula 01',
2086        estimatedMinutes: 35,
2087        layout: 'grid',
2088        title: 'Fundamentos da segurança em aplicações web',
2089        goal: 'Entenda segurança como redução de risco, não como medo ou promessa absoluta, e aprenda a olhar uma aplicação com mentalidade defensiva.',
2090        focus: 'Antes de revisar um sistema, defina autorização ética, ambiente de teste, impacto possível e o que precisa ser protegido.',
2091        practice: 'Escolha uma aplicação real e liste: dados importantes, ações críticas, usuários envolvidos e o pior impacto caso algo seja acessado indevidamente.',
2092        exerciseFeedback: 'A boa resposta separa ativo, risco, impacto e limite de teste autorizado. Segurança começa com contexto claro.',
2093        lessons: [
2094            'Risco combina probabilidade, impacto e exposição.',
2095            'Teste de segurança só deve acontecer com autorização explícita.',
2096            'Uma visão defensiva procura proteger pessoas, dados e continuidade do serviço.'
2097        ],
2098        conceitos: [
2099            apiConceito('Risco de segurança', 'A chance de uma falha causar dano real a pessoas, dados, dinheiro ou operação. Risco não é só a existência técnica de um bug: ele combina o que pode acontecer, quem seria afetado, quão fácil é abusar e quais controles já reduzem o problema.', 'Use para priorizar o que precisa ser corrigido primeiro e para explicar por que uma falha merece atenção agora, depois ou apenas monitoramento.', 'Deixa mais seguro.', 'Priorizar o fluxo de recuperação de senha porque ele pode dar acesso indevido à conta do aluno, tem rota pública e afeta dados pessoais, certificados e histórico de pagamento.', 'Security risk', ['impacto', 'probabilidade', 'exposição'], { comoFunciona: ['Identifique o que pode dar errado em uma tela, API ou integração.', 'Defina o impacto: dado vazado, dinheiro perdido, acesso indevido, indisponibilidade ou dano operacional.', 'Avalie a probabilidade: precisa estar logado? é rota pública? exige conhecimento raro? pode ser automatizado?', 'Observe a exposição: quantas pessoas, registros, integrações ou ambientes ficam alcançáveis.', 'Priorize a correção que reduz mais dano com menor risco de regressão.'], segundoExemplo: 'Um botão desalinhado e um endpoint que libera certificado de outro aluno são bugs, mas só o segundo vira risco de segurança relevante porque cruza impacto, probabilidade e exposição.', comparacao: 'Impacto mede o tamanho do dano; probabilidade mede a chance de acontecer; exposição mede quanto o alvo está acessível. Risco nasce da comb
2099inação dos três.', errosComuns: ['Tratar todo problema como crítico sem explicar impacto.', 'Focar só em invasão e esquecer vazamento, fraude, indisponibilidade e erro operacional.', 'Priorizar pela técnica mais assustadora em vez do dano real para usuário e negócio.'] }),
2100            apiConceito('Ativo protegido', 'Algo que tem valor e precisa de proteção: dados, conta, pagamento, conteúdo, certificado, configuração, integração ou reputação. Um ativo protegido não precisa ser apenas um arquivo secreto; pode ser qualquer coisa que, se for vista ou alterada indevidamente, gere dano.', 'Use para explicar o que está em jogo antes de discutir vulnerabilidade, controle ou correção. Sem saber o ativo, a conversa fica genérica demais.', 'Protege o sistema.', 'Proteger dados pessoais dos alunos, cobranças, sessões administrativas, emissão de certificados, cupons de parceiros e conteúdo pago, cada um com regra própria de acesso e registro.', 'Protected asset', ['dado sensível', 'conta', 'recurso crítico'], { comoFunciona: ['Liste o que a aplicação guarda, mostra ou permite alterar.', 'Separe ativos por tipo: dados pessoais, dinheiro, acesso, conteúdo, operação e configuração.', 'Defina quem deveria acessar cada ativo e em qual situação.', 'Relacione cada ativo aos fluxos onde ele aparece: login, checkout, painel, certificado, webhook ou suporte.', 'Use essa lista para guiar permissões, logs, validação e testes.'], segundoExemplo: 'Em uma escola online, aulas pagas, histórico financeiro, progresso do aluno e credenciais do gestor são ativos diferentes, cada um com risco próprio.', comparacao: 'Ativo protegido é o que precisa ser defendido; controle é a medida usada para defender; vulnerabilidade é a falha que ameaça esse ativo.', errosComuns: ['Dizer “proteger o sistema” sem listar dados, contas, ações e integrações concretas.', 'Proteger apenas banco de dados e esquecer tokens, logs, arquivos e configurações.', 'Aplicar a mesma regra para ativos com riscos diferentes, como comentário público e dado financeiro.'] }),
2101            apiConceito('Autorização ética', 'Permissão clara para testar uma aplicação, com escopo, ambiente, horário, dados permitidos e limites definidos. Ela protege usuários, empresa e quem testa, porque separa aprendizado defensivo de acesso indevido.', 'Use antes de qualquer varredura, tentativa de exploração, teste invasivo, automação, análise de produção ou reprodução de vulnerabilidade.', 'Vou testar vulnerabilidade.', 'Testar apenas o ambiente sandbox autorizado, com contas fictícias, sem acessar dados reais, sem afetar disponibilidade e registrando evidências defensivas suficientes para correção.', 'Authorized security testing', ['escopo', 'ambiente de teste', 'responsabilidade'], { comoFunciona: ['Defina qual aplicação, domínio, ambiente e conta podem ser usados.', 'Combine o que está fora do limite: produção, dados reais, força bruta, negação de serviço ou terceiros.', 'Use dados fictícios e evidência mínima, sem copiar informação sensível.', 'Pare o teste se surgir risco de dano e comunique o responsável.', 'Registre achados com passos autorizados e sugestão de correção.'], segundoExemplo: 'É aceitável testar IDOR com dois alunos fictícios no sandbox; não é aceitável trocar IDs em produção para ver dados de clientes reais.', comparacao: 'Autorização ética não é burocracia: é o controle que transforma curiosidade técnica em revisão defensiva responsável.', errosComuns: ['Testar produção sem autorização formal.', 'Usar dados reais quando um ambiente controlado resolveria o aprendizado.', 'Achar que “era só um teste” justifica acessar conta, arquivo ou dado de outra pessoa.'] })
2102        ],
2103        codigos: [
2104            { codigo: '/risco' },
2105            { codigo: '/ativos' }
2106        ],
2107        scenario: {
2108            title: 'Cenário completo: primeira revisão de segurança de uma escola online',
2109            intro: 'Antes de falar em ferramenta, o time organiza o que precisa proteger e qual teste é permitido.',
2110            steps: [
2111                { text: 'O time lista dados de alunos, cobranças, aulas pagas, certificados e painel gestor como ativos protegidos.', tag: 'Ativo protegido' },
2112                { text: 'A recuperação de senha e o login do gestor entram como riscos prioritários porque podem abrir acesso indevido.', tag: 'Risco de segurança' },
2113                { text: 'A revisão fica limitada ao ambiente sandbox, com dados fictícios e horário combinado.', tag: 'Autorização ética' },
2114                { text: 'Cada achado precisa trazer impacto, evidência defensiva e sugestão de correção.', tag: 'Relato responsável' }
2115            ]
2116        }
2117    },
2118    {
2119        stage: 'Aula 02',
2120        estimatedMinutes: 35,
2121        layout: 'grid',
2122        title: 'Como ataques web acontecem',
2123        goal: 'Aprenda os pontos de entrada mais comuns de uma aplicação: URL, parâmetros, formulários, cookies, headers e uploads.',
2124        focus: 'Toda entrada enviada pelo usuário ou por outro sistema deve ser tratada como não confiável até ser validada.',
2125        practice: 'Mapeie uma tela de login ou checkout e identifique quais dados entram por rota, formulário, cookie, header e chamada de API.',
2126        exerciseFeedback: 'A resposta fica forte quando mostra por onde o dado entra e qual validação deveria existir antes de processar.',
2127        lessons: [
2128            'Ataques geralmente começam em entradas comuns da aplicação.',
2129            'Parâmetros, cookies e headers também carregam decisões importantes.',
2130            'Mapear entradas ajuda a descobrir onde validar, bloquear ou registrar.'
2131        ],
2132        conceitos: [
2133            apiConceito('Ponto de entrada', 'Qualquer lugar por onde dados chegam ao sistema: formulário, parâmetro de URL, header, cookie, upload, webhook, importação, busca ou chamada feita por outro serviço. Ataques costumam começar justamente por entradas comuns que parecem inofensivas.', 'Use para revisar formulários, APIs, uploads, URLs e integrações antes de pensar em solução. Se você não sabe por onde o dado entra, não sabe onde validar.', 'Confere os campos.', 'Validar dados que chegam pelo formulário de checkout, query string de cupom, parâmetros de aula, cookie de sessão e webhook de pagamento antes de calcular preço, liberar acesso ou exibir conteúdo.', 'Entry point', ['input', 'formulário', 'requisição'], { comoFunciona: ['Abra o fluxo e liste todos os lugares onde o usuário ou sistema externo envia algo.', 'Inclua entradas visíveis, como campos, e invisíveis, como headers, cookies e payloads de API.', 'Para cada entrada, defina formato esperado, tamanho máximo, origem e consequência.', 'Valide no servidor antes de gravar, consultar banco, chamar integração ou renderizar resposta.', 'Registre entradas críticas quando ajudam auditoria ou suporte.'], segundoExemplo: 'No checkout, cursos selecionados, cupom, e-mail, origem de parceiro, evento do gateway e retorno pós-pagamento são pontos de entrada diferentes.', comparacao: 'Ponto de entrada é a porta; entrada não confiável é a postura de segurança aplicada a tudo que passa por essa porta.', errosComuns: ['Revisar só campos visíveis e esquecer query string, cookie, header e webhook.', 'Assumir que uma entrada é segura porque veio do próprio frontend.', 'Não documentar o que c
2133ada entrada pode causar depois de processada.'] }),
2134            apiConceito('Entrada não confiável', 'Dado recebido de fora do controle direto do backend, mesmo quando vem de uma tela bonita, app oficial, parceiro conhecido ou navegador autenticado. O usuário pode alterar requisições, repetir eventos, trocar parâmetros e chamar APIs sem passar pela interface.', 'Use para evitar confiar cegamente no navegador, app, parceiro, webhook, arquivo ou payload. A regra forte é: valide antes de usar.', 'O front já valida.', 'Revalidar no backend o curso comprado, o valor cobrado, o cupom aplicado, o aluno logado, o dono do recurso e a assinatura do webhook antes de liberar matrícula.', 'Untrusted input', ['validação no servidor', 'payload', 'parâmetro'], { comoFunciona: ['Receba o dado sem assumir que ele é verdadeiro.', 'Normalize formato quando necessário: texto, número, e-mail, URL, slug ou ID.', 'Compare com regras internas: plano existe, preço confere, cupom vale, sessão pertence ao aluno.', 'Recuse valores fora do esperado com mensagem clara e sem vazar detalhe interno.', 'Use logs para entradas sensíveis ou rejeições importantes.'], segundoExemplo: 'Mesmo que o botão da interface não permita preço zero, alguém pode enviar payload manualmente. O backend deve recalcular o preço a partir do banco.', comparacao: 'Validação no frontend melhora experiência; validação no servidor protege a regra. Uma complementa a outra, mas só a do servidor é barreira real.', errosComuns: ['Confiar em campo hidden, disabled ou valor vindo do localStorage.', 'Validar só tipo e esquecer regra de negócio.', 'Aceitar payload de webhook sem assinatura ou segredo de origem.'] }),
2135            apiConceito('Superfície de ataque', 'Conjunto de telas, rotas, APIs, arquivos, integrações, permissões e automações que podem ser alcançados ou influenciados por usuários, parceiros, sistemas externos ou tráfego público. Quanto maior e menos controlada a superfície, maior o esforço de proteção.', 'Use para reduzir exposição, priorizar revisão e decidir onde colocar controles primeiro. Ela ajuda a transformar “revisar tudo” em mapa de áreas críticas.', 'Tem muita coisa aberta.', 'Revisar rotas públicas, login, upload, checkout, webhook, redefinição de senha, certificados, cupons e endpoints administrativos como superfície principal antes de publicar o curso.', 'Attack surface', ['rota pública', 'endpoint', 'exposição'], { comoFunciona: ['Liste tudo que pode receber acesso direto ou indireto.', 'Separe por exposição: público, autenticado, administrativo, integração externa ou interno.', 'Marque quais itens mexem com dados, dinheiro, permissões, arquivos ou disponibilidade.', 'Remova o que não precisa estar exposto e proteja o que precisa ficar aberto.', 'Repita o mapa quando novas rotas, cursos, webhooks ou uploads forem criados.'], segundoExemplo: 'Uma página pública de vendas, uma API de login e um webhook de pagamento pertencem à superfície mesmo que só uma delas apareça no menu.', comparacao: 'Exposição mede o alcance de um item; superfície de ataque é o mapa completo desses itens acessíveis.', errosComuns: ['Achar que rota sem link no menu não é acessível.', 'Esquecer ambientes de teste publicados, arquivos estáticos e endpoints antigos.', 'Mapear só frontend e deixar APIs e integrações fora da revisão.'] })
2136        ],
2137        codigos: [
2138            { codigo: '/mapeia-entradas' },
2139            { codigo: '/nao-confie-no-front' }
2140        ],
2141        scenario: {
2142            title: 'Cenário completo: mapa de entradas do checkout',
2143            intro: 'A mesma compra recebe dados por vários caminhos, e todos precisam ser conferidos no servidor.',
2144            steps: [
2145                { text: 'O aluno escolhe cursos e o navegador envia slugs no checkout.', tag: 'Ponto de entrada' },
2146                { text: 'O cupom chega por campo de texto e não pode definir sozinho o valor final.', tag: 'Entrada não confiável' },
2147                { text: 'O gateway envia um webhook depois do pagamento e também precisa validação de origem.', tag: 'Integração externa' },
2148                { text: 'Login, checkout, cupom, webhook e painel gestor formam a superfície inicial de revisão.', tag: 'Superfície de ataque' }
2149            ]
2150        }
2151    },
2152    {
2153        stage: 'Aula 03',
2154        estimatedMinutes: 45,
2155        layout: 'groups',
2156        title: 'Autenticação, sessão e senhas',
2157        goal: 'Entenda como falhas de login, sessão e senha podem permitir acesso indevido mesmo quando a tela parece funcionar para o usuário comum.',
2158        focus: 'Separe quatro decisões: como a pessoa prova identidade, como o sistema limita abuso, quanto tempo a sessão dura e como o acesso é encerrado ou recuperado.',
2159        practice: 'Escreva uma especificação de login seguro para o gestor: senha, MFA, limite de tentativas, mensagem de erro, duração da sessão, logout e recuperação de senha.',
2160        exerciseFeedback: 'A resposta boa não diz só "login seguro"; ela define tentativa, bloqueio, sessão, expiração, logout, recuperação e o que nunca deve aparecer na mensagem pública.',
2161        lessons: [
2162            'Autenticação confirma quem a pessoa diz ser.',
2163            'Sessão mantém o acesso depois do login e precisa expirar com segurança.',
2164            'Mensagens e limites de tentativa reduzem abuso sem revelar informação sensível.'
2165        ],
2166        groups: [
2167            { title: 'Entrada', desc: 'Senha, MFA e recuperação de acesso.' },
2168            { title: 'Continuidade', desc: 'Sessão, expiração e encerramento seguro.' }
2169        ],
2170        conceitos: [
2171            apiConceito('Authentication Bypass', 'Falha que permite entrar ou executar ação protegida sem passar corretamente pelo login.', 'Use para revisar páginas, APIs e atalhos que deveriam exigir autenticação antes de responder.', 'Só esconde o botão do gestor.', 'Toda rota administrativa valida a sessão no backend antes de listar alunos, pagamentos ou configurações, mesmo que alguém chame a API direto.', 'Authentication bypass', ['login', 'sessão', 'rota protegida'], { grupo: 0, comoFunciona: ['A pessoa tenta abrir uma tela ou API protegida.', 'O backend confere se existe sessão válida antes de buscar dados.', 'Se não houver sessão, responde bloqueio e não executa a ação.', 'A interface só melhora a experiência;
2171 a proteção real fica no servidor.'], errosComuns: ['Confiar apenas em esconder links e botões no frontend.', 'Proteger a página HTML, mas esquecer as rotas de API que devolvem os dados.'], segundoExemplo: 'Mesmo que alguém abra /api/admin/students manualmente, a API deve recusar sem sessão administrativa válida.' }),
2172            apiConceito('Brute Force', 'Tentativas repetidas de adivinhar senha, código MFA ou token de recuperação.', 'Use para pedir limite de tentativas, atraso progressivo, bloqueio temporário e registro de auditoria.', 'Se errar senha, deixa tentar de novo.', 'Após 5 tentativas de login do gestor em 60 segundos, bloquear temporariamente novas tentativas, responder mensagem genérica e registrar o evento.', 'Brute force attack', ['rate limit', 'tentativa de login', 'bloqueio temporário'], { grupo: 0, comoFunciona: ['O sistema conta tentativas por e-mail, IP ou combinação dos dois.', 'Ao passar do limite, novas tentativas são recusadas por um período curto.', 'A mensagem pública continua genérica para não confirmar se o e-mail existe.', 'O evento fica registrado para revisão do gestor ou suporte.'], errosComuns: ['Bloquear só por e-mail e permitir trocar o e-mail para continuar tentando.', 'Mostrar mensagens diferentes para e-mail inexistente e senha errada, ajudando enumeração de contas.'], segundoExemplo: 'O mesmo cuidado vale para código MFA e link de redefinição de senha: tentativa repetida também precisa limite.' }),
2173            apiConceito('Session Hijacking', 'Uso indevido de uma sessão válida que foi roubada, copiada, vazada ou mantida aberta por tempo demais.', 'Use para proteger cookies, tokens, expiração, logout e reautenticação em ações sensíveis.', 'Mantém o gestor logado pra sempre.', 'Sessões administrativas expiram por tempo e inatividade, usam cookie/token protegido e são invalidadas quando o gestor faz logout.', 'Session hijacking', ['cookie seguro', 'token de sessão', 'logout'], { grupo: 1, comoFunciona: ['Depois do login, o servidor cria uma sessão com identificador difícil de adivinhar.', 'Esse identificador deve trafegar de forma protegida e expirar.', 'Logout remove a sessão para impedir reutilização.', 'Ações críticas podem pedir nova confirmação mesmo com sessão ativa.'], errosComuns: ['Sessão sem expiração em painel administrativo.', 'Logout que só muda a tela, mas não invalida o token no servidor.'], comparacao: 'Authentication Bypass pula o login; Session Hijacking usa uma sessão que já existia. Os dois levam a acesso indevido, mas a correção muda.' })
2174        ],
2175        codigos: [
2176            { codigo: '/protege-no-servidor' },
2177            { codigo: '/rate-limit-login' }
2178        ],
2179        scenario: {
2180            title: 'Cenário completo: login do gestor sob tentativa repetida',
2181            intro: 'Uma tela de login segura precisa proteger entrada, sessão, mensagens e encerramento de acesso.',
2182            steps: [
2183                { text: 'A rota administrativa rejeita requisições sem sessão válida, mesmo se alguém chamar a API direto.', tag: 'Authentication Bypass' },
2184                { text: 'Senha e MFA são verificados no servidor; a mensagem de erro continua genérica quando algo não confere.', tag: 'Prova de identidade' },
2185                { text: 'Depois de várias tentativas falhas, o rate limit bloqueia temporariamente novas tentativas e registra auditoria.', tag: 'Brute Force' },
2186                { text: 'A sessão criada expira por tempo e inatividade, e o logout invalida o acesso.', tag: 'Session Hijacking' },
2187                { text: 'A recuperação de senha invalida tokens anteriores pendentes e nunca revela se uma conta existe para terceiros.', tag: 'Recuperação segura' }
2188            ]
2189        }
2190    },
2191    {
2192        stage: 'Aula 04',
2193        estimatedMinutes: 45,
2194        layout: 'grid',
2195        title: 'Autorização e controle de acesso',
2196        goal: 'Aprenda a diferença entre estar logado e ter permissão para ver, alterar, exportar ou apagar um recurso específico.',
2197        focus: 'Toda ação protegida deve responder quatro perguntas: quem é a pessoa, qual recurso ela quer acessar, qual papel ela tem e se existe relação legítima com aquele recurso.',
2198        practice: 'Especifique as regras para aluno, gestor e parceiro acessarem certificados, pedidos e dados de alunos sem misturar permissões.',
2199        exerciseFeedback: 'O ponto central é checar permissão no backend usando usuário, papel, recurso e relação de propriedade. Se a regra depende só de esconder botão, ela ainda está fraca.',
2200        lessons: [
2201            'Autorização decide o que uma identidade autenticada pode fazer.',
2202            'IDOR acontece quando trocar um ID dá acesso ao recurso de outra pessoa.',
2203            'Permissão precisa estar no servidor, não só na interface.'
2204        ],
2205        conceitos: [
2206            apiConceito('Broken Access Control', 'Falha em regras de permissão que permite ver, alterar, exportar ou apagar algo que aquela pessoa não deveria acessar.', 'Use para revisar telas administrativas, área do aluno, painel de parceiro, APIs internas e qualquer rota que muda dados.', 'Se logou, pode ver tudo.', 'Aluno só acessa matrícula, progresso e certificado ligados ao próprio ID de sessão; parceiro só vê vendas atribuídas a ele; gestor vê dados administrativos conforme sessão válida.', 'Broken access control', ['permissão', 'papel', 'recurso'], { comoFunciona: ['A pessoa autentica e recebe uma identidade no sistema.', 'Cada rota protegida confere o papel dessa identidade.', 'A rota também confere se o recurso solicitado pertence ou está autorizado para aquela identidade.', 'Se a regra falhar, o servidor bloqueia antes de buscar ou alterar dados sensíveis.'], segundoExemplo: 'Um parceiro pode ver comissão e vendas atribuídas ao próprio cupom, mas não deve listar todos os alunos da plataforma.', errosComuns: ['Confundir login com permissão total.', 'Validar permissão apenas na tela e esquecer a API.', 'Permitir exportação de dados sem checar o mesmo nível de acesso da listagem.'] }),
2207            apiConceito('IDOR', 'Falha em que alterar um identificador na URL, query string ou payload permite acessar recurso de outra pessoa.', 'Use quando rotas usam IDs de aluno, pedido, certificado, pagamento, cupom, ticket ou arquivo.', 'Busca certificado por ID.', 'Antes de mostrar certificado por ID, confirmar que ele pertence ao aluno logado ou que a sessão é de gestor autorizado.', 'Insecure Direct Object Reference', ['id de recurso', 'dono do recurso', 'autorização'], { comoFunciona: ['A URL ou payload carrega um identificador, como certificate_id ou order_id.', 'O servidor busca o registro 
2207solicitado.', 'Antes de responder, compara o dono do registro com a identidade da sessão.', 'Se não pertencer ou não houver papel autorizado, retorna bloqueio.'], segundoExemplo: 'Em /api/student/support-tickets?id=123, o ticket 123 só deve abrir se for do aluno logado; conhecer o número não prova permissão.', errosComuns: ['Achar que IDs longos ou aleatórios substituem autorização.', 'Checar apenas se o registro existe, não se pertence à pessoa logada.', 'Permitir ação em lote com IDs misturados sem validar cada item.'] }),
2208            apiConceito('Dono do recurso', 'A pessoa, conta, parceiro, turma ou organização que tem relação legítima com aquele registro.', 'Use para definir acesso por propriedade quando vários usuários usam a mesma aplicação.', 'Mostra os pedidos.', 'Um aluno vê apenas pedidos vinculados ao próprio cadastro, mesmo que conheça outro order_id; o gestor vê pedidos por regra administrativa registrada.', 'Resource owner', ['propriedade', 'escopo', 'registro'], { comoFunciona: ['Cada registro crítico guarda uma referência ao dono ou ao escopo: student_id, partner_id, organization_id ou course_plan.', 'A sessão informa quem está pedindo acesso.', 'O backend compara sessão e registro antes de responder.', 'A regra administrativa precisa ser explícita quando alguém pode ver recursos de terceiros.'], comparacao: 'Papel diz o tipo de usuário; dono do recurso diz a relação daquele usuário com um registro específico. Um aluno e outro aluno têm o mesmo papel, mas donos diferentes.', errosComuns: ['Guardar o dono do recurso só no frontend.', 'Não definir dono para registros compartilhados, como pedidos, tickets ou certificados.', 'Deixar uma pessoa trocar o owner_id no payload para assumir recurso de outra.'] })
2209        ],
2210        codigos: [
2211            { codigo: '/confere-dono' },
2212            { codigo: '/sem-id-livre' }
2213        ],
2214        scenario: {
2215            title: 'Cenário completo: certificado acessado pelo aluno certo',
2216            intro: 'Estar logado não basta; o sistema precisa confirmar papel, recurso e relação com o registro solicitado.',
2217            steps: [
2218                { text: 'O aluno abre a página de certificado usando um token ou identificador.', tag: 'Recurso solicitado' },
2219                { text: 'O backend identifica a sessão e descobre qual aluno está fazendo a requisição.', tag: 'Identidade autenticada' },
2220                { text: 'O sistema busca o certificado e confere se o student_id do certificado é o mesmo da sessão.', tag: 'Dono do recurso' },
2221                { text: 'Se o aluno trocar o ID por outro certificado, a resposta é bloqueada antes de renderizar qualquer dado.', tag: 'IDOR' },
2222                { text: 'Gestor autenticado pode consultar certificados por uma regra administrativa separada e auditável.', tag: 'Broken Access Control' }
2223            ]
2224        }
2225    },
2226    {
2227        stage: 'Aula 05',
2228        estimatedMinutes: 50,
2229        layout: 'grid',
2230        title: 'Injeções e execução indevida',
2231        goal: 'Entenda por que dados digitados não podem virar comando e como pedir consultas seguras, validação de entrada e saída renderizada sem execução indevida.',
2232        focus: 'Separe dado de comando: o usuário informa valores, mas não deve controlar SQL, HTML, JavaScript, template ou qualquer instrução interna da aplicação.',
2233        practice: 'Escreva uma especificação defensiva para proteger uma busca de alunos, um campo de observação do gestor e um comentário exibido publicamente.',
2234        exerciseFeedback: 'A resposta boa combina consulta parametrizada, validação de entrada, limite de tamanho, escaping na saída e testes com caracteres perigosos tratados como texto.',
2235        lessons: [
2236            'SQL Injection acontece quando entrada externa consegue interferir na consulta ao banco.',
2237            'XSS acontece quando conteúdo externo é exibido como código executável no navegador.',
2238            'Validação de entrada e escaping de saída são proteções complementares: uma não substitui a outra.'
2239        ],
2240        conceitos: [
2241            apiConceito('SQL Injection', 'Falha em que texto enviado pelo usuário altera a intenção de uma consulta ao banco de dados.', 'Use para pedir queries parametrizadas em login, busca, filtros, relatórios, checkout e qualquer rota que consulte ou grave dados.', 'Busca direto no banco com o texto digitado.', 'A busca de alunos usa parâmetros preparados, valida tamanho do termo e nunca concatena o texto digitado dentro do SQL.', 'SQL injection', ['query parametrizada', 'banco de dados', 'validação'], { comoFunciona: ['A aplicação recebe um valor externo, como busca, e-mail ou filtro.', 'Esse valor deve ser passado como parâmetro da consulta, não misturado ao texto do comando SQL.', 'O banco trata o valor como dado comum, mesmo que contenha aspas ou símbolos.', 'A resposta da API não mostra erro interno de banco se algo falhar.'], segundoExemplo: 'No login, e-mail e senha são valores da consulta; eles nunca devem mudar a estrutura do SELECT.', errosComuns: ['Montar SQL por concatenação porque parece mais rápido.', 'Validar apenas no frontend e esquecer que alguém pode chamar a API direto.', 'Expor erro interno como SQLITE_CONSTRAINT ou stack trace para o usuário.'] }),
2242            apiConceito('Cross-Site Scripting', 'Falha em que conteúdo vindo de usuário, integração ou banco aparece na página e executa JavaScript indevido.', 'Use para revisar comentários, nomes exibidos, campos de descrição, mensagens de suporte, HTML salvo e qualquer conteúdo livre.', 'Mostra o texto do usuário do jeito que veio.', 'Escapar nome, observação e c
2242omentário antes de renderizar, garantindo que tags apareçam como texto e não como script.', 'XSS', ['escaping', 'HTML', 'script'], { comoFunciona: ['Uma pessoa envia conteúdo por formulário, API ou integração.', 'O sistema salva ou devolve esse conteúdo.', 'Em algum momento, a página renderiza o conteúdo no navegador.', 'Se a saída não for tratada para o contexto certo, o navegador pode interpretar texto como código.'], segundoExemplo: 'Um nome de aluno exibido no painel gestor também precisa escaping; não é só campo público que pode causar problema.', errosComuns: ['Achar que XSS só acontece em comentários públicos.', 'Usar innerHTML com conteúdo vindo de usuário sem escaping.', 'Sanitizar uma vez ao salvar e depois renderizar em contextos diferentes sem novo cuidado.'] }),
2243            apiConceito('Escaping', 'Tratamento que transforma caracteres especiais em texto seguro para o contexto onde serão exibidos.', 'Use quando dados externos aparecem em HTML, atributo, URL, JSON, Markdown ou texto dentro de script.', 'Limpa o texto.', 'Renderizar `<script>` como texto visível, não como código executável, e tratar aspas corretamente quando o dado aparece em atributo HTML.', 'Output escaping', ['sanitização', 'contexto', 'saída segura'], { comoFunciona: ['Identifique onde o dado vai aparecer: HTML, atributo, URL, JSON ou outro contexto.', 'Aplique escaping próprio daquele contexto.', 'Renderize o valor tratado, nunca o texto cru vindo do usuário.', 'Teste caracteres especiais para confirmar que aparecem como texto.'], comparacao: 'Validação decide se o dado pode entrar; escaping decide como o dado pode sair sem virar código. Em muitos fluxos você precisa dos dois.', errosComuns: ['Usar o mesmo tratamento para HTML, URL e atributo como se fossem contextos iguais.', 'Chamar escaping de sanitização e achar que qualquer biblioteca resolve todo caso.', 'Escapar no lugar errado e depois concatenar novamente em HTML bruto.'] })
2244        ],
2245        codigos: [
2246            { codigo: '/query-parametrizada' },
2247            { codigo: '/escapa-na-saida' }
2248        ],
2249        scenario: {
2250            title: 'Cenário completo: busca e comentário sem execução indevida',
2251            intro: 'O mesmo texto digitado pode afetar banco de dados e tela se não for tratado no contexto certo.',
2252            steps: [
2253                { text: 'A busca de alunos recebe texto livre, mas o backend valida tamanho e usa parâmetros preparados no SQL.', tag: 'SQL Injection' },
2254                { text: 'Um campo de observação do gestor pode conter caracteres de HTML, mas isso não deve virar código.', tag: 'Entrada externa' },
2255                { text: 'Ao salvar, a API registra o conteúdo como dado comum e não mostra erro interno de banco em caso de falha.', tag: 'Resposta segura' },
2256                { text: 'Ao exibir no painel, a interface escapa o conteúdo para o contexto correto.', tag: 'Escaping' },
2257                { text: 'O teste de aceite confirma que aspas, sinais de menor/maior e tags aparecem como texto, sem execução.', tag: 'XSS' }
2258            ]
2259        }
2260    },
2261    {
2262        stage: 'Aula 06',
2263        estimatedMinutes: 45,
2264        layout: 'timeline',
2265        title: 'CSRF, clickjacking e redirecionamentos',
2266        goal: 'Aprenda três falhas que abusam do navegador, da sessão ativa ou da confiança em links aparentemente legítimos.',
2267        focus: 'A aplicação precisa confirmar intenção, origem e destino antes de aceitar ações sensíveis, renderizar dentro de outras páginas ou redirecionar o usuário.',
2268        practice: 'Especifique defesas para alterar senha, abrir o painel gestor em navegador e redirecionar o aluno depois do login.',
2269        exerciseFeedback: 'A resposta boa separa as defesas: token CSRF ou SameSite para ação sensível, headers anti-frame para clickjacking e allowlist de caminhos internos para redirecionamento.',
2270        lessons: [
2271            'CSRF tenta usar uma sessão ativa para enviar ação sem intenção real da pessoa.',
2272            'Clickjacking tenta esconder uma ação legítima dentro de uma interface enganosa.',
2273            'Open Redirect usa a confiança no seu domínio para levar o usuário a um destino perigoso.'
2274        ],
2275        sequenceCount: 3,
2276        connectors: ['intenção', 'origem', 'destino'],
2277        conceitos: [
2278            apiConceito('CSRF', 'Ataque que tenta fazer o navegador autenticado enviar uma ação sem consentimento real do usuário.', 'Use para proteger formulários e rotas sensíveis quando a sessão viaja automaticamente, como em cookies.', 'Confia que está logado.', 'Alterar senha exige sessão válida, confirmação da senha atual, token CSRF ou cookie SameSite adequado, e rejeita requisições sem essa proteção.', 'Cross-Site Request Forgery', ['token CSRF', 'SameSite', 'ação sensível'], { comoFunciona: ['A pessoa está logada em uma aplicação legítima.', 'Outra página tenta induzir o navegador a enviar uma requisição para essa aplicação.', 'Como cookies podem viajar automaticamente, o servidor poderia achar que a ação é legítima.', 'A proteção exige um sinal de intenção/origem que a página externa não consegue produzir.'], segundoExemplo: 'Cancelar matrícula, alterar e-mail, trocar senha e ativar/desativar cupom são ações que merecem proteção adicional.', errosComuns: ['Achar que CSRF só afeta formulário antigo e ignorar rotas POST/PATCH/DELETE.', 'Proteger apenas a tela, mas aceitar a ação sensível pela API sem token ou verificação equivalente.', 'Usar GET para ação que muda estado, como cancelar ou excluir.'] }),
2279            apiConceito('Clickjacking', 'Técnica que coloca uma página legítima dentro de um frame invisível ou enganoso para induzir clique indevido.', 'Use para proteger painéis, checkout, login, confirmação de pagamento e ações administrativas.', 'Pode abrir dentro de qualquer site.', 'Enviar headers de segurança para impedir que o painel gestor seja exibido dentro de iframe externo e reduzir cliques enganados.', 'Clickjacking', ['iframe', 'frame-ancestors', 'X-Frame-Options'], { comoFunciona: ['Um site externo tenta carregar uma página sua dentro de um iframe.', 'A pessoa vê outra interface por cima ou uma composição enganosa.', 'O clique pode atingir um botão real da sua aplicação sem a pessoa perceber.', 'Headers como frame-ancestors bloqueiam esse encaixe fora dos domínios permitidos.'], segundoExemplo: 'Uma tela de "confirmar exclusão" no gestor não deve poder ser embutida por outro domínio.', errosComuns: ['Confiar só em JavaScript para sair do iframe.', 'Permitir qualquer origem embutir telas administrativas.', 'Esquecer que checkout e login também são telas sensíveis.'] }),
2280            apiConceito('Open Redirect', 'Falha em que uma URL confiável redireciona para qualquer destino informado por parâmetro.', 'Use para validar links de retorno depois de login, checkout, convite, redefinição de senha ou compartilhamento.', 'Depois do login, volta para qualquer link enviado na URL.', 'Após login, aceitar retorno apenas para caminhos internos permitidos, como /painel-aluno, /gestor ou /planos, recusando URLs externas.', 'Open redirect', ['redirect', 'allowlist', 'URL de retorno'], { comoFunciona: ['A aplicação recebe um parâmetro de retorno, como ?next=.', 'Se aceitar qualquer URL externa, o domínio legítimo vira trampolim para phishing.', 'A correção compara o destino com uma allowlist de caminhos internos.', 'Quando o destino não é permitido, a aplicação usa uma rota padrão segura.'], segundoExemplo: 'Um link com domínio real da plataforma pode terminar levando a uma página falsa de pagamento se o redirect aceitar destino externo.', errosComuns: ['Validar apenas se a URL começa com https.', 'Permitir //dominio-externo ou URLs codificadas sem normalizar antes.', 'Usar redirect livre em links de convite e recuperação de senha.'] })
2281        ],
2282        codigos: [
2283            { codigo: '/acao-sensivel-protegida' },
2284            { codigo: '/redirect-so-interno' }
2285        ],
2286        scenario: {
2287            title: 'Cenário completo: alterar senha sem cair em abuso de navegador',
2288            intro: 'A ação é legítima, mas precisa confirmar intenção, impedir clique enganado e val
2288idar para onde o usuário volta depois.',
2289            steps: [
2290                { text: 'O formulário de alterar senha exige sessão válida e confirmação da senha atual.', tag: 'Ação sensível' },
2291                { text: 'A requisição inclui token CSRF ou proteção equivalente para provar que saiu da tela legítima.', tag: 'CSRF' },
2292                { text: 'A rota usa método que muda estado, nunca GET, e rejeita chamadas sem proteção.', tag: 'Regra de API' },
2293                { text: 'O painel não pode ser embutido em páginas externas invisíveis ou enganosas.', tag: 'Clickjacking' },
2294                { text: 'Depois do login, o retorno só aceita caminhos internos permitidos; destino externo cai numa rota padrão segura.', tag: 'Open Redirect' }
2295            ]
2296        }
2297    },
2298    {
2299        stage: 'Aula 07',
2300        estimatedMinutes: 45,
2301        layout: 'groups',
2302        title: 'Dados sensíveis, JWT e exposição de informações',
2303        goal: 'Entenda como vazamentos acontecem por resposta, log, token, erro ou configuração fraca.',
2304        focus: 'Antes de expor dados, pergunte quem precisa ver, por quanto tempo, em qual detalhe e o que nunca deve aparecer publicamente.',
2305        practice: 'Revise uma resposta de API de perfil do aluno e marque quais campos devem aparecer, ocultar ou ir apenas para log interno.',
2306        exerciseFeedback: 'A resposta boa evita CPF, token, segredo, stack trace e dado financeiro detalhado em respostas públicas.',
2307        lessons: [
2308            'Dado sensível exige acesso mínimo e justificativa clara.',
2309            'JWT mal configurado pode carregar informação demais ou validar assinatura de forma errada.',
2310            'Logs e mensagens de erro também podem vazar segredos.'
2311        ],
2312        groups: [
2313            { title: 'Dados', desc: 'O que pode aparecer para usuário, suporte e relatório.' },
2314            { title: 'Credenciais', desc: 'Tokens, segredos e configurações que não devem vazar.' }
2315        ],
2316        conceitos: [
2317            apiConceito('Sensitive Data Exposure', 'Exposição indevida de dados pessoais, financeiros, tokens, segredos ou detalhes internos em telas, APIs, logs, exportações ou mensagens de erro.', 'Use para revisar respostas, logs, relatórios, exports, telas de suporte e integrações que manipulam dados de pessoas reais.', 'Retorna tudo do aluno.', 'Perfil público retorna apenas nome, e-mail e cursos ativos; CPF, hash de senha, tokens, chaves internas e dados financeiros detalhados nunca saem na resposta pública.', 'Sensitive data exposure', ['dado sensível', 'log', 'resposta pública'], { grupo: 0, comoFunciona: ['A aplicação busca dados no banco para montar uma tela ou resposta de API.', 'Antes de responder, o backend escolhe apenas os campos necessários para aquele contexto.', 'Dados sensíveis ficam mascarados, omitidos ou restritos a papéis específicos.', 'Logs internos também evitam registrar segredo, token, senha, CPF completo ou payload financeiro desnecessário.'], segundoExemplo: 'Uma exportação de alunos pode precisar de nome e e-mail, mas não precisa incluir hash de senha, token de convite ou histórico completo de pagamento.', errosComuns: ['Retornar o objeto inteiro do banco por conveniência.', 'Mascarar dado na tela, mas deixar o valor completo na API.', 'Gravar tokens e payloads sensíveis em logs permanentes sem necessidade.'] }),
2318            apiConceito('JWT Misconfiguration', 'Erro na criação, conteúdo, expiração ou validação de um JWT que enfraquece autenticação e autorização.', 'Use para revisar assinatura, algoritmo, expiração, escopo, rotação e dados colocados dentro do token.', 'Guarda tudo no token.', 'JWT contém apenas identificador e escopo necessário, expira rápido, valida assinatura no servidor e nunca carrega dados sensíveis como senha, CPF ou permissões livres para o usuário alterar.', 'JWT misconfiguration', ['token', 'assinatura', 'expiração'], { grupo: 1, comoFunciona: ['O servidor emite um token assinado após autenticação.', 'Cada requisição protegida valida assinatura, expiração e escopo.', 'O token carrega apenas dados mínimos para identificar a sessão ou permissão.', 'Quando o risco é alto, o sistema usa sessão revogável ou revalidação adicional.'], segundoExemplo: 'Um token de aluno pode identificar o aluno e a sessão, mas a lista de cursos ativos deve ser conferida no servidor para refletir bloqueios e expirações.', errosComuns: ['Colocar dados sensíveis dentro do JWT porque ele parece escondido.', 'Aceitar token expirado para evitar atrito.', 'Confiar em permissões vindas do token sem conferir estado atual no banco.'] }),
2319            apiConceito('Segredo de aplicação', 'Chave privada usada para assinar tokens, chamar serviços externos, validar webhooks ou acessar recursos protegidos.', 'Use para separar segredo de código, frontend, logs, banco exportável e mensagens de erro.', 'Coloca a chave no front.', 'RESEND_API_KEY e chaves de pagamento ficam em secrets do ambiente, nunca aparecem em JavaScript público, HTML, repositório, log ou resposta de API.', 'Application secret', ['API key', 'variável de ambiente', 'credencial'], { grupo: 1, comoFunciona: ['A chave é criada no serviço externo ou ambiente de deploy.', 'O backend lÃ
2319ª a chave por variável segura em tempo de execução.', 'O frontend chama o backend, não o serviço externo diretamente com a chave privada.', 'Se houver suspeita de vazamento, a chave é rotacionada e o uso antigo é revogado.'], segundoExemplo: 'A assinatura de webhook deve ser validada com segredo no servidor; colocar esse segredo no navegador permitiria falsificar chamadas.', errosComuns: ['Comitar .env ou .dev.vars no repositório.', 'Mostrar chave em erro de configuração.', 'Usar a mesma chave para produção, sandbox e testes locais.'] })
2320        ],
2321        codigos: [
2322            { codigo: '/minimo-necessario' },
2323            { codigo: '/token-sem-segredo' }
2324        ],
2325        scenario: {
2326            title: 'Cenário completo: resposta de perfil com o mínimo necessário',
2327            intro: 'Uma API útil não precisa devolver tudo que existe no banco.',
2328            steps: [
2329                { text: 'O perfil do aluno retorna nome, e-mail e cursos ativos.', tag: 'Dado necessário' },
2330                { text: 'Hash de senha, tokens, CPF completo e chaves internas ficam fora da resposta.', tag: 'Sensitive Data Exposure' },
2331                { text: 'Se houver token de sessão, ele carrega apenas identificador e escopo mínimo.', tag: 'JWT Misconfiguration' },
2332                { text: 'Chaves de envio de e-mail e pagamento vivem em variáveis de ambiente.', tag: 'Segredo de aplicação' }
2333            ]
2334        }
2335    },
2336    {
2337        stage: 'Aula 08',
2338        estimatedMinutes: 45,
2339        layout: 'grid',
2340        title: 'Arquivos, uploads e inclusão de arquivos',
2341        goal: 'Aprenda por que upload e caminho de arquivo precisam de validação forte antes de salvar, abrir ou executar qualquer conteúdo.',
2342        focus: 'Defina tipo permitido, tamanho, armazenamento, nome seguro, varredura e regra para nunca executar arquivo enviado pelo usuário.',
2343        practice: 'Especifique um upload seguro de comprovante em PDF ou imagem, incluindo tipo, tamanho, local de armazenamento e mensagem de erro.',
2344        exerciseFeedback: 'A resposta boa não confia só na extensão; ela valida tipo real, renomeia, isola e impede execução.',
2345        lessons: [
2346            'Upload inseguro pode virar execução de código, vazamento ou sobrescrita.',
2347            'Path Traversal tenta escapar da pasta esperada usando caminhos manipulados.',
2348            'Incluir arquivo por parâmetro é perigoso quando o usuário controla o caminho.'
2349        ],
2350        conceitos: [
2351            apiConceito('File Upload Vulnerability', 'Falha em upload que aceita arquivo perigoso, grande demais, com tipo falso ou salvo em local onde pode ser executado ou exposto indevidamente.', 'Use para proteger comprovantes, avatares, anexos, importações, material de aula e qualquer entrada de arquivo.', 'Deixa anexar qualquer arquivo.', 'Aceitar só PDF/JPG/PNG até 5MB, validar tipo real no servidor, renomear o arquivo, salvar fora de pasta executável e negar extensões perigosas mesmo que venham disfarçadas.', 'File upload vulnerability', ['upload', 'tipo de arquivo', 'armazenamento'], { comoFunciona: ['O usuário envia um arquivo pelo navegador ou API.', 'O servidor valida tamanho, tipo real, extensão permitida e necessidade do arquivo.', 'O arquivo recebe nome interno novo e é salvo em local isolado.', 'Download ou visualização passam por permissão e não executam o conteúdo enviado.'], segundoExemplo: 'Um avatar deve virar imagem processada ou armazenada de modo seguro; o nome original do arquivo não deve decidir caminho nem tipo.', errosComuns: ['Confiar apenas na extensão .jpg.', 'Salvar upload dentro de pasta pública executável.', 'Permitir arquivo grande demais e abrir risco de consumo de armazenamento ou memória.'] }),
2352            apiConceito('Path Traversal', 'Tentativa de acessar arquivos fora da pasta permitida manipulando caminhos como ../, barras, encoding ou nomes absolutos.', 'Use quando a aplicação abre, baixa, importa ou remove arquivos por nome informado pelo usuário.', 'Baixa pelo nome do arquivo enviado na URL.', 'A API recebe um file_id, confere permissão no banco e monta internamente o caminho do arquivo, sem aceitar caminho livre na URL.', 'Path traversal', ['caminho de arquivo', '../', 'download'], { comoFunciona: ['A requisição pede um arquivo por nome, caminho ou identificador.', 'O risco aparece quando o usuário controla partes do caminho no servidor.', 'A proteção troca caminho livre por ID interno e normaliza qualquer valor necessário.', 'O servidor confirma que o arquivo final continua dentro da pasta permitida.'], segundoExemplo: 'Em vez de /download?file=../../secrets, prefira /download?id=abc123 e resolva o arquivo pelo banco.', errosComuns: ['Remover apenas a sequência ../ e esquecer variações codificadas.', 'Concatenar pasta base com texto vindo da URL.', 'Permitir apagar arquivo por caminho informado no payload.'] }),
2353            apiConceito('File Inclusion', 'Falha em que a aplicação inclui, interpreta ou executa arquivo escolhido por entrada externa.', 'Use para evitar parâmetros que definem template, página, script, idioma, tema ou arquivo interno.', 'Carrega a página pelo parâmetro.', 'Permitir apenas templates internos mapeados em allowlist, nunca caminho enviado pelo usuário, e impedir URL externa como fonte de inclusão.', 'Local/Remote File Inclusion', ['LFI', 'RFI', 'allowlist'], { comoFunciona: ['A aplicação recebe uma escolha de página, idioma, tema ou template.', 'Se essa escolha vira caminho direto, um usuário pode tentar incluir arquivo indevido.', 'A proteção converte opções ace
2353itas em nomes internos controlados pelo servidor.', 'Arquivos enviados por usuário nunca são tratados como código ou template.'], segundoExemplo: 'Um parâmetro ?template=certificado deve aceitar valores como certificado_padrao, não caminho de arquivo.', errosComuns: ['Confundir incluir arquivo com baixar arquivo.', 'Permitir URL remota como template para facilitar customização.', 'Reaproveitar upload de usuário como HTML renderizado sem isolamento.'] })
2354        ],
2355        codigos: [
2356            { codigo: '/upload-seguro' },
2357            { codigo: '/sem-caminho-livre' }
2358        ],
2359        scenario: {
2360            title: 'Cenário completo: envio seguro de comprovante',
2361            intro: 'Upload seguro trata arquivo como dado externo, não como algo confiável.',
2362            steps: [
2363                { text: 'O formulário aceita apenas PDF, JPG ou PNG dentro do tamanho permitido.', tag: 'File Upload Vulnerability' },
2364                { text: 'O servidor valida o tipo real e troca o nome por um identificador interno.', tag: 'Armazenamento seguro' },
2365                { text: 'Download usa ID salvo no banco, não caminho livre vindo da URL.', tag: 'Path Traversal' },
2366                { text: 'Nenhum arquivo enviado é incluído como template ou código executável.', tag: 'File Inclusion' }
2367            ]
2368        }
2369    },
2370    {
2371        stage: 'Aula 09',
2372        estimatedMinutes: 40,
2373        layout: 'grid',
2374        title: 'SSRF e comunicação entre servidores',
2375        goal: 'Entenda riscos quando o servidor faz chamadas para URLs informadas por usuário ou integração.',
2376        focus: 'Toda chamada servidor-servidor precisa limitar destino, protocolo, rede interna e tempo de resposta.',
2377        practice: 'Descreva uma importação por URL segura, impedindo acesso a localhost, IP interno e metadados da nuvem.',
2378        exerciseFeedback: 'A resposta boa usa allowlist de domínio, bloqueio de rede interna, timeout e log do destino chamado.',
2379        lessons: [
2380            'SSRF usa o servidor como intermediário para acessar destinos proibidos.',
2381            'URLs livres são perigosas em importação, preview, webhook configurável e captura de imagem.',
2382            'Allowlist é mais segura do que tentar bloquear alguns casos conhecidos.'
2383        ],
2384        conceitos: [
2385            apiConceito('SSRF', 'Falha em que o servidor é induzido a chamar um endereço interno, restrito ou inesperado a partir de uma URL informada externamente.', 'Use quando usuário informa URL para importar, pré-visualizar, capturar imagem, configurar webhook ou sincronizar dados.', 'Busca qualquer URL que o usuário mandar.', 'Importação aceita apenas domínios aprovados, bloqueia localhost, IP privado e metadados internos, e registra qual destino foi chamado.', 'Server-Side Request Forgery', ['URL externa', 'rede interna', 'allowlist'], { comoFunciona: ['A aplicação recebe uma URL externa para buscar dados.', 'O servidor, não o navegador do usuário, faz a chamada.', 'Se não houver restrição, a URL pode apontar para rede interna ou metadados do ambiente.', 'A proteção valida destino final, bloqueia redes internas e usa allowlist quando possível.'], segundoExemplo: 'Um preview de imagem por URL pode virar risco se o servidor aceitar endereço interno em vez de apenas domínios de mídia permitidos.', errosComuns: ['Bloquear só localhost escrito por extenso e esquecer 127.0.0.1, IPv6, redirecionamentos e IP privado.', 'Validar a URL antes, mas seguir redirect para destino proibido depois.', 'Permitir qualquer protocolo em vez de limitar a HTTPS.'] }),
2386            apiConceito('Allowlist', 'Lista explícita de destinos, valores, protocolos ou formatos permitidos antes de executar uma ação.', 'Use quando a escolha segura é pequena, conhecida e controlável pela aplicação.', 'Bloqueia alguns domínios ruins.', 'Permitir webhooks e importações apenas para domínios cadastrados, verificados e necessários para o produto.', 'Allowlist', ['lista permitida', 'validação', 'destino confiável'], { comoFunciona: ['O time define o conjunto de opções realmente necessárias.', 'A entrada externa é normalizada antes de comparar.', 'A aplicação aceita apenas o que está na lista permitida.', 'Qualquer valor fora da lista cai em erro claro e registro interno.'], segundoExemplo: 'No retorno pós-login, permitir /painel-aluno e /gestor é mais seguro que tentar bloquear todos os domínios externos possíveis.', errosComuns: ['Criar blockl
2386ist infinita de casos ruins.', 'Comparar texto sem normalizar domínio, protocolo e redirects.', 'Deixar operador cadastrar qualquer domínio sem verificação.'] }),
2387            apiConceito('Timeout', 'Limite de tempo para uma chamada externa responder antes de ser encerrada com falha controlada.', 'Use para evitar travamento, fila presa, consumo abusivo de recursos e experiência ruim.', 'Espera até responder.', 'A chamada para integração externa expira em 5 segundos, registra falha parcial e permite reprocessamento controlado.', 'Request timeout', ['resiliência', 'chamada externa', 'limite'], { comoFunciona: ['O servidor inicia uma chamada para outro serviço.', 'Um temporizador define quanto tempo a aplicação aceita esperar.', 'Se estourar o limite, a chamada é cancelada ou tratada como falha.', 'O usuário recebe mensagem clara e o sistema registra o evento para tentativa futura.'], segundoExemplo: 'Na validação de pagamento, timeout evita deixar checkout pendurado se o gateway demorar demais.', errosComuns: ['Não definir timeout e prender o processo indefinidamente.', 'Repetir chamadas sem limite e piorar uma instabilidade externa.', 'Mostrar erro técnico cru em vez de registrar internamente e orientar o usuário.'] })
2388        ],
2389        codigos: [
2390            { codigo: '/allowlist-destino' },
2391            { codigo: '/timeout-externo' }
2392        ],
2393        scenario: {
2394            title: 'Cenário completo: importar dados por URL com destino controlado',
2395            intro: 'Quando o servidor chama uma URL externa, ele precisa saber para onde está indo.',
2396            steps: [
2397                { text: 'O usuário informa uma URL para importar um arquivo ou feed.', tag: 'Ponto de entrada' },
2398                { text: 'O backend aceita apenas domínios previamente permitidos.', tag: 'Allowlist' },
2399                { text: 'Endereços internos, localhost e metadados de nuvem são bloqueados.', tag: 'SSRF' },
2400                { text: 'A chamada tem timeout e registro do destino consultado.', tag: 'Timeout' }
2401            ]
2402        }
2403    },
2404    {
2405        stage: 'Aula 10',
2406        estimatedMinutes: 45,
2407        layout: 'timeline',
2408        title: 'Race Condition e falhas de fluxo',
2409        goal: 'Aprenda como ações simultâneas podem duplicar cobrança, matrícula, cupom, estoque ou qualquer recurso limitado.',
2410        focus: 'A aplicação precisa tratar repetição, concorrência e ordem dos eventos nas ações críticas.',
2411        practice: 'Descreva como impedir que duas pessoas usem o último cupom disponível ao mesmo tempo.',
2412        exerciseFeedback: 'A resposta boa define identificador único, trava/transação, rechecagem final e mensagem para quem perdeu a disputa.',
2413        lessons: [
2414            'Race condition acontece quando a ordem de eventos simultâneos muda o resultado.',
2415            'Duplo clique, webhook repetido e estoque limitado são sinais de alerta.',
2416            'Idempotência e transação reduzem duplicidade e inconsistência.'
2417        ],
2418        sequenceCount: 4,
2419        connectors: ['checa', 'reserva', 'confirma', 'registra'],
2420        conceitos: [
2421            apiConceito('Race Condition', 'Falha causada por duas ou mais operações disputando o mesmo recurso ao mesmo tempo e produzindo um resultado incorreto.', 'Use em cupom limitado, matrícula, estoque, saldo, vaga, pagamento, reembolso e emissão de certificado.', 'Se tiver vaga, cadastra.', 'Reservar a última vaga dentro de uma transação, revalidar disponibilidade antes de confirmar e responder claramente para quem chegou depois.', 'Race condition', ['concorrência', 'operação simultânea', 'recurso limitado'], { comoFunciona: ['Duas requisições chegam quase ao mesmo tempo.', 'As duas leem o mesmo estado inicial, como "1 cupom restante".', 'Sem trava ou transação, as duas podem confirmar a ação.', 'A correção torna a checagem e a reserva uma operação única e consistente.'], segundoExemplo: 'Em certificado, duas abas não deveriam emitir dois certificados diferentes para a mesma conclusão se a regra permite apenas um registro ativo.', errosComuns: ['Testar só com um usuário e achar que o fluxo está seguro.', 'Conferir disponibilidade antes da transação e c
2421onfirmar depois sem revalidar.', 'Resolver no botão desabilitado do frontend, mas deixar a API aceitar duplicidade.'] }),
2422            apiConceito('Transação', 'Bloco de operações que deve confirmar tudo junto ou desfazer tudo se qualquer parte falhar.', 'Use em pagamento, pedido, matrícula, estoque, cupom, certificado e qualquer fluxo que grava em mais de uma tabela.', 'Salva em várias tabelas e torce para dar certo.', 'Criar pedido, itens do pedido e matrícula pendente na mesma transação para evitar cobrança sem matrícula ou matrícula sem pedido.', 'Database transaction', ['atomicidade', 'rollback', 'consistência'], { comoFunciona: ['O servidor inicia uma transação no banco.', 'Executa todas as gravações relacionadas dentro desse bloco.', 'Se uma etapa falha, desfaz o conjunto inteiro.', 'Se tudo passa, confirma o estado final de uma vez.'], segundoExemplo: 'Ao aplicar cupom no checkout, pedido e desconto precisam nascer consistentes para não cobrar valor diferente do registrado.', errosComuns: ['Gravar pedido antes e matrícula depois sem plano de falha intermediária.', 'Misturar chamada externa lenta dentro de transação de banco.', 'Não criar restrição única quando o banco poderia impedir duplicidade.'] }),
2423            apiConceito('Idempotência', 'Garantia de que repetir a mesma ação não duplica o resultado nem causa efeito colateral repetido.', 'Use em botões, webhooks, pagamentos, reprocessamentos, envio de e-mail e criação de registros críticos.', 'Se clicar duas vezes, cria duas matrículas.', 'Usar chave idempotente por cobrança ou evento para não criar duas matrículas, dois e-mails ou dois pedidos para a mesma confirmação.', 'Idempotency', ['duplicidade', 'evento repetido', 'chave única'], { comoFunciona: ['Cada ação crítica recebe um identificador único estável.', 'Antes de processar, o servidor verifica se aquele identificador já foi tratado.', 'Se já foi, retorna o resultado conhecido ou sucesso sem repetir efeito.', 'Se ainda não foi, processa e registra o identificador.'], segundoExemplo: 'Webhook de pagamento pode chegar mais de uma vez; a plataforma deve reconhecer o payment_id/event_id e não liberar o curso duas vezes.', errosComuns: ['Gerar uma chave nova a cada tentativa, tornando a idempotência inútil.', 'Aplicar idempotência só no frontend.', 'Responder erro para evento repetido quando o melhor comportamento é sucesso sem repetir ação.'] })
2424        ],
2425        codigos: [
2426            { codigo: '/transacao-unica' },
2427            { codigo: '/chave-idempotente' }
2428        ],
2429        scenario: {
2430            title: 'Cenário completo: último cupom disponível',
2431            intro: 'Dois usuários podem tentar consumir o mesmo benefício ao mesmo tempo.',
2432            steps: [
2433                { text: 'Duas compras chegam quase juntas usando o mesmo cupom limitado.', tag: 'Race Condition' },
2434                { text: 'O servidor abre uma transação, confere disponibilidade e reserva uma vez só.', tag: 'Transação' },
2435                { text: 'A segunda compra recebe mensagem clara de cupom indisponível.', tag: 'Rechecagem final' },
2436                { text: 'Se o pagamento mandar webhook repetido, a matrícula não duplica.', tag: 'Idempotência' }
2437            ]
2438        }
2439    },
2440    {
2441        stage: 'Aula 11',
2442        estimatedMinutes: 40,
2443        layout: 'grid',
2444        title: 'Como reportar, priorizar e corrigir vulnerabilidades',
2445        goal: 'Aprenda a transformar uma descoberta de segurança em relato claro, priorização e plano de correção sem sensacionalismo.',
2446        focus: 'Um bom relatório explica evidência, impacto, passos de reprodução autorizados, severidade, correção sugerida e reteste.',
2447        practice: 'Escreva um relatório curto para IDOR em certificado, incluindo impacto, passos, evidência e correção esperada.',
2448        exerciseFeedback: 'A resposta boa permite corrigir e retestar sem expor dados reais nem incentivar exploração fora do escopo.',
2449        lessons: [
2450            'Evidência boa prova o problema sem aumentar o dano.',
2451            'Severidade depende de impacto, alcance e facilidade de exploração.',
2452            'Correção só termina depois do reteste.'
2453        ],
2454        conceitos: [
2455            apiConceito('Relatório de vulnerabilidade', 'Documento que descreve uma falha de segurança com contexto, impacto, evidência, passos autorizados de reprodução e sugestão de correção.', 'Use para comunicar segurança a de
2455senvolvimento, produto, suporte, jurídico e gestão sem transformar o achado em alarme solto.', 'Tem um bug grave no certificado.', 'IDOR permite aluno A tentar abrir certificado de aluno B trocando o ID; impacto: exposição indevida de dados de certificado; correção esperada: checar ownership no backend antes de responder.', 'Vulnerability report', ['evidência', 'impacto', 'reprodução'], { comoFunciona: ['Dê um título claro para a falha.', 'Explique onde ela acontece e qual usuário/perfil é afetado.', 'Mostre evidência mínima, usando conta de teste ou dados fictícios.', 'Descreva impacto, correção esperada e como retestar depois.'], segundoExemplo: 'Em vez de "login inseguro", escreva: "Rate limit ausente no login do gestor permite tentativas repetidas de senha sem bloqueio temporário".', errosComuns: ['Relatar só a técnica sem explicar impacto no negócio.', 'Colar dados reais sensíveis como evidência.', 'Misturar várias falhas diferentes em um único relatório sem prioridade.'] }),
2456            apiConceito('Severidade', 'Classificação da urgência de uma vulnerabilidade considerando impacto, exposição, facilidade de abuso, quantidade de pessoas afetadas e controles existentes.', 'Use para ordenar correções sem tratar tudo como crítico nem empurrar risco sério para depois.', 'Arruma quando der.', 'Prioridade alta para falha que expõe dados pessoais sem autenticação; prioridade média para falha restrita a gestor autenticado com baixo impacto e trilha de auditoria.', 'Severity', ['prioridade', 'impacto', 'risco'], { comoFunciona: ['Avalie que dado, dinheiro, acesso ou operação pode ser afetado.', 'Veja se a falha exige login, papel específico ou conhecimento difícil.', 'Estime quantas pessoas/registros podem ser atingidos.', 'Classifique urgência e combine prazo realista de correção.'], comparacao: 'Risco é o problema em si; severidade é a etiqueta usada para decidir ordem de correção. Um bug pode ser tecnicamente interessante e ainda ter severidade baixa.', errosComuns: ['Chamar tudo de crítico e perder credibilidade.', 'Baixar severidade só porque "ninguém explorou ainda".', 'Ignorar contexto: produção, sandbox, dados reais e dados fictícios têm pesos diferentes.'] }),
2457            apiConceito('Reteste', 'Nova verificação após a correção para confirmar que a falha foi resolvida e que a proteção não quebrou o fluxo legítimo.', 'Use antes de fechar incidente, publicar release ou marcar uma correção de segurança como concluída.', 'Já corrigiu, pode fechar.', 'Repetir o fluxo autorizado depois do patch e confirmar que outro aluno recebe 403 ao tentar acessar certificado que não pertence a ele, enquanto o dono legítimo continua conseguindo abrir.', 'Retest', ['validação', 'correção', 'QA'], { comoFunciona: ['Pegue os mesmos passos autorizados do relatório.', 'Execute novamente depois da correção.', 'Confirme que o abuso foi bloqueado.', 'Confirme também que o uso legítimo continua funcionando.', 'Registre resultado, data, ambiente e evidência final.'], segundoExemplo: 'Após criar rate limit, teste tanto várias tentativas inválidas quanto um login correto depois do período de bloqueio.', errosComuns: ['Retestar só o caso de ataque e esquecer o fluxo normal.', 'Testar em ambiente diferente daquele em que a correção foi aplicada.', 'Fechar o item sem registrar evidência do resultado.'] })
2458        ],
2459        codigos: [
2460            { codigo: '/relato-claro' },
2461            { codigo: '/retesta-os-dois-lados' }
2462        ],
2463        scenario: {
2464            title: 'Cenário completo: relato de IDOR sem expor dado real',
2465            intro: 'O relatório precisa provar o problema e ajudar a corrigir, sem aumentar o dano.',
2466            steps: [
2467                { text: 'O achado descreve que trocar um ID permite tentar abrir certificado de outro aluno.', tag: 'Relatório de vulnerabilidade' },
2468                { text: 'A evidência usa contas de teste, oculta dados pessoais e captura apenas o necessário.', tag: 'Evidência responsável' },
2469                { text: 'A severidade considera exposição de dados, facilidade de abuso, ambiente e alcance.', tag: 'Severidade' },
2470                { text: 'A correção esperada é checar ownership no backend antes de responder.', tag: 'Plano de correção' },
2471                { text: 'Depois do patch, o mesmo teste deve receber 403, e o certificado do próprio aluno deve continuar abrindo.', tag: 'Reteste' }
2472            ]
2473        }
2474    },
2475    {
2476        stage: 'Aula 12',
2477        estimatedMinutes: 50,
2478        layout: 'grid',
2479        title: 'Projeto final defensivo',
2480        goal: 'Monte um checklist de revisão defensiva para uma aplicação web real, conectando todos os conceitos do curso.',
2481        focus: 'Feche o curso com um plano de revisão que cubra login, permissão, entrada, dados, arquivos, integrações, concorrência e resposta a falhas.',
2482        practice: 'Crie um checklist de segurança para checkout + área do aluno + gestor, com pelo menos uma verificação por tema do curso.',
2483        exerciseFeedback: 'O projeto fica pronto quando alguém consegue aplicar o checklist, registrar evidências e priorizar correções.',
2484        lessons: [
2485            'Checklist defensivo transforma teoria em rotina de revisão.',
2486            'Cada item deve ter critério observável de aprovação.',
2487            'Segurança melhora por ciclos: revisar, corrigir, retestar e registrar.'
2488        ],
2489        conceitos: [
2490            apiConceito('Checklist defensivo', 'Lista prática de verificações de segurança para revisar uma aplicação antes de publicar, depois de uma mudança sensível ou em ciclos periódicos.', 'Use para organizar revisão recorrente por área do sistema: login, permissão, entrada, dados, arquivos, integrações, concorrência, logs e headers.', 'Confere segurança geral.', 'Checklist inclui: rotas protegidas exigem sessão; aluno não acessa recurso de outro; entradas são validadas no backend; uploads são isolados; webhooks têm idempotência; ações críticas têm log.', 'Security checklist', ['revisão', 'critérios de aceite', 'controle'], { comoFunciona: ['Divida a aplicação por fluxos críticos.', 'Para cada fluxo, liste riscos aprendidos no curso.', 'Transforme cada risco em uma pergunta verificável.', 'Registre resultado: ok, pendente, não aplicável ou precisa decisão.', 'Revise de novo depois das correções.'], segundoExemplo: 'No Como Pedir, um checklist mínimo cobre checkout, login do aluno, login do gestor, certificados, cupons, webhooks e painel de parceiros.', errosComuns: ['Criar checklist genérico demais, impossível de testar.', 'Usar checklist uma vez e abandonar depois do deploy.', 'Marcar item como ok sem evidência observável.'] }),
2491            apiConceito('Critério de aceite', 'Condição objetiva que mostra se uma proteção foi implementada corretamente e pode ser validada por teste ou revisão.', 'Use para evitar tarefas vagas como "melhorar segurança", "proteger login" ou "deixar upload seguro".', 'Melhora o login.', 'Após 5 tentativas inválidas em 60 segundos, a API retorna bloqueio temporário, mantém mensagem pública genérica e registra evento de auditoria.', 'Acceptance criteria', ['teste', 'validação', 'pronto'], { comoFunciona: ['Comece com o risco que precisa ser reduzido.', 'Defina comportamento esperado em sucesso e falha.', 'Inclua limite, mensagem, registro ou permissão quando for relevante.', 'Escreva como alguém vai validar o resultado.', 'Só aceite a entrega quando o critério puder ser observado.'], segundoExemplo: 'Para IDOR: "aluno A recebe 403 ao acessar pedido do aluno B; aluno B continua acessando o próprio pedido".', errosComuns: ['Aceitar critério subjetivo como "ficou seguro".', 'Descrever só o caminho feliz e esquecer o abuso que deve ser bloqueado.', 'Não combinar mensagem de erro e registro interno.'] }),
2492            apiConceito('Plano de correção', 'Sequência priorizada de mudanças para reduzir riscos encontrados, com responsável, prazo, ambiente, validação e ordem de execução.', 'Use quando há várias falhas e é preciso decidir o que corrigir primeiro sem paralisar o produto.', 'Corrige tudo.', 'Corrigir primeiro acesso indevido sem login, depois vazamento de dados em log, depois endurecer headers; cada item tem dono, critério de aceite e reteste.', 'Remediation plan', ['priorização', 'responsável', 'reteste'], { comoFunciona: ['Agrupe achados por severi
2492dade e área afetada.', 'Escolha correções que reduzem maior impacto primeiro.', 'Defina responsável, prazo e ambiente de validação.', 'Aplique mudanças pequenas e verificáveis.', 'Reteste e registre o fechamento de cada item.'], segundoExemplo: 'Se checkout tem race condition e sitemap tem cabeçalho faltando, a race condition entra antes porque pode afetar pagamento e acesso.', errosComuns: ['Tentar corrigir tudo de uma vez e aumentar risco de regressão.', 'Começar pelo item mais fácil quando existe falha crítica aberta.', 'Não reservar tempo para reteste depois do patch.'] })
2493        ],
2494        codigos: [
2495            { codigo: '/checklist-por-fluxo' },
2496            { codigo: '/prioriza-por-impacto' }
2497        ],
2498        scenario: {
2499            title: 'Cenário completo: checklist final antes de publicar',
2500            intro: 'O fechamento do curso vira um material que pode ser usado numa aplicação real.',
2501            steps: [
2502                { text: 'A revisão começa pelos fluxos críticos: checkout, login, sessão, permissões, certificados e painel gestor.', tag: 'Checklist defensivo' },
2503                { text: 'Cada item vira pergunta verificável: "sem sessão recebe 401?", "outro aluno recebe 403?", "webhook repetido não duplica?".', tag: 'Critério de aceite' },
2504                { text: 'Falhas são ordenadas por impacto, exposição e facilidade de exploração.', tag: 'Plano de correção' },
2505                { text: 'Cada correção tem responsável, ambiente, prazo e evidência esperada.', tag: 'Execução' },
2506                { text: 'Depois do ajuste, o time retesta o abuso e o fluxo legítimo, registra o resultado e só então fecha o item.', tag: 'Ciclo defensivo' }
2507            ]
2508        }
2509    },
2510    {
2511        stage: 'Aula 13',
2512        estimatedMinutes: 30,
2513        layout: 'grid',
2514        title: 'Códigos Secretos: a régua de segurança do curso inteiro',
2515        goal: 'Aula bônus. Recapitula as 12 aulas e reúne os 24 comandos que cobrem risco, entrada, autenticação, acesso, injeção, navegador, dados sensíveis, arquivos, SSRF, concorrência, relato e correção.',
2516        focus: 'Antes de aprovar um pedido de sistema ou revisar um código, passe a régua: tem ponto de entrada não validado? a rota confere sessão e dono do recurso? a saída está protegida contra script? existe chance de duplicidade ou corrida? o relato tem impacto e correção esperada?',
2517        practice: 'Pegue uma funcionalidade real que você precisa revisar esta semana. Rode pelo menos 5 códigos desta lista sobre ela e junte tudo num checklist só.',
2518        exerciseFeedback: 'Não existe resposta única. O que importa: o checklist final não deixa buraco — cada código aplicado virou uma verificação específica (sessão, dono do recurso, validação de entrada, escaping, transação, allowlist), não uma frase genérica como "ficou seguro".',
2519        lessons: [
2520            'Reconheça, num pedido ou código pronto, quais riscos já foram tratados e quais ficaram de fora.',
2521            'Monte a régua de revisão defensiva que você vai usar em qualquer aplicação daqui pra frente.',
2522            'Combine vários códigos num checklist único, sem repetir nem deixar lacuna.'
2523        ],
2524        conceitos: [],
2525        bonus: true
2526    }
2527];
2528
2529const dadosInformacoesJourney = [
2530    {
2531        stage: 'Aula 01',
2532        layout: 'grid',
2533        title: 'Cadastro e regras de campo',
2534        goal: 'Aprenda o vocabulário básico de como um sistema recebe e guarda uma informação nova: o que é um cadastro, quando um campo é obrigatório, quando ele não pode se repetir, e como o sistema confere se o valor digitado faz sentido.',
2535        focus: 'Antes de pedir uma tela de cadastro, pergunte: quais campos são realmente obrigatórios? Algum deles não pode se repetir entre registros diferentes? Existe um formato específico que o valor precisa seguir?',
2536        practice: 'Escolha um cadastro que você usa no dia a dia (cliente, produto, funcionário) e escreva: quais campos são obrigatórios, qual campo não pode se repetir entre registros, e qual validação de formato os campos mais importantes precisam ter.',
2537        exerciseFeedback: 'Não existe resposta única aqui — o que importa é cada regra estar específica: quais campos exatamente são obrigatórios (não "os principais"), qual valor não pode se repetir e o que acontece se alguém tentar repetir, e qual é o formato exato esperado em cada validação. Releia e veja se um dev entenderia sem perguntar nada de volta.',
2538        lessons: [
2539            'Aprenda o que realmente acontece quando um cadastro é criado.',
2540            'Veja a diferença entre um campo obrigatório e um campo que precisa ser único.',
2541            'Pratique como descrever a validação de um campo sem deixar o formato em aberto.'
2542        ],
2543        conceitos: [
2544            {
2545                nome: 'Cadastro',
2546                oQueE: 'O ato de criar um novo registro dentro do sistema, guardando pela primeira vez as informações de algo ou alguém — é a porta de entrada de qualquer dado que vai existir no sistema.',
2547                quandoUsar: 'Use sempre que o sistema precisa passar a lembrar de algo que ainda não existe nele: um cliente novo, um produto novo, uma reserva nova.',
2548                pedidoConfuso: 'Cria lá um jeito de adicionar cliente novo.',
2549                pedidoClaro: 'A tela "Novo cliente" pede nome, e-mail e telefone. Ao salvar, o sistema cria um registro novo na base de clientes, com um identificador único gerado automaticamente e a data de criação registrada.',
2550                comoFunciona: [
2551                    'O usuário preenche os campos do formulário de cadastro.',
2552                    'Ao confirmar, o sistema confere se os campos obrigatórios e as validações passaram.',
2553                    'Se passou, cria um registro novo com um identificador único e guarda a data/hora da criação.',
2554                    'Se não passou, mostra o que falta corrigir sem criar nada.'
2555                ],
2556                segundoExemplo: 'Um app de tarefas cria um novo registro de tarefa assim que você digita o título e aperta Enter — mesmo sem preencher prazo ou descrição, que são opcionais.',
2557                errosComuns: [
2558                    'Não deixar claro o que é obrigatório vs. opcional no pedido, fazendo o dev adivinhar.',
2559                    'Esquecer de pedir o que acontece quando o cadastro dá certo (confirmação, redirecionamento).'
2560                ],
2561                termoTecnico: 'Cadastro (create record)',
2562                termosRelacionados: ['criar registro', 'novo registro', 'inserção de dados']
2563            },
2564            {
2565                nome: 'Campo obrigatório',
2566                oQueE: 'Uma informação que o sistema exige estar preenchida antes de aceitar o cadastro ou a alteração — sem ela, a ação não é permitida.',
2567                quandoUsar: 'Use quando o sistema não consegue funcionar direito sem aquela informação — um pedido sem endereço de entrega, uma conta sem e-mail de contato.',
2568                pedidoConfuso: 'Não deixa salvar sem preencher os principais.',
2569                pedidoClaro: 'Os campos "Nome" e "E-mail" são obrigatórios no cadastro de cliente. Enquanto estiverem vazios, o botão "Salvar" fica desabilitado e aparece "Campo obrigatório" em vermelho abaixo de cada um.',
2570                comoFunciona: [
2571                    'Você define quais campos não podem ficar vazios.',
2572                    'O sistema confere isso antes de salvar qualquer coisa.',
2573                    'Se algum obrigatório estiver vazio, bloqueia o salvamento e aponta exatamente qual campo falta.',
2574                    'Só libera o salvamento quando todos os obrigatórios estão preenchidos.'
2575                ],
2576                errosComuns: [
2577                    'Marcar campo demais como obrigatório, tornando o cadastro cansativo sem necessidade real.',
2578                    'Não avisar QUAL campo está faltando, só dizer "preencha os campos obrigatórios" de forma genérica.'
2579                ],
2580                termoTecnico: 'Campo obrigatório (required field)',
2581                termosRelacionados: ['campo requerido', 'preenchimento obrigatório', 'validação de presença']
2582            },
2583            {
2584                nome: 'Campo único',
2585                oQueE: 'Uma informação que não pode se repetir entre dois registros diferentes — o sistema impede cadastrar duas vezes o mesmo valor nesse campo.',
2586                quandoUsar: 'Use quando duplicar aquele valor causaria confusão ou erro real — dois clientes com o mesmo e-mail, dois produtos com o mesmo código.',
2587                quandoNaoUsar: 'Não use em campos onde repetir é normal e esperado, como o campo "Cidade" num cadastro de endereço — várias pessoas moram na mesma cidade sem problema nenhum.',
2588                pedidoConfuso: 'Não deixa cadastrar o mesmo e-mail duas vezes.',
2589                pedidoClaro: 'O campo "E-mail" não pode se repetir entre clientes. Se alguém tentar cadastrar um e-mail que já existe, o sistema bloqueia e mostra "Este e-mail já está cadastrado" em vez de criar um segundo registro.',
2590                comoFunciona: [
2591                    'Você define qual campo não pode ter valor repetido.',
2592                    'Ao tentar salvar, o sistema confere se já existe outro registro com o mesmo valor nesse campo.',
2593                    'Se existir, bloqueia e explica o motivo.',
2594                    'Se não existir, salva normalmente.'
2595                ],
2596                segundoExemplo: 'Um sistema de cupons de desconto não deixa cadastrar dois cupons com o mesmo código — o segundo cadastro é bloqueado com "Já existe um cupom com este código".',
2597                termoTecnico: 'Campo único (unique constraint)',
2598                termosRelacionados: ['restrição de unicidade', 'valor duplicado', 'chave única']
2599            },
2600            {
2601                nome: 'Validação de campo',
2602                oQueE: 'Uma regra que confere se o valor digitado num campo está no formato ou dentro dos limites esperados, antes de aceitar aquele dado.',
2603                quandoUsar: 'Use sempre que um campo tem um formato específico esperado — CPF, e-mail, telefone, ou um valor numérico que precisa ficar dentro de uma faixa.',
2604                pedidoConfuso: 'Confere se o CPF tá certo.',
2605                pedidoClaro: 'O campo "CPF" precisa ter 11 dígitos e passar pelo cálculo de dígito verificador. Se o valor for inválido, mostrar "CPF inválido" embaixo do campo assim que o usuário sair dele, sem deixar salvar o formulário inteiro.',
2606                comoFunciona: [
2607                    'Você define a regra de formato ou limite esperado pro campo.',
2608                    'O sistema confere o valor digitado contra essa regra.',
2609                    'Se não bater, mostra uma mensagem específica explicando o que está errado.',
2610                    'Se bater, aceita o valor normalmente.'
2611                ],
2612                segundoExemplo: 'Um campo "Quantidade" numa loja online só aceita números inteiros entre 1 e 99 — digitar "0" ou "abc" mostra "Digite um número entre 1 e 99".',
2613                errosComuns: [
2614                    'Validar só no clique de salvar, obrigando o usuário a de
2614scobrir todos os erros de uma vez no final.',
2615                    'Mostrar mensagem de erro genérica ("valor inválido") em vez de explicar o formato esperado.'
2616                ],
2617                termoTecnico: 'Validação de campo (field validation)',
2618                termosRelacionados: ['formato esperado', 'regra de validação', 'checagem de dado']
2619            }
2620        ],
2621        codigos: [
2622            { codigo: '/obrigatorio-ou-nao' },
2623            { codigo: '/campo-unico' }
2624        ]
2625    },
2626    {
2627        stage: 'Aula 02',
2628        layout: 'diagram',
2629        diagramCount: 1,
2630        diagramNodeLeft: 'Um cadastro',
2631        diagramNodeRight: 'Outro cadastro',
2632        diagramIntro: 'Como uma informação se liga a outra · clique na conexão pra ver o conteúdo completo',
2633        diagramTraitsCaption: 'Um problema que a boa modelagem evita · não é sobre a conexão em si',
2634        title: 'Dados que se conectam',
2635        goal: 'Aprenda a descrever quando uma informação depende de outra pra fazer sentido, e o problema real que aparece quando a mesma coisa acaba registrada duas vezes sem querer.',
2636        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: essa informação faz sentido sozinha, ou ela pertence a outra? Se um cadastro for apagado, o que acontece com quem depende dele? Existe risco de a mesma pessoa/coisa ser cadastrada duas vezes?',
2637        practice: 'Pegue dois cadastros do seu sistema que se conectam (um pedido e um cliente, por exemplo) e descreva: como um se liga ao outro, e o que acontece se tentarem cadastrar a mesma pessoa/coisa de novo.',
2638        exerciseFeedback: 'Não existe resposta única aqui — o que importa é ficar claro qual lado "pertence" ao outro (um pedido pertence a um cliente, não o contrário) e o que exatamente conta como duplicado (mesmo CPF? mesmo e-mail? nome parecido?). Releia e veja se a regra de "isso é a mesma pessoa/coisa" está específica o bastante pra um dev implementar.',
2639        lessons: [
2640            'Aprenda a diferença entre uma informação independente e uma que depende de outra pra existir.',
2641            'Veja por que "relacionamento" não é sobre sistemas se conectando, é sobre dados dentro do mesmo sistema se referenciando.',
2642            'Pratique como descrever o que conta como registro duplicado antes que isso vire um problema real.'
2643        ],
2644        conceitos: [
2645            {
2646                nome: 'Relacionamento entre informações',
2647                icon: '<path d="M10 13a5 5 0 0 0 7 7l3-3a5 5 0 0 0-7-7"/><path d="M14 11a5 5 0 0 0-7-7L4 7a5 5 0 0 0 7 7"/>',
2648                oQueE: 'Quando um dado se refere a outro pra fazer sentido completo — um pedido pertence a um cliente, uma resposta pertence a um formulário, um comentário pertence a um post.',
2649                quandoUsar: 'Use sempre que uma informação sozinha fica incompleta sem saber a quem ou a que ela pertence — praticamente todo cadastro do mundo real se conecta a outro de alguma forma.',
2650                pedidoConfuso: 'Liga o pedido no cliente certo.',
2651                pedidoClaro: 'Todo pedido deve estar vinculado a exatamente um cliente. Ao abrir o pedido, o sistema mostra nome e histórico desse cliente. Se o cliente for excluído, os pedidos antigos continuam existindo, só mostram "Cliente removido" no lugar do nome.',
2652                comoFunciona: [
2653                    'Você identifica quais dados dependem de outros pra fazer sentido.',
2654                    'Cada registro dependente guarda uma referência ao registro do qual ele depende.',
2655                    'Ao consultar o dado dependente, o sistema busca junto as informações do outro lado da relação.',
2656                    'Você decide o que acontece do lado dependente se o outro lado for removido — normalmente não some, só perde a referência.'
2657                ],
2658                segundoExemplo: 'Numa plataforma de cursos, cada certificado se relaciona a um aluno e a um curso específico — sem os dois, o certificado não faz sentido sozinho.',
2659                errosComuns: [
2660                    'Não decidir o que acontece do lado dependente quando o outro lado é removido, deixando dado "órfão" sem explicação.',
2661                    'Duplicar a mesma informação nos dois lados em vez de relacionar, criando risco de ficarem desincronizadas.'
2662                ],
2663                termoTecnico: 'Relacionamento entre informações (data relationship)',
2664                termosRelacionados: ['referência', 'vínculo entre registros', 'chave estrangeira']
2665            },
2666            {
2667                nome: 'Registro duplicado',
2668                icon: '<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>',
2669                oQueE: 'Quando a mesma pessoa, produto ou informação real acaba criada como dois (ou mais) registros diferentes no sistema, geralmente sem querer.',
2670                quandoUsar: 'Use quando existe risco real de a mesma coisa ser cadastrada mais de uma vez — clientes que se cadastram de novo por engano, produtos importados de fontes diferentes.',
2671                pedidoConfuso: 'Evita cadastro repetido.',
2672                pedidoClaro: 'Antes de criar um cliente novo, o sistema confere se já existe um com o mesmo CPF. Se existir, mostra "Já encontramos um cadastro com este CPF — deseja usar o existente?" em vez de criar um segundo registro automaticamente.',
2673                comoFunciona: [
2674                    'Você define qual combinação de campos identifica "a mesma coisa" de verdade (nem sempre é um campo único só).',
2675                    'Antes de criar um registro novo, o sistema busca por algo parecido usando esses critérios.',
2676                    'Se encontrar algo muito parecido, avisa e deixa a pessoa decidir (usar o existente ou confirmar que é diferente mesmo).',
2677                    'Casos já duplicados podem ser unidos manualmente depois, juntando o histórico dos dois num só.'
2678                ],
2679                errosComuns: [
2680                    'Confiar só num campo único (como e-mail) pra evitar duplicidade, quando a pessoa pode se cadastrar com e-mail diferente.',
2681                    'Bloquear automaticamente sem deixar a pessoa confirmar — às vezes duas coisas parecidas são mesmo diferentes.'
2682                ],
2683                termoTecnico: 'Registro duplicado (duplicate record)',
2684                termosRelacionados: ['deduplicação', 'unificação de cadastro', 'cadastro repetido']
2685            },
2686            {
2687                nome: 'Referência quebrada',
2688                icon: '<path d="M9 17H7a5 5 0 0 1 0-10h2"/><path d="M15 7h2a5 5 0 0 0 0-10h-2" stroke-dasharray="2 3"/><path d="M12 2v20" stroke-dasharray="2 3"/>',
2689                oQueE: 'Quando um dado aponta pra outro que não existe mais — o vínculo ficou "no ar", apontando pra um registro que já foi excluído ou nunca existiu.',
2690                quandoUsar: 'Preste atenção nisso sempre que um registro puder ser excluído enquanto outros ainda dependem dele — é o risco direto de não decidir com antecedência o que acontece com quem depende de algo removido.',
2691                pedidoConfuso: 'Não deixa dar erro se o cliente já foi apagado.',
2692                pedidoClaro: 'Se um cliente for excluído, o sistema não permite a exclusão enquanto existirem pedidos vinculados a ele — mostra "Este cliente tem 3 pedidos ativos e não pode ser removido" em vez de deixar os pedidos com uma referência quebrada.',
2693                comoFunciona: [
2694                    'Antes de excluir um registro, o sistema confere se algo mais depende dele.',
2695                    'Se depender, o sistema decide entre 3 caminhos: bloquear a exclusão, excluir em cascata o que depende dele, ou desvincular (deixar o dependente sem essa referência).',
2696                    'Essa decisão precisa ser explícita — deixar sem decidir é o que gera referência quebrada.',
2697                    'Sistemas bem modelados escolhem o comportamento certo por tipo de relação, não um padrão único pra tudo.'
2698                ],
2699                errosComuns: [
2700                    'Não decidir o comportamento e deixar o banco de dados permitir a exclusão mesmo com dependentes — gera referência quebrada silenciosa.',
2701                    'Usar exclusão em cascata em toda relação por padrão, apagando coisas que na verdade deveriam ser preservadas.'
2702                ],
2703                termoTecnico: 'Referência quebrada (orphaned reference / broken foreign key)',
2704                termosRelacionados: ['dado órfão', 'integridade referencial', 'exclusão em cascata']
2705            },
2706            {
2707                nome: 'Dependência circular',
2708                icon: '<path d="M17 2.1l4 4-4 4"/><path d="M3 12.6v-2a4 4 0 0 1 4-4h14"/><path d="M7 21.9l-4-4 4-4"/><path d="M21 11.4v2a4 4 0 0 1-4 4H3"/>',
2709                oQueE: 'Quando dois (ou mais) registros dependem um do outro numa cadeia fechada — A depende de B, que depende de A — deixando o sistema sem saber por onde começar.',
2710                quandoUsar: 'Vale verificar isso ao desenhar relacionamentos que podem se referenciar de volta — categorias que podem conter subcategorias, aprovações que dependem de outra aprovação.',
2711                pedidoConfuso: 'Não deixa criar uma categoria dentro dela mesma.',
2712                pedidoClaro: 'Uma categoria de produto pode ter uma categoria "pai", mas o sistema impede que ela vire pai (direto ou indiretamente) de uma categoria que já é ancestral dela — bloqueando a ação com "Isso criaria um ciclo entre categorias" antes de salvar.',
2713                comoFunciona: [
2714                    'Ao criar ou editar uma relação, o sistema percorre a cadeia de dependências a partir dela.',
2715                    'Se, seguindo essa cadeia, ela chegar de volta no ponto de partida, existe um ciclo.',
2716                    'A ação que criaria o ciclo é bloqueada antes de ser salva, com uma explicação do problema.',
2717                    'Relações desse tipo (hierarquias que podem se referenciar) precisam dessa checagem desde o desenho, não como correção depois.'
2718                ],
2719                errosComuns: [
2720                    'Só descobrir a dependência circular quando o sistema trava tentando processar a cadeia (loop infinito).',
2721                    'Permitir a relação sem checar ciclos, deixando dados hierárquicos inconsistentes.'
2722                ],
2723                termoTecnico: 'Dependência circular (circular reference)',
2724                termosRelacionados: ['ciclo de referências', 'hierarquia inconsistente', 'referência recursiva']
2725            }
2726        ],
2727        codigos: [
2728            { codigo: '/vincula-e-decide-o-resto' },
2729            { codigo: '/evita-duplicado' }
2730        ]
2731    },
2732    {
2733        stage: 'Aula 03',
2734        layout: 'grid',
2735        title: 'O que fica registrado',
2736        goal: 'Aprenda a diferença entre guardar o rastro de cada mudança que um dado sofreu, e apenas tirar algo de circulação sem apagar nada.',
2737        focus: 'Antes de pedir uma tela ou funcionalidade, pergunte: alguém precisa poder ver como esse dado mudou ao longo do tempo? Existe algo que precisa sair da lista principal sem deixar de existir?',
2738        practice: 'Pegue um dado importante do seu sistema (um pedido, um contrato) e descreva: o que deveria ficar registrado toda vez que ele muda, e em que situação ele deveria ser arquivado em vez de excluído.',
2739        exerciseFeedback: 'Não existe resposta única aqui — o que importa é a diferença ficar clara: histórico é sobre GUARDAR CADA MUDANÇA (quem mudou o quê e quando), arquivamento é sobre TIRAR DE CIRCULAÇÃO sem apagar. Releia e veja se sua resposta não confundiu os dois.',
2740        lessons: [
2741            'Aprenda o que exatamente fica guardado quando um sistema registra histórico de alterações.',
2742            'Veja a diferença entre arquivar algo e excluir de vez (isso volta na próxima aula).',
2743            'Pratique como decidir o que realmente precisa de histórico, sem registrar tudo por excesso de zelo.'
2744        ],
2745        conceitos: [
2746            {
2747                nome: 'Histórico de alterações',
2748                oQueE: 'Um registro de cada mudança que um dado sofreu ao longo do tempo — o que era antes, o que passou a ser, quem mudou e quando.',
2749                quandoUsar: 'Use quando é importante poder responder "o que mudou aqui e quem fez isso" depois — contratos, preços, permissões, qualquer dado onde o passado importa tanto quanto o presente.',
2750                quandoNaoUsar: 'Não use pra todo campo do sistema — guardar histórico de um campo que muda toda hora sem importância real (como "última vez que a página foi visualizada") só gera dado inútil acumulado.',
2751                pedidoConfuso: 'Guarda o histórico de mudanças.',
2752                pedidoClaro: 'Toda vez que o preço de um produto for alterado, o sistema guarda o valor anterior, o valor novo, quem fez a alteração e a data. Essas mudanças aparecem numa lista "Histórico de preços" dentro da tela do produto.',
2753                comoFunciona: [
2754                    'Você decide quais campos precisam ter histórico rastreado.',
2755                    'Toda vez que um desses campos muda, o sistema guarda uma entrada com valor antigo, valor novo, quem mudou e quando.',
2756                    'Essas entradas nunca são apagadas nem sobrescritas — elas se acumulam.',
2757                    'Uma tela mostra essa lista em ordem cronológica pra quem precisa consultar.'
2758                ],
2759                segundoExemplo: 'Um editor de documento colaborativo guarda cada versão salva, permitindo ver "quem mudou o parágrafo 3 ontem às 14h".',
2760                errosComuns: [
2761                    'Guardar histórico de campos que não importam, inchando o banco de dados à toa.',
2762                    'Permitir editar ou apagar uma entrada de histórico já registrada — isso quebra a confiança no próprio histórico.'
2763                ],
2764                termoTecnico: 'Histórico de alterações (audit trail / change log)',
2765                termosRelacionados: ['trilha de auditoria', 'log de mudanças', 'versionamento']
2766            },
2767            {
2768                nome: 'Arquivamento',
2769                oQueE: 'Tirar um registro da lista ativa/principal sem apagar nenhuma informação dele — ele continua existindo, só para de aparecer no dia a dia.',
2770                quandoUsar: 'Use quando algo não é mais relevante pro uso do dia a dia, mas ainda pode precisar ser consultado depois — um pedido antigo já finalizado, um funcionário que já saiu da empresa.',
2771                quandoNaoUsar: 'Não use quando o objetivo é realmente remover o dado de vez, por exemplo por pedido do próprio dono do dado — nesse caso o conceito certo é exclusão, não arquivamento.',
2772                pedidoConfuso: 'Tira os pedidos antigos da lista.',
2773                pedidoClaro: 'Pedidos com mais de 1 ano e já finalizados saem automaticamente da lista principal "Pedidos" e vão para uma seção "Arquivados", acessível por um filtro. Nada nesses pedidos é apagado, só deixam de aparecer por padrão.',
2774                comoFunciona: [
2775                    'Você define o critério que torna um registro elegível pra arquivamento (idade, status, inatividade).',
2776                    'O registro ganha uma marcação de "arquivado" — continua no banco de dados, intacto.',
2777                    'A lista principal, por padrão, esconde os registros marcados como arquivados.',
2778                    'Um filtro ou seção separada permite consultar os arquivados quando necessário, e desarquivar se preciso.'
2779                ],
2780                segundoExemplo: 'Um app de e-mail arquiva mensagens que você marca como "concluídas" — elas somem da caixa de entrada, mas continuam pesquisáveis.',
2781                errosComuns: [
2782                    'Confundir arquivamento com exclusão e prometer "sumir para sempre" quando n
2782a verdade o dado continua existindo.',
2783                    'Não dar um jeito fácil de consultar ou desarquivar o que foi arquivado, tornando a função inútil na prática.'
2784                ],
2785                termoTecnico: 'Arquivamento (archiving)',
2786                termosRelacionados: ['inativação', 'fora de circulação', 'arquivo morto']
2787            }
2788        ],
2789        codigos: [
2790            { codigo: '/historico-so-do-que-importa' },
2791            { codigo: '/arquiva-nao-apaga' }
2792        ]
2793    },
2794    {
2795        stage: 'Aula 04',
2796        layout: 'compare',
2797        compareSymbol: '≠',
2798        title: 'Apagar não é sempre apagar',
2799        goal: 'Aprenda a diferença entre remover um dado de vez e apenas escondê-lo — uma decisão que muda tudo sobre o que ainda pode ser recuperado depois.',
2800        focus: 'Antes de pedir "excluir" alguma coisa, pergunte: isso precisa sumir de verdade e pra sempre, ou só precisa parar de aparecer, mantendo a possibilidade de recuperar depois? A resposta muda completamente como isso deve ser construído.',
2801        practice: 'Pegue uma ação de "excluir" do seu sistema (excluir um pedido, uma conta, um comentário) e decida: deveria ser exclusão de verdade ou exclusão lógica? Escreva o motivo da escolha.',
2802        exerciseFeedback: 'Não existe resposta única — o que importa é o motivo da escolha estar claro: se existe qualquer chance de precisar recuperar, auditar, ou se há obrigação legal de manter o registro, é exclusão lógica; se o dado realmente não pode continuar existindo em lugar nenhum, é exclusão de verdade. Releia e veja se você não escolheu só "porque sim".',
2803        lessons: [
2804            'Aprenda a diferença entre um dado que desaparece de vez e um que só fica escondido.',
2805            'Veja por que a maioria dos sistemas usa exclusão lógica como padrão, não exclusão de verdade.',
2806            'Pratique como decidir qual das duas faz sentido pra cada situação, em vez de assumir "excluir" sempre significa a mesma coisa.'
2807        ],
2808        conceitos: [
2809            {
2810                nome: 'Exclusão',
2811                compareLabel: 'O dado desaparece de vez — não tem volta.',
2812                icon: '<path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/>',
2813                oQueE: 'A remoção definitiva de um dado do sistema — depois de excluído, ele não existe mais em lugar nenhum e não pode ser recuperado.',
2814                quandoUsar: 'Use quando existe uma razão forte pra o dado realmente deixar de existir — pedido explícito do dono do dado (direito de exclusão), ou informação que nunca deveria ter sido salva.',
2815                quandoNaoUsar: 'Não use como padrão pra qualquer botão "excluir" — a maioria dos casos do dia a dia (excluir um pedido, um comentário) se resolve melhor com exclusão lógica, que permite desfazer.',
2816                comparacao: 'Diferente da exclusão lógica, aqui não existe "desfazer" — por isso costuma vir com uma confirmação extra, tipo digitar o nome da coisa que vai ser apagada.',
2817                pedidoConfuso: 'Apaga de vez quando o usuário pedir.',
2818                pedidoClaro: 'Ao clicar em "Excluir minha conta permanentemente", o sistema pede que o usuário digite a palavra "EXCLUIR" para confirmar. Depois disso, todos os dados pessoais são removidos do banco de dados de forma definitiva, sem possibilidade de recuperação.',
2819                comoFunciona: [
2820                    'O usuário solicita a exclusão de algo específico.',
2821                    'O sistema pede uma confirmação forte (não um clique simples), já que a ação não tem volta.',
2822                    'Confirmado, o registro é removido de verdade do banco de dados.',
2823                    'Nenhuma cópia do dado continua acessível depois disso, respeitando o que foi prometido.'
2824                ],
2825                errosComuns: [
2826                    'Chamar de "exclusão" uma ação que na verdade é reversível — isso quebra a confiança de quem pediu.',
2827                    'Não confirmar de forma clara antes de uma ação sem volta, permitindo exclusão acidental.'
2828                ],
2829                termoTecnico: 'Exclusão definitiva (hard delete)',
2830                termosRelacionados: ['remoção permanente', 'apagar de vez', 'direito ao esquecimento']
2831            },
2832            {
2833                nome: 'Exclusão lógica',
2834                compareLabel: 'O dado só fica escondido — dá pra recuperar.',
2835                icon: '<path d="M17.94 17.94A10.94 10.94 0 0 1 12 20c-7 0-11-8-11-8a18.5 18.5 0 0 1 5.06-5.94"/><path d="M9.9 4.24A10.94 10.94 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-4.22 5"/><path d="M1 1l22 22"/>',
2836                oQueE: 'Marcar um registro como "excluído" sem removê-lo de verdade do banco de dados — ele para de aparecer nas listas normais, mas continua existindo por trás.',
2837                quandoUsar: 'Use como padrão pra maioria das ações de "excluir" do dia a dia — permite desfazer, manter histórico, e cumprir obrigações legais de retenção de dados sem esconder isso do usuário.',
2838                comparacao: 'Diferente da exclusão de verdade, aqui existe "desfazer" — o dado só ficou invisível, não sumiu.',
2839                pedidoConfuso: 'Exclui o pedido mas guarda escondido.',
2840                pedidoClaro: 'Ao clicar em "Excluir pedido", o sistema marca o pedido com uma data de exclusão e ele some da lista principal. O pedido continua no banco de dados e pode ser recuperado em até 30 dias através da seção "Lixeira".',
2841                comoFunciona: [
2842                    'O usuário pede pra excluir um registro.',
2843                    'O sistema não remove nada — só grava uma marcação (e a data) dizendo que aquilo está "excluído".',
2844                    'As listas normais do sistema filtram automaticamente o que está marcado como excluído, escondendo-o.',
2845                    'Uma tela específica (tipo "Lixeira") permite ver e restaurar o que foi excluído, dentro de um prazo definido.'
2846                ],
2847                segundoExemplo: 'Ao excluir uma foto do celular, ela some do álbum principal mas fica disponível em "Recentemente excluídos" por 30 dias antes de sumir de vez.',
2848                errosComuns: [
2849                    'Prometer "exclusão" pro usuário sem deixar claro que o dado continua existindo por trás — isso pode até virar problema de confiança/privacidade.',
2850                    'Deixar o registro marcado como excluído aparecer em algum lugar que deveria filtrá-lo (busca, relatório).'
2851                ],
2852                termoTecnico: 'Exclusão lógica (soft delete)',
2853                termosRelacionados: ['exclusão reversível', 'marcação de exclusão', 'lixeira']
2854            }
2855        ],
2856        codigos: [
2857            { codigo: '/decide-o-tipo-de-exclusao' },
2858            { codigo: '/lixeira-com-prazo' }
2859        ]
2860    },
2861    {
2862        stage: 'Aula 05',
2863        layout: 'compare',
2864        compareSymbol: '⇄',
2865        title: 'Dados que trocam de casa',
2866        goal: 'Aprenda a diferença entre trazer dados de fora pra dentro do sistema e tirar dados de dentro pra usar em outro lugar.',
2867        focus: 'Antes de pedir uma funcionalidade de "trazer" ou "tirar" dados, pergunte: de onde vêm os dados e em que formato? O que acontece se algum dado vier errado ou incompleto? Pra onde vão os dados exportados e em que formato eles precisam sair?',
2868        practice: 'Pegue uma situação real do seu sistema onde dados entram ou saem em lote (uma planilha de clientes, um relatório baixado) e descreva: o formato esperado, o que fazer com linhas inválidas, e o que exatamente sai no arquivo final.',
2869        exerciseFeedback: 'Não existe resposta única — o que importa é cobrir os dois lados de cada operação: pra importação, o que fazer quando algo vem errado (ignorar a linha? travar tudo? avisar e deixar corrigir?); pra exportação, exatamente quais campos saem e em qual formato. Releia e veja se as regras de erro estão específicas.',
2870        lessons: [
2871            'Aprenda o que precisa ser decidido antes de pedir uma importação de dados em lote.',
2872            'Veja o que precisa ser decidido antes de pedir uma exportação de dados.',
2873            'Pratique como lidar com dados que vêm errados numa importação, em vez de assumir que tudo sempre chega perfeito.'
2874        ],
2875        conceitos: [
2876            {
2877                nome: 'Importação',
2878                compareLabel: 'Dado de fora entrando no sistema.',
2879                icon: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/>',
2880                oQueE: 'Trazer um conjunto de dados de fora (uma planilha, um arquivo, outro sistema) e criar ou atualizar registros no sistema a partir deles, de uma vez só.',
2881                quandoUsar: 'Use quando alguém precisa cadastrar muitos registros de uma vez em vez de um por um — migrar clientes de uma planilha, importar um catálogo de produtos de um fornecedor.',
2882                pedidoConfuso: 'Deixa subir uma planilha de clientes.',
2883                pedidoClaro: 'A tela "Importar clientes" aceita um arquivo CSV com as colunas nome, e-mail e telefone (nessa ordem). Antes de importar de verdade, o sistema mostra uma prévia com quantas linhas são válidas e quantas têm erro, permitindo baixar um relatório das linhas com problema antes de confirmar.',
2884                comoFunciona: [
2885                    'O usuário envia um arquivo num formato esperado (CSV, planilha).',
2886                    'O sistema lê o arquivo linha por linha, validando cada uma contra as mesmas regras de um cadastro normal.',
2887                    'Linhas válidas ficam prontas pra virar registros; linhas inválidas são listadas com o motivo do erro.',
2888                    'O usuário confirma, e só as linhas válidas viram registros de verdade no sistema.'
2889                ],
2890                segundoExemplo: 'Uma ferramenta de e-mail marketing importa uma lista de contatos de um arquivo CSV, ignorando automaticamente linhas com e-mail em formato inválido e avisando quantas foram ignoradas.',
2891                errosComuns: [
2892                    'Travar a importação inteira por causa de 1 linha com erro, em vez de importar o que está certo e apontar o que não está.',
2893                    'Não mostrar uma prévia antes de confirmar, fazendo a pessoa descobrir problemas só depois que tudo já foi importado.'
2894                ],
2895                termoTecnico: 'Importação de dados (data import)',
2896                termosRelacionados: ['upload em lote', 'carga de dados', 'importação em massa']
2897            },
2898            {
2899                nome: 'Exportação',
2900                compareLabel: 'Dado de dentro saindo do sistema.',
2901                icon: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/>',
2902                oQueE: 'Tirar um conjunto de dados de dentro do sistema e gerar um arquivo (planilha, PDF, CSV) que a pessoa pode baixar e usar em outro lugar.',
2903                quandoUsar: 'Use quando alguém precisa levar os dados pra fora do sistema — abrir numa planilha própria, mandar pra outro sistema, guardar um relatório.',
2904                pedidoConfuso: 'Deixa baixar os dados numa planilha.',
2905                pedidoClaro: 'O botão "Exportar" na tela de pedidos gera um arquivo CSV com número do pedido, cliente, data, valor total e status — respeitando os filtros aplicados na tela no momento do clique, não todos os pedidos do sistema.',
2906                comoFunciona: [
2907                    'O usuário decide o que exportar (todos os registros ou só os filtrados na tela).',
2908                    'O sistema define exatamente quais campos vão pro arquivo, na ordem certa.',
2909                    'Gera o arquivo no formato pedido (CSV, Excel, PDF).',
2910                    'Disponibiliza pra download, direto ou por link/e-mail se for um arquivo grande que demora pra gerar.'
2911                ],
2912                segundoExemplo: 'Um sistema financeiro exporta o extrato do mês em PDF, com um layout pronto pra impressão, diferente da planilha CSV usada pra importar em outro sistema contábil.',
2913                errosComuns: [
2914                    'Exportar campos sensíveis que a pessoa não deveria conseguir tirar do sistema em lote (senhas, dados de outros clientes).',
2915                    'Não deixar claro se a exportação respeita os filtros aplicados na tela ou sempre traz tudo.'
2916                ],
2917                termoTecnico: 'Exportação de dados (data export)',
2918                termosRelacionados: ['download em lote', 'geração de relatório', 'extração de dados']
2919            }
2920        ],
2921        codigos: [
2922            { codigo: '/importa-com-previa' },
2923            { codigo: '/exporta-so-o-necessario' }
2924        ]
2925    },
2926    {
2927        stage: 'Aula 06',
2928        layout: 'timeline',
2929        sequenceCount: 2,
2930        connectors: ['permite'],
2931        title: 'Continuidade dos dados',
2932        goal: 'Aprenda a cadeia que garante que um dado sobrevive a um desastre: uma cópia é guardada periodicamente, pode ser trazida de volta se algo der errado, e existe um prazo definido até quando ela precisa continuar existindo.',
2933        focus: 'Antes de pedir uma funcionalidade que envolve dado importante, pergunte: existe uma cópia de segurança dele? Se algo for perdido, como isso volta? Até quando esse dado (ou a cópia dele) precisa continuar existindo por lei ou por política interna?',
2934        practice: 'Pegue um dado crítico do seu sistema (cadastro de clientes, histórico financeiro) e descreva: com que frequência ele deveria ser copiado, como a restauração funcionaria se algo desse errado, e por quanto tempo ele (ou a cópia) precisa ser mantido.',
2935        exerciseFeedback: 'Não existe resposta única — o que importa é a cadeia fazer sentido: a frequência do backup bate com o quanto dado a empresa ace
2935itaria perder (perder 1 dia de dados é aceitável? 1 hora?), a restauração tem um responsável e um processo claro, e o prazo de retenção tem um motivo real (lei, política) por trás, não é só um número chutado.',
2936        lessons: [
2937            'Aprenda por que uma cópia de segurança sozinha não resolve nada sem um plano de restauração.',
2938            'Veja como retenção de dados define até quando manter algo guardado, mesmo que não seja mais usado no dia a dia.',
2939            'Pratique como descrever a frequência de backup em função do que a empresa pode se dar ao luxo de perder.'
2940        ],
2941        conceitos: [
2942            {
2943                nome: 'Backup',
2944                when: 'Antes de qualquer problema acontecer',
2945                icon: '<path d="M3.05 13A9 9 0 1 0 6 5.3L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/>',
2946                oQueE: 'Uma cópia de segurança dos dados, guardada separadamente, feita pra ser usada caso os dados originais sejam perdidos, corrompidos ou apagados por engano.',
2947                quandoUsar: 'Use pra qualquer dado que a empresa não pode se dar ao luxo de perder — cadastros de clientes, histórico financeiro, conteúdo pago pelos usuários.',
2948                pedidoConfuso: 'Faz um backup dos dados.',
2949                pedidoClaro: 'O banco de dados de clientes recebe um backup completo automático todo dia às 3h da manhã, guardado por 30 dias, além de um backup incremental a cada 6 horas guardado por 7 dias.',
2950                comoFunciona: [
2951                    'Você decide com que frequência a cópia é feita, baseado em quanto dado seria aceitável perder no pior cenário.',
2952                    'O sistema gera a cópia automaticamente, no horário definido, sem precisar de ação manual.',
2953                    'A cópia fica guardada num lugar separado do dado original — se o original for perdido, a cópia não vai junto.',
2954                    'Cópias antigas são descartadas conforme uma política de quantas cópias manter.'
2955                ],
2956                errosComuns: [
2957                    'Fazer backup mas nunca testar se ele realmente funciona na hora de restaurar.',
2958                    'Guardar a cópia no mesmo lugar do dado original — se aquele lugar falhar, os dois somem juntos.'
2959                ],
2960                termoTecnico: 'Cópia de segurança (backup)',
2961                termosRelacionados: ['cópia de segurança', 'snapshot', 'rotina de backup']
2962            },
2963            {
2964                nome: 'Restauração',
2965                when: 'Depois que algo deu errado',
2966                icon: '<path d="M1 4v6h6"/><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>',
2967                oQueE: 'Trazer os dados de volta a partir de uma cópia de segurança, depois que os dados originais foram perdidos, corrompidos ou apagados sem querer.',
2968                quandoUsar: 'Use quando algo deu errado o suficiente pra precisar recuperar dados a partir de uma cópia — uma falha grave, uma exclusão em massa acidental.',
2969                pedidoConfuso: 'Recupera os dados do backup se der problema.',
2970                pedidoClaro: 'Se for necessário restaurar, o time técnico escolhe o backup mais recente antes do problema, restaura primeiro num ambiente separado pra conferir a integridade, e só depois substitui os dados de produção — processo que deve levar no máximo 2 horas.',
2971                comoFunciona: [
2972                    'Identifica-se o problema e a partir de que momento os dados ficaram comprometidos.',
2973                    'Escolhe-se a cópia de segurança mais recente anterior a esse momento.',
2974                    'A cópia é trazida de volta, idealmente testada num ambiente separado antes de virar definitiva.',
2975                    'Os dados restaurados substituem os dados problemáticos, e o que aconteceu depois do backup escolhido pode ser perdido — por isso a frequência do backup importa tanto.'
2976                ],
2977                segundoExemplo: 'Um editor de texto com "histórico de versões" permite restaurar o documento pra como ele estava numa versão salva anterior, descartando as mudanças feitas depois.',
2978                errosComuns: [
2979                    'Não ter um processo testado de restauração — descobrir na hora do desespero que ninguém sabe como fazer.',
2980                    'Restaurar direto em produção sem antes conferir se a cópia está íntegra.'
2981                ],
2982                termoTecnico: 'Restauração (restore / recovery)',
2983                termosRelacionados: ['recuperação de dados', 'restore', 'recuperação de desastre']
2984            },
2985            {
2986                nome: 'Retenção de dados',
2987                icon: '<rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/><path d="M12 14v3l2 1"/>',
2988                oQueE: 'A política que define por quanto tempo um dado (ou uma cópia dele) deve continuar sendo guardado, antes de poder ser descartado de verdade.',
2989                quandoUsar: 'Use sempre que existe uma obrigação legal, contratual ou de negócio sobre até quando um dado precisa continuar existindo — notas fiscais, registros financeiros, logs de acesso.',
2990                pedidoConfuso: 'Guarda os dados fiscais o tempo que precisar.',
2991                pedidoClaro: 'Notas fiscais emitidas devem ser mantidas no sistema por no mínimo 5 anos a partir da emissão, conforme exigência legal. Depois desse prazo, o sistema pode arquivá-las ou excluí-las conforme a política definida.',
2992                comoFunciona: [
2993                    'Você identifica o motivo do prazo (lei, contrato, política interna) e o tempo exato exigido.',
2994                    'O sistema guarda, junto de cada dado sujeito a essa regra, a data a partir da qual a contagem começa.',
2995                    'Enquanto o prazo não vence, o dado não pode ser excluído de verdade, mesmo que o usuário peça.',
2996                    'Depois que o prazo vence, o dado pode ser arquivado ou excluído, conforme a decisão de negócio.'
2997                ],
2998                errosComuns: [
2999                    'Excluir um dado antes do prazo de retenção obrigatório vencer, gerando risco legal.',
3000                    'Manter dado guardado pra sempre "por via das dúvidas", sem prazo nenhum — isso também é um risco, principalmente com dado pessoal.'
3001                ],
3002                termoTecnico: 'Retenção de dados (data retention)',
3003                termosRelacionados: ['prazo de guarda', 'política de retenção', 'tempo mínimo de armazenamento']
3004            },
3005            {
3006                nome: 'Expiração automática',
3007                icon: '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 3"/><path d="M3 3l3 3M21 3l-3 3"/>',
3008                oQueE: 'Quando o próprio sistema remove, arquiva ou anonimiza um dado sozinho, assim que o prazo de retenção dele vence — sem precisar de alguém pedir manualmente.',
3009                quandoUsar: 'Use como consequência prática da retenção de dados: definir o prazo não adianta muito se alguém precisa lembrar de agir manualmente quando ele vence — a expiração automática fecha esse ciclo.',
3010                quandoNaoUsar: 'Não automatize a exclusão de dados cujo prazo de retenção envolve análise caso a caso (ex.: processos jurídicos em andamento) — nesses casos, a expiração deveria gerar um alerta pra revisão humana, não uma ação automática direta.',
3011                pedidoConfuso: 'Apaga sozinho depois de um tempo.',
3012                pedidoClaro: 'Logs de acesso com mais de 12 meses são excluídos automaticamente por uma rotina que roda todo dia 1º do mês, sem intervenção manual — exceto os logs marcados como "em investigação", que ficam de fora dessa rotina até serem liberados manualmente.',
3013                comparacao: 'Retenção de dados define QUANDO um dado pode deixar de existir; expiração automática é o mecanismo que executa essa decisão sozinho, sem depender de alguém lembrar.',
3014                comoFunciona: [
3015                    'Uma rotina automática roda periodicamente (diária, mensal) checando quais dados já passaram do prazo de retenção.',
3016                    'Dados encontrados nessa checagem seguem a ação definida: exclusão, arquivamento ou anonimização.',
3017                    'Casos que precisam de revisão humana (ex.: em disputa legal) ficam de fora da rotina automática por uma marcação explícita.',
3018                    'A execução da rotina fica registrada, pra existir prova de quando e o que foi processado.'
3019                ],
3020                errosComuns: [
3021                    'Automatizar a exclusão sem uma forma de excluir casos especiais da rotina, apagando algo que precisava ser preservado.',
3022                    'Não registrar a execução da rotina automática, ficando sem como provar depois o que foi removido e quando.'
3023                ],
3024                termoTecnico: 'Expiração automática (automatic data expiry / TTL)',
3025                termosRelacionados: ['rotina de limpeza', 'TTL (time to live)', 'purga automática']
3026            }
3027        ],
3028        codigos: [
3029            { codigo: '/backup-pelo-que-pode-perder' },
3030            { codigo: '/prazo-de-retencao' }
3031        ]
3032    },
3033    {
3034        stage: 'Aula 07',
3035        layout: 'groups',
3036        title: 'Privacidade e proteção',
3037        goal: 'Aprenda a diferença entre dado pessoal e dado sensível, e o que fazer quando é preciso usar uma informação sem expor quem ela pertence.',
3038        focus: 'Antes de pedir uma tela ou relatório que envolve dado de gente real, pergunte: esse dado identifica uma pessoa? Alguma informação aqui é sensível o bastante pra exigir cuidado redobrado? É possível usar esse dado sem expor a identidade de quem ele pertence?',
3039        practice: 'Pegue um relatório ou tela do seu sistema que mostra dados de pessoas reais e descreva: quais campos são dados pessoais, quais (se houver) são sensíveis, e se existe alguma versão desse relatório que poderia ser mostrada de forma anonimizada.',
3040        exerciseFeedback: 'Não existe resposta única — o que importa é a distinção ficar clara: dado pessoal é qualquer coisa que identifica uma pessoa (nome, e-mail, CPF); dado sensível é uma categoria mais restrita e exige cuidado redobrado (saúde, orientação, dados financeiros detalhados); anonimização é a técnica que permite usar o dado sem conseguir voltar a saber de quem ele é.',
3041        lessons: [
3042            'Aprenda a diferença entre dado pessoal e dado sensível, e por que a segunda categoria exige mais cuidado.',
3043            'Veja como anonimização permite usar dado sem expor a identidade de quem ele pertence.',
3044            'Pratique como identificar, num sistema real, quais dados merecem esse cuidado extra.'
3045        ],
3046        groups: [
3047            { title: 'O que precisa de cuidado extra', desc: 'Que tipos de dado identificam uma pessoa, e quais entre eles são especialmente delicados.' },
3048            {
3049                title: 'Como proteger sem esconder',
3050                desc: 'O que fazer quando é preciso usar o dado (num relatório, numa análise) sem expor quem ele é.',
3051                note: 'Os dois andam juntos: só faz sentido pensar em anonimizar depois de saber exatamente o que no seu sistema é dado pessoal ou sensível.'
3052            }
3053        ],
3054        scenario: {
3055            title: 'Cenário completo: relatório de uso de um app de saúde',
3056            intro: 'Um único fluxo real, mostrando onde cada conceito desta aula aparece.',
3057            steps: [
3058                { text: 'O app guarda nome, e-mail e telefone de cada usuário — todos dados pessoais, protegidos por padrão.', tag: 'Dados pessoais' },
3059                { text: 'O app também guarda o histórico de condições de saúde relatadas — isso é dado sensível, com camada extra de proteção e acesso mais restrito.', tag: 'Dados sensíveis' },
3060                { text: 'O time de produto quer entender quais condições são mais comuns entre os usuários, sem precisar saber quem é quem.', tag: 'Necessidade de análise' },
3061                { text: 'O relatório gerado mostra "34% dos usuários relataram X condição", sem nenhum nome, e-mail ou identificador que permita voltar a saber de quem é cada relato.', tag: 'Anonimização' }
3062            ]
3063        },
3064        conceitos: [
3065            {
3066                nome: 'Dados pessoais',
3067                grupo: 0,
3068                icon: '<circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/>',
3069                oQueE: 'Qualquer informação que identifica ou pode identificar uma pessoa específica — nome, e-mail, CPF, telefone, endereço.',
3070                quandoUsar: 'Trate como dado pessoal qualquer campo que, sozinho ou combinado com outros, permita saber quem é a pessoa por trás daquele registro.',
3071                pedidoConfuso: 'Protege os dados dos usuários.',
3072                pedidoClaro: 'Os campos nome, e-mail, telefone e endereço do cliente são dados pessoais: só ficam visíveis pra quem tem permissão de atendimento, nunca aparecem em relatórios agregados exportáveis por qualquer usuário do sistema, e o cliente pode solicitar a exclusão deles a qualquer momento.',
3073                comparacao: 'Todo dado sensível é também dado pessoal, mas nem todo dado pessoal é sensível — dado sensível é uma categoria mais restrita, com regras mais rígidas ainda.',
3074                errosComuns: [
3075                    'Não perceber que a combinação de vários campos "inofensivos" (cidade + idade + profissão) pode, junta, identificar alguém.',
3076                    'Deixar dado pessoal visível em telas ou relatórios que não precisavam dele pra funcionar.'
3077                ],
3078                termoTecnico: 'Dado pessoal (personal data)',
3079                termosRelacionados: ['informação identificável', 'PII', 'dado do titular']
3080            },
3081            {
3082                nome: 'Dados sensíveis',
3083                grupo: 0,
3084                icon: '<path d="M12 2 4 5v6c0 5 3.4 9 8 11 4.6-2 8-6 8-11V5l-8-3Z"/>',
3085                oQueE: 'Uma categoria mais restrita de dado pessoal, que por sua natureza exige cuidado redobrado por causa do risco de discriminação ou dano se for exposto — saúde, orientação sexual, religião, dados financeiros detalhados.',
3086                quandoUsar: 'Trate como sensível qualquer dado que, se vazado, pudesse causar prejuízo real ou discriminação contra a pessoa — não é sobre "informação embaraçosa", é sobre risco concreto.',
3087                pedidoConfuso: 'Esses dados de saúde precisam ser bem protegidos.',
3088                pedidoClaro: 'O campo "condições de saúde relatadas" é dado sensível: acesso restrito só ao próprio usuário e a profissionais autorizados, criptografado no banco de dados, e toda consulta a esse campo específico fica registrada em log de auditoria — diferente do nível de proteção padrão dos outros dados pessoais.',
3089                comoFunciona: [
3090                    'Você identifica quais campos se encaixam nas categorias de dado sensível (saúde, biometria, orientação, convicção religiosa/política, dados financeiros detalhados).',
3091                    'Esses campos recebem controle de acesso mais restrito que o padrão de dado pessoal comum.',
3092                    'Consultas a esses campos específicos ficam registradas em log de auditoria, não só a edição.',
3093                    'Esses campos ficam de fora, por padrão, de qualquer exportação ou relatório em lote, mesmo que dados pessoais comuns estejam liberados.'
3094                ],
3095                errosComuns: [
3096                    'Tratar dado sensível com o mesmo nível de proteção de qualquer outro dado pessoal, sem a camada extra que ele exige.',
3097                    'Incluir campo sensível numa exportação em lote "por conveniência", mesmo sem necessidade real.'
3098                ],
3099                termoTecnico: 'Dado sensível (sensitive data)',
3100                termosRelacionados: ['dado de categoria especial', 'informação restrita', 'dado protegido']
3101            },
3102            {
3103                nome: 'Anonimização',
3104                grupo: 1,
3105                icon: '<path d="M4 15c0-4 3-7 8-7s8 3 8 7"/><circle cx="8.5" cy="15" r="1.5"/><circle cx="15.5" cy="15" r="1.5"/><path d="M2 15h2M20 15h2"/>',
3106                oQueE: 'Transformar um dado de forma que não seja mais possível saber a que pessoa específica ele pertence — permite usar a informação (pra análise, relatório) sem expor identidade.',
3107                quandoUsar: 'Use quando você precisa analisar padrões num conjunto de dados (quantos usuários fazem X, qual a média de Y) sem precisar saber quem é cada pessoa individualmente.',
3108                quandoNaoUsar: 'Não funciona quando você ainda precisa identificar a pessoa depois — nesses casos, o conceito certo geralmente é apenas restringir o acesso, não anonimizar (que é irreversível por definição).',
3109                pedidoConfuso: 'Tira os nomes do relatório.',
3110                pedidoClaro: 'O relatório de "condições de saúde mais relatadas" agrupa os dados em faixas (por região e faixa etária) e mostra só porcentagens agregadas — nenhum nome, e-mail ou identificador individual aparece, e não é possível voltar a descobrir de quem é cada relato a partir do relatório.',
3111                comparacao: 'Diferente de só "esconder" o nome na tela (que ainda deixa o dado original recuperável por trás), anonimização de verdade remove a possibilidade de voltar a identificar a pessoa a partir daquele dado.',
3112                errosComuns: [
3113                    'Achar que só remover nome e e-mail já é suficie
3113nte — combinações de outros campos (CEP + idade + profissão) ainda podem reidentificar alguém.',
3114                    'Chamar de "anonimizado" um dado que na verdade só está com acesso restrito, mas continua identificável por trás.'
3115                ],
3116                termoTecnico: 'Anonimização (anonymization)',
3117                termosRelacionados: ['dado agregado', 'reidentificação', 'privacidade por design']
3118            },
3119            {
3120                nome: 'Pseudonimização',
3121                grupo: 1,
3122                icon: '<rect x="3" y="11" width="18" height="10" rx="1"/><path d="M7 11V8a5 5 0 0 1 10 0v3"/><circle cx="12" cy="16" r="1.5"/>',
3123                oQueE: 'Substituir a identificação de uma pessoa por um código/token, guardando separadamente (e protegida) a chave que permite voltar a ligar o código à pessoa real — diferente de anonimizar, aqui ainda dá pra reidentificar se necessário.',
3124                quandoUsar: 'Use quando você precisa reduzir o risco de exposição de um dado (ex.: numa análise, num ambiente de testes) mas ainda existe uma razão legítima pra eventualmente voltar a identificar a pessoa — investigação de fraude, correção de um erro específico.',
3125                quandoNaoUsar: 'Não é suficiente quando o objetivo é publicar ou compartilhar o dado amplamente sem risco nenhum de reidentificação — nesse caso o conceito certo é anonimização de verdade, que não guarda essa chave de volta.',
3126                pedidoConfuso: 'Troca o nome por um código.',
3127                pedidoClaro: 'No ambiente de testes, os nomes reais dos clientes são substituídos por códigos como "CLIENTE_4821" — a tabela que liga esse código ao cliente real fica só no ambiente de produção, com acesso restrito à equipe de dados, nunca exposta no ambiente de testes.',
3128                comparacao: 'Anonimização é uma via de mão única (não tem como voltar); pseudonimização guarda a chave de volta separada e protegida — é o meio-termo pra quando anonimizar de verdade tornaria o dado inútil pro que você precisa fazer com ele.',
3129                comoFunciona: [
3130                    'Cada identificador real (nome, CPF, e-mail) é substituído por um código/token gerado.',
3131                    'A tabela que liga o código de volta ao dado real fica separada, com acesso bem mais restrito que o dado pseudonimizado em si.',
3132                    'Quem trabalha com o dado pseudonimizado no dia a dia não tem acesso a essa tabela de ligação.',
3133                    'Reidentificar exige acesso explícito e justificado à tabela de ligação — não é algo que acontece sem querer.'
3134                ],
3135                errosComuns: [
3136                    'Guardar a tabela de ligação no mesmo lugar (ou com o mesmo nível de acesso) do dado pseudonimizado, anulando a proteção.',
3137                    'Tratar dado pseudonimizado como se já fosse anônimo pra fins de compartilhamento público — legalmente, geralmente ainda conta como dado pessoal.'
3138                ],
3139                termoTecnico: 'Pseudonimização (pseudonymization)',
3140                termosRelacionados: ['tokenização', 'dado codificado', 'chave de reidentificação']
3141            }
3142        ],
3143        codigos: [
3144            { codigo: '/separa-o-sensivel' },
3145            { codigo: '/agrega-sem-identificar' }
3146        ]
3147    },
3148    {
3149        stage: 'Aula 08',
3150        layout: 'grid',
3151        title: 'Onde tudo mora',
3152        goal: 'Feche a trilha entendendo onde os dados realmente ficam guardados no sistema, e como transformar tudo isso em números e listas que ajudam alguém a decidir algo.',
3153        focus: 'Antes de pedir um relatório ou uma tela de dados, pergunte: de onde exatamente esses números vêm? O relatório precisa ser gerado na hora ou pode ser algo calculado com antecedência? Quem vai usar esse relatório e pra decidir o quê?',
3154        practice: 'Pegue um relatório que você gostaria de ter no seu sistema e descreva: quais dados ele usa como fonte, como esses dados estão organizados, e o que exatamente a pessoa que olha o relatório precisa conseguir decidir com ele.',
3155        exerciseFeedback: 'Não existe resposta única aqui — o que importa é o relatório ter um propósito claro (decidir o quê?), não só "mostrar dados". Releia e veja se dá pra responder "e daí, o que alguém faz com esse número?" — se não der, o relatório provavelmente está incompleto.',
3156        lessons: [
3157            'Aprenda, em termos simples, o que é um banco de dados e por que ele organiza tudo em tabelas relacionadas.',
3158            'Veja como um relatório transforma dado bruto em algo que ajuda alguém a decidir.',
3159            'Feche a trilha revisando como todos os conceitos do curs
3159o se conectam: cadastro → relacionamento → histórico → exclusão → backup → privacidade → relatório.'
3160        ],
3161        conceitos: [
3162            {
3163                nome: 'Banco de dados',
3164                oQueE: 'O lugar onde o sistema guarda, organiza e recupera todas as informações — cadastros, pedidos, histórico — de forma estruturada, geralmente em tabelas que se relacionam entre si.',
3165                quandoUsar: 'É bom entender esse conceito sempre que for pedir algo que envolve grande volume de dados ou relação entre várias informações — praticamente todo sistema real depende de um banco de dados por trás.',
3166                pedidoConfuso: 'Guarda tudo direitinho no banco.',
3167                pedidoClaro: 'O sistema usa um banco de dados com tabelas separadas para clientes, pedidos e produtos, relacionadas entre si — um pedido referencia um cliente e um ou mais produtos, em vez de repetir essas informações em cada pedido.',
3168                comoFunciona: [
3169                    'Cada tipo de informação (cliente, pedido, produto) fica numa tabela própria.',
3170                    'Cada registro dentro de uma tabela tem um identificador único.',
3171                    'Tabelas se relacionam entre si através desses identificadores, evitando repetir a mesma informação em vários lugares.',
3172                    'Quando o sistema precisa mostrar algo (como um pedido com o nome do cliente), ele busca e junta as informações das tabelas relacionadas na hora.'
3173                ],
3174                errosComuns: [
3175                    'Pedir pra "guardar tudo numa tabela só" por parecer mais simples, o que na prática gera repetição de dado e inconsistência.',
3176                    'Não considerar como as tabelas vão se relacionar antes de pedir a funcionalidade, deixando essa decisão importante pro dev descobrir sozinho.'
3177                ],
3178                termoTecnico: 'Banco de dados (database)',
3179                termosRelacionados: ['tabela', 'estrutura de dados', 'armazenamento estruturado']
3180            },
3181            {
3182                nome: 'Relatório baseado em dados',
3183                oQueE: 'Uma visão organizada de informações do sistema, geralmente resumida ou agrupada, feita pra ajudar alguém a entender uma situação e tomar uma decisão.',
3184                quandoUsar: 'Use quando alguém precisa enxergar um panorama (não um registro individual) pra decidir algo — quanto foi vendido esse mês, quais produtos têm mais devolução, quantos usuários estão inativos.',
3185                pedidoConfuso: 'Faz um relatório de vendas.',
3186                pedidoClaro: 'O "Relatório mensal de vendas" mostra o total vendido por categoria de produto, comparado com o mês anterior, com o objetivo de ajudar o time comercial a decidir em qual categoria focar a próxima campanha. Atualiza automaticamente todo dia 1º, com dados até o último dia do mês anterior.',
3187                comoFunciona: [
3188                    'Você define qual decisão o relatório precisa ajudar a tomar — isso guia quais dados entram nele.',
3189                    'O sistema busca os dados brutos das tabelas relevantes do banco de dados.',
3190                    'Os dados são agrupados, somados ou comparados conforme o objetivo (por categoria, por período, por região).',
3191                    'O resultado é apresentado de forma visual (tabela, gráfico) fácil de interpretar rapidamente.'
3192                ],
3193                segundoExemplo: 'Um relatório de "alunos em risco de desistência" cruza frequência nas aulas e progresso no curso, ajudando o time pedagógico a decidir quem contatar primeiro.',
3194                errosComuns: [
3195                    'Pedir um relatório sem saber que decisão ele deveria ajudar a tomar, resultando numa tela de números sem propósito claro.',
3196                    'Misturar dado sensível ou pessoal detalhado demais num relatório que deveria ser só uma visão agregada.'
3197                ],
3198                termoTecnico: 'Relatório baseado em dados (data report)',
3199                termosRelacionados: ['dashboard', 'visão agregada', 'análise de dados']
3200            }
3201        ],
3202        codigos: [
3203            { codigo: '/relaciona-nao-repete' },
3204            { codigo: '/relatorio-com-proposito' }
3205        ]
3206    },
3207    {
3208        stage: 'Aula 09',
3209        layout: 'grid',
3210        title: 'Códigos Secretos: a régua de dados do curso inteiro',
3211        goal: 'Aula bônus. Recapitula as 8 aulas e reúne os 16 comandos que cobrem cadastro, relacionamento, histórico, exclusão, importação/exportação, continuidade, privacidade e relatório.',
3212        focus: 'Antes de pedir algo que envolve dado, passe a régua: os campos obrigatórios e únicos estão nomeados? o relacionamento e o que fazer se o dono sumir estão definidos? é exclusão de vez ou lógica? existe backup, prazo de retenção e proteção pro dado sensível?',
3213        practice: 'Pegue um cadastro ou relatório real que você precisa pedir ou revisar esta semana. Rode pelo menos 4 códigos desta lista sobre ele e junte tudo numa especificação só.',
3214        exerciseFeedback: 'Não existe resposta única. O que importa: a especificação final não deixa buraco — campos, relacionamento, histórico, exclusão, backup e privacidade ficam explícitos, não implícitos.',
3215        lessons: [
3216            'Reconheça, num cadastro ou relatório pronto, quais decisões sobre dado já foram tomadas e quais ficaram em aberto.',
3217            'Monte a régua de revisão que você vai usar em qualquer funcionalidade que envolva dado daqui pra frente.',
3218            'Combine vários códigos numa especificação única, sem contradição.'
3219        ],
3220        conceitos: [],
3221        bonus: true
3222    }
3223];
3224
3225const segurancaAcessosJourney = [
3226    {
3227        stage: 'Aula 01',
3228        estimatedMinutes: 40,
3229        layout: 'timeline',
3230        title: 'Identidade, conta e login',
3231        goal: 'Entenda que login não é só uma tela: é o processo de confirmar quem está tentando entrar e iniciar um acesso controlado.',
3232        focus: 'Antes de pedir uma tela de login, defina quem é a pessoa, como a conta é criada, como a identidade é confirmada e o que acontece depois.',
3233        practice: 'Escolha uma plataforma que você conhece e descreva o caminho completo: criação da conta, confirmação, login, sessão iniciada e encerramento do acesso.',
3234        exerciseFeedback: 'Uma boa resposta separa identidade, login, sessão e acesso. Ela também prevê falhas sem revelar se o e-mail ou a senha estavam errados.',
3235        lessons: [
3236            'Identidade é a representação de uma pessoa ou organização dentro do sistema.',
3237            'Cadastro cria a conta; login confirma a identidade; sessão mantém o reconhecimento.',
3238            'Uma conta pode estar pendente, ativa, bloqueada ou encerrada.',
3239            'Toda tela de login precisa prever sucesso, falha, espera, expiração e tentativa novamente.'
3240        ],
3241        connectors: ['confirma', 'cria', 'libera'],
3242        sequenceCount: 4,
3243        conceitos: [
3244            { titulo: 'Identidade', definicao: 'A representação de uma pessoa ou organização dentro do sistema. Ela pode ter nome, e-mail, tipo de usuário, status e outros dados que ajudam a reconhecer a conta.', exemplo: 'Uma aluna tem uma conta identificada por seu e-mail e um status ativo.', confuso: 'Cria usuários para o sistema.', claro: 'Crie contas de alunos usando e-mail como identificador único. Cada conta deve ter nome, e-mail, data de criação e status.', comoFunciona: ['Defina os tipos de pessoa que usarão o sistema.', 'Escolha o identificador que não pode se repetir.', 'Liste os dados mínimos da conta.', 'Defina os estados possíveis: convite pendente, ativo, bloqueado e encerrado.'], segundoExemplo: 'Um gestor e um aluno podem ter o mesmo nome, mas pertencem a contas diferentes e têm contextos distintos.', errosComuns: ['Usar “usuário” sem dizer quem é a pessoa.', 'Misturar nome de exibição com identificador da conta.', 'Criar conta sem definir status.'], termos: ['conta', 'identificador', 'status'] },
3245            { titulo: 'Login', definicao: 'O momento em que a pessoa apresenta credenciais para o sistema confirmar que ela é dona da conta.', exemplo: 'A pessoa informa e-mail e senha, o servidor valida os dados e inicia uma sessão.', confuso: 'Faz um login seguro.', claro: 'Valide e-mail e senha no servidor. Em sucesso, crie uma sessão protegida; em falha, mostre uma mensagem genérica e permita tentar novamente.', comoFunciona: ['Receba as credenciais.', 'Valide formato e regras básicas.', 'Confirme os dados no servidor.', 'Crie a sessão somente depois da validação.', 'Redirecione para a área adequada.'], segundoExemplo: 'Uma conta bloqueada pode ter a senha correta e ainda assim não conseguir entrar.', errosComuns: ['Validar apenas no navegador.', 'Informar se o e-mail ou a senha está errado.', 'Redirecionar antes de confirmar a sessão.'], termos: ['credencial', 'autenticação', 'servidor'] },
3246            { titulo: 'Sessão', definicao: 'O período em que o sistema reconhece a pessoa depois de um login válido. A sessão começa após a confirmação e termina por logout, expiração, bloqueio ou troca de senha.', exemplo: 'Depois de entrar, a pessoa abre outra página sem digitar a senha novamente porque a sessão continua válida.', confuso: 'Deixa a pessoa logada.', claro: 'Crie uma sessão protegida após o login e encerre-a no logout, após a expiração definida ou quando a conta for bloqueada.', comoFunciona: ['Defina quando a sessão começa.', 'Escolha sua duração e renovação.', 'Defina como o logout encerra o reconhecimento.', 'Liste eventos que invalidam a sessão.', 'Mostre uma orientação quando a sessão expirar.'], segundoExemplo: 'Ao trocar a senha, encerre sessões antigas para evitar que um acesso esquecido continue válido.', errosComuns: ['Tratar login e sessão como a mesma coisa.', 'Nunca expirar a sessão.', 'Deixar uma sessão ativa após bloqueio.'], termos: ['expiração', 'logout', 'sessão ativa'] },
3247            { titulo: 'Acesso', definicao: 'O que a pessoa pode consultar ou fazer depois que sua identidade foi confirmada. Estar logado não significa poder ver tudo.', exemplo: 'Um aluno entra na plataforma, mas vê somente os cursos em que tem matrícula ativa.', confuso: 'Depois do login libera o painel.', claro: 'Após o login, direcione a pessoa para o painel correspondente e mostre apen
3247as os recursos permitidos para o tipo e o status da conta.', comoFunciona: ['Confirme a identidade.', 'Consulte o status e o contexto da conta.', 'Verifique o que pode ser exibido.', 'Bloqueie recursos fora do escopo.', 'Explique como solicitar acesso quando necessário.'], segundoExemplo: 'Um gestor pode acessar alunos e relatórios; um aluno pode acessar seu progresso e suas aulas.', errosComuns: ['Confundir estar logado com ter acesso total.', 'Esconder um botão sem proteger a operação no servidor.', 'Não diferenciar aluno, gestor e professor.'], termos: ['permissão', 'recurso', 'escopo'] }
3248        ],
3249        scenario: { title: 'Cenário completo: entrada de uma escola online', intro: 'Uma escola terá alunos, professores e gestores. O pedido inicial é apenas “preciso de login para todo mundo entrar”.', steps: [
3250            { tag: 'Identificar', text: 'Separe os tipos de conta e diga quais dados identificam cada pessoa.' },
3251            { tag: 'Cadastrar', text: 'Defina nome, e-mail, senha, confirmação de e-mail e estados da conta.' },
3252            { tag: 'Confirmar', text: 'Descreva o que o servidor valida e quais mensagens aparecem quando algo falha.' },
3253            { tag: 'Reconhecer', text: 'Explique quando a sessão começa, quando expira e como o logout funciona.' },
3254            { tag: 'Orientar', text: 'Indique para onde cada tipo de pessoa vai depois do login e o que pode ver.' }
3255        ] },
3256        exerciseFields: [
3257            { key: 'tipo', label: '01 · Quem usa o sistema?', help: 'Liste os tipos de conta e o que cada pessoa precisa fazer.', placeholder: 'Aluno, professor, gestor...' },
3258            { key: 'conta', label: '02 · Como a conta é criada?', help: 'Descreva dados, confirmação e estados possíveis.', placeholder: 'A conta começa como...' },
3259            { key: 'login', label: '03 · Como o login funciona?', help: 'Informe entrada, validação, sucesso e falha.', placeholder: 'A pessoa informa... O servidor...' },
3260            { key: 'sessao', label: '04 · Quando o acesso começa e termina?', help: 'Defina sessão, logout, expiração e bloqueio.', placeholder: 'A sessão começa quando...' },
3261            { key: 'pedido', label: '05 · Escreva o pedido completo', help: 'Junte identidade, login, sessão, acesso e estados.', placeholder: 'Crie um fluxo de acesso que...' }
3262        ],
3263        exerciseModelAnswer: 'Exemplo didático: alunos, professores e gestores têm contas identificadas por e-mail. O cadastro cria uma conta pendente e envia confirmação. O login valida e-mail e senha no servidor; erros usam uma mensagem genérica. O sucesso cria sessão protegida e leva cada pessoa ao seu painel. Logout, expiração, troca de senha e bloqueio encerram a sessão. Cada painel mostra somente recursos do tipo e status da conta.',
3264        codigos: [
3265            { codigo: '/identidade-da-conta' },
3266            { codigo: '/login-com-fluxo' },
3267            { codigo: '/sessao-com-expiracao' },
3268            { codigo: '/erro-sem-vazamento' }
3269        ]
3270    },
3271    {
3272        stage: 'Aula 02',
3273        estimatedMinutes: 40,
3274        layout: 'access-flow',
3275        title: 'Sessão, cookies e tokens',
3276        goal: 'Entenda como o sistema continua reconhecendo uma pessoa depois do login e como pedir esse reconhecimento com duração e limites claros.',
3277        focus: 'Login confirma a identidade uma vez; sessão mantém o contexto. O pedido precisa dizer onde a credencial circula, quanto dura e como é invalidada.',
3278        practice: 'Compare uma área web e um aplicativo. Descreva qual mecanismo mantém o acesso em cada caso e o que deve acontecer quando a sessão expirar.',
3279        exerciseFeedback: 'Uma boa resposta diferencia cookie, token e sessão sem tratar nenhum mecanismo como solução universal. Ela define expiração, logout, renovação e falha.',
3280        lessons: [
3281            'Login confirma a identidade; sessão mantém o reconhecimento entre requisições.',
3282            'Cookie é um mecanismo comum para transportar a referência de uma sessão no navegador.',
3283            'Token é uma credencial que precisa de escopo, duração e proteção contra uso indevido.',
3284            'Logout, expiração, troca de senha e bloqueio precisam invalidar o acesso quando necessário.'
3285        ],
3286        diagramIntro: 'A pessoa apresenta uma credencial; o sistema cria um contexto de sessão;
3286 cada requisição é conferida antes de liberar o próximo passo.',
3287        diagramNodeLeft: 'Pessoa / dispositivo',
3288        diagramNodeRight: 'Aplicação',
3289        diagramCount: 3,
3290        diagramTraitsCaption: 'Regras que atravessam o fluxo · não são apenas detalhes técnicos',
3291        conceitos: [
3292            { titulo: 'Sessão', definicao: 'O contexto temporário que liga várias requisições à mesma pessoa depois de um login válido. A sessão permite navegar sem reenviar a senha a cada página.', exemplo: 'O aluno entra uma vez e consegue abrir painel, aulas e progresso enquanto a sessão continua válida.', confuso: 'Mantém logado para sempre.', claro: 'Crie uma sessão para o painel web com duração definida, renovação controlada e encerramento no logout, expiração, troca de senha ou bloqueio.', comoFunciona: ['Defina o evento que cria a sessão.', 'Escolha duração e renovação.', 'Associe a sessão à conta correta.', 'Valide a sessão em cada recurso protegido.', 'Invalide sessões quando houver mudança de risco.'], segundoExemplo: 'Um banco pode exigir nova confirmação depois de um período sem atividade ou antes de uma operação sensível.', errosComuns: ['Confundir sessão com senha.', 'Não definir quando a sessão expira.', 'Aceitar uma sessão antiga depois de bloquear a conta.'], termos: ['cookie', 'expiração', 'logout'] },
3293            { titulo: 'Cookie de sessão', definicao: 'Uma informação armazenada pelo navegador e enviada de volta ao site para ajudar a identificar a sessão. O cookie não deve carregar a senha nem virar uma credencial sem proteção.', exemplo: 'O navegador guarda uma referência protegida e a envia apenas ao domínio permitido.', confuso: 'Salva o login no navegador.', claro: 'Use cookie de sessão protegido pelo servidor, com escopo de domínio e caminho definidos, sem armazenar senha ou dados sensíveis em texto aberto.', comoFunciona: ['O servidor cria a sessão.', 'O navegador recebe apenas a referência necessária.', 'O cookie limita onde e quando pode ser enviado.', 'O servidor consulta a sessão antes de responder.', 'Logout remove ou invalida a referência.'], segundoExemplo: 'Uma preferência de idioma pode ser um cookie comum; uma sessão autenticada exige controles mais fortes.', errosComuns: ['Guardar senha no cookie.', 'Usar o mesmo cookie para sessão e preferências sem separar os riscos.', 'Achar que esconder o cookie substitui a validação no servidor.'], termos: ['HttpOnly', 'Secure', 'SameSite'] },
3294            { titulo: 'Token', definicao: 'Uma credencial que representa autorização para uma chamada ou conjunto de chamadas. Token não é sinônimo de sessão: ele precisa de escopo, duração, armazenamento e revogação definidos.', exemplo: 'Um aplicativo móvel recebe um token com acesso limitado às funções daquela conta.', confuso: 'Gera um token para acessar tudo.', claro: 'Defina quem recebe o token, quais rotas ele pode usar, quando expira, como é renovado e como pode ser revogado sem expor a credencial principal.', comoFunciona: ['Defina o consumidor da API.', 'Limite o escopo do token.', 'Escolha validade curta e renovação segura.', 'Evite colocar dados secretos no conteúdo visível.', 'Registre e revogue quando houver suspeita.'], segundoExemplo: 'Uma integração servidor a servidor pode usar uma chave própria com acesso somente à criação de pedidos.', errosComuns: ['Criar token sem validade.', 'Dar escopo administrativo por conveniência.', 'Colocar token em URL ou mensagem pública.'], termos: ['Bearer', 'escopo', 'revogação'] }
3295        ],
3296        scenario: { title: 'Cenário completo: painel web e aplicativo móvel', intro: 'A mesma escola terá um painel web para gestores e um aplicativo para alunos. O pedido “usa cookie no site e token no app” ainda não explica os limites.', steps: [
3297            { tag: 'Separar', text: 'Diga quem chama cada ambiente e qual tipo de sessão ou credencial faz sentido.' },
3298            { tag: 'Proteger', text: 'Defina onde a referência fica, por quanto tempo vale e quais controles reduzem exposição.' },
3299            { tag: 'Limitar', text: 'Especifique quais recursos cada sessão ou token pode usar.' },
3300            { tag: 'Encerrar', text: 'Descreva logout, expiração, troca de senha, bloqueio e revogação.' },
3301            { tag: 'Testar', text: 'Liste cenários de sessão válida, expirada, revogada, ausente e usada no recurso errado.' }
3302        ] },
3303        exerciseFields: [
3304            { key: 'ambientes', label: '01 · Quem chama o sistema?', help: 'Separe painel web, aplicativo e integrações externas.', placeholder: 'No painel web... No aplicativo...' },
3305            { key: 'mecanismo', label: '02 · Qual mecanismo mantém o acesso?', help: 'Explique quando usaria sessão por cookie ou token.', placeholder: 'Para o painel... Para a API...' },
3306            { key: 'limites', label: '03 · Quais são os limites?', help: 'Defina duração, escopo, renovação e armazenamento.', placeholder: 'A credencial vale por...' },
3307            { key: 'encerramento', label: '04 · Como o acesso termina?', help: 'Inclua logout, expiração, bloqueio e troca de senha.', placeholder: 'Quando acontecer..., o sistema...' },
3308            { key: 'testes', label: '05 · Como você vai conferir?', help: 'Liste casos válidos e inválidos para a sessão.', placeholder: 'Testarei uma sessão...' }
3309        ],
3310        exerciseModelAnswer: 'Exemplo didático: o painel web usa sessão por cookie protegido e o aplicativo usa token com escopo de aluno. O painel exige renovação controlada; o token expira em período curto e pode ser revogado. Nenhuma credencial fica em URL. Logout, troca de senha e bloqueio invalidam acessos ativos. Os testes cobrem sessão ausente, válida, expirada, revogada e usada para acessar recurso de outro usuário.',
3311        codigos: [
3312            { codigo: '/sessao-com-limite' },
3313            { codigo: '/cookie-sem-senha' },
3314            { codigo: '/token-com-escopo' }
3315        ]
3316    },
3317    {
3318        stage: 'Aula 03',
3319        estimatedMinutes: 45,
3320        layout: 'groups',
3321        title: 'Papéis e permissões',
3322        goal: 'Aprenda a separar quem a pessoa é do que ela pode fazer, transformando “cada um vê uma coisa” em regras de acesso verificáveis.',
3323        focus: 'Um papel organiza responsabilidades; uma permissão descreve uma ação sobre um recurso; a autorização decide se aquela ação pode acontecer agora.',
3324        practice: 'Escolha um painel conhecido e monte uma pequena matriz: pessoa, papel, recurso, ação permitida e motivo do bloqueio.',
3325        exerciseFeedback: 'Uma boa resposta não para em “admin pode tudo”. Ela nomeia recursos e ações, diferencia leitura de alteração e explica o que acontece quando a regra nega.',
3326        lessons: [
3327            'Papel agrupa responsabilidades; não é uma autorização genérica para tudo.',
3328            'Permissão descreve uma ação sobre um recurso, como visualizar, criar, editar ou excluir.',
3329            'Autorização verifica a regra no momento da ação, no servidor.',
3330            'A regra precisa prever permitido, negado, recurso inexistente e tentativa de acesso fora do escopo.'
3331        ],
3332        groups: [
3333            { title: 'Quem é a pessoa?', desc: 'Contexto e responsabilidade não são a mesma coisa que acesso.', note: 'Comece pelo papel, mas não use o nome do papel como atalho para “pode tudo”.' },
3334            { title: 'O que pode acontecer?', desc: 'Recurso e ação tornam a permissão observável.', note: 'Leia, crie, edite e exclua são ações diferentes, mesmo no mesmo recurso.' }
3335        ],
3336        conceitos: [
3337            { titulo: 'Papel', definicao: 'Um agrupamento de responsabilidades que ajuda a organizar pessoas com necessidades parecidas. Papel dá contexto, mas não deve substituir a lista de ações permitidas.', exemplo: 'Aluno, professor e gestor podem ter responsabilidades diferentes na mesma escola.', confuso: 'Cria um perfil de administrador que pode tudo.', claro: 'Defina os papéis aluno, professor e gestor. Para cada papel, liste recursos e ações permitidas; não conceda acesso total por padrão.', comoFunciona: ['Liste as pessoas ou contextos existentes.', 'Agrupe responsabilidades semelhantes.', 'Relacione cada papel aos recursos necessários.', 'Revise exceções antes de liberar acesso amplo.'], segundoExemplo: 'Uma pessoa pode ser professora em um curso e aluna em outro, então papel pode depender do contexto.', errosComuns: ['Criar apenas usuário comum e administrador.', 'Usar “admin” como solução para qualquer bloqueio.', 'Esquecer que uma pessoa pode ter mais de um papel.'], termos: ['função', 'contexto', 'responsabilidade'], grupo: 0 },
3338            { titulo: 'Permissão', definicao: 'A autorização nomeada para executar uma ação sobre um recurso. Ela deve ser específica o bastante para ser revisada e testada.', exemplo: 'A permissão “editar aula” é diferente de “visualizar aula” e de “publicar aula”.', confuso: 'Dá acesso ao curs
3338o.', claro: 'Crie permissões separadas para visualizar aluno, editar aluno, alterar matrícula, publicar aula e emitir certificado.', comoFunciona: ['Nomeie o recurso.', 'Nomeie a ação.', 'Defina o papel ou contexto que recebe a permissão.', 'Registre o que acontece quando a permissão falta.', 'Teste leitura e alteração separadamente.'], segundoExemplo: 'Um professor pode editar o conteúdo da própria aula, mas não alterar o preço do curso.', errosComuns: ['Tratar acesso como uma chave única.', 'Misturar visualizar, editar e excluir.', 'Conceder permissão por tela, sem proteger a ação correspondente.'], termos: ['recurso', 'ação', 'escopo'], grupo: 1 },
3339            { titulo: 'Autorização', definicao: 'A decisão tomada quando uma pessoa tenta executar uma ação. O sistema verifica identidade, papel, recurso, ação e contexto antes de permitir ou negar.', exemplo: 'O servidor recebe a tentativa de editar uma aula e confere se aquela pessoa pode fazer isso.', confuso: 'Se estiver logado, pode entrar.', claro: 'Em cada ação protegida, valide no servidor se a sessão pertence à pessoa certa e se o papel tem permissão para aquele recurso e contexto.', comoFunciona: ['Identifique quem está fazendo a ação.', 'Confirme que a sessão é válida.', 'Carregue papel, recurso e contexto.', 'Permita ou negue no servidor.', 'Registre a decisão quando a ação for sensível.'], segundoExemplo: 'Esconder o botão “Excluir” melhora a interface, mas a autorização precisa bloquear a requisição quando alguém tenta chamar a rota diretamente.', errosComuns: ['Confiar apenas em botão escondido.', 'Validar papel no navegador.', 'Retornar sucesso mesmo quando a ação foi negada.'], termos: ['decisão', 'servidor', 'negação'], grupo: 1 },
3340            { titulo: 'Matriz de acesso', definicao: 'Uma tabela que cruza pessoas ou papéis, recursos e ações para deixar visível quem pode fazer o quê.', exemplo: 'Aluno pode ver suas aulas; professor pode editar suas aulas; gestor pode administrar matrículas.', confuso: 'Cada perfil vê uma tela diferente.', claro: 'Monte uma matriz por papel, recurso e ação. Marque permitido, negado e condicionado; inclua exemplos de recursos pertencentes a outra pessoa.', comoFunciona: ['Liste papéis e c
3340ontextos.', 'Liste recursos reais.', 'Separe ações de leitura e alteração.', 'Marque condições como “somente seus alunos”.', 'Transforme cada linha crítica em cenário de teste.'], segundoExemplo: 'Dois professores podem editar aulas diferentes no mesmo curso porque a regra considera o dono do recurso.', errosComuns: ['Escrever matriz só com telas.', 'Não registrar condições e propriedade.', 'Não testar uma pessoa tentando acessar recurso de outra.'], termos: ['regra', 'propriedade', 'cenário'], grupo: 1 }
3341        ],
3342        scenario: { title: 'Cenário completo: painel de uma escola', intro: 'O gestor pede: “aluno vê suas aulas, professor cuida do conteúdo e gestor administra tudo”. Transforme isso em regras que podem ser conferidas.', steps: [
3343            { tag: 'Mapear', text: 'Liste papéis, recursos e ações reais: aulas, alunos, matrículas, certificados e preços.' },
3344            { tag: 'Separar', text: 'Diferencie visualizar, criar, editar, publicar, excluir e administrar.' },
3345            { tag: 'Condicionar', text: 'Inclua propriedade e contexto: professor edita suas aulas; aluno vê seus próprios dados.' },
3346            { tag: 'Decidir', text: 'Descreva a resposta para permissão negada e para recurso que não existe.' },
3347            { tag: 'Testar', text: 'Crie um caso permitido e um caso proibido para cada ação sensível.' }
3348        ] },
3349        exerciseFields: [
3350            { key: 'papeis', label: '01 · Quais papéis existem?', help: 'Liste responsabilidades sem usar “admin pode tudo” como resposta.', placeholder: 'Aluno..., professor..., gestor...' },
3351            { key: 'recursos', label: '02 · Quais recursos serão protegidos?', help: 'Use objetos reais do produto.', placeholder: 'Aula, matrícula, certificado...' },
3352            { key: 'acoes', label: '03 · Quais ações cada papel pode fazer?', help: 'Separe visualizar, criar, editar, publicar e excluir.', placeholder: 'Aluno pode visualizar...' },
3353            { key: 'condicoes', label: '04 · Quais condições limitam o acesso?', help: 'Inclua propriedade, curso, status ou contexto.', placeholder: 'Professor só pode...' },
3354            { key: 'testes', label: '05 · Como a autorização será testada?', help: 'Escreva um cenário permitido e um negado.', placeholder: 'Se um aluno tentar..., o sistema...' }
3355        ],
3356        exerciseModelAnswer: 'Exemplo didático: aluno pode visualizar suas aulas e progresso; professor pode visualizar alunos do curso e editar o conteúdo das próprias aulas; gestor pode administrar matrículas e publicar aulas. A autorização é verificada no servidor em cada ação. Um professor tentando editar aula de outro curso recebe acesso negado e a tentativa sensível é registrada.',
3357        codigos: [
3358            { codigo: '/papel-nao-e-acesso-total' },
3359            { codigo: '/recurso-e-acao' },
3360            { codigo: '/autoriza-no-servidor' }
3361        ]
3362    },
3363    {
3364        stage: 'Aula 04',
3365        estimatedMinutes: 45,
3366        layout: 'timeline',
3367        title: 'Dados sensíveis e privacidade',
3368        goal: 'Aprenda a pedir dados com propósito, proteção e prazo, reduzindo exposição sem transformar privacidade em uma promessa vaga.',
3369        focus: 'Dado pessoal não deve ser coletado por hábito. Defina finalidade, acesso, armazenamento, retenção e descarte antes de desenhar o cadastro.',
3370        practice: 'Escolha um fluxo de matrícula e classifique cada campo: necessário, opcional, sensível, quem acessa, por quanto tempo e como será removido.',
3371        exerciseFeedback: 'Uma boa resposta explica por que cada dado existe, limita quem consulta e define o que acontece quando a finalidade termina. “Guardar com segurança” sozinho não é uma regra testável.',
3372        lessons: [
3373            'Dado pessoal identifica ou pode tornar uma pessoa identificável; dado sensível exige cuidado ainda maior.',
3374            'Finalidade explica por que o sistema precisa de um campo e ajuda a evitar coleta excessiva.',
3375            'Minimização, acesso restrito e proteção reduzem o impacto de um vazamento.',
3376            'Retenção e descarte precisam ser definidos antes de o dado entrar no banco.'
3377        ],
3378        connectors: ['entender', 'limitar', 'proteger'],
3379        sequenceCount: 4,
3380        conceitos: [
3381            { titulo: 'Dado pessoal', definicao: 'Informação relacionada a uma pessoa identificada ou que pode ser identificada, sozinha ou combinada com outros dad
3381os.', exemplo: 'Nome, e-mail, telefone e identificador de matrícula podem apontar para uma pessoa dentro do sistema.', confuso: 'É só qualquer informação do cadastro.', claro: 'Liste quais campos identificam a pessoa, por que cada um é necessário e quem poderá consultá-los.', comoFunciona: ['Liste os campos coletados.', 'Verifique se cada campo identifica alguém.', 'Relacione o campo a uma finalidade.', 'Defina acesso e retenção.', 'Revise se é possível remover ou reduzir o dado.'], segundoExemplo: 'Um ID interno pode não ter significado fora do sistema, mas junto com a tabela de alunos ainda identifica uma pessoa.', errosComuns: ['Coletar dados “para usar depois”.', 'Confundir dado público com dado sem risco.', 'Espalhar a mesma informação em vários lugares sem necessidade.'], termos: ['identificação', 'cadastro', 'titular'] },
3382            { titulo: 'Dado sensível', definicao: 'Categoria de dado pessoal que pode causar impacto maior se for exposta ou usada de forma inadequada, como saúde, biometria, religião ou origem racial.', exemplo: 'Uma informação de saúde usada para adaptação de uma atividade precisa de finalidade e acesso muito bem definidos.', confuso: 'É um dado importante para o negócio.', claro: 'Separe dados sensíveis dos demais, documente a finalidade, restrinja o acesso e evite colocá-los em logs, URLs ou mensagens.', comoFunciona: ['Identifique se o campo pertence a uma categoria sensível.', 'Defina a necessidade real da coleta.', 'Restrinja leitura e exportação.', 'Proteja armazenamento e trânsito.', 'Registre consultas e descarte quando a finalidade acabar.'], segundoExemplo: 'Um relatório pode mostrar apenas que uma adaptação é necessária, sem exibir o diagnóstico para quem não precisa conhecê-lo.', errosComuns: ['Gravar dado sensível em log.', 'Mostrar a informação para toda a equipe.', 'Usar dado sensível como filtro ou exemplo fictício em ambiente real.'], termos: ['categoria especial', 'restrição', 'exposição'] },
3383            { titulo: 'Finalidade e minimização', definicao: 'A regra de coletar somente o que é necessário para uma finalidade clara e comunicada.', exemplo: 'Para enviar um aviso de aula, o sistema precisa de um canal de contato; não precisa guardar todos os dados da família.', confuso: 'Pede todos os dados para ficar completo.', claro: 'Para cada campo, escreva a finalidade, o uso esperado e o que acontece se ele não for preenchido.', comoFunciona: ['Descreva o resultado que o fluxo precisa produzir.', 'Liste os dados indispensáveis.', 'Remova campos sem uso definido.', 'Separe o obrigatório do opcional.', 'Revise a coleta quando o produto mudar.'], segundoExemplo: 'Uma compra pode precisar do endereço de entrega, mas não da data de nascimento se ela não muda preço, entrega ou segurança.', errosComuns: ['Manter campo obrigatório por conveniência.', 'Copiar dados entre formulários sem revisar necessidade.', 'Usar a mesma finalidade para justificar qualquer coleta.'], termos: ['necessidade', 'transparência', 'coleta'] },
3384            { titulo: 'Retenção e descarte', definicao: 'A regra que define por quanto tempo um dado permanece armazenado e como deixa de estar acessível quando a finalidade termina.', exemplo: 'Um documento usado para confirmar um cadastro pode ter prazo de guarda e depois ser removido ou anonimizado.', confuso: 'Nunca apaga porque pode ser útil.', claro: 'Defina prazo, responsável e ação de encerramento: excluir, anonimizar, bloquear ou manter somente o registro exigido.', comoFunciona: ['Associe prazo a cada finalidade.', 'Identifique registros que precisam ser preservados.', 'Automatize ou agende a revisão.', 'Remova cópias, arquivos e índices relacionados.', 'Registre a ação sem guardar o conteúdo descartado.'], segundoExemplo: 'Depois do encerramento de uma conta, o sistema pode bloquear o acesso, apagar dados de contato e manter apenas o registro financeiro obrigatório.', errosComuns: ['Definir prazo apenas no documento, sem rotina técnica.', 'Esquecer anexos e cópias de backup.', 'Apagar o registro de que uma ação ocorreu junto com o dado original.'], termos: ['prazo de guarda', 'anonimização', 'eliminação'] }
3385        ],
3386        scenario: { title: 'Cenário completo: matrícula em uma escola', intro: 'A escola pede nome, e-mail, telefone, documento, endereço e informações de saúde. Transforme esse cadastro em regras de necessidade, acesso e prazo.', steps: [
3387            { tag: 'Classificar', text: 'Separe dados de identificação, contato, cobrança e informações sensíveis.' },
3388            { tag: 'Justificar', text: 'Escreva a finalidade de ca
3388da campo e remova o que não tiver uso definido.' },
3389            { tag: 'Restringir', text: 'Defina quais papéis podem consultar, editar, exportar ou apenas receber um resultado resumido.' },
3390            { tag: 'Proteger', text: 'Indique onde o dado não pode aparecer: URL, log, mensagem, planilha aberta ou resposta excessiva.' },
3391            { tag: 'Encerrar', text: 'Defina prazo de retenção e a ação quando a matrícula ou a finalidade terminar.' }
3392        ] },
3393        exerciseFields: [
3394            { key: 'dados', label: '01 · Quais dados entram?', help: 'Liste somente campos necessários e marque os sensíveis.', placeholder: 'Nome..., e-mail..., informação sensível...' },
3395            { key: 'finalidade', label: '02 · Para que cada dado serve?', help: 'Relacione campo e resultado que ele permite produzir.', placeholder: 'O e-mail serve para...' },
3396            { key: 'acesso', label: '03 · Quem pode consultar?', help: 'Defina leitura, edição, exportação e resumo por papel.', placeholder: 'Aluno..., professor..., gestor...' },
3397            { key: 'protecao', label: '04 · Onde o dado não pode aparecer?', help: 'Inclua logs, URLs, mensagens e telas.', placeholder: 'Nunca exibiremos...' },
3398            { key: 'retencao', label: '05 · Quando o dado deixa de existir?', help: 'Defina prazo e ação: excluir, anonimizar ou bloquear.', placeholder: 'Ao terminar..., o sistema...' }
3399        ],
3400        exerciseModelAnswer: 'Exemplo didático: a matrícula coleta nome e e-mail para identificar e comunicar a pessoa; endereço só entra se houver entrega ou exigência operacional; informação de saúde fica separada, com acesso restrito e sem aparecer em logs ou exportações gerais. Cada finalidade tem prazo definido. Ao encerrar a matrícula, o sistema bloqueia o acesso, remove dados sem obrigação de guarda e registra a ação sem manter o conteúdo descartado.',
3401        codigos: [
3402            { codigo: '/finalidade-antes-do-campo' },
3403            { codigo: '/dado-sensivel-restrito' },
3404            { codigo: '/prazo-e-descarte' }
3405        ]
3406    },
3407    {
3408        stage: 'Aula 05',
3409        estimatedMinutes: 45,
3410        layout: 'timeline',
3411        title: 'Recuperação de acesso, expiração e bloqueio',
3412        goal: 'Aprenda a desenhar uma recuperação de acesso que ajude a pessoa sem criar uma nova porta de entrada para invasões.',
3413        focus: 'Recuperar uma conta exige provar controle de um canal, limitar tentativas, expirar o pedido e encerrar acessos antigos quando a credencial muda.',
3414        practice: 'Desenhe o fluxo “esqueci minha senha” de uma plataforma: início, envio, validação, troca, expiração, bloqueio e mensagem apresentada em cada etapa.',
3415        exerciseFeedback: 'Uma boa resposta não envia senha por e-mail nem aceita código infinito. Ela define validade, uso único, limite de tentativas, resposta sem vazamento e invalidação das sessões anteriores.',
3416        lessons: [
3417            'Recuperação não confirma a senha antiga: ela cria uma prova temporária de controle de um canal confiável.',
3418            'Código e link de recuperação precisam ter validade curta, uso único e limite de tentativas.',
3419            'Expiração encerra uma oportunidade; bloqueio reduz tentativas abusivas e precisa de caminho de retorno.',
3420            'Depois de trocar a credencial, sessões antigas e pedidos de recuperação devem ser revogados quando o risco exigir.'
3421        ],
3422        connectors: ['iniciar', 'validar', 'encerrar'],
3423        sequenceCount: 4,
3424        conceitos: [
3425            { titulo: 'Recuperação de acesso', definicao: 'Fluxo que permite à pessoa retomar uma conta sem conhecer a credencial anterior, depois de uma verificação temporária e controlada.', exemplo: 'A pessoa informa o e-mail, recebe um link de uso único e cria uma nova senha dentro do prazo.', confuso: 'Manda a senha por e-mail.', claro: 'Crie recuperação por link ou código temporário, sem revelar se a conta existe e sem enviar a senha anterior.', comoFunciona: ['Receba o identificador sem confirmar a existência da conta.', 'Envie a instrução pelo canal cadastrado.', 'Valide o segredo temporário.', 'Permita definir nova credencial.', 'Encerre o pedido e revise sessões ativas.'], segundoExemplo: 'O suporte pode iniciar uma recuperação assistida, mas precisa registrar a decisão e exigir uma verificação equivalente.', errosComuns: ['
3425Enviar a senha atual.', 'Mostrar “e-mail não cadastrado”.', 'Permitir recuperação sem prazo ou sem registro.'], termos: ['canal confiável', 'credencial', 'verificação'] },
3426            { titulo: 'Código de uso único', definicao: 'Segredo temporário que pode ser aceito uma única vez para confirmar uma etapa do fluxo.', exemplo: 'Um código de seis dígitos chega ao e-mail e deixa de funcionar depois do primeiro uso ou do vencimento.', confuso: 'Gera um código que vale até a pessoa usar quando quiser.', claro: 'Gere um código aleatório, com validade curta, uso único, limite de tentativas e armazenamento que não revele o valor original.', comoFunciona: ['Gere o código no servidor.', 'Associe-o à conta e à finalidade.', 'Defina expiração e tentativas.', 'Compare com proteção contra repetição.', 'Marque como usado e invalide códigos anteriores.'], segundoExemplo: 'Um código para trocar senha não deve funcionar para confirmar uma compra ou alterar o e-mail.', errosComuns: ['Reutilizar o mesmo código.', 'Colocar o código na URL por tempo indefinido.', 'Não invalidar o código após uma tentativa bem-sucedida.'], termos: ['uso único', 'tentativa', 'expiração'] },
3427            { titulo: 'Expiração', definicao: 'Momento em que um link, código, sessão ou autorização deixa de ser aceito porque o prazo definido terminou.', exemplo: 'Um link de recuperação enviado há uma hora mostra que expirou e oferece iniciar o processo novamente.', confuso: 'O sistema deixa de funcionar do nada.', claro: 'Defina duração, relógio usado pelo servidor, mensagem de retorno e o caminho para solicitar uma nova tentativa.', comoFunciona: ['Defina o prazo no servidor.', 'Compare a validade em toda tentativa.', 'Não aceite o item vencido mesmo que o valor esteja correto.', 'Mostre uma recuperação clara.', 'Registre vencimento sem expor o segredo.'], segundoExemplo: 'Uma sessão pode expirar por tempo sem atividade, enquanto um código de recuperação expira por prazo fixo.', errosComuns: ['Confiar no relógio do navegador.', 'Manter link válido depois de trocar a senha.', 'Usar a mesma mensagem para erro interno e item expirado.'], termos: ['prazo', 'tempo sem atividade', 'renovação'] },
3428            { titulo: 'Bloqueio e desbloqueio', definicao: 'Regra que interrompe novas tentativas quando há sinais de abuso ou risco, junto de um caminho controlado para retomar o acesso.', exemplo: 'Após várias tentativas inválidas, o fluxo reduz a velocidade e orienta uma recuperação segura.', confuso: 'Bloqueia para sempre quando errar.', claro: 'Defina o limite, a duração ou revisão do bloqueio, a mensagem genérica e o processo de desbloqueio sem permitir contornar a proteção.', comoFunciona: ['Conte tentativas no servidor.', 'Aplique atraso, limite ou bloqueio proporcional.', 'Evite informar qual parte da credencial falhou.', 'Ofereça recuperação independente do login.', 'Registre e revise bloqueios suspeitos.'], segundoExemplo: 'Uma conta pode exigir confirmação adicional após muitas tentativas, sem revelar ao atacante se o e-mail existe.', errosComuns: ['Bloquear por um único erro.', 'Permitir desbloqueio por pergunta fácil.', 'Deixar o suporte reativar a conta sem registrar a decisão.'], termos: ['limite', 'atraso progressivo', 'desbloqueio'] }
3429        ],
3430        scenario: { title: 'Cenário completo: “esqueci minha senha”', intro: 'Uma pessoa perdeu acesso ao e-mail usado no cadastro e pede um botão para recuperar a conta. Transforme esse pedido em um fluxo seguro e compreensível.', steps: [
3431            { tag: 'Iniciar', text: 'Receba o identificador sem confirmar se existe uma conta e explique o próximo passo.' },
3432            { tag: 'Enviar', text: 'Use o canal confiável cadastrado e informe prazo, uso único e o que fazer se a mensagem não chegar.' },
3433            { tag: 'Validar', text: 'Confira o código no servidor, limite tentativas e descarte pedidos anteriores quando necessário.' },
3434            { tag: 'Trocar', text: 'Permita uma nova senha com regras claras e não reutilize a credencial anterior.' },
3435            { tag: 'Encerrar', text: 'Invalide o link, códigos e sessões antigas; registre o evento sem expor o segredo.' }
3436        ] },
3437        exerciseFields: [
3438            { key: 'inicio', label: '01 · Como a recuperação começa?', help: 'Defina o identificador, a mensagem e o que não será revelado.', placeholder: 'A pessoa informa..., e o sistema responde...' },
3439            { key: 'canal', label: '02 · Qual canal confirma o controle?', help: 'Explique por onde o link ou código chega e quais limites existem.', placeholder: 'O código é enviado para...' },
3440            { key: 'validade', label: '03 · Qual validade e limite?', help: 'Defina prazo, uso único e tentativas.', placeholder: 'O código vale por..., com...' },
3441            { key: 'bloqueio', label: '04 · Quando bloquear ou atrasar?', help: 'Descreva o comportamento após tentativas suspeitas.', placeholder: 'Depois de..., o sistema...' },
3442            { key: 'encerramento', label: '05 · O que é invalidado depois?', help: 'Inclua sessões, códigos, links e registros.', placeholder: 'Ao trocar a senha..., o sistema...' }
3443        ],
3444        exerciseModelAnswer: 'Exemplo didático: a pessoa informa o e-mail e recebe a mesma resposta exista ou não uma conta. O servidor gera um código de uso único enviado ao canal cadastrado, válido por 15 minutos e com cinco tentativas. Após a troca da senha, o código e pedidos anteriores são invali
3444dados, sessões antigas são encerradas e o evento é registrado sem guardar o segredo. Tentativas excessivas recebem atraso progressivo e um caminho de recuperação controlado.',
3445        codigos: [
3446            { codigo: '/recuperacao-sem-vazamento' },
3447            { codigo: '/codigo-com-validade' },
3448            { codigo: '/troca-encerra-sessoes' }
3449        ]
3450    },
3451    {
3452        stage: 'Aula 06',
3453        estimatedMinutes: 45,
3454        layout: 'timeline',
3455        title: 'Auditoria, histórico e rastreabilidade',
3456        goal: 'Aprenda a pedir registros que expliquem o que aconteceu sem transformar logs em um depósito de dados sensíveis.',
3457        focus: 'Um registro útil conecta pessoa, ação, recurso, momento e resultado. Ele precisa ser protegido, pesquisável e proporcional ao risco.',
3458        practice: 'Escolha uma ação sensível — alterar permissão, trocar senha ou exportar dados — e defina quais eventos serão registrados e revisados.',
3459        exerciseFeedback: 'Uma boa resposta registra contexto suficiente para investigar, evita senha e token, diferencia evento técnico de decisão de negócio e define quem consulta o histórico.',
3460        lessons: [
3461            'Auditoria responde perguntas sobre uma ação: quem fez, o quê, em qual recurso, quando e com qual resultado.',
3462            'Histórico de negócio ajuda a pessoa a entender mudanças; log técnico ajuda a equipe a investigar falhas.',
3463            'O registro precisa proteger dados sensíveis, evitar segredos e manter integridade 
3463contra alteração silenciosa.',
3464            'Rastreabilidade depende de busca, retenção, correlação e procedimento de revisão — não só de gravar linhas.'
3465        ],
3466        connectors: ['registrar', 'proteger', 'investigar'],
3467        sequenceCount: 4,
3468        conceitos: [
3469            { titulo: 'Evento de auditoria', definicao: 'Registro estruturado de uma ação relevante, com contexto suficiente para entender quem tentou fazer o quê e qual foi o resultado.', exemplo: 'Uma alteração de permissão registra a conta, o recurso afetado, o horário, a origem e se foi aceita ou negada.', confuso: 'Salva tudo que acontece na tela.', claro: 'Defina eventos de risco, campos obrigatórios, resultado e regra de acesso ao histórico.', comoFunciona: ['Escolha ações que merecem registro.', 'Capture ator, recurso, ação, horário e resultado.', 'Associe uma referência de correlação.', 'Proteja o registro contra alteração indevida.', 'Defina consulta e retenção.'], segundoExemplo: 'Uma tentativa de exportar dados pode gerar evento mesmo quando a autorização nega a ação.', errosComuns: ['Registrar somente sucesso.', 'Gravar texto solto sem contexto.', 'Colocar senha ou token no evento.'], termos: ['ator', 'recurso', 'resultado'] },
3470            { titulo: 'Histórico de mudanças', definicao: 'Visão compreensível das alterações feitas em um item ao longo do tempo, com autor e momento de cada mudança.', exemplo: 'O histórico de uma matrícula mostra quem alterou o status de pendente para ativa e quando.', confuso: 'Mostra os logs do sistema para todo mundo.', claro: 'Apresente mudanças relevantes em linguagem clara, limite a visualização por permissão e diferencie valor antigo, novo e motivo.', comoFunciona: ['Escolha o objeto que terá histórico.', 'Registre campo, valor anterior e novo quando necessário.', 'Mostre autor e data no fuso definido.', 'Separe detalhe técnico de resumo para o usuário.', 'Permita consultar sem editar o passado.'], segundoExemplo: 'Um aluno vê que sua matrícula foi atualizada; somente o gestor autorizado acessa o contexto técnico completo.', errosComuns: ['Usar histórico como lixeira de dados.', 'Permitir editar o passado sem deixar evidência.', 'Exibir informação sensível para quem só precisa do status.'], termos: ['valor anterior', 'motivo', 'linha do tempo'] },
3471            { titulo: 'Integridade do registro', definicao: 'Garantia de que um evento não foi alterado ou removido sem que isso seja detectável.', exemplo: 'Um registro de auditoria protegido mantém a evidência mesmo quando alguém com acesso administrativo consulta o painel.', confuso: 'Coloca o log em uma tabela diferente.', claro: 'Defina quem pode escrever, consultar e administrar registros, como alterações são detectadas e qual cópia protegida existe.', comoFunciona: ['Restrinja escrita e exclusão.', 'Separe operação de consulta e administração.', 'Use encadeamento, assinatura ou armazenamento protegido quando necessário.', 'Monitore falhas de gravação.', 'Teste tentativa de alteração e remoção.'], segundoExemplo: 'Uma exportação periódica para armazenamento somente leitura ajuda a comparar o histórico original com a base operacional.', errosComuns: ['Confiar no mesmo administrador para apagar e investigar.', 'Não alertar quando o registro falha.', 'Confundir backup com prova de integridade.'], termos: ['imutabilidade', 'integridade', 'armazenamento protegido'] },
3472            { titulo: 'Rastreabilidade', definicao: 'Capacidade de reconstruir a sequência de eventos de uma ação ou incidente, ligando requisições, decisões e efeitos.', exemplo: 'Um identificador de correlação conecta o pedido de troca de senha, o envio do código e a criação da nova sessão.', confuso: 'Tem um log para cada tela.', claro: 'Use um identificador de correlação, relógio consistente, contexto mínimo e consulta que permita seguir o fluxo completo.', comoFunciona: ['Gere uma referência por operação.', 'Propague-a entre serviços e tarefas.', 'Registre início, decisão e efeito.', 'Concentre eventos relacionados numa busca.', 'Documente como investigar o caso.'], segundoExemplo: 'Uma compra pode ser ligada ao webhook, à matrícula criada e ao e-mail enviado sem guardar o conteúdo completo de cada mensagem.', errosComuns: ['Usar somente o ID da pessoa.', 'Não sincronizar horário entre serviços.', 'Registrar eventos sem indicar se a ação terminou.'], termos: ['correlação', 'sequência', 'investigação'] }
3473        ],
3474        scenario: { title: 'Cenário completo: alteração de permissão', intro: 'Um gestor altera o papel de uma pessoa e depois surge uma dúvida sobre quem autorizou a mudança. Transforme o pedido em uma trilha investigável.', steps: [
3475            { tag: 'Definir', text: 'Escolha o evento e os campos mínimos: ator, ação, recurso, contexto, horário e resultado.' },
3476            { tag: 'Registrar', text: 'Grave a decisão permitida ou negada sem incluir senha, token ou conteúdo desnecessário.' },
3477            { tag: 'Proteger', text: 'Restrinja escrita, consulta e remoção; defina como detectar alteração indevida.' },
3478            { tag: 'Relacionar', text: 'Conecte a alteração a pedido, tela, API e operação por um identificador de correlação.' },
3479            { tag: 'Investigar', text: 'Descreva como uma pessoa autorizada encontra a sequência e qual ação toma ao detectar anomalia.' }
3480        ] },
3481        exerciseFields: [
3482            { key: 'evento', label: '01 · Qual ação será auditada?', help: 'Escolha uma ação sensível e diga por que ela precisa de registro.', placeholder: 'Vamos registrar quando...' },
3483            { key: 'campos', label: '02 · Quais campos entram?', help: 'Inclua ator, ação, recurso, horário e resultado.', placeholder: 'O evento terá...' },
3484            { key: 'protecao', label: '03 · Como o registro é protegido?', help: 'Defina escrita, consulta, remoção e dados proibidos.', placeholder: 'Somente..., nunca...' },
3485            { key: 'correlacao', label: '04 · Como seguir a sequência?', help: 'Explique correlação entre serviços e etapas.', placeholder: 'Usaremos o identificador...' },
3486            { key: 'revisao', label: '05 · Quem revisa e quando?', help: 'Defina acesso, retenção e resposta a anomalias.', placeholder: 'A revisão acontece...' }
3487        ],
3488        exerciseModelAnswer: 'Exemplo didático: cada alteração de permissão registra o ator, a ação, o recurso, o contexto, data e hora, origem, resultado e identificador de correlação. Senhas, tokens e conteúdo sensível ficam fora. A aplicação grava; a equipe autorizada consulta; remoções são bloqueadas ou deixam evidência. Uma busca por correlação reconstrói pedido, decisão e efeito, e anomalias geram revisão.',
3489        codigos: [
3490            { codigo: '/auditoria-com-contexto' },
3491            { codigo: '/historico-legivel' },
3492            { codigo: '/rastreia-por-correlacao' }
3493        ]
3494    },
3495    {
3496        stage: 'Aula 07',
3497        estimatedMinutes: 45,
3498        layout: 'groups',
3499        title: 'Riscos comuns em pedidos de sistema',
3500        goal: 'Aprenda a reconhecer pedidos aparentemente simples que podem abrir brechas de acesso, expor dados ou enfraquecer uma conta.',
3501        focus: 'Troque frases vagas como “deixa público” ou “facilita o login” por regras verificáveis de proteção, escopo e recuperação.',
3502        practice: 'Escolha uma tela ou fluxo e faça uma revisão defensiva: quem acessa, que dado aparece, que credencial é exigida e qual abuso precisa ser bloqueado.',
3503        exerciseFeedback: 'Uma boa resposta aponta o risco, o comportamento seguro, o teste que comprova a proteção e a mensagem que a pessoa receberá.',
3504        lessons: [
3505            'Acesso indevido acontece quando a pessoa consegue uma ação ou recurso fora do papel, propriedade ou contexto permitido.',
3506            'Vazamento pode nascer de resposta, log, URL, exportação, mensagem ou ambiente mal separado.',
3507            'Senha forte ajuda, mas proteção também depende de limite, sessão, recuperação e bloqueio.',
3508            'Link público precisa de escopo, prazo, revogação e decisão explícita sobre o que pode ser visto.'
3509        ],
3510        groups: [
3511            { title: 'Onde o risco aparece?', desc: 'Pedidos de tela, API, cadastro e compartilhamento podem esconder consequências.', note: 'Leia a frase e procure quem ganha acesso, por quanto tempo e a qual dado.' },
3512            { title: 'Como provar a proteção?', desc: 'Toda regra precisa de cenário permitido, negado e de recuperação.', note: 'Se não dá para testar, ainda está vaga.' }
3513        ],
3514        conceitos: [
3515            { titulo: 'Acesso indevido', definicao: 'Uso de uma ação ou recurso por alguém que não deveria ter aquela permissão, propriedade ou condição.', exemplo: 'Um aluno altera a matrícula de outra pessoa trocando o ID na URL.', confuso: 'Esconde o botão para quem não pode.', claro: 'Valide no servidor identidade, papel, recurso e contexto; crie testes com recurso próprio e de outra pessoa.', comoFunciona: ['Liste o recurso protegido.', 'Defina dono, papel e condição.', 'Valide a regra no servidor.', 'Retorne negação sem vazar dados.', 'Teste acesso direto e variação de identificador.'], segundoExemplo: 'Um professor pode editar sua aula, mas recebe negação ao enviar uma requisição para a aula de outro curso.', errosComuns: ['Confiar na tela.', 'Usar IDs previsíveis sem checagem.', 'Responder dados do recurso antes de autorizar.'], termos: ['controle de acesso', 'propriedade', 'negação'] , grupo: 0 },
3516            { titulo: 'Vazamento de dados', definicao: 'Exposição de informação para pessoas, sistemas ou ambientes que não deveriam recebê-la.', exemplo: 'Uma resposta de API envia endereço e telefone mesmo quando a tela só precisava do nome.', confuso: 'Só existe vazamento quando alguém invade.', claro: 'Retorne o mínimo necessário, remova segredos de logs e controle exportações, URLs, mensagens e ambientes.', comoFunciona: ['Mapeie onde o dado aparece.', 'Reduza respostas ao necessário.', 'Separe ambientes e acessos.', 'Proteja logs e arquivos.', 'Teste usuário, suporte e exportação.'], segundoExemplo: 'Um erro detalhado exibido no navegador pode revelar tabela, caminho de arquivo ou credencial mesmo sem invasão.', errosComuns: ['
3516Enviar objeto completo por conveniência.', 'Colocar token em URL.', 'Usar dados reais em ambiente de teste.'], termos: ['exposição', 'resposta mínima', 'ambiente de teste'], grupo: 0 },
3517            { titulo: 'Senha fraca', definicao: 'Credencial fácil de adivinhar, reutilizada ou protegida por regras insuficientes para o risco do sistema.', exemplo: 'Uma senha curta e repetida em vários serviços amplia o impacto de um vazamento externo.', confuso: 'Pede uma senha com símbolo e está seguro.', claro: 'Defina tamanho mínimo, bloqueio de tentativas, recuperação segura, armazenamento protegido e orientação contra reutilização.', comoFunciona: ['Defina política proporcional ao risco.', 'Armazene somente representação protegida.', 'Limite tentativas e monitore abuso.', 'Ofereça troca e recuperação controladas.', 'Exija nova senha quando houver suspeita.'], segundoExemplo: 'Para uma ação sensível, a pessoa pode precisar confirmar a senha ou usar uma etapa adicional mesmo com sessão válida.', errosComuns: ['Obrigar troca frequente sem motivo.', 'Guardar senha reversível.', 'Confundir complexidade visual com resistência real.'], termos: ['força da senha', 'reutilização', 'tentativa'] , grupo: 1 },
3518            { titulo: 'Link público sem controle', definicao: 'Endereço compartilhável que permite acesso além do escopo, prazo ou pessoa esperada.', exemplo: 'Um link de certificado pode ser público; um link de documento pessoal precisa de expiração ou autenticação.', confuso: 'Gera um link para qualquer pessoa ver.', claro: 'Defina conteúdo, público, validade, revogação e resposta quando o link for usado fora do contexto.', comoFunciona: ['Escolha o que o link representa.', 'Limite escopo e duração.', 'Evite dados excessivos na URL.', 'Permita revogar ou invalidar.', 'Registre uso e teste compartilhamento indevido.'], segundoExemplo: 'Um convite para uma turma pode expirar depois do aceite e não deve revelar a lista de alunos para quem recebeu o endereço.', errosComuns: ['Usar ID secreto como única proteção.', 'Nunca expirar link.', 'Deixar o conteúdo acessível depois de revogar.'], termos: ['escopo', 'revogação', 'compartilhamento'], grupo: 1 }
3519        ],
3520        scenario: { title: 'Cenário completo: compartilhar um certificado', intro: 'O produto quer permitir que uma pessoa compartilhe um certificado por link. Transforme a ideia em uma decisão segura e testável.', steps: [
3521            { tag: 'Definir', text: 'Diga exatamente qual conteúdo aparece e para quem o link serve.' },
3522            { tag: 'Limitar', text: 'Escolha escopo, prazo, revogação e se haverá autenticação.' },
3523            { tag: 'Reduzir', text: 'Remova dados que não ajudam a verificar o certificado.' },
3524            { tag: 'Testar', text: 'Confira link válido, expirado, revogado, alterado e compartilhado com outra pessoa.' },
3525            { tag: 'Responder', text: 'Defina mensagem clara para acesso negado ou conteúdo indisponível sem revelar detalhes.' }
3526        ] },
3527        exerciseFields: [
3528            { key: 'risco', label: '01 · Qual risco está presente?', help: 'Escolha acesso indevido, vazamento, senha fraca ou link sem controle.', placeholder: 'O risco principal é...' },
3529            { key: 'regra', label: '02 · Qual regra protege?', help: 'Escreva o comportamento esperado do servidor.', placeholder: 'O sistema deve...' },
3530            { key: 'dados', label: '03 · O que pode aparecer?', help: 'Liste o mínimo necessário e o que deve ficar oculto.', placeholder: 'Será exibido..., nunca...' },
3531            { key: 'testes', label: '04 · Quais testes provam?', help: 'Inclua permitido, negado, expirado e revogado quando fizer sentido.', placeholder: 'Testarei quando...' },
3532            { key: 'recuperacao', label: '05 · Como a pessoa se recupera?', help: 'Defina mensagem e próximo passo sem vazamento.', placeholder: 'Se for negado ou expirado...' }
3533        ],
3534        exerciseModelAnswer: 'Exemplo didático: o link mostra somente nome, curso e autenticidade do certificado. Ele tem escopo de leitura, prazo definido e pode ser revogado. A API não retorna dados de contato nem aceita alteração pelo link. Os testes cobrem link válido, expirado, revogado, ID alterado e tentativa de acesso a outro re
3534curso. A mensagem informa que o conteúdo não está disponível e orienta procurar o dono ou o suporte.',
3535        codigos: [
3536            { codigo: '/teste-acesso-fora-do-escopo' },
3537            { codigo: '/resposta-minima' },
3538            { codigo: '/link-com-prazo' }
3539        ]
3540    },
3541    {
3542        stage: 'Aula 08',
3543        estimatedMinutes: 55,
3544        layout: 'timeline',
3545        title: 'Projeto final: revisão defensiva de um fluxo',
3546        goal: 'Consolide o curso transformando um pedido de sistema em regras de acesso, proteção, testes e evidências que outra pessoa consiga revisar.',
3547        focus: 'O projeto final não é desenhar uma tela bonita: é demonstrar quem entra, o que pode fazer, quais dados aparecem, como o acesso termina e como provar que as regras funcionam.',
3548        practice: 'Escolha um fluxo de escola, loja ou equipe e entregue uma ficha defensiva com atores, estados, recursos, riscos, testes e decisões pendentes.',
3549        exerciseFeedback: 'Uma entrega completa conecta regra e evidência: cada decisão tem um cenário permitido, um negado ou expirado, uma mensagem segura e um registro de auditoria quando necessário.',
3550        lessons: [
3551            'Comece pelo fluxo e pelas pessoas: identidade, sessão, papéis e recursos vêm antes da interface.',
3552            'Para cada ação, defina permissão, contexto, dado mínimo e resposta quando a regra negar.',
3553            'Inclua ciclo de vida: recuperação, expiração, bloqueio, revogação, retenção e descarte.',
3554            'Feche com checklist, evidências e decisões que ainda precisam de produto, jurídico ou operação.'
3555        ],
3556        connectors: ['mapear', 'proteger', 'provar'],
3557        sequenceCount: 4,
3558        conceitos: [
3559            { titulo: 'Checklist defensivo', definicao: 'Lista de perguntas verificáveis que percorre um fluxo para encontrar riscos antes ou depois de uma mudança.', exemplo: 'O checklist pergunta se uma pessoa sem sessão recebe negação, se outro aluno acessa o recurso e se o evento sensível fica registrado.', confuso: 'Confere segurança geral.', claro: 'Monte perguntas por identidade, sessão, autorização, dados, recuperação, auditoria e compartilhamento; marque evidência e pendência.', comoFunciona: ['Divida o fluxo em etapas.', 'Transforme cada risco em pergunta.', 'Defina resultado esperado.', 'Anexe evidência ou cenário de teste.', 'Registre pendências e responsável.'], segundoExemplo: 'Depois de publicar um link de certificado, o checklist verifica conteúdo, prazo, revogação, dados expostos e acesso fora do escopo.', errosComuns: ['Escrever perguntas vagas.', 'Marcar tudo como ok sem evidência.', 'Usar checklist só uma vez.'], termos: ['verificação', 'evidência', 'pendência'] },
3560            { titulo: 'Critério de aceite', definicao: 'Condição observável que permite decidir se uma regra ou entrega está correta.', exemplo: '“Aluno não consegue editar a aula” é observável quando o teste chama a rota e recebe negação sem alterar o dado.', confuso: 'Quando estiver seguro, está pronto.', claro: 'Escreva critérios com pessoa, ação, recurso, condição e resposta esperada; inclua casos de erro e recuperação.', comoFunciona: ['Descreva o cenário.', 'Defina a ação.', 'Declare o resultado esperado.', 'Escolha evidência.', 'Revise com quem aprova a entrega.'], segundoExemplo: 'Um link expirado deve negar acesso, não revelar o conteúdo e orientar uma nova solicitação quando fizer sentido.', errosComuns: ['Usar adjetivos sem teste.', 'Cobrir só o caminho feliz.', 'Aceitar tela sem validar API e dados.'], termos: ['aceite', 'cenário', 'resultado esperado'] },
3561            { titulo: 'Plano de correção', definicao: 'Sequência priorizada de ações para reduzir um risco, com responsável, impacto, prazo e forma de verificar o resultado.', exemplo: 'Após encontrar exportação excessiva, o plano reduz a resposta, testa papéis e revisa logs antes de publicar.', confuso: 'Corrige os problemas de segurança.', claro: 'Para cada risco, registre impacto, ação, responsável, prazo, dependência e teste de reteste.', comoFunciona: ['Descreva o risco e o impacto.', 'Escolha a correção mínima segura.', 'Atribua responsável e prioridade.', 'Defina reteste e evidência.', 'Feche ou reabra com motivo.'], segundoExemplo: 'Uma senha fraca pode exigir limite de tentativas agora e migração gradual de credenciais numa etapa posterior.', errosComuns: ['Priorizar pelo susto, sem impacto.', 'Corrigir sem retestar.', 'Deixar ação sem responsável.'], termos: ['prioridade', 'reteste', 'responsável'] },
3562            { titulo: 'Decisão pendente', definicao: 'Questão que impede uma regra de ser fe
3562chada e precisa de escolha explícita antes da implementação ou publicação.', exemplo: 'O time ainda precisa decidir se o certificado será público ou exigirá autenticação.', confuso: 'Depois a gente vê.', claro: 'Liste a decisão, opções, risco de cada caminho, pessoa responsável e prazo para não transformar ambiguidade em comportamento inseguro.', comoFunciona: ['Escreva a pergunta concreta.', 'Apresente opções possíveis.', 'Explique impacto e risco.', 'Defina quem decide.', 'Converta a decisão em regra e teste.'], segundoExemplo: 'A retenção de um documento pode depender de decisão jurídica; enquanto isso, o sistema restringe o acesso e evita novas cópias.', errosComuns: ['Esconder decisão dentro do código.', 'Liberar caminho mais amplo por padrão.', 'Não registrar o motivo da escolha.'], termos: ['opção', 'risco', 'responsável'] }
3563        ],
3564        scenario: { title: 'Projeto completo: área do aluno', intro: 'Uma escola quer uma área em que o aluno vê aulas e progresso, o professor edita conteúdo e o gestor administra matrículas. Entregue uma revisão defensiva antes da publicação.', steps: [
3565            { tag: 'Mapear', text: 'Liste pessoas, estados da conta, sessões, papéis, recursos e ações.' },
3566            { tag: 'Proteger', text: 'Defina autorização no servidor, dados mínimos, recuperação, expiração e compartilhamentos.' },
3567            { tag: 'Testar', text: 'Escreva cenários permitido, negado, expirado, revogado, recurso inexistente e tentativa repetida.' },
3568            { tag: 'Registrar', text: 'Escolha eventos de auditoria e a evidência que comprova cada critério de aceite.' },
3569            { tag: 'Decidir', text: 'Separe pendências de produto, operação ou jurídico e atribua próximo passo.' }
3570        ] },
3571        exerciseFields: [
3572            { key: 'fluxo', label: '01 · Qual fluxo será revisado?', help: 'Defina início, pessoa principal e resultado esperado.', placeholder: 'O fluxo começa quando...' },
3573            { key: 'regras', label: '02 · Quais regras protegem?', help: 'Resuma identidade, sessão, permissão e dados.', placeholder: 'A pessoa..., o servidor...' },
3574            { key: 'testes', label: '03 · Quais cenários provam?', help: 'Inclua permitido, negado, expirado, revogado e erro.', placeholder: 'Vou testar...' },
3575            { key: 'evidencias', label: '04 · Qual evidência será guardada?', help: 'Liste resposta, captura, registro, teste ou decisão.', placeholder: 'A evidência será...' },
3576            { key: 'pendencias', label: '05 · O que ainda precisa de decisão?', help: 'Atribua responsável e próximo passo.', placeholder: 'Falta decidir..., responsável...' }
3577        ],
3578        exerciseModelAnswer: 'Exemplo didático: o aluno entra com sessão protegida, vê somente suas aulas e progresso e não edita conteúdo. Professor edita aulas do próprio curso; gestor administra matrículas. A API retorna dados mínimos e autoriza cada ação no servidor. Os testes cobrem sessão ausente, recurso de outra pessoa, expiração, revogação, tentativa repetida e link compartilhado. Cada risco tem evidência, responsável e pendência registrada.',
3579        codigos: [
3580            { codigo: '/checklist-por-fluxo' },
3581            { codigo: '/criterio-observavel' },
3582            { codigo: '/risco-com-responsavel' }
3583        ]
3584    },
3585    {
3586        stage: 'Aula 09',
3587        estimatedMinutes: 35,
3588        layout: 'grid',
3589        title: 'Códigos Secretos: a régua de Segurança e Acessos',
3590        goal: 'Aula bônus. Recapitule as oito aulas e reúna os comandos curtos que transformam decisões de segurança em pedidos claros e verificáveis.',
3591        focus: 'Passe a régua antes de pedir uma tela ou API: identidade, sessão, permissão, dados, recuperação, auditoria, riscos e critérios de aceite precisam aparecer no pedido.',
3592        practice: 'Escolha um fluxo real e aplique pelo menos cinco Códigos Secretos das aulas. Junte os resultados em um pedido único, com cenários permitidos, negados e de recuperação.',
3593        exerciseFeedback: 'Não existe resposta única. O pedido final precisa ligar cada código a uma decisão concreta, sem esconder pendências atrás de “deixe seguro”.',
3594        lessons: [
3595            'Reconheça quais decisões de acesso e proteção já estão explícitas num pedido.',
3596            'Use os códigos da aula certa para preencher lacunas sem repetir regras.',
3597            'Feche o pedido com testes, evidências, responsável e decisão pendente.'
3598        ],
3599        conceitos: [],
3600        bonus: true
3601    }
3602];
3603
3604segurancaAcessosJourney.forEach(aula => {
3605    aula.conceitos = aula.conceitos.map(conceito => ({
3606        nome: conceito.titulo,
3607        oQueE: conceito.definicao,
3608        quandoUsar: conceito.exemplo,
3609        pedidoConfuso: conceito.confuso,
3610        pedidoClaro: conceito.claro,
3611        termoTecnico: conceito.titulo,
3612        termosRelacionados: conceito.termos,
3613        comoFunciona: conceito.comoFunciona,
3614        segundoExemplo: conceito.segundoExemplo,
3615        errosComuns: conceito.errosComuns,
3616        grupo: conceito.grupo
3617    }));
3618});
3619
3620// Primeira aula visual do curso de automações. O conteúdo começa pelo
3621// raciocínio de processo antes de apresentar ferramentas: o aluno aprende a
3622// separar repetição, decisão e resultado observável.
3623const automacoesJourney = [
3624    {
3625        stage: 'Aula 01',
3626        estimatedMinutes: 40,
3627        layout: 'grid',
3628        showCodesInline: true,
3629        title: 'O que automatizar — e o que manter manual',
3630        goal: 'Aprenda a encontrar uma rotina repetitiva, descrevê-la com precisão e decidir onde a automação ajuda sem retirar a responsabilidade humana.',
3631        focus: 'Antes de abrir n8n, Make, Zapier ou Manychat, transforme a ideia “quero automatizar” em um fluxo com gatilho, dados, decisão, ação e resultado.',
3632        practice: 'Escolha uma rotina que acontece toda semana. Mapeie o que inicia o processo, quais dad
3632os entram, o que pode ser decidido por regra e qual resultado precisa ficar registrado.',
3633        exerciseFeedback: 'Uma boa resposta deixa claro o começo, o meio e o fim do fluxo. Se ainda houver “e depois vejo”, falta definir uma saída ou uma decisão humana. Revise também se outra pessoa conseguiria executar o fluxo apenas lendo sua resposta.',
3634        visualMarkup: `
3635            <div class="mt-8 border-2 border-mondrian-black bg-white p-5 lg:p-7" aria-label="Mapa visual de uma automação">
3636                <p class="font-mono text-xs uppercase tracking-[0.22em] text-warm-gray mb-5">Mapa visual · da rotina ao resultado</p>
3637                <div class="grid grid-cols-1 md:grid-cols-5 gap-2 items-stretch">
3638                    <div class="border-2 border-mondrian-black bg-blue-50 p-4"><span class="font-mono text-xs font-bold">01</span><strong class="block mt-2">Gatilho</strong><span class="text-sm text-warm-gray">algo acontece</span></div>
3639                    <div class="hidden md:flex items-center justify-center text-2xl font-bold">→</div>
3640                    <div class="border-2 border-mondrian-black bg-yellow-50 p-4"><span class="font-mono text-xs font-bold">02</span><strong class="block mt-2">Dados</strong><span class="text-sm text-warm-gray">o que chegou</span></div>
3641                    <div class="hidden md:flex items-center justify-center text-2xl font-bold">→</div>
3642                    <div class="border-2 border-mondrian-black bg-red-50 p-4"><span class="font-mono text-xs font-bold">03</span><strong class="block mt-2">Decisão</strong><span class="text-sm text-warm-gray">regra ou pessoa</span></div>
3643                </div>
3644                <div class="flex justify-center text-2xl font-bold py-2" aria-hidden="true">↓</div>
3645                <div class="grid grid-cols-1 md:grid-cols-3 gap-2">
3646                    <div class="border-2 border-mondrian-black bg-green-50 p-4"><span class="font-mono text-xs font-bold">04</span><strong class="block mt-2">Ação</strong><span class="text-sm text-warm-gray">o sistema executa</span></div>
3647                    <div class="border-2 border-mondrian-black bg-white p-4"><span class="font-mono text-xs font-bold">05</span><strong class="block mt-2">Resultado</strong><span class="text-sm text-warm-gray">alguém consegue conferir</span></div>
3648                    <div class="border-2 border-mondrian-black bg-gray-50 p-4"><span class="font-mono text-xs font-bold">06</span><strong class="block mt-2">Responsável</strong><span class="text-sm text-warm-gray">quem acompanha exceções</span></div>
3649                </div>
3650                <div class="mt-7 grid grid-cols-1 lg:grid-cols-2 gap-2">
3651                    <div class="border-2 border-mondrian-black p-5 bg-white"><p class="font-mono text-xs uppercase tracking-widest mb-3">Bons candidatos</p><ul class="space-y-2 text-sm"><li><strong>Lead novo:</strong> registrar, classificar e avisar a equipe.</li><li><strong>Pedido aprovado:</strong> atualizar status e enviar confirmação.</li><li><strong>Relatório semanal:</strong> reunir dados e entregar um resumo.</li></ul></div>
3652                    <div class="border-2 border-mondrian-black p-5 bg-gray-50"><p class="font-mono text-xs uppercase tracking-widest mb-3">Mantenha manual quando...</p><ul class="space-y-2 text-sm"><li>o caso exige empatia ou interpretação;</li><li>o erro pode causar prejuízo ou exposição de dados;</li><li>a regra ainda muda toda semana.</li></ul></div>
3653                </div>
3654            </div>`,
3655        lessons: [
3656            'Automação tira repetição da rotina; ela não tira a responsabilidade pelo resultado.',
3657            'Todo fluxo precisa de um gatilho, dados de entrada, uma decisão, uma ação e um resultado observável.',
3658            'Mantenha a decisão com uma pessoa quando houver contexto, risco, exceção ou impacto sensível.',
3659            'Defina como saberemos que funcionou e o que acontece quando algo der errado antes de escolher a ferramenta.',
3660            'Comece pequeno: um gatilho, uma condição e uma ação. Só adicione novas etapas depois de observar o primeiro ciclo.',
3661            'Uma automação confiável tem dono, registro do que aconteceu e um caminho claro para a exceção.'
3662        ],
3663        conceitos: [
3664            { nome: 'Gatilho', oQueE: 'O evento que inicia um fluxo automático.', quandoUsar: 'Quando um formulário é enviado, um pagamento é aprovado ou uma mensagem chega.', pedidoConfuso: '“Quando tiver cliente novo, faz o processo.”', pedidoClaro: '“Quando o formulário de orçamento for enviado, crie um registro com nome, e-mail e origem.”', comoFunciona: 'O sistema observa um evento definido e inicia as próximas etapas.', segundoExemplo: 'Um webhook re
3664cebido depois que uma loja confirma o pagamento.', quandoNaoUsar: 'Quando o evento não tem fonte confiável ou não pode ser identificado de forma única.', errosComuns: 'Usar horários vagos ou eventos que ninguém consegue verificar.', termosRelacionados: ['evento', 'webhook', 'entrada'] },
3665            { nome: 'Ação', oQueE: 'A tarefa executada depois que o gatilho e as regras são atendidos.', quandoUsar: 'Para criar registro, enviar aviso, atualizar uma planilha ou iniciar uma conversa.', pedidoConfuso: '“Notifica o time.”', pedidoClaro: '“Envie um aviso no canal comercial com o nome do lead e um link para o registro.”', comoFunciona: 'A automação transforma os dados recebidos em uma operação definida.', segundoExemplo: 'Atualizar o status do pedido e guardar o horário da atualização.', quandoNaoUsar: 'Quando ainda não está claro quem recebe a saída ou como conferir que ela aconteceu.', errosComuns: 'Não definir destinatário, formato, permissão ou condição de execução.', termosRelacionados: ['operação', 'saída', 'integração'] },
3666            { nome: 'Decisão humana', oQueE: 'O ponto em que uma pessoa avalia contexto, risco ou exceção antes de continuar.', quandoUsar: 'Quando o caso não cabe em uma regra segura ou pode afetar uma pessoa, cliente ou negócio.', pedidoConfuso: '“Se parecer importante, manda para alguém.”', pedidoClaro: '“Se o valor superar R$ 5.000 ou faltar documento, pause e atribua ao responsável financeiro.”', comoFunciona: 'A automação prepara o contexto e deixa a escolha explícita para a pessoa certa.', segundoExemplo: 'Um pedido com informação inconsistente fica em revisão em vez de ser enviado ao cliente.', quandoNaoUsar: 'Quando a regra é objetiva, estável e pode ser testada sem interpretação.', errosComuns: 'Esconder a decisão em uma regra frágil ou não definir quem assume a fila.', termosRelacionados: ['exceção', 'aprovação', 'responsável'] }
3667        ],
3668        scenario: {
3669            title: 'Cenário completo · um lead pede contato',
3670            intro: 'A equipe diz “automatiza os leads”. Antes de escolher a ferramenta, transforme a frase em um fluxo verificável.',
3671            steps: [
3672                { tag: 'Gatilho', text: 'O formulário de contato é enviado com nome, e-mail, interesse e origem.' },
3673                { tag: 'Dados', text: 'O sistema valida o e-mail e guarda os campos em um registro único.' },
3674                { tag: 'Decisão', text: 'Se o interesse for empresarial, encaminha ao comercial;
3674 caso contrário, entra na fila geral.' },
3675                { tag: 'Ação', text: 'Envia uma confirmação ao lead e uma notificação para a fila correta.' },
3676                { tag: 'Resultado', text: 'O registro fica com status, responsável e horário para revisão.' },
3677                { tag: 'Exceção', text: 'Se o e-mail for inválido ou a integração falhar, não dispara uma mensagem incompleta: marca “revisão necessária” e avisa o responsável.' }
3678            ]
3679        },
3680        exerciseFields: [
3681            { key: 'rotina', label: 'Qual rotina repetitiva você quer melhorar?', help: 'Descreva a situação atual em uma frase.', placeholder: 'Toda vez que...' },
3682            { key: 'gatilho', label: 'O que inicia o fluxo?', help: 'Escreva um evento que possa ser observado.', placeholder: 'Quando...' },
3683            { key: 'dados', label: 'Quais dados entram?', help: 'Liste apenas os dados necessários para agir.', placeholder: 'Nome, e-mail, pedido...' },
3684            { key: 'decisao', label: 'Onde entra uma regra ou uma pessoa?', help: 'Indique uma condição ou exceção.', placeholder: 'Se..., então...; caso contrário...' },
3685            { key: 'resultado', label: 'Como saberemos que funcionou?', help: 'Defina uma evidência observável.', placeholder: 'Ao final, deve existir...' }
3686        ],
3687        exerciseModelAnswer: 'Quando um formulário de orçamento for enviado, validar nome e e-mail, registrar a origem e encaminhar leads empresariais ao comercial. Enviar confirmação ao lead, registrar status “novo” e deixar casos sem e-mail válido para revisão manual.',
3688        codigos: [
3689            { codigo: '/separa-repeticao-de-decisao' },
3690            { codigo: '/gatilho-acao-resultado' }
3691        ]
3692    }
3693];
3694
3695// Registro das trilhas que usam o mecanismo de card→modal (renderAulaJourney/
3696// conceitosSectionMarkup/openConceitoModal). Cada página que usa esse
3697// mecanismo declara `<body data-journey="...">` com a chave correspondente;
3698// sem esse atributo, cai no padrão (Regras e Funcionamento), preservando o
3699// comportamento de antes.
3700const AULA_JOURNEY_REGISTRY = {
3701    'regras-e-funcionamento': {
3702        journey: conceitoJourney,
3703        planSlug: REGRAS_FUNCIONAMENTO_PLAN_SLUG,
3704        courseLabel: 'Regras e Funcionamento',
3705        basePath: 'regras-e-funcionamento',
3706        lessonIdPrefix: 'rf'
3707    },
3708    'do-pedido-ao-sistema': {
3709        journey: pedidoCompletoJourney,
3710        planSlug: 'equipe',
3711        courseLabel: 'Do Pedido ao Sistema',
3712        basePath: 'do-pedido-ao-sistema',
3713        lessonIdPrefix: 'dp'
3714    },
3715    'dados-e-informacoes': {
3716        journey: dadosInformacoesJourney,
3717        planSlug: DADOS_INFORMACOES_PLAN_SLUG,
3718        courseLabel: 'Dados e Informações',
3719        basePath: 'dados-e-informacoes',
3720        lessonIdPrefix: 'di'
3721    },
3722    'apis-que-funcionam': {
3723        journey: apisQueFuncionamJourney,
3724        planSlug: APIS_QUE_FUNCIONAM_PLAN_SLUG,
3725        courseLabel: 'APIs que Funcionam',
3726        basePath: 'apis-que-funcionam',
3727        lessonIdPrefix: 'api'
3728    },
3729    'seguranca-aplicacoes-web': {
3730        journey: segurancaAplicacoesWebJourney,
3731        planSlug: SEGURANCA_APLICACOES_WEB_PLAN_SLUG,
3732        courseLabel: 'Do Zero a Segurança em Aplicações Web',
3733        basePath: 'seguranca-aplicacoes-web',
3734        lessonIdPrefix: 'sec'
3735    },
3736    'prototipando-com-ia': {
3737        journey: prototipandoComIaJourney,
3738        planSlug: PROTOTIPANDO_COM_IA_PLAN_SLUG,
3739        courseLabel: 'Vibecoding na Prática: da Ideia ao Protótipo',
3740        basePath: 'prototipando-com-ia',
3741        lessonIdPrefix: 'ia'
3742    },
3743    'ui-descomplicada': {
3744        journey: uiDescomplicadaJourney,
3745        planSlug: UI_DESCOMPLICADA_PLAN_SLUG,
3746        courseLabel: 'UI Descomplicada: Crie Interfaces que Orientam',
3747        basePath: 'ui-descomplicada',
3748        lessonIdPrefix: 'ui'
3749    },
3750    'seguranca-e-acessos': {
3751        journey: segurancaAcessosJourney,
3752        planSlug: 'seguranca_e_acessos',
3753        courseLabel: 'Segurança e Acessos',
3754        basePath: 'seguranca-e-acessos',
3755        lessonIdPrefix: 'sa'
3756    },
3757    'automacoes': {
3758        journey: automacoesJourney,
3759        planSlug: 'automacoes_que_funcionam',
3760        courseLabel: 'Automações que Funcionam: do Gatilho ao Resultado',
3761        basePath: 'automacoes',
3762        lessonIdPrefix: 'aut'
3763    }
3764};
3765
3766// Exemplos visuais das aulas bônus. Cada passo é texto, nunca HTML fornecido pelo aluno.
3767// Os exemplos visuais são carregados da API protegida para alunos com matrícula ativa.

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.