# Plano — Portal De Paula (Protótipo Interno) ## Visão geral Reformulação do portal interno da De Paula Contadores. O pedido original foi "estruturar um protótipo visual, mas funcional, para avaliarmos e estruturarmos melhor o frontend do portal" — reaproveitando a essência do portal atual (referência: capturas de tela do sistema em produção, tema escuro roxo/dourado, sidebar com dezenas de seções, busca de aplicações, cards de atalho, login com logo circular), não recriando do zero. Decisões de arquitetura tomadas no início e válidas até a rodada 13: - **Stack: HTML + CSS + JS puro**, sem framework, sem build step, sem dependência externa além das fontes do sistema. - **Sem backend**: todo o estado (sessão, usuários, perfis de acesso, favoritos, widgets, compromissos, tema) vivia no `localStorage` do navegador. - **Visual**: modernizar a estrutura existente (sidebar + topbar + busca + cards + login), não clonar pixel a pixel as capturas de referência. Isso valeu como protótipo de validação de fluxo/desenho. Na rodada 13 o usuário definiu a stack real de backend e pediu a migração completa — ver essa seção para a arquitetura atual. Este documento foca no histórico de decisões **estruturais/transversais** — rodadas que mudaram mais de uma aplicação ao mesmo tempo, o mecanismo de autenticação, o modelo de permissões em si, a identidade visual, ou a organização de pastas do projeto — e no que ainda está em aberto no nível do portal como um todo. Para arquitetura técnica atual (páginas, API, ordem de scripts, modelo de permissões, como o CSS está dividido entre arquivos), ver `CLAUDE.md`. **A partir de 2026-08-26, o histórico específico de cada aplicação foi extraído deste arquivo** para um `CHANGELOG.md` dentro da pasta de cada uma (`portal_api//CHANGELOG.md` para as 3 com pacote Python; `docs//CHANGELOG.md` para as demais — ver `README.md` na raiz para o mapa completo), pelo mesmo motivo que levou a documentação técnica a ser dividida por aplicação (`CLAUDE.md`/`docs/*.md`): reduzir conflito de edição quando mais de uma aplicação está sendo trabalhada ao mesmo tempo, e manter cada changelog focado no que interessa a quem só mexe naquela aplicação. A numeração de rodada usada aqui é a mesma referenciada nos changelogs de cada aplicação (e vice-versa) — uma rodada que só aparece no `CHANGELOG.md` de uma aplicação específica não tem entrada aqui, e vice-versa. ## Cronologia de construção ### 1. Protótipo inicial — login + shell Tela de login (logo, usuário/senha, "esqueceu senha" apontando para Integração e Inovação) e o shell principal: sidebar com as 14 seções do documento original de arquitetura, topbar com hamburger/"Acessar Ramais"/notificações, busca de aplicações e uma grade de cards de atalho fixos (Links, OS, Ramais, Relatórios, Calendário, Solicitações). Tema claro/escuro com toggle salvo em `localStorage`. Um modal de "simular perfil de acesso" (persona) deixava escolher entre as 7 personas do documento original para ver o menu mudar — mecanismo **removido depois** (ver rodada 4). ### 4. Login real (fim do simulador de persona) O usuário pediu para poder testar de verdade o cadastro de acessos — "podemos testar na realidade como irá funcionar". Isso trocou o mecanismo inteiro: - Login parou de aceitar qualquer senha; passou a validar contra contas reais em `pid_users_v1`/`v2` (`assets/js/users.js`). - Duas contas de demonstração criadas: `gabriel`/`gabriel` (perfil Integração e Inovação) e `bruno`/`bruno` (sem perfil vinculado, para testar o estado "sem acesso"). - O modal de simulação de persona foi **removido**; a visibilidade do menu passou a vir do perfil de acesso de verdade vinculado ao usuário logado (`access.js` → `pidResolveAccess`). - Cada um dos 7 perfis do documento original ganhou um registro real em Perfis de Acesso (Diretoria, Departamento Pessoal, Gerencial, Legalização, Fisco/Contábil, Financeiro, Protocolo), mais Integração e Inovação (8 ao todo) — ver `docs/perfis-usuarios/CHANGELOG.md` pro histórico específico da tela de Perfis de Acesso/Usuários. ### 6. Bugs encontrados e corrigidos Três bugs reais surgiram e foram corrigidos durante o uso, em áreas diferentes do portal: - **`users-admin.js` ficou preso na assinatura antiga** de `pidResolveAccess()` (`{ profile }` em vez de `{ profiles }`) depois da migração da rodada 5 (ver `docs/perfis-usuarios/CHANGELOG.md`) — a tela de Usuários redirecionava sempre de volta pro Principal antes de renderizar, dando a impressão de "não abre". Corrigido ajustando a desestruturação. - **CSS sobrescrevendo o atributo `hidden`**: componentes que definem seu próprio `display` (`.no-access`, `.notif-badge`, `.app-card`) ignoravam o `hidden` do JS, porque uma regra de autor com `display` explícito vence a regra padrão do navegador `[hidden]{display:none}` mesmo com especificidade igual. A mensagem de "sem perfil vinculado" continuava aparecendo por cima do dashboard normal mesmo com o perfil já vinculado. Corrigido com uma regra global em `base.css`: `[hidden] { display: none !important; }` — resolveu esse caso e mais dois latentes (badge de notificação, filtro de busca nos cards). - **Submenus recolhidos continuavam clicáveis/focáveis**: o accordion do sidebar só usava `max-height:0` + `overflow:hidden` para esconder, o que não impede clique nem navegação por Tab. Corrigido adicionando `visibility:hidden` + `pointer-events:none` ao estado fechado. ### 13. Migração para backend Django + PostgreSQL O usuário definiu a stack real do backend: **Python 3.13 + Django 6.0 + PostgreSQL 14**, e pediu três coisas na mesma rodada: 1. Construir esse backend completo (models, API REST, banco Postgres) — não só adaptar o frontend a um contrato assumido. 2. Migrar para o banco **tudo** que hoje existia no `localStorage`, exceto a preferência de tema: usuários, perfis de acesso, favoritos, widgets e compromissos do Calendário Individual. 3. Autenticação via **sessão/cookie do Django** (decisão explícita, não token/JWT) — o que levou à escolha de servir o frontend estático pelo próprio Django (mesma origem), evitando complicação de cookie de sessão cross-origin. O que mudou estruturalmente: - Projeto Django novo, inicialmente em `Portal/backend/` (`config/` + app único `portal_api/`) — reorganizado na rodada 14 para o padrão de pastas de um projeto Django de verdade. Ver `CLAUDE.md` → "Arquitetura" para o detalhamento de cada arquivo e a tabela de endpoints. - Model de usuário customizado (`Usuario`, estendendo `AbstractUser`) desde o início — trocar depois de criado o projeto é doloroso. - Senha passou a usar hashing nativo do Django (`set_password`/ `check_password`) — antes era texto puro no protótipo; melhoria de segurança real, não só troca de armazenamento. - Catálogo de módulos/aplicações do menu (antes `PID_MODULES`/ `PID_MODULE_APPS` hardcoded em `profiles.js`) virou `portal_api/catalogo.py`, fonte única da verdade, exposto só leitura via `GET /api/catalogo/`. - A união de permissões entre múltiplos perfis de um usuário (rodada 5), antes calculada no cliente (`access.js` → `pidResolveAccess`/ `pidApplyAccessVisibility`), passou a ser calculada uma única vez no servidor (`permissoes_efetivas()` em `views.py`) e consumida pronta via `GET /api/me/`. - A lógica de "quem vê qual compromisso" do Calendário Individual (rodada 10 — `pidEventsVisibleTo`) também migrou pro backend (`CompromissoAgendaViewSet.get_queryset`). - Todo o frontend que lia/escrevia `localStorage` foi reescrito para `async`/`fetch` contra a API (`assets/js/api.js`, hoje `static/js/api.js` — ver rodada 14 — é o módulo novo), mantendo a mesma estrutura de arquivos e nomes de função sempre que possível para minimizar o tamanho do diff. - `assets/js/users.js` (armazenamento local de usuários) foi **removido** — usuários agora só existem no banco, via `/api/usuarios/`. **Limitação do ambiente onde essa rodada foi feita**: só havia Python 3.8 e nenhum servidor PostgreSQL disponíveis, então não foi possível rodar `makemigrations`/`migrate`/`runserver` nem testar o fluxo ponta a ponta durante a implementação — o código foi revisado estaticamente com cuidado (sintaxe, convenções do DRF, coerência entre serializers/views/frontend). Rodar de verdade — incluindo gerar a migração inicial — fica para o ambiente com Python 3.13 + Postgres 14 (ver rodada 17 no `CHANGELOG.md` de Links & Ferramentas/Acessos Gerais, onde esse ambiente ficou disponível). ### 14. Reorganização de pastas no padrão Django O backend tinha sido criado numa subpasta `Portal/backend/`, com o frontend (HTML/CSS/JS) solto na raiz de `Portal/` ao lado dela — funcional, mas não era a estrutura convencional de um projeto Django. Pedido: "reorganize a estrutura das pastas, conforme o padrão de um projeto Django". Mudanças: - `manage.py`, `requirements.txt`, `.env`, `config/` e `portal_api/` subiram de `Portal/backend/` para a raiz de `Portal/` — `Portal/` passou a **ser** o projeto Django, não conter um subprojeto. - As 5 páginas HTML foram movidas para `Portal/templates/` (antes soltas na raiz) — é para lá que `TEMPLATES[0]["DIRS"]` aponta agora. - `assets/css`, `assets/js` e `img/` viraram `static/css`, `static/js` e `static/img` (pasta `assets/` removida) — `STATICFILES_DIRS` (novo em `settings.py`) aponta pra lá, e `STATIC_ROOT` foi adicionado para `collectstatic` em produção. - Os 5 templates passaram a usar `{% load static %}` + `{% static 'css/x.css' %}` em vez de caminhos hardcoded (`assets/css/x.css`) — forma idiomática do Django de referenciar estáticos. - `config/urls.py` perdeu os `re_path`/`static_serve` manuais para `assets/`/`img/` — `django.contrib.staticfiles` já serve `static/` automaticamente em `DEBUG` a partir de `STATICFILES_DIRS`, sem configuração extra. - Bug corrigido de passagem: o `.env` já existia (com as credenciais do Postgres) mas `settings.py` nunca chamava `load_dotenv()` — ele nunca tinha sido lido de verdade. Corrigido ao mexer em `settings.py` por causa da mudança de `BASE_DIR`. Nenhuma mudança de comportamento — só de organização de arquivos; os endpoints da API, os models e a lógica do frontend continuam os mesmos da rodada 13. ### 15. Dispensar notificações persiste por usuário Bug relatado: ao clicar no X de uma notificação individual, o card do dropdown fechava inteiro (comportamento incorreto) e, além disso, qualquer notificação dispensada (X individual ou "Limpar tudo") voltava a aparecer depois de um reload da página. - **Fechamento indevido do dropdown**: causado pela ordem de eventos — `list.innerHTML` era reconstruído (removendo o botão clicado do DOM) antes do clique terminar de subir até o listener global em `document` que fecha o dropdown em clique fora; como o botão já não estava mais na árvore, `dropdown.contains(event.target)` avaliava falso, fechando o card como se fosse clique externo. Corrigido com `event.stopPropagation()` no handler de dismiss em `notifications.js`. - **Notificação reaparecendo no reload**: era o comportamento documentado até então (`notifications.js` guardava a lista só em memória, "zera a cada reload") — o usuário pediu explicitamente para persistir no backend em vez de usar `localStorage`. Criado o model `NotificacaoDispensada` (chave natural `usuario` + `notif_id`, mesmo padrão de `Favorito`/`WidgetUsuario`) e o endpoint `/api/notificacoes-dispensadas/`. `notifications.js` agora busca os IDs já dispensados no carregamento e filtra a lista antes de renderizar, e persiste via POST a cada X clicado ou "Limpar tudo". Importante: só o **estado de dispensada** passou a ser real — o conteúdo das notificações de "nova ferramenta" (`PID_NEW_TOOLS_NOTIFICATIONS`) continua mockado/hardcoded no frontend, só os itens de compromisso vêm de dados reais. Ver rodada 87 para o restante da evolução desse sistema. ### 20. Logo com texto branco + sidebar maior Dois bugs visuais de marca reportados com print: (1) a logo da tela de login (`logo.png`) tem o texto "DePaula Contadores" em preto, ilegível contra o fundo escuro do card de login (`--bg-surface`); (2) a logo da sidebar (`logo-mono.png`, versão toda em branco/cinza claro) estava pequena demais (`.sidebar__logo` era 56×56 fixo, espremendo uma arte que é bem mais larga que alta) — pedido explícito pra usar ali uma versão com o **D colorido e a letra branca**, num tamanho maior. Essa variante (D colorido + texto branco) não existia como arquivo — só existiam `logo.png` (D colorido, texto preto) e `logo-mono.png` (tudo branco, D incluído). Gerada uma nova, `logo-branco.png`, com um script Python usando Pillow: varre `logo.png` pixel a pixel e recolore pra branco só os opacos quase-neutros/escuros (`max(r,g,b) < 70` e `spread(r,g,b) < 12`) — o degradê dourado/marrom do D nunca bate nesse filtro porque mesmo na sombra mais escura mantém um matiz quente (testado: pixel mais escuro do D amostrado foi `(80,65,42)`, spread 38; texto era `(0,0,0)` puro, spread 0). 572.209 pixels alterados. Resultado confirmado visualmente (Read da imagem gerada) antes de usar. Trocado `logo.png` → `logo-branco.png` no login (`index.html`) e `logo-mono.png` → `logo-branco.png` nos 5 shells (mesmo arquivo nos dois lugares — ambos os fundos são escuros, então a mesma variante serve para os dois). `.sidebar__logo` foi de `56×56` fixo pra `width:200px; height:auto` (a arte é ~1.41:1, largura bem maior que altura — forçar quadrado a esmagava); adicionado encolhimento pra `44px` nos dois estados de sidebar colapsada (`.is-collapsed` no desktop, breakpoint mobile) pra não vazar da faixa de 76px. `logo.png` e `logo-mono.png` continuam no repo (não usados em nenhum template agora) — o primeiro como fonte pra regerar `logo-branco.png` se a arte oficial mudar, o segundo como variante alternativa disponível. Essa logo foi substituída pela marca "P.I.D." na rodada 73. ### 25. Favicon Pedido: "no ícone da aba do navegador, deve constar esta logo minimalista do escritório" — o usuário passou um print de referência: só o D (o mesmo pen/quill em degradê dourado/marrom de `logo.png`), sem o texto "De Paula Contadores", sobre fundo transparente. Esse recorte isolado do D não existia como arquivo — as três variantes de `static/img/` sempre têm o texto junto. Gerado `favicon.png` a partir de `logo.png` reaproveitando o mesmo filtro criado na rodada 20 pra `logo-branco.png` (pixel opaco quase-neutro/escuro = texto, nunca o D, que mantém matiz quente até na sombra mais escura do degradê) — mas em vez de recolorir esses pixels pra branco, apagados (`alpha=0`) pra isolar só o D; resultado recortado pelo bounding box do que sobrou e centralizado num canvas quadrado transparente (o D é mais alto que largo), redimensionado pra 192×192. Resultado comparado visualmente (`Read` da imagem gerada) contra o print do usuário antes de usar — bateu. Ligado via `` no `` das 7 páginas (`index.html` + os 5 shells + `ramais.html`). Nenhuma mudança de Python; `manage.py check` limpo por precaução. Substituído pelo ícone da marca "P.I.D." na rodada 73. ### 26. Bug: busca de aplicações deixava grupos do menu abertos depois de limpar Reportado com print: depois de pesquisar em "Pesquise por Aplicação..." (`search.js`) e apagar a busca, os grupos do menu (Portais, Geradoc etc.) que a busca tinha aberto pra mostrar o resultado continuavam abertos, em vez de voltar ao estado de antes de buscar. Causa: `search.js` só tinha o caminho de **abrir** um `nav-group` (`classList.add("is-open")`) quando ele dava match durante a digitação — nunca fechava de volta quando o termo era apagado. Corrigido guardando, no início de cada sessão de busca (primeira tecla digitada), quais grupos já estavam abertos manualmente (`gruposAbertosAntesDaBusca`, um `Set`); ao limpar o campo, cada grupo volta exatamente pro estado daquele snapshot, em vez de simplesmente remover `is-open` de todo mundo (o que fecharia até um grupo que o usuário tinha aberto manualmente antes de buscar). Só front-end (`static/js/search.js`), nenhuma mudança de backend. ### 27. Botão "Criar Ausência" fora do padrão + texto do menu no tema claro Dois ajustes pequenos: - **Botão "Criar Ausência"** (`ramais.html`) usava `btn-outline`, destoando dos outros dois botões da mesma barra de ações ("Adicionar Ramal", "Novo Chamado"), ambos `btn-solid`. Trocado pra `btn-solid`, sem nada mais mudar no comportamento — ver `docs/ramais/CHANGELOG.md` pro restante do histórico de Ramais. - **Texto do menu no tema claro**: a sidebar é intencionalmente congelada (fundo sempre escuro nos dois temas, ver `CLAUDE.md` → "CSS — organização entre arquivos"), mas as variáveis de texto (`--sidebar-text-primary`/`--sidebar-text-secondary`/`--sidebar-text-muted`) nunca tinham sido sobrescritas pro tema claro — pedido explícito pra virarem branco puro nesse tema, pra melhorar a legibilidade contra o fundo que continua escuro. Adicionada uma exceção deliberada em `tokens.css` (`:root[data-theme="light"]`) só pra essas três variáveis; fundo/borda da sidebar continuam frozen como antes. `CLAUDE.md` atualizado pra documentar a exceção, já que a regra geral (não usar overrides de tema na sidebar) continua valendo pro resto. ### 28. Fonte "Bree Serif" no título da tela inicial Pedido: trocar a fonte do texto "Portal De Paula" no topbar da tela inicial (`portal.html`) pra Bree Serif, serif, negrito — só ali, não em `--font-sans` (usada em todo o resto do portal) nem no `.topbar__title` dos outros shells (que mostram outros títulos, como "Ramais"/"Administração"). Primeira fonte externa do projeto — até aqui só fontes do sistema (`--font-sans`). Carregada via Google Fonts (`` + ``) no `` de `portal.html`. Estilo aplicado num id novo, `#portal-title` (no mesmo `

` de sempre, só ganhou o id), colocado em `widgets.css` por ser o único CSS próprio da página — mesmo não sendo um widget. Ajustado por iterações diretas do usuário logo em seguida: negrito removido, tamanho aumentado (1.3rem), negrito devolvido, negrito removido de novo — estado final é Bree Serif 400/1.3rem. Na mesma leva de ajustes, o usuário pediu a mesma fonte também no título "Portal De Paula" da tela de login (`.login-card__title`, `login.css`) — `index.html` ganhou o mesmo `` do Google Fonts. **Este título foi removido de `portal.html` na rodada 73** (identidade visual "P.I.D."), quando o slogan da marca foi movido pro login — ver `login.css` em "CSS — organização entre arquivos" no `CLAUDE.md`. ### 29. Animações — pedido explícito pra ignorar `prefers-reduced-motion` Pedido: "inclua animações nas páginas, para deixá-las fluidas", com duas condições explícitas — rápidas ("otimizando o tempo") e que **ignorem** a preferência de acessibilidade do sistema operacional/navegador do usuário (`prefers-reduced-motion`). Isso é uma inversão deliberada da prática padrão de acessibilidade (que normalmente desativaria animação quando essa preferência está ativa) — decisão do dono do produto pro próprio portal interno, registrada aqui pra não ser "corrigida" de volta sem confirmar de novo. Implementado com três `@keyframes` genéricos em `base.css` (`pidFadeIn`, `pidFadeSlideUp`, `pidScaleIn` — único CSS carregado por toda página, inclusive `index.html`), aplicados via `animation` (não `transition`, que não anima a troca `display:none` ↔ visível causada por `hidden`) em: `.modal-overlay`/`.modal-card` (todo modal do app), `.account-dropdown`/`.notif-dropdown`, `.page-content` (toda navegação entre páginas) e `.login-card`. Botões (`.btn-solid`/`.btn-outline`/`.btn-ghost`/`.btn-danger-outline`/`.icon-btn`) ganharam `transform: scale()` no `:active` como feedback de clique, e `.pa-table td` (reaproveitada por Perfis de Acesso/Usuários/Ramais) ganhou `transition: background` no hover da linha, que antes trocava sem transição nenhuma. Durações todas curtas (120–200ms, a maioria usando os tokens `--transition-fast`/`--transition-base` que já existiam). Nenhum `@media (prefers-reduced-motion: reduce)` foi adicionado — ausência deliberada, não descuido (ver `CLAUDE.md` → "Animações"). ### 54. Sexta cor de tema: Vermelho Adicionada mais uma opção de tema de cor (`--accent`) ao seletor de swatches do modal "Gerenciar Usuário": "Vermelho" (`key: "vermelho"`, `#ef4444` no tema escuro / `#b91c1c` no tema claro), ao lado das 5 já existentes (roxo/azul/verde/âmbar/rosa). Só duas mudanças, já que a UI do seletor é gerada dinamicamente a partir de `PID_COLOR_THEMES` (`theme.js`, consumida por `account.js`): nova entrada no array `PID_COLOR_THEMES` e o par de blocos `:root[data-color-theme="vermelho"]`/`:root[data-theme="light"][data-color-theme="vermelho"]` em `tokens.css`, mesmo padrão das outras cores. Como `--danger` (usado em feriados/selos de ausente etc.) já era um tom de vermelho (`#e5484d`) escolhido antes de "vermelho" existir como cor de tema selecionável, agora há uma sobreposição visual esperada quando o usuário escolhe o tema "Vermelho": elementos que usam `--accent` (ex.: pill "somente_eu" do Calendário Individual) ficam bem parecidos com elementos de erro/alerta que usam `--danger`. Diferente de `--gold`/`--teal`/`--slate`/`--coral` (escolhidos deliberadamente fora das cores de tema pra nunca colidir com `--accent`), `--danger` não foi criado pensando nisso — é uma coincidência aceita ao adicionar "Vermelho" à lista, não um bug. Se isso incomodar na prática, ajustar `--danger` para um tom fora da família "vermelho" é a correção futura, não mudar a cor do tema. ### 73. Nova identidade visual "P.I.D." — favicon, login e sidebar Usuário forneceu uma nova marca (`C:\Users\Depaula\Documents\Projetos\LOGOS\pid-marca`, ver `pid-marca-leiame.md`) e pediu a adoção progressiva no Portal, mantendo o logo cursivo "D De Paula Contadores" só nos documentos/PDFs gerados pela aplicação (contrato, procuração, recibo do Indicador de Desempenho, simulação de custo de contratação) — decisão explícita: esses documentos são emitidos como se o próprio escritório os tivesse gerado, carregam a identidade dele perante o cliente, não a do Portal como ferramenta interna. - **Favicon**: trocado de `favicon.png` (variante antiga do "D", rodada 25) pra `pid-icone-escuro.svg` (ícone quadrado da marca nova, variante fundo escuro) nas 11 páginas. - **Login (`index.html`)**: o card ficou **congelado escuro nos dois temas** (tokens dedicados `--login-card-bg`/`--login-field-bg`/`--login-border`/`--login-text-*` em `tokens.css`, nunca redefinidos em `:root[data-theme="light"]` — mesmo padrão da sidebar) — pedido explícito depois de uma primeira tentativa (só a caixa de login/senha escura) não ter sido o que o usuário queria; só o fundo da página ao redor do card continua claro/escuro por tema. A logo alterna entre `pid-icone.svg`/`pid-icone-escuro.svg` conforme `data-theme` (`pidSyncLoginLogo()` em `theme.js` — **removido na rodada 76**), e o texto "Portal De Paula" virou "Portal Interno da De Paula". Um slogan da marca ("Grandes aplicações de todos os tamanhos.", fonte "Pinyon Script"/dourado `#c6a24a`) foi adicionado — testado primeiro no topbar de `portal.html` (rodada 28), removido de lá a pedido do usuário, e reintroduzido no login (rodapé, depois movido pra logo abaixo do título), tamanho ajustado até ficar legível sem competir com o título. - **Sidebar** (10 shells): `sidebar__brand` passou a ter duas logos sempre no DOM, sobrepostas com `position:absolute` dentro de um container de altura fixa, alternando por `opacity`+`scale()` (crossfade, não troca de `src`) conforme a sidebar está expandida (`pid-logo-horizontal-escuro.svg`, assinatura com texto) ou colapsada (`pid-icone-escuro.svg`, só o símbolo) — sempre as variantes "-escuro", já que a sidebar é sempre escura nos dois temas. Tamanho da logo expandida aumentado em duas rodadas (176px/70% → 220px/92% → 250px/96% da largura útil) até o subtítulo "PORTAL INTERNO DA DE PAULA" ficar legível. Ver `CLAUDE.md` ("Logos em `static/img/`") pro estado final detalhado de cada arquivo. ### 74. Animação de intro pós-login (porta de `pid-intro-escuro.html`) Usuário forneceu um segundo arquivo da mesma pasta de marca, `pid-intro-escuro.html` — um bundle de "Design Canvas" (Anthropic) com uma animação de apresentação da marca P.I.D. (composição em 4 cenas: o ícone se desenha, ganha rosto, desliza pra revelar o wordmark "P.I.D." + tagline, e por fim voa/esmaece). Pediu pra reproduzir essa animação como transição entre o login e a tela Principal: ao clicar "Entrar" com sucesso, a animação toca por inteiro e só depois a tela Principal aparece, "pela barra lateral primeiro". O arquivo fornecido não é HTML/CSS simples — é um bundle auto-contido do editor de canvas com um runtime próprio (React + motor de composição por tempo autorado "OM", fontes e o JSX fonte embutidos como blobs base64/gzip dentro de um `