Inclusão do tema de aniversário De Paula
This commit is contained in:
parent
fcd5a70579
commit
cdc02fb311
@ -125,14 +125,30 @@
|
|||||||
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\Logo intro animation Aniversário\\\\pid-intro-aniversario.html\")",
|
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\Logo intro animation Aniversário\\\\pid-intro-aniversario.html\")",
|
||||||
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\pid-marca-aniversario\\\\pid-aniversario-logo-horizontal-escuro.svg\")",
|
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\pid-marca-aniversario\\\\pid-aniversario-logo-horizontal-escuro.svg\")",
|
||||||
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\pid-marca-aniversario\\\\pid-aniversario-icone-escuro.svg\")",
|
"Bash(start \"\" \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\\\\pid-marca-aniversario\\\\pid-aniversario-icone-escuro.svg\")",
|
||||||
"Bash(rm -rf \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\")"
|
"Bash(rm -rf \"C:\\\\Users\\\\Depaula\\\\Documents\\\\Logos\\\\P.I.D. Logo Design Aniversário\")",
|
||||||
|
"Bash(cp \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/pid-marca-56/pid-56-favicon.svg\" \"static/img/pid-aniversario-favicon.svg\")",
|
||||||
|
"Bash(cp \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/pid-marca-56/pid-56-icone-escuro.svg\" \"static/img/pid-aniversario-icone-escuro.svg\")",
|
||||||
|
"Bash(cp \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/pid-marca-56/pid-56-logo-horizontal-escuro.svg\" \"static/img/pid-aniversario-logo-horizontal-escuro.svg\")",
|
||||||
|
"Bash(awk '{print NR\": \"length\\($0\\)}' \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/Logo intro animation/export-56/pid-56-anos.html\")",
|
||||||
|
"Bash(sort -t: -k2 -n -r)",
|
||||||
|
"Bash(sed -n '382p' \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/Logo intro animation/export-56/pid-56-anos.html\" > /tmp/aniv_template_line.txt *)",
|
||||||
|
"Bash(/c/Users/Depaula/AppData/Local/Programs/Python/Python313/python.exe -c ' *)",
|
||||||
|
"Bash(cygpath -w /tmp/aniv_template_line.txt)",
|
||||||
|
"Bash(sed -n '370p' \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/Logo intro animation/export-56/pid-56-anos.html\" > \"C:/Users/Depaula/AppData/Local/Temp/aniv_manifest_line.txt\" *)",
|
||||||
|
"Bash(C:/Users/Depaula/Documents/Portal/.venv/Scripts/python.exe -c ' *)",
|
||||||
|
"Bash(cp \"C:/Users/Depaula/Documents/Logos/P.I.D. Logo Design Aniversário/Logo intro animation/export-56/Logo - DE PAULA CONTADORES - escrita branca \\(1\\).png\" \"C:/Users/Depaula/Documents/Portal/static/img/pid-aniversario-depaula-wordmark.png\" *)",
|
||||||
|
"Bash(grep -o '`' \"C:/Users/Depaula/Documents/Portal/static/js/login-intro-aniversario.js\")",
|
||||||
|
"Bash(\"./.venv/Scripts/python.exe\" -)"
|
||||||
],
|
],
|
||||||
"additionalDirectories": [
|
"additionalDirectories": [
|
||||||
"C:\\Users\\Depaula\\AppData\\Local\\Temp\\claude\\c--Users-Depaula-Documents-Portal\\3ea0ee22-e5fd-4030-98b1-ee2e71c16ce0\\scratchpad\\halloween-design",
|
"C:\\Users\\Depaula\\AppData\\Local\\Temp\\claude\\c--Users-Depaula-Documents-Portal\\3ea0ee22-e5fd-4030-98b1-ee2e71c16ce0\\scratchpad\\halloween-design",
|
||||||
"C:\\Users\\Depaula\\AppData\\Local\\Temp\\claude\\c--Users-Depaula-Documents-Portal\\3ea0ee22-e5fd-4030-98b1-ee2e71c16ce0\\scratchpad",
|
"C:\\Users\\Depaula\\AppData\\Local\\Temp\\claude\\c--Users-Depaula-Documents-Portal\\3ea0ee22-e5fd-4030-98b1-ee2e71c16ce0\\scratchpad",
|
||||||
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário",
|
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário",
|
||||||
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário\\pid-marca-aniversario",
|
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário\\pid-marca-aniversario",
|
||||||
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário\\Logo intro animation Aniversário"
|
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário\\Logo intro animation Aniversário",
|
||||||
|
"\\tmp",
|
||||||
|
"C:\\Users\\Depaula\\AppData\\Local\\Temp",
|
||||||
|
"C:\\Users\\Depaula\\Documents\\Logos\\P.I.D. Logo Design Aniversário\\Logo intro animation\\export-56"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -381,6 +381,6 @@ Ao apertar "Entrar" com sucesso, toca uma animação de marca em tela cheia (~6,
|
|||||||
|
|
||||||
## Temas Sazonais
|
## Temas Sazonais
|
||||||
|
|
||||||
Troca sazonal da marca P.I.D. (logo, ícone da sidebar/login, favicon, intro de abertura pós-login) por uma variante temática durante uma janela de datas — hoje só o Halloween (mascote "vampiro", outubro), mecanismo pensado pra outro tema sazonal futuro reaproveitar. Inclui também, só durante o Halloween: uma animação de "susto" ao clicar na logo (substitui a piscada normal) e teias de aranha decorativas nos cantos do login/tela Principal (com uma aranha que pode ser "morta" ao clicar). Um toggle "Temas sazonais" no menu da conta deixa o próprio usuário desligar isso (preferência pessoal ou acessibilidade, ex.: aracnofobia). 100% runtime — fora da janela ativa (ou com o opt-out ligado), tudo volta sozinho ao P.I.D. de sempre, sem nenhuma ação manual.
|
Troca sazonal da marca P.I.D. (logo, ícone da sidebar/login, favicon, intro de abertura pós-login) por uma variante temática durante uma janela de datas — hoje dois: Halloween (mascote "vampiro", 16 a 31/10) e Aniversário (56 anos da De Paula Contadores, mascote com balões, só 15/10) — as duas janelas nunca coincidem por desenho. Mecanismo pensado pra outro tema sazonal futuro reaproveitar o mesmo esqueleto. Durante o Halloween, clicar na logo dispara um "susto" (substitui a piscada normal) e há teias de aranha decorativas nos cantos do login/tela Principal (aranha "matável"); durante o Aniversário, clicar na logo dispara um "clique de festa" (pula + joga confete + língua-de-sogra, convive com a piscada normal em vez de substituí-la), mais um bolo com velinhas (apagáveis ao clicar) fixo no canto da tela Principal. Um toggle "Temas sazonais" no menu da conta deixa o próprio usuário desligar isso (preferência pessoal ou acessibilidade, ex.: aracnofobia). 100% runtime — fora da janela ativa (ou com o opt-out ligado), tudo volta sozinho ao P.I.D. de sempre, sem nenhuma ação manual.
|
||||||
|
|
||||||
Ver `docs/temas-sazonais/temas-sazonais.md`.
|
Ver `docs/temas-sazonais/temas-sazonais.md`.
|
||||||
|
|||||||
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
> Criado em 2026-09-15, seguindo o mesmo padrão das demais aplicações sem pacote Python dedicado (ver `CLAUDE.md` na raiz, "Documentação dividida por aplicação") — como o Halloween foi só o primeiro de possivelmente vários temas sazonais, faz mais sentido documentar isso como uma "aplicação" própria desde já do que deixar acumulando dentro do `CLAUDE.md` raiz. Este arquivo não é auto-carregado pelo Claude Code (não há pacote Python — o mecanismo é só frontend, `static/js`/`static/css`/templates) — leia manualmente ao mexer nisto. Ver `CLAUDE.md` na raiz para arquitetura geral/transversal do Portal.
|
> Criado em 2026-09-15, seguindo o mesmo padrão das demais aplicações sem pacote Python dedicado (ver `CLAUDE.md` na raiz, "Documentação dividida por aplicação") — como o Halloween foi só o primeiro de possivelmente vários temas sazonais, faz mais sentido documentar isso como uma "aplicação" própria desde já do que deixar acumulando dentro do `CLAUDE.md` raiz. Este arquivo não é auto-carregado pelo Claude Code (não há pacote Python — o mecanismo é só frontend, `static/js`/`static/css`/templates) — leia manualmente ao mexer nisto. Ver `CLAUDE.md` na raiz para arquitetura geral/transversal do Portal.
|
||||||
|
|
||||||
Troca sazonal da marca P.I.D. (logo, ícone da sidebar/login, favicon, intro de abertura) por uma variante temática durante uma janela de datas — hoje só o Halloween (mascote "vampiro", outubro), mas o mecanismo (`pidIsHalloweenActive()`/`seasonal-theme.js`) foi pensado pra ser genérico o bastante pra outro tema sazonal futuro reaproveitar o mesmo esqueleto sem reescrever nada.
|
Troca sazonal da marca P.I.D. (logo, ícone da sidebar/login, favicon, intro de abertura) por uma variante temática durante uma janela de datas — hoje dois: Halloween (mascote "vampiro", 16 a 31/10) e Aniversário (56 anos da De Paula Contadores, mascote com balões, só 15/10). O mecanismo (`pidIsHalloweenActive()`/`pidIsAniversarioActive()`/`seasonal-theme.js`) foi pensado pra ser genérico o bastante pra outro tema sazonal futuro reaproveitar o mesmo esqueleto sem reescrever nada — as duas janelas atuais nunca coincidem por desenho (15 vs 16-31/10), não por checagem explícita de conflito.
|
||||||
|
|
||||||
## Status atual
|
## Status atual
|
||||||
|
|
||||||
**Confirmado e programado** — revisão visual concluída pelo usuário. `PID_HALLOWEEN_PREVIEW_FORCE = false` em `static/js/seasonal-theme.js` (deixado de propósito na constante, documentando a decisão, em vez de removido): daqui pra frente só a janela de outubro (`PID_HALLOWEEN_MES = 9`, `Date#getMonth()` 0-indexed, mês inteiro, todo ano) decide se o tema está ativo, sem nenhuma ação manual — nem pra ligar em outubro, nem pra desligar depois. Nada mais precisa mudar de código pra isso valer.
|
**Confirmado e programado** — revisão visual concluída pelo usuário. `PID_HALLOWEEN_PREVIEW_FORCE`/`PID_ANIVERSARIO_PREVIEW_FORCE = false` em `static/js/seasonal-theme.js` (deixados de propósito nas constantes, documentando a decisão, em vez de removidos): daqui pra frente só a data decide se cada tema está ativo, sem nenhuma ação manual — nem pra ligar, nem pra desligar depois. Nada mais precisa mudar de código pra isso valer.
|
||||||
|
|
||||||
|
O Halloween só começa em 16/10 (antes: outubro inteiro) desde que o tema de Aniversário foi criado — 15/10 é o aniversário da empresa e passou a ter tema próprio, então os dois janelas foram desenhadas pra nunca disputar o mesmo dia.
|
||||||
|
|
||||||
|
**"56 ANOS" é específico deste ano** — a pílula do lockup horizontal, o texto "56" da intro e os 3 arquivos `static/img/pid-aniversario-*.svg` precisam ser atualizados manualmente todo ano (novo número, nova pasta `pid-marca-<N>` de referência) — ver `leiame.md` original: "é uma marca temporária". Isso não é automatizado de propósito (não vale a complexidade pra um ajuste anual de um texto).
|
||||||
|
|
||||||
## Origem dos assets (Halloween)
|
## Origem dos assets (Halloween)
|
||||||
|
|
||||||
@ -16,14 +20,15 @@ Mascote "vampiro" (a mesma marca P.I.D. fantasiada de Drácula — capa com gola
|
|||||||
|
|
||||||
Incluído em toda página (mesmo padrão de `confirm-modal.js`, logo depois dele) — concentra tudo que é compartilhado entre login e sidebar:
|
Incluído em toda página (mesmo padrão de `confirm-modal.js`, logo depois dele) — concentra tudo que é compartilhado entre login e sidebar:
|
||||||
|
|
||||||
- `pidIsHalloweenActive()` — a checagem de data (mais o preview force e o opt-out do usuário, ver "Opt-out do usuário" abaixo — checado **antes** de tudo, prioridade máxima).
|
- `pidIsHalloweenActive()`/`pidIsAniversarioActive()` — a checagem de data de cada tema (mais o respectivo preview force e o opt-out do usuário, ver "Opt-out do usuário" abaixo — checado **antes** de tudo, prioridade máxima).
|
||||||
- `pidApplySeasonalFavicon()` — troca `<link rel="icon">` de `pid-icone-escuro.svg` pra `pid-halloween-icone-escuro.svg` via substring no `href` (funciona com qualquer prefixo de `STATIC_URL`). **Não** usa `pid-halloween-favicon.svg` — essa é uma variante simplificada (sem capa/gola, cores diferentes) que destoava do resto da marca sazonal na aba do navegador (bug relatado pelo usuário logo na revisão visual); mesmo padrão do P.I.D. normal, cujo favicon também usa `pid-icone-escuro.svg` em vez do `pid-favicon.svg` dedicado.
|
- `pidApplySeasonalFavicon(iconFile)` — troca `<link rel="icon">` de `pid-icone-escuro.svg` pro arquivo passado (`pid-halloween-icone-escuro.svg` ou `pid-aniversario-icone-escuro.svg`) via substring no `href` (funciona com qualquer prefixo de `STATIC_URL`). **Não** usa o favicon dedicado de cada tema (`pid-halloween-favicon.svg`/`pid-aniversario-favicon.svg`) — são variantes simplificadas (sem capa/gola ou sem balões, cores diferentes) que destoam do resto da marca sazonal na aba do navegador (bug relatado pelo usuário na revisão visual do Halloween, aplicado por precedente ao Aniversário); mesmo padrão do P.I.D. normal, cujo favicon também usa `pid-icone-escuro.svg` em vez do `pid-favicon.svg` dedicado.
|
||||||
- `pidApplyVampireIcon(svg)`/`pidApplyVampireFull(svg)` — substituem o `viewBox`+`innerHTML` de um `<svg>` existente (ícone 64×64 → vampiro 76×80; assinatura horizontal 300×80 → 320×96) pelo markup do vampiro, com dois grupos de rosto sempre presentes (`.pid-vamp-face--friendly`/`--scare`, cruzando opacidade) dentro de um `<g class="pid-vamp-icon-rig">` — mesma ideia de "dois estados sempre no DOM, alternando opacidade" já usada pelo crossfade da sidebar (`sidebar__logo--full`/`--icon`).
|
- `pidApplyVampireIcon(svg)`/`pidApplyVampireFull(svg)` — substituem o `viewBox`+`innerHTML` de um `<svg>` existente (ícone 64×64 → vampiro 76×80; assinatura horizontal 300×80 → 320×96) pelo markup do vampiro, com dois grupos de rosto sempre presentes (`.pid-vamp-face--friendly`/`--scare`, cruzando opacidade) dentro de um `<g class="pid-vamp-icon-rig">` — mesma ideia de "dois estados sempre no DOM, alternando opacidade" já usada pelo crossfade da sidebar (`sidebar__logo--full`/`--icon`).
|
||||||
- `pidTriggerVampireScare(root)` — dispara a animação de susto (`.is-scaring`, ver `base.css`) em todo `.pid-vamp-icon-rig` dentro de `root`.
|
- `pidTriggerVampireScare(root)` — dispara a animação de susto (`.is-scaring`, ver `base.css`) em todo `.pid-vamp-icon-rig` dentro de `root`.
|
||||||
|
- `pidApplyAniversarioIcon(svg)`/`pidApplyAniversarioFull(svg)` — substituem só o `innerHTML` (viewBox não muda, ao contrário do vampiro) pelo markup com balões; preservam os dois `.pid-icon-eye` do ícone normal (a cara não muda nesse tema), então a piscada ao clicar continua funcionando sem nenhum código de interação dedicado.
|
||||||
|
|
||||||
## Troca do ícone/assinatura (sidebar + login)
|
## Troca do ícone/assinatura (sidebar + login)
|
||||||
|
|
||||||
Como o markup do ícone P.I.D. normal já é idêntico (copiado inline) nas 13 páginas-shell + login, a troca é feita **em runtime**, não editando cada template — `sidebar-brand.js`/`login-logo-blink.js` chamam `pidApplyVampireIcon`/`pidApplyVampireFull`/`pidApplySeasonalFavicon` no próprio `DOMContentLoaded`, só se `pidIsHalloweenActive()`. Isso também é o que torna o "retorno" depois de outubro automático: o innerHTML original nunca é tocado no arquivo, só sobrescrito em memória a cada carregamento de página enquanto a janela estiver ativa.
|
Como o markup do ícone P.I.D. normal já é idêntico (copiado inline) nas 13 páginas-shell + login, a troca é feita **em runtime**, não editando cada template — `sidebar-brand.js`/`login-logo-blink.js` chamam `pidApplyVampireIcon`/`pidApplyVampireFull`/`pidApplyAniversarioIcon`/`pidApplyAniversarioFull`/`pidApplySeasonalFavicon` no próprio `DOMContentLoaded`, escolhendo o tema (`pidIsHalloweenActive()` checado primeiro, `pidIsAniversarioActive()` senão — nunca os dois juntos, mas Halloween ganha prioridade de código por clareza). Isso também é o que torna o "retorno" ao P.I.D. normal automático fora de qualquer janela: o innerHTML original nunca é tocado no arquivo, só sobrescrito em memória a cada carregamento de página enquanto alguma janela estiver ativa.
|
||||||
|
|
||||||
## Susto ao clicar (substitui a piscada de olhos)
|
## Susto ao clicar (substitui a piscada de olhos)
|
||||||
|
|
||||||
@ -54,10 +59,59 @@ Decoração de canto pedida pelo usuário depois de revisar um protótipo isolad
|
|||||||
- **Atalho Tab+B reseta todas as aranhas** (`pidResetDeadSpiders()`, limpa o `localStorage` e recarrega a página): segurar Tab (sem `preventDefault`, a navegação por foco continua normal) e apertar B enquanto ele está pressionado. Listener global (`seasonal-theme.js`, incluído em toda página), só tem efeito de verdade nas duas páginas com teia e só quando há alguma aranha morta salva.
|
- **Atalho Tab+B reseta todas as aranhas** (`pidResetDeadSpiders()`, limpa o `localStorage` e recarrega a página): segurar Tab (sem `preventDefault`, a navegação por foco continua normal) e apertar B enquanto ele está pressionado. Listener global (`seasonal-theme.js`, incluído em toda página), só tem efeito de verdade nas duas páginas com teia e só quando há alguma aranha morta salva.
|
||||||
- **Bug real — aranha caindo pra cima e caveira de cabeça pra baixo no canto inferior-direito**: o canto `--br` originalmente era só o mesmo desenho do `--tl` com `transform:scale(-1,-1)` no `<div class="pw-corner">` inteiro (jeito mais simples de reaproveitar o mesmo markup pro canto oposto). Só que `scale(-1,-1)` num ancestral vira **tudo** de cabeça pra baixo, inclusive a "aranha"/"caveira" (que têm uma orientação certa — pernas, olhos, queixo) e a direção das animações de queda/subida (`pwSpiderDie`/`pwSkullRise`, que "sobem" em vez de "descer" depois de espelhadas). Corrigido separando as duas coisas: só a teia em si (`.pw-strands`, sem opinião de "lado certo pra cima") continua sendo espelhada, agora com `transform-box:view-box; transform-origin:100px 100px` (o centro do viewBox autorado, não a caixa delimitadora dos fios — importante porque o fio "perdido" mais comprido deslocaria esse centro se fosse `fill-box`); a aranha e a caveira passaram a usar variáveis CSS (`--rx`/`--ry`/`--rr`/`--ox`/`--oy` na aranha, `--sx`/`--sy` na caveira) pra descanso/passeio/posição, redefinidas com os valores espelhados (`200-x`, `200-y`, `+180deg`) só pra `.pw-corner--br`, **sem nenhum `scale`/`rotate` extra nelas** — ficam sempre em pé, e as `@keyframes` de queda/subida (agora usando `calc(var(--r*) + valor)`) sempre significam "pra baixo"/"pra cima" de verdade na tela, nos dois cantos.
|
- **Bug real — aranha caindo pra cima e caveira de cabeça pra baixo no canto inferior-direito**: o canto `--br` originalmente era só o mesmo desenho do `--tl` com `transform:scale(-1,-1)` no `<div class="pw-corner">` inteiro (jeito mais simples de reaproveitar o mesmo markup pro canto oposto). Só que `scale(-1,-1)` num ancestral vira **tudo** de cabeça pra baixo, inclusive a "aranha"/"caveira" (que têm uma orientação certa — pernas, olhos, queixo) e a direção das animações de queda/subida (`pwSpiderDie`/`pwSkullRise`, que "sobem" em vez de "descer" depois de espelhadas). Corrigido separando as duas coisas: só a teia em si (`.pw-strands`, sem opinião de "lado certo pra cima") continua sendo espelhada, agora com `transform-box:view-box; transform-origin:100px 100px` (o centro do viewBox autorado, não a caixa delimitadora dos fios — importante porque o fio "perdido" mais comprido deslocaria esse centro se fosse `fill-box`); a aranha e a caveira passaram a usar variáveis CSS (`--rx`/`--ry`/`--rr`/`--ox`/`--oy` na aranha, `--sx`/`--sy` na caveira) pra descanso/passeio/posição, redefinidas com os valores espelhados (`200-x`, `200-y`, `+180deg`) só pra `.pw-corner--br`, **sem nenhum `scale`/`rotate` extra nelas** — ficam sempre em pé, e as `@keyframes` de queda/subida (agora usando `calc(var(--r*) + valor)`) sempre significam "pra baixo"/"pra cima" de verdade na tela, nos dois cantos.
|
||||||
|
|
||||||
|
## Tema "Aniversário" (56 anos da De Paula Contadores, só 15/10)
|
||||||
|
|
||||||
|
Segundo tema sazonal, criado numa rodada seguinte à do Halloween — mesmo personagem P.I.D., "o acento é o número" (ver `leiame.md` original abaixo): ganha três balões (azul/âmbar/rosa) segurados por um braço saindo da lateral direita do corpo. A cara de **repouso** não muda (sem susto como o vampiro) — mas o clique na logo ganhou uma animação própria numa rodada seguinte (ver "Clique de festa" abaixo), e o tema também ganhou um bolo com velinhas na tela Principal (ver "Bolo de aniversário" abaixo).
|
||||||
|
|
||||||
|
### Origem dos assets
|
||||||
|
|
||||||
|
Desenhado em `C:\Users\Depaula\Documents\Logos\P.I.D. Logo Design Aniversário\pid-marca-56\` (SVGs de referência + `leiame.md`, fora do repositório, mesmo papel de `pid-marca-halloween/` pro Halloween) e a intro de abertura em `...\P.I.D. Logo Design Aniversário\Logo intro animation\export-56\pid-56-anos.html` (mesmo formato de bundle "Design Canvas", desta vez um componente React/JSX — `pid-56.jsx` — em vez de JS puro; extraído do bundle com o mesmo processo ad-hoc de gzip+base64 por uuid já usado no Halloween/intro normal e portado fielmente pra vanilla JS/CSS em `login-intro-aniversario.js`). Cópias dos SVGs de referência (não usadas diretamente pela UI, que injeta o markup inline) vivem em `static/img/pid-aniversario-favicon.svg`/`pid-aniversario-icone-escuro.svg`/`pid-aniversario-logo-horizontal-escuro.svg`; a logo da De Paula Contadores usada na intro (imagem `<img>`, não SVG) vive em `static/img/pid-aniversario-depaula-wordmark.png`.
|
||||||
|
|
||||||
|
**Exceção deliberada à separação logo cursivo vs "P.I.D."** (ver "Logos em `static/img/`" no `CLAUDE.md` raiz): a intro de abertura deste tema mostra a logo cursiva oficial da De Paula Contadores (não a marca "P.I.D.") ao lado do texto "Parabéns" — normalmente essa logo é reservada só a documentos/PDFs gerados pela aplicação, nunca à UI do Portal. Aqui a exceção é intencional: a composição celebra o aniversário da **empresa**, não do sistema, e o próprio usuário forneceu essa imagem como parte do design de referência (não é uma migração de logo decidida unilateralmente).
|
||||||
|
|
||||||
|
**"56 ANOS" é específico deste ano** — ver "Status atual" no topo deste arquivo.
|
||||||
|
|
||||||
|
### Ícone/assinatura (sidebar + login)
|
||||||
|
|
||||||
|
`pidApplyAniversarioIcon(svg)`/`pidApplyAniversarioFull(svg)` (`seasonal-theme.js`) substituem só o `innerHTML` — ao contrário do vampiro, a caixa (`viewBox`) não muda (64×64/300×80, iguais ao P.I.D. normal), já que os balões cabem na mesma composição sem precisar de mais espaço. Os dois `.pid-icon-eye` do ícone normal são preservados dentro do markup novo, então a piscada ao clicar (`sidebar-brand.js`/`login-logo-blink.js`) continua funcionando — inclusive **junto** com o clique de festa (ver abaixo), não em vez dele.
|
||||||
|
|
||||||
|
Estrutura em dois `<g>` aninhados — `<g class="pid-aniv-hop">` (recebe o "pulo" via CSS, sem nenhum `transform` de atributo XML) por fora, `<g class="pid-aniv-icon-rig" transform="...">` (o posicionamento estático dentro da caixa) por dentro — pelo mesmo motivo já documentado pra `.pid-icon-eye`/`.pid-vamp-icon-rig`: um `transform` de CSS aplicado ao MESMO elemento que já tem um `transform` de atributo substitui o atributo inteiro, perdendo a posição.
|
||||||
|
|
||||||
|
### Clique de festa (pulo + confete + língua-de-sogra), pedido numa rodada seguinte
|
||||||
|
|
||||||
|
Pedido explícito do usuário depois de ver o tema pela primeira vez ("aplique a mesma animação do mascote de pular com os confetes e a língua de sogra ao clicar no botão da logo"). Ao contrário do susto do vampiro (que **substitui** a piscada), o clique de festa **convive** com ela — a cara de repouso do mascote de aniversário não muda, então a piscada de olhos continua fazendo sentido; o clique dispara os dois efeitos juntos.
|
||||||
|
|
||||||
|
- **Markup**: `pidAniversarioIconRigMarkup()` (`seasonal-theme.js`) ganhou, além dos balões já existentes, um braço/balões agrupados (`.pid-aniv-balloons`/`.pid-aniv-arm`, antes soltos), um grupo de confete escondido (`.pid-aniv-confetti`, 8 formas pequenas perto do braço) e, dentro do corpo, um sorriso/bico-de-pato que se alternam (`.pid-aniv-smile`/`.pid-aniv-pucker-outer`/`-inner`) mais uma língua-de-sogra (`.pid-aniv-horn`, um tubo + traço pontilhado + 2 "bolhas" de sopro) — todos escondidos por padrão (`opacity:0`/`scaleX(0)`), revelados só durante o clique.
|
||||||
|
- **Trigger**: `pidTriggerAniversarioParty(root)` (mesmo padrão de `pidTriggerVampireScare` — remove e reaplica `.is-partying` em todo `.pid-aniv-hop` dentro de `root`, permitindo cliques repetidos re-tocarem do zero). Chamado por `sidebar-brand.js`/`login-logo-blink.js` quando `pidIsAniversarioActive()`, sempre antes de também disparar a piscada normal.
|
||||||
|
- **Animação** (`base.css`, `PID_ANIVERSARIO_PARTY_MS = 650ms` pra tudo, mesmo truque do susto do vampiro de escalonar o efeito só via percentuais internos de cada `@keyframes`): o personagem pula (`pidAnivHop`, translateY+squash), o braço joga os balões pra cima (`pidAnivArmThrow`, rotate), os balões balançam mais forte (`pidAnivBalloonSwing`), as 8 peças de confete explodem em direções fixas (`pidAnivConfettiPiece`, uma por `:nth-child` com `--tx`/`--ty` próprios e um pequeno atraso escalonado) e a boca faz bico enquanto a língua-de-sogra desenrola e recolhe (`pidAnivPuckerIn`/`pidAnivSmileOut`/`pidAnivHornUnroll`).
|
||||||
|
- **Pivôs de transform**: `.pid-aniv-hop`/`.pid-aniv-balloons`/`.pid-aniv-arm` usam `transform-box:fill-box; transform-origin:center bottom` (pivô aproximado na base do próprio desenho, suficiente pra um ícone pequeno — não vale a complexidade de calcular o pivô exato em `view-box`); `.pid-aniv-horn` usa `transform-origin:left center` (a ponta esquerda da própria caixa delimitadora é exatamente onde o tubo nasce, perto da boca — `scaleX` a partir dali "desenrola" corretamente sem precisar de coordenada manual).
|
||||||
|
- **Bug real — a língua-de-sogra saía do lugar errado e não chegava a sair do corpo**: ela tinha nascido DENTRO do grupo do corpo (`<g transform="translate(2,18) scale(0.72)">`), com as coordenadas da ponta esquerda do sorriso (`M25 47`, o começo do traço da boca, não o centro dela) — resultado: o tubo atravessava o rosto na diagonal e terminava ainda dentro do disquete, sem nunca aparecer pra fora. Corrigida movendo o grupo pra FORA do corpo (irmão dele, depois dele no DOM pra continuar desenhando por cima) e recalculando o ponto de origem no espaço do rig: o bico da boca está em `(32, 48.5)` no espaço local do corpo, que vira `(2 + 32×0,72, 18 + 48,5×0,72) = (25, 53)` no espaço do rig. Daí o tubo segue `q12.6 -5 30 -11` (as mesmas proporções da composição original, onde o efeito também cruza o corpo de propósito) e termina 11 unidades além da borda direita do disquete, em espaço livre.
|
||||||
|
|
||||||
|
### Intro de abertura pós-login (`static/css/login-intro-aniversario.css` + `static/js/login-intro-aniversario.js`)
|
||||||
|
|
||||||
|
Variante de `login-intro.css`/`.js`, com seu próprio overlay (`#login-intro-aniversario` em `index.html`) — reaproveita `PID_INTRO_EASING`/`pidIntroAnimate`/`pidIntroClamp`/`pidIntroCornerTarget` já globais de `login-intro.js`. `auth.js` escolhe a intro certa (Halloween > Aniversário > normal, nessa ordem de prioridade de código — nunca duas ativas ao mesmo tempo de verdade).
|
||||||
|
|
||||||
|
- **Cenas originais** (`PID_INTRO_A_CUES` — `Chegada:0, Confete:1.1, Texto:2.5, Serpentina:4.1`, somando 5,9s, extraídas de `pid-56.jsx`): `Chegada` (o personagem cai com os balões e quica ao pousar), `Confete` (dá um pulo, joga o braço com os balões pra cima e arremessa uma explosão de confete — 58 peças com física determinística, mesma semente pseudo-aleatória do original pra toda visita produzir a mesma explosão), `Texto` (o ícone desliza pra esquerda enquanto "Parabéns" + a logo da De Paula + "56 Anos" entram), `Serpentina` (a boca faz bico e uma língua-de-sogra desenrola da boca, depois recolhe totalmente, enquanto o slogan pousa e a cena esmaece).
|
||||||
|
- **Diferenças em relação à composição original**: (1) a composição original foi autorada num canvas de vídeo 1920×1080 com elementos posicionados por coordenadas absolutas — aqui virou uma única linha flexível (ícone + mensagem lado a lado), com o "deslizar pra esquerda" reproduzido pela mesma técnica de `SHIFT_X` já usada nas outras duas intros (o ícone se desloca por `transform`, a mensagem só aparece fixa ao lado); (2) o "zoom" de respiro do fundo (1.05→1.0→1.02) e as vinhetas radiais do canvas original foram descartados — floreios de vídeo sem efeito perceptível numa janela de overlay pequena; (3) `Fly` (~0,9s, acrescentada, não existe no arquivo original, mesma ideia já usada nas outras duas intros) — a composição original termina com tudo esmaecendo junto (pensada como vídeo solto); aqui o ícone encolhe e voa até o canto real da sidebar antes do stage inteiro esmaecer.
|
||||||
|
- **Reescala de tamanho**: a composição original foi autorada pra um ícone de 420px (`Party` component, `size=420` default); aqui o ícone roda a `PID_INTRO_A_ICON_SIZE` (260px — maior que os 200px das outras duas intros, pedido explícito do usuário numa rodada seguinte, "aumente um pouco o tamanho do mascote... para que fique mais fácil sua visualização"), com `PID_INTRO_A_ICON_K = PID_INTRO_A_ICON_SIZE / 420` calculado **a partir** dessa constante (não fixo) — só os deslocamentos aplicados via CSS `transform` em px reais no elemento `<svg>` (a queda `drop`, o "lift" do pulo, o blur da sombra) são multiplicados por esse fator; as transformações internas em unidades do `viewBox` (balanço dos balões, ângulo do braço, comprimento da língua-de-sogra) escalam junto automaticamente, e a física do confete (px reais fora do SVG) é multiplicada pelo mesmo fator.
|
||||||
|
- **Tema claro**: mesmo raciocínio das outras duas intros — fundo acompanha `--bg-canvas`, texto claro (pensado pra contrastar com fundo escuro) vira escuro; o ícone não precisa de override (roxo/dourado com contraste de sobra nos dois fundos).
|
||||||
|
|
||||||
|
### Bolo de aniversário (`static/css/aniversario-decor.css`), pedido numa rodada seguinte
|
||||||
|
|
||||||
|
Pedido explícito do usuário pra "deixar o tema mais completo". A mesma rodada também trouxe bandeirinhas de festa nos cantos (login + Principal, nos mesmos 2 cantos das teias do Halloween), **removidas a pedido do usuário logo depois** de vê-las na tela — não tentar ressuscitá-las sem pedido explícito. O bolo ficou.
|
||||||
|
|
||||||
|
- **Bolo de aniversário** (`pid-aniv-cake`, `pid-cake-*`): só na Principal, `position:fixed` no canto inferior-direito da **janela** (não da página — continua visível mesmo com scroll, ao contrário das teias de aranha, que são `position:absolute` dentro do conteúdo). Duas camadas com gotejamento de cobertura (`pid-cake-drip1`/`drip2`, um path ondulado) e 3 velas com chama tremulando continuamente (`pidCakeFlicker`, atrasos diferentes por vela pra não tremerem em sincronia). Cada gotejamento usa a cor da camada DE CIMA (creme sobre o dourado, dourado sobre o roxo) — pintá-lo da cor da própria camada o deixaria invisível, que foi como nasceu.
|
||||||
|
- **Iluminação das velas** (pedida numa rodada seguinte), em duas camadas: um `drop-shadow` duplo no próprio `.pid-cake-flame` (o brilho colado na chama, que pulsa junto com `pidCakeFlicker`) e o `.pid-cake-halo` — uma elipse com `<radialGradient>` âmbar, posicionada **depois das camadas do bolo e antes das velas**, pra a luz lavar o topo do bolo sem desbotar as chamas. Os dois pulsam em ciclos de durações diferentes (1,4s e 2,6s) de propósito, pra não parecerem sincronizados. O `filter` está também na regra base de `.pid-cake-flame`, não só nos keyframes, pra o brilho sobreviver enquanto `pidCakeFlameOut` toca; e o halo tem seu próprio `pidCakeHaloOut`, senão sobraria uma mancha quente boiando no canto depois das velas apagadas. No tema claro a intensidade cai pela metade (`--halo-max`) — brilho quente sobre fundo claro lê como névoa suja, não como luz.
|
||||||
|
- **Clicar apaga as velinhas** (pedido explícito do usuário): `pidApplyAniversarioCake(containerId)` liga o clique num `<button>` (não numa `<div>`, pra ganhar foco por teclado/`aria-label` de graça) — adiciona `.is-blowing` (toca `pidCakeFlameOut`/`pidCakeSmokeRise` uma vez, a fumaça sobe e some enquanto a chama encolhe) e, depois de `PID_CAKE_BLOW_MS` (1400ms), troca pra `.is-blown` (estado final em repouso, sem `animation` — mesma ideia de `.pw-spider.is-dead`).
|
||||||
|
- **O estado apagado NÃO é persistido, de propósito** — as velinhas reacendem a cada carregamento da página, e o atalho Tab+B continua sendo só da aranha do Halloween. A persistência chegou a existir aqui (`pid_aniversario_cake_blown` no `localStorage`, copiada de `pidMarkSpiderDead`) e foi **removida depois que o usuário relatou como bug**: "não aparece o fogo das velinhas para funcionar a animação de apagar elas ao clickar". O que acontecia: quem clicasse no bolo uma vez nunca mais via as velas acesas (nem o listener de clique era ligado, já que o bolo nascia direto em `.is-blown`), sem nenhuma pista disso na tela, e só voltava com um atalho de teclado obscuro. A aranha persiste porque o usuário **pediu** isso explicitamente; aqui o pedido foi só "ao clicar nele as velas se apagam" — a persistência era invenção minha por analogia. **Lição pra decoração futura**: só persistir estado de easter egg quando pedido, senão o efeito desaparece de vez no primeiro uso.
|
||||||
|
- **Bug real — o bolo nasceu ocupando a tela inteira, fora do canto**: a `<div>` em `portal.html` tinha só o `id` (`#pid-aniv-cake`), sem a classe `.pid-aniv-cake` que carrega TODO o dimensionamento/posicionamento (`position:fixed`, `right`/`bottom`, `width:clamp(...)`) — sem ela a div virou um bloco normal do fluxo, com a largura inteira do conteúdo, e o `<svg>` (`width:100%`) esticou junto. As classes `.is-blowing`/`.is-blown` também não teriam efeito, pelo mesmo motivo. **Ao adicionar um container decorativo novo, conferir que ele leva classe E id** — o id é só o gancho do JS, quem estiliza é a classe (mesmo padrão dos containers de teia, que já nasceram com as duas).
|
||||||
|
- **Bug real — as chamas flutuavam acima das velas**: nasciam alguns pontos acima do topo de cada vela, soltas no ar; agora começam exatamente no `y` do `<rect>` da vela correspondente. A fumaça também precisou de `transform-box:fill-box` — sem isso, o `scale` da subida cresce a partir da origem do viewBox e joga o traço pra longe do pavio.
|
||||||
|
- **Outros dois ajustes visuais da mesma rodada**: as chamas nasciam alguns pontos acima do topo das velas (flutuando soltas) — agora começam exatamente no `y` do `<rect>` de cada vela; e o gotejamento de cobertura de cada camada estava pintado da mesma cor da própria camada (portanto invisível) — agora cada gotejamento usa a cor da camada de cima (creme escorrendo sobre o dourado, dourado sobre o roxo).
|
||||||
|
|
||||||
## Opt-out do usuário ("Temas sazonais", menu da conta)
|
## Opt-out do usuário ("Temas sazonais", menu da conta)
|
||||||
|
|
||||||
Pedido explícito do usuário — "essa medida serve para caso o usuário tenha alguma preferência com relação aos temas, ou no caso do halloween, se tiver aracnofobia". Botão-toggle (`.seasonal-theme-toggle`, `components.css`) no dropdown da conta (`#account-dropdown`, duplicado nos 13 shells — mesmo padrão de duplicação de `sidebar__brand` já documentado no `CLAUDE.md` raiz, "Ordem de `<script>`"), logo abaixo de "Tema de cores", com dois estados "Ativo"/"Inativo" (`aria-pressed`, cor de destaque quando ativo). Wiring em `account.js`, junto do bloco de `theme-swatches` — não em `seasonal-theme.js`, que só guarda a preferência.
|
Pedido explícito do usuário — "essa medida serve para caso o usuário tenha alguma preferência com relação aos temas, ou no caso do halloween, se tiver aracnofobia". Botão-toggle (`.seasonal-theme-toggle`, `components.css`) no dropdown da conta (`#account-dropdown`, duplicado nos 13 shells — mesmo padrão de duplicação de `sidebar__brand` já documentado no `CLAUDE.md` raiz, "Ordem de `<script>`"), logo abaixo de "Tema de cores", com dois estados "Ativo"/"Inativo" (`aria-pressed`, cor de destaque quando ativo). Wiring em `account.js`, junto do bloco de `theme-swatches` — não em `seasonal-theme.js`, que só guarda a preferência.
|
||||||
|
|
||||||
- **Armazenamento**: `pid_seasonal_theme_opt_out` no `localStorage` (`"1"` = desligado), mesmo padrão de `pid_theme`/`pid_color_theme` (`theme.js`) — preferência de navegador, nunca migrada pro banco.
|
- **Armazenamento**: `pid_seasonal_theme_opt_out` no `localStorage` (`"1"` = desligado), mesmo padrão de `pid_theme`/`pid_color_theme` (`theme.js`) — preferência de navegador, nunca migrada pro banco.
|
||||||
- **Prioridade máxima**: `pidIsHalloweenActive()` checa `pidSeasonalThemesOptedOut()` **antes** de qualquer outra coisa, inclusive `PID_HALLOWEEN_PREVIEW_FORCE` — se o usuário desligou, fica desligado mesmo com o preview forçado ligado.
|
- **Prioridade máxima**: `pidIsHalloweenActive()`/`pidIsAniversarioActive()` checam `pidSeasonalThemesOptedOut()` **antes** de qualquer outra coisa, inclusive o respectivo preview force — se o usuário desligou, fica desligado mesmo com o preview forçado ligado. O toggle vale pros dois temas de uma vez (não há opt-out por tema).
|
||||||
- **Recarrega a página ao alternar**: o tema sazonal (ícone, teias, favicon) já foi aplicado via substituição de `innerHTML` no carregamento da página atual — não dá pra desfazer isso in-place de forma confiável, então o clique já salva a preferência e chama `window.location.reload()`. Importante sobretudo pra quem desligou por aracnofobia: a aranha precisa sumir na hora, não só na próxima navegação.
|
- **Recarrega a página ao alternar**: o tema sazonal (ícone, teias, favicon) já foi aplicado via substituição de `innerHTML` no carregamento da página atual — não dá pra desfazer isso in-place de forma confiável, então o clique já salva a preferência e chama `window.location.reload()`. Importante sobretudo pra quem desligou por aracnofobia: a aranha precisa sumir na hora, não só na próxima navegação.
|
||||||
|
|||||||
232
static/css/aniversario-decor.css
Normal file
232
static/css/aniversario-decor.css
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
/* Bolo de aniversário do tema sazonal Aniversário (56 anos da De Paula
|
||||||
|
Contadores, só 15/10) — canto inferior-direito da tela Principal, com
|
||||||
|
velinhas que o usuário apaga clicando. Escondido por padrão (`hidden` no
|
||||||
|
HTML) e só exibido via `pidApplyAniversarioCake()` (seasonal-theme.js)
|
||||||
|
quando `pidIsAniversarioActive()` — ver
|
||||||
|
`docs/temas-sazonais/temas-sazonais.md`. */
|
||||||
|
|
||||||
|
.pid-aniv-cake {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 16px;
|
||||||
|
width: clamp(90px, 9vw, 130px);
|
||||||
|
z-index: 5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake__btn {
|
||||||
|
all: unset;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake__btn svg {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
overflow: visible;
|
||||||
|
filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.4));
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-shadow {
|
||||||
|
fill: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cada gotejamento é da cor da camada DE CIMA (escorre sobre a de baixo) —
|
||||||
|
pintá-lo da mesma cor da própria camada o tornaria invisível. */
|
||||||
|
.pid-cake-tier2 {
|
||||||
|
fill: #7b5ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-drip2 {
|
||||||
|
fill: #c6a24a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-tier1 {
|
||||||
|
fill: #c6a24a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-drip1 {
|
||||||
|
fill: #f2ede3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-dot--blue {
|
||||||
|
fill: #2e86de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-dot--pink {
|
||||||
|
fill: #e8446e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-dot--amber {
|
||||||
|
fill: #f2a93b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A luz das velas é feita em duas camadas: o `drop-shadow` aqui é o brilho
|
||||||
|
colado na própria chama (o "núcleo"), e `.pid-cake-halo` abaixo é o halo
|
||||||
|
largo de luz no ar em volta das velas. Os dois pulsam, mas em ciclos de
|
||||||
|
durações diferentes (1,4s e 2,6s) pra não parecerem sincronizados —
|
||||||
|
fogo real não pisca no compasso. O `filter` fica também na regra base (não
|
||||||
|
só nos keyframes) pra o brilho continuar existindo enquanto a chama se
|
||||||
|
apaga, quando `pidCakeFlameOut` assume o lugar do tremular. */
|
||||||
|
.pid-cake-flame {
|
||||||
|
fill: #f2a93b;
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
filter: drop-shadow(0 0 2px rgba(242, 169, 59, 0.95)) drop-shadow(0 0 7px rgba(242, 169, 59, 0.5));
|
||||||
|
animation: pidCakeFlicker 1.4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-halo {
|
||||||
|
/* Pico da intensidade — o tema claro abaixa (um brilho quente sobre fundo
|
||||||
|
claro vira névoa suja em vez de luz). */
|
||||||
|
--halo-max: 1;
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center;
|
||||||
|
animation: pidCakeHalo 2.6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] .pid-cake-halo {
|
||||||
|
--halo-max: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* `nth-of-type` (não `nth-child`): as 3 velas são os 3 únicos `<g>` do SVG,
|
||||||
|
mas estão cercadas de `<path>`/`<circle>` do bolo, então a contagem por
|
||||||
|
posição absoluta não acertaria nenhuma delas. */
|
||||||
|
.pid-cake-candle:nth-of-type(2) .pid-cake-flame {
|
||||||
|
animation-delay: -0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-candle:nth-of-type(3) .pid-cake-flame {
|
||||||
|
animation-delay: -0.9s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-cake-smoke {
|
||||||
|
stroke: #b9aecb;
|
||||||
|
stroke-width: 1.1;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
opacity: 0;
|
||||||
|
/* Sem `fill-box`, o `scale` da fumaça subindo cresceria a partir da origem
|
||||||
|
do viewBox e jogaria o traço pra longe do pavio. */
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clicar apaga as velinhas — toca uma vez (`pidApplyAniversarioCake()`,
|
||||||
|
seasonal-theme.js, adiciona `.is-blowing` no clique e troca pra `.is-blown`
|
||||||
|
`PID_CAKE_BLOW_MS` depois). `.is-blown` é o estado final em repouso (sem
|
||||||
|
`animation`), e dura só até o próximo carregamento da página: as velinhas
|
||||||
|
reacendem sozinhas, o estado apagado não é persistido de propósito (ver
|
||||||
|
`docs/temas-sazonais/temas-sazonais.md`). */
|
||||||
|
.pid-aniv-cake.is-blowing .pid-cake-flame {
|
||||||
|
animation: pidCakeFlameOut 500ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blowing .pid-cake-smoke {
|
||||||
|
animation: pidCakeSmokeRise 1400ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blowing .pid-cake-halo {
|
||||||
|
animation: pidCakeHaloOut 600ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blown .pid-cake-flame {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blown .pid-cake-smoke {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blown .pid-cake-halo {
|
||||||
|
animation: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-cake.is-blowing .pid-aniv-cake__btn,
|
||||||
|
.pid-aniv-cake.is-blown .pid-aniv-cake__btn {
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidCakeFlicker {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scale(1) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
filter: drop-shadow(0 0 2px rgba(242, 169, 59, 0.95)) drop-shadow(0 0 7px rgba(242, 169, 59, 0.5));
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.08) rotate(-3deg);
|
||||||
|
opacity: 0.9;
|
||||||
|
filter: drop-shadow(0 0 3px rgba(242, 169, 59, 1)) drop-shadow(0 0 11px rgba(242, 169, 59, 0.7));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidCakeHalo {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: calc(var(--halo-max) * 0.72);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: var(--halo-max);
|
||||||
|
transform: scale(1.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidCakeFlameOut {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) skewX(0deg);
|
||||||
|
filter: drop-shadow(0 0 3px rgba(242, 169, 59, 1)) drop-shadow(0 0 11px rgba(242, 169, 59, 0.7));
|
||||||
|
}
|
||||||
|
40% {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: scale(1.3) skewX(14deg);
|
||||||
|
filter: drop-shadow(0 0 2px rgba(242, 169, 59, 0.6)) drop-shadow(0 0 6px rgba(242, 169, 59, 0.3));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.2) skewX(0deg);
|
||||||
|
filter: drop-shadow(0 0 0 rgba(242, 169, 59, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A luz morre junto com a chama — um halo sobrevivendo às velas apagadas
|
||||||
|
deixaria uma mancha quente boiando no canto da tela. */
|
||||||
|
@keyframes pidCakeHaloOut {
|
||||||
|
0% {
|
||||||
|
opacity: calc(var(--halo-max) * 0.85);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.82);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidCakeSmokeRise {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
}
|
||||||
|
15% {
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-14px) scale(1.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.pid-aniv-cake {
|
||||||
|
width: clamp(72px, 20vw, 96px);
|
||||||
|
right: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -249,6 +249,205 @@ svg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Variante "clique de festa" da logo, só durante o tema sazonal Aniversário
|
||||||
|
(`seasonal-theme.js`/`pidIsAniversarioActive()`) — convive com a piscada
|
||||||
|
normal em vez de substituí-la (o clique dispara os dois: primeiro este
|
||||||
|
pulo/confete/língua-de-sogra, depois a piscada de olhos de sempre — ver
|
||||||
|
`sidebar-brand.js`/`login-logo-blink.js`), já que a cara de repouso do
|
||||||
|
mascote de aniversário não muda. `.pid-aniv-hop` é o grupo raiz injetado
|
||||||
|
(sidebar colapsada/expandida e botão do login) por FORA do grupo que
|
||||||
|
carrega o `transform` de atributo estático (`.pid-aniv-icon-rig`) — mesmo
|
||||||
|
cuidado de `.pid-icon-eye`/`.pid-vamp-icon-rig` acima: um `transform` de
|
||||||
|
CSS no MESMO elemento que já tem `transform` de atributo substitui o
|
||||||
|
atributo inteiro, perdendo a posição. Todas as sub-animações usam a mesma
|
||||||
|
duração (`PID_ANIVERSARIO_PARTY_MS`, 650ms) e escalonam o efeito só via
|
||||||
|
percentuais internos do próprio `@keyframes`, mesmo truque do susto do
|
||||||
|
vampiro (que também usa uma única duração de 900ms pra tudo). */
|
||||||
|
.pid-aniv-hop,
|
||||||
|
.pid-aniv-balloons,
|
||||||
|
.pid-aniv-arm {
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-horn {
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: left center;
|
||||||
|
transform: scaleX(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-pucker-outer,
|
||||||
|
.pid-aniv-pucker-inner {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-confetti > * {
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying {
|
||||||
|
animation: pidAnivHop 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-balloons {
|
||||||
|
animation: pidAnivBalloonSwing 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-arm {
|
||||||
|
animation: pidAnivArmThrow 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-smile {
|
||||||
|
animation: pidAnivSmileOut 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-pucker-outer,
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-pucker-inner {
|
||||||
|
animation: pidAnivPuckerIn 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-horn {
|
||||||
|
animation: pidAnivHornUnroll 650ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > * {
|
||||||
|
animation-name: pidAnivConfettiPiece;
|
||||||
|
animation-duration: 650ms;
|
||||||
|
animation-timing-function: ease-out;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(1) {
|
||||||
|
--tx: 9px;
|
||||||
|
--ty: -12px;
|
||||||
|
animation-delay: 0ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(2) {
|
||||||
|
--tx: -7px;
|
||||||
|
--ty: -16px;
|
||||||
|
animation-delay: 30ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(3) {
|
||||||
|
--tx: 13px;
|
||||||
|
--ty: -4px;
|
||||||
|
animation-delay: 60ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(4) {
|
||||||
|
--tx: -11px;
|
||||||
|
--ty: -6px;
|
||||||
|
animation-delay: 15ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(5) {
|
||||||
|
--tx: 5px;
|
||||||
|
--ty: -18px;
|
||||||
|
animation-delay: 90ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(6) {
|
||||||
|
--tx: -4px;
|
||||||
|
--ty: -20px;
|
||||||
|
animation-delay: 45ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(7) {
|
||||||
|
--tx: 15px;
|
||||||
|
--ty: -13px;
|
||||||
|
animation-delay: 75ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pid-aniv-hop.is-partying .pid-aniv-confetti > *:nth-child(8) {
|
||||||
|
--tx: -15px;
|
||||||
|
--ty: -9px;
|
||||||
|
animation-delay: 20ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivHop {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0) scaleY(1);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: translateY(-7px) scaleY(1.06);
|
||||||
|
}
|
||||||
|
55% {
|
||||||
|
transform: translateY(1px) scaleY(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivBalloonSwing {
|
||||||
|
0%, 100% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
30% {
|
||||||
|
transform: rotate(-9deg);
|
||||||
|
}
|
||||||
|
60% {
|
||||||
|
transform: rotate(6deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivArmThrow {
|
||||||
|
0%, 100% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
35% {
|
||||||
|
transform: rotate(-28deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivSmileOut {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivPuckerIn {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
30%, 70% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivHornUnroll {
|
||||||
|
0%, 100% {
|
||||||
|
transform: scaleX(0);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
20% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
45%, 68% {
|
||||||
|
transform: scaleX(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
90% {
|
||||||
|
transform: scaleX(0.15);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pidAnivConfettiPiece {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(0, 0) scale(1) rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(var(--tx), var(--ty)) scale(0.4) rotate(200deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* scrollbar discreta */
|
/* scrollbar discreta */
|
||||||
* {
|
* {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
|||||||
308
static/css/login-intro-aniversario.css
Normal file
308
static/css/login-intro-aniversario.css
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
/* Intro pós-login sazonal "Aniversário" (56 anos da De Paula Contadores) —
|
||||||
|
variante de login-intro.css, ativa só quando `pidIsAniversarioActive()`
|
||||||
|
(seasonal-theme.js, só 15/10) é verdadeiro; fora desse dia,
|
||||||
|
login-intro.css/login-intro.js (ou a variante Halloween) continuam sendo
|
||||||
|
os únicos usados. Porta a composição de cenas Chegada/Confete/Texto/
|
||||||
|
Serpentina autorada em "P.I.D. Logo Design Aniversário/Logo intro
|
||||||
|
animation/export-56/pid-56-anos.html" (pasta de logos, fora do
|
||||||
|
repositório) — ver `login-intro-aniversario.js` e
|
||||||
|
`docs/temas-sazonais/temas-sazonais.md`. Mesma paleta da marca P.I.D.,
|
||||||
|
acrescida das cores dos balões/confete (azul/âmbar/rosa), hardcoded aqui
|
||||||
|
como no arquivo de origem (não são tokens de tema). Só usado em
|
||||||
|
index.html. */
|
||||||
|
|
||||||
|
.login-intro-a {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 200;
|
||||||
|
background: radial-gradient(120% 80% at 50% 38%, rgba(198, 162, 74, 0.14), transparent 60%), #1a1430;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 500ms ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a.is-visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__flash {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: #c6a24a;
|
||||||
|
opacity: 0;
|
||||||
|
mix-blend-mode: screen;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__stage {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__icon-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 260px;
|
||||||
|
flex: none;
|
||||||
|
transform: translate(0, 0) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__icon {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
overflow: visible;
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloons path {
|
||||||
|
stroke: #b9aecb;
|
||||||
|
stroke-width: 0.99;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon--blue {
|
||||||
|
fill: #2e86de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon-tie--blue {
|
||||||
|
fill: #2e86de;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon--amber {
|
||||||
|
fill: #f2a93b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon-tie--amber {
|
||||||
|
fill: #f2a93b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon--pink {
|
||||||
|
fill: #e8446e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__balloon-tie--pink {
|
||||||
|
fill: #e8446e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__arm path {
|
||||||
|
stroke: #7b5ba8;
|
||||||
|
stroke-width: 3.5;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__arm circle {
|
||||||
|
fill: #7b5ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__body-path {
|
||||||
|
fill: #7b5ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__tab {
|
||||||
|
fill: #c6a24a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__tab-shadow {
|
||||||
|
fill: #7b5ba8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__screen {
|
||||||
|
fill: #241c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__eye {
|
||||||
|
fill: #f2ede3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__pupil {
|
||||||
|
fill: #241c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__smile {
|
||||||
|
stroke: #c6a24a;
|
||||||
|
stroke-width: 3;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__pucker-outer {
|
||||||
|
fill: #c6a24a;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__pucker-inner {
|
||||||
|
fill: #241c33;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__horn-tube {
|
||||||
|
stroke: #e8446e;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__horn-dash {
|
||||||
|
stroke: #f2ede3;
|
||||||
|
stroke-width: 1.3;
|
||||||
|
stroke-dasharray: 2 4;
|
||||||
|
fill: none;
|
||||||
|
stroke-linecap: round;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__horn-puff--1 {
|
||||||
|
fill: #f2a93b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__horn-puff--2 {
|
||||||
|
fill: #c6a24a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__confetti {
|
||||||
|
position: absolute;
|
||||||
|
left: 28%;
|
||||||
|
top: 26%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__confetti-piece {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 14px;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__parabens-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__parabens {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
color: #f2ede3;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__wordmark-img {
|
||||||
|
height: 44px;
|
||||||
|
width: auto;
|
||||||
|
display: block;
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: left center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__rule {
|
||||||
|
width: 260px;
|
||||||
|
height: 3px;
|
||||||
|
background: linear-gradient(90deg, #c6a24a, #e8446e);
|
||||||
|
transform-origin: left center;
|
||||||
|
transform: scaleX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__numrow {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__num {
|
||||||
|
display: inline-block;
|
||||||
|
font-family: "Space Grotesk", sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 4.4rem;
|
||||||
|
line-height: 1;
|
||||||
|
color: #c6a24a;
|
||||||
|
opacity: 0;
|
||||||
|
transform-origin: left bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__num-label {
|
||||||
|
font-family: "DM Mono", ui-monospace, monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
letter-spacing: 0.2em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #f2ede3;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__tagline {
|
||||||
|
font-family: "DM Mono", ui-monospace, monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
letter-spacing: 0.22em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c6a24a;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tema claro: mesmo raciocínio de login-intro.css/login-intro-halloween.css —
|
||||||
|
fundo escuro fixo destoava do resto do Portal nesse tema, então vira
|
||||||
|
`--bg-canvas` claro; o texto pensado pra contrastar com fundo escuro
|
||||||
|
precisa virar escuro. O ícone não precisa de override (o roxo/dourado tem
|
||||||
|
contraste de sobra nos dois fundos). */
|
||||||
|
:root[data-theme="light"] .login-intro-a {
|
||||||
|
background: radial-gradient(120% 80% at 50% 38%, rgba(198, 162, 74, 0.1), transparent 60%), var(--bg-canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] .login-intro-a__parabens {
|
||||||
|
color: #241c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root[data-theme="light"] .login-intro-a__num-label {
|
||||||
|
color: #241c33;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.login-intro-a__stage {
|
||||||
|
gap: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__icon-wrap {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__parabens {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__wordmark-img {
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__rule {
|
||||||
|
width: 170px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__num {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__num-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-intro-a__tagline {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
letter-spacing: 0.16em;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
static/img/pid-aniversario-depaula-wordmark.png
Normal file
BIN
static/img/pid-aniversario-depaula-wordmark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 279 KiB |
7
static/img/pid-aniversario-favicon.svg
Normal file
7
static/img/pid-aniversario-favicon.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 8.1 KiB |
26
static/img/pid-aniversario-icone-escuro.svg
Normal file
26
static/img/pid-aniversario-icone-escuro.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.5 KiB |
32
static/img/pid-aniversario-logo-horizontal-escuro.svg
Normal file
32
static/img/pid-aniversario-logo-horizontal-escuro.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 10 KiB |
@ -67,7 +67,9 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
const tocaIntro =
|
const tocaIntro =
|
||||||
typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive() && typeof pidPlayLoginIntroHalloween === "function"
|
typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive() && typeof pidPlayLoginIntroHalloween === "function"
|
||||||
? pidPlayLoginIntroHalloween
|
? pidPlayLoginIntroHalloween
|
||||||
: pidPlayLoginIntro;
|
: typeof pidIsAniversarioActive === "function" && pidIsAniversarioActive() && typeof pidPlayLoginIntroAniversario === "function"
|
||||||
|
? pidPlayLoginIntroAniversario
|
||||||
|
: pidPlayLoginIntro;
|
||||||
tocaIntro(() => {
|
tocaIntro(() => {
|
||||||
sessionStorage.setItem("pid_reveal_portal", "1");
|
sessionStorage.setItem("pid_reveal_portal", "1");
|
||||||
window.location.href = "portal.html";
|
window.location.href = "portal.html";
|
||||||
|
|||||||
278
static/js/login-intro-aniversario.js
Normal file
278
static/js/login-intro-aniversario.js
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Intro pós-login sazonal "Aniversário" (56 anos da De Paula Contadores) —
|
||||||
|
* variante de pidPlayLoginIntro (login-intro.js), portada da composição
|
||||||
|
* autorada em "P.I.D. Logo Design Aniversário/Logo intro animation/export-56/
|
||||||
|
* pid-56-anos.html" (pasta de logos, fora do repositório — um componente
|
||||||
|
* React/JSX, `pid-56.jsx`, extraído do bundle "Design Canvas" com o mesmo
|
||||||
|
* processo ad-hoc de gzip+base64 por uuid já usado pra extrair a composição
|
||||||
|
* do Halloween). Cenas originais Chegada/Confete/Texto/Serpentina
|
||||||
|
* (1.1s/1.4s/1.6s/1.8s, total 5.9s): o personagem cai com os balões e
|
||||||
|
* quica ao pousar, dá um pulo e joga confete pro alto, desliza pra esquerda
|
||||||
|
* enquanto "Parabéns" + a logo da De Paula + "56 Anos" entram, e por fim
|
||||||
|
* assopra uma língua-de-sogra que desenrola e recolhe enquanto o slogan
|
||||||
|
* pousa. Só é chamada quando `pidIsAniversarioActive()` (seasonal-theme.js,
|
||||||
|
* só 15/10); fora desse dia, `pidPlayLoginIntro()` (ou a variante Halloween)
|
||||||
|
* continua sendo a usada. Reaproveita PID_INTRO_EASING/pidIntroAnimate/
|
||||||
|
* pidIntroClamp/pidIntroCornerTarget já globais de login-intro.js, mesma
|
||||||
|
* página — só a coreografia é própria daqui.
|
||||||
|
*
|
||||||
|
* Diferenças em relação à composição original:
|
||||||
|
* - A composição original foi autorada num canvas de vídeo 1920×1080 com o
|
||||||
|
* ícone e o texto posicionados por coordenadas absolutas; aqui, seguindo o
|
||||||
|
* mesmo padrão de `.login-intro__stage`/`.login-intro-h__stage`, os dois
|
||||||
|
* viram uma única linha flexível (ícone + mensagem lado a lado) — o
|
||||||
|
* "deslizar pra esquerda" do ícone (cena Texto) é reproduzido pela mesma
|
||||||
|
* técnica de SHIFT_X já usada nas outras duas intros (o ícone se desloca
|
||||||
|
* por `transform`, a mensagem só aparece no lugar fixo ao lado).
|
||||||
|
* - O "zoom" de fundo do canvas original (respiro sutil de 1.05→1.0→1.02) e
|
||||||
|
* os dois gradientes de vinheta foram descartados — floreios de canvas de
|
||||||
|
* vídeo sem efeito perceptível numa janela de overlay pequena.
|
||||||
|
* - A composição original termina com tudo esmaecendo junto (pensada como
|
||||||
|
* vídeo solto); foi acrescentada uma cena final "Fly" (mesma ideia/
|
||||||
|
* matemática do `pidIntroCornerTarget`, já usada pelas outras duas intros)
|
||||||
|
* pra o ícone voar até o canto real da sidebar antes de tudo desaparecer.
|
||||||
|
* - "56 ANOS" é específico deste ano (56º aniversário) — ano que vem,
|
||||||
|
* atualizar o número aqui e em `seasonal-theme.js`/`index.html` a partir
|
||||||
|
* de uma composição nova (mesmo espírito de "marca temporária" do
|
||||||
|
* `leiame.md` original).
|
||||||
|
*/
|
||||||
|
|
||||||
|
const PID_INTRO_A_CUES = { Chegada: 0, Confete: 1.1, Texto: 2.5, Serpentina: 4.1, Fly: 5.9 };
|
||||||
|
const PID_INTRO_A_TOTAL = 6.8;
|
||||||
|
const PID_INTRO_A_ICON_SIZE = 260; // maior que os 200px das outras 2 intros — pedido explícito do usuário, "mais fácil sua visualização"
|
||||||
|
const PID_INTRO_A_ICON_K = PID_INTRO_A_ICON_SIZE / 420; // coreografia original autorada pra um ícone de 420px
|
||||||
|
|
||||||
|
const PID_INTRO_A_CONFETTI_COLORS = ["#C6A24A", "#2E86DE", "#F2A93B", "#E8446E", "#F2EDE3", "#7B5BA8"];
|
||||||
|
|
||||||
|
// mesmo gerador pseudo-aleatório determinístico do original (pid-56.jsx),
|
||||||
|
// pra toda visita produzir a mesma explosão de confete.
|
||||||
|
function pidIntroARnd(i, salt) {
|
||||||
|
const x = Math.sin(i * 127.1 + salt * 311.7) * 43758.5453;
|
||||||
|
return x - Math.floor(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pidIntroABuildConfettiPieces() {
|
||||||
|
return Array.from({ length: 58 }, (_, i) => {
|
||||||
|
const a = (pidIntroARnd(i, 1) - 0.5) * 2.5 - Math.PI / 2;
|
||||||
|
const speed = 300 + pidIntroARnd(i, 2) * 620;
|
||||||
|
return {
|
||||||
|
i,
|
||||||
|
vx: Math.cos(a) * speed * (0.75 + pidIntroARnd(i, 7) * 0.6),
|
||||||
|
vy: Math.sin(a) * speed,
|
||||||
|
delay: pidIntroARnd(i, 3) * 0.24,
|
||||||
|
size: 9 + pidIntroARnd(i, 4) * 15,
|
||||||
|
long: pidIntroARnd(i, 8) > 0.5,
|
||||||
|
spin: (pidIntroARnd(i, 5) - 0.5) * 1500,
|
||||||
|
color: PID_INTRO_A_CONFETTI_COLORS[i % PID_INTRO_A_CONFETTI_COLORS.length],
|
||||||
|
sway: (pidIntroARnd(i, 6) - 0.5) * 90,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pidPlayLoginIntroAniversario(onDone) {
|
||||||
|
const overlay = document.getElementById("login-intro-aniversario");
|
||||||
|
const icon = document.getElementById("login-intro-a-icon");
|
||||||
|
const iconWrap = document.getElementById("login-intro-a-iconwrap");
|
||||||
|
const stage = document.getElementById("login-intro-a-stage");
|
||||||
|
const message = document.getElementById("login-intro-a-message");
|
||||||
|
const flash = document.getElementById("login-intro-a-flash");
|
||||||
|
const confettiHost = document.getElementById("login-intro-a-confetti");
|
||||||
|
if (!overlay || !icon || !iconWrap || !stage || !message || !flash || !confettiHost) {
|
||||||
|
onDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const balloons = icon.querySelector(".login-intro-a__balloons");
|
||||||
|
const arm = icon.querySelector(".login-intro-a__arm");
|
||||||
|
const smile = document.getElementById("login-intro-a-smile");
|
||||||
|
const puckerOuter = document.getElementById("login-intro-a-pucker-outer");
|
||||||
|
const puckerInner = document.getElementById("login-intro-a-pucker-inner");
|
||||||
|
const hornGroup = document.getElementById("login-intro-a-horn");
|
||||||
|
const hornTube = document.getElementById("login-intro-a-horn-tube");
|
||||||
|
const hornDash = document.getElementById("login-intro-a-horn-dash");
|
||||||
|
const puff1 = document.getElementById("login-intro-a-puff1");
|
||||||
|
const puff2 = document.getElementById("login-intro-a-puff2");
|
||||||
|
const parabens = document.getElementById("login-intro-a-parabens");
|
||||||
|
const wordmarkImg = document.getElementById("login-intro-a-wordmark-img");
|
||||||
|
const rule = document.getElementById("login-intro-a-rule");
|
||||||
|
const num = document.getElementById("login-intro-a-num");
|
||||||
|
const numLabel = document.getElementById("login-intro-a-num-label");
|
||||||
|
const tagline = document.getElementById("login-intro-a-tagline");
|
||||||
|
|
||||||
|
// Peças de confete criadas uma vez (mesmo padrão de `pidApplyHalloweenCobwebs`
|
||||||
|
// pro markup só existir em memória) e reaproveitadas se a intro tocar de novo.
|
||||||
|
if (!confettiHost.dataset.piecesBuilt) {
|
||||||
|
const pieces = pidIntroABuildConfettiPieces();
|
||||||
|
pieces.forEach((p) => {
|
||||||
|
const el = document.createElement("div");
|
||||||
|
el.className = "login-intro-a__confetti-piece";
|
||||||
|
el.style.width = `${(p.size * PID_INTRO_A_ICON_K).toFixed(1)}px`;
|
||||||
|
el.style.height = `${((p.long ? p.size * 0.42 : p.size) * PID_INTRO_A_ICON_K).toFixed(1)}px`;
|
||||||
|
el.style.background = p.color;
|
||||||
|
el.style.borderRadius = p.long ? "1px" : "50%";
|
||||||
|
el.style.opacity = "0";
|
||||||
|
confettiHost.appendChild(el);
|
||||||
|
p.el = el;
|
||||||
|
});
|
||||||
|
confettiHost._pidPieces = pieces;
|
||||||
|
confettiHost.dataset.piecesBuilt = "1";
|
||||||
|
}
|
||||||
|
const confettiPieces = confettiHost._pidPieces;
|
||||||
|
|
||||||
|
const { easeOutCubic, easeInOutQuad, easeOutBack } = PID_INTRO_EASING;
|
||||||
|
const easeOutExpo = (t) => (t >= 1 ? 1 : 1 - Math.pow(2, -10 * t));
|
||||||
|
const CUES = PID_INTRO_A_CUES;
|
||||||
|
const K = PID_INTRO_A_ICON_K;
|
||||||
|
|
||||||
|
// ── cena Chegada: o personagem cai e quica ao pousar ──────────────────
|
||||||
|
const dropFn = pidIntroAnimate({ from: -260 * K, to: 0, start: CUES.Chegada + 0.05, end: CUES.Chegada + 0.95, ease: easeOutBack });
|
||||||
|
const appearFn = pidIntroAnimate({ start: CUES.Chegada + 0.05, end: CUES.Chegada + 0.5, ease: easeOutCubic });
|
||||||
|
|
||||||
|
// ── cena Confete: pulo + arremesso + explosão ─────────────────────────
|
||||||
|
const hopFn = pidIntroAnimate({ start: CUES.Confete - 0.12, end: CUES.Confete + 0.12, ease: easeOutExpo });
|
||||||
|
const hopDownFn = pidIntroAnimate({ start: CUES.Confete + 0.2, end: CUES.Confete + 0.6, ease: easeInOutQuad });
|
||||||
|
const confettiT0 = CUES.Confete + 0.05;
|
||||||
|
|
||||||
|
// ── cena Texto: ícone desliza, "Parabéns" + logo + "56 Anos" entram ──
|
||||||
|
const shiftFn = pidIntroAnimate({ start: CUES.Texto - 0.2, end: CUES.Texto + 0.6, ease: easeOutCubic });
|
||||||
|
const B = CUES.Texto + 0.1;
|
||||||
|
const parabensOFn = pidIntroAnimate({ start: B + 0.05, end: B + 0.45, ease: easeOutCubic });
|
||||||
|
const parabensYFn = pidIntroAnimate({ from: 28, to: 0, start: B + 0.05, end: B + 0.6, ease: easeOutBack });
|
||||||
|
const logoInFn = pidIntroAnimate({ start: B + 0.28, end: B + 0.85, ease: easeOutBack });
|
||||||
|
const ruleFn = pidIntroAnimate({ start: B + 0.45, end: B + 0.95, ease: easeInOutQuad });
|
||||||
|
const numFn = pidIntroAnimate({ start: B + 0.55, end: B + 1.15, ease: easeOutBack });
|
||||||
|
const tagFn = pidIntroAnimate({ start: B + 1.5, end: B + 2.0, ease: easeOutCubic });
|
||||||
|
|
||||||
|
// ── cena Serpentina: boca faz bico, língua-de-sogra desenrola e recolhe ─
|
||||||
|
const blowFn = pidIntroAnimate({ start: CUES.Serpentina + 0.05, end: CUES.Serpentina + 0.5, ease: easeOutExpo });
|
||||||
|
const recoilFn = pidIntroAnimate({ start: CUES.Serpentina + 0.65, end: CUES.Serpentina + 1.0, ease: easeInOutQuad });
|
||||||
|
|
||||||
|
// ── cena Fly (acrescentada): ícone voa até o canto real da sidebar ────
|
||||||
|
const flyFn = pidIntroAnimate({ start: CUES.Fly + 0.05, end: CUES.Fly + 0.55, ease: easeOutCubic });
|
||||||
|
const wordmarkOutFn = pidIntroAnimate({ from: 1, to: 0, start: CUES.Fly, end: CUES.Fly + 0.3, ease: easeOutCubic });
|
||||||
|
const stageFadeFn = pidIntroAnimate({ from: 1, to: 0, start: CUES.Fly + 0.5, end: CUES.Fly + 0.9, ease: easeOutCubic });
|
||||||
|
|
||||||
|
const SHIFT_X = -90;
|
||||||
|
const ICON_SIZE = PID_INTRO_A_ICON_SIZE;
|
||||||
|
const FINAL_ICON_SIZE = 40;
|
||||||
|
const FINAL_SCALE = FINAL_ICON_SIZE / ICON_SIZE;
|
||||||
|
const CORNER_MARGIN = 40;
|
||||||
|
|
||||||
|
const loginCard = document.querySelector(".login-card");
|
||||||
|
if (loginCard) loginCard.classList.add("is-leaving");
|
||||||
|
overlay.hidden = false;
|
||||||
|
void overlay.offsetWidth;
|
||||||
|
overlay.classList.add("is-visible");
|
||||||
|
|
||||||
|
let start;
|
||||||
|
|
||||||
|
function frame(now) {
|
||||||
|
const T = Math.min((now - start) / 1000, PID_INTRO_A_TOTAL);
|
||||||
|
|
||||||
|
const drop = dropFn(T);
|
||||||
|
const appear = appearFn(T);
|
||||||
|
const land = pidIntroClamp(1 - Math.abs(T - (CUES.Chegada + 0.92)) / 0.28, 0, 1);
|
||||||
|
const squash = 1 - land * 0.1;
|
||||||
|
|
||||||
|
const bob = Math.sin((T - CUES.Chegada) * 2.1) * 1.6;
|
||||||
|
const swing = Math.sin((T - CUES.Chegada) * 1.5) * 3.4;
|
||||||
|
|
||||||
|
const throwUp = pidIntroClamp(1 - Math.abs(T - (CUES.Confete + 0.1)) / 0.45, 0, 1);
|
||||||
|
const hop = hopFn(T);
|
||||||
|
const hopDown = hopDownFn(T);
|
||||||
|
const lift = pidIntroClamp(hop - hopDown, 0, 1) * 38 * K;
|
||||||
|
|
||||||
|
const blow = blowFn(T);
|
||||||
|
const recoil = recoilFn(T);
|
||||||
|
const horn = pidIntroClamp(blow - recoil, 0, 1);
|
||||||
|
const hornLen = 30 * horn;
|
||||||
|
const puff = pidIntroClamp(blow * 1.4 - recoil * 2, 0, 1);
|
||||||
|
const blowing = pidIntroClamp(blow * 1.8 - recoil * 2.2, 0, 1);
|
||||||
|
|
||||||
|
icon.style.opacity = String(appear);
|
||||||
|
icon.style.transform = `translateY(${(drop - lift).toFixed(2)}px) scaleY(${squash.toFixed(3)})`;
|
||||||
|
icon.style.filter = `drop-shadow(0 ${(26 * K).toFixed(1)}px ${(44 * K).toFixed(1)}px rgba(0,0,0,0.5))`;
|
||||||
|
|
||||||
|
balloons.setAttribute("transform", `translate(${swing.toFixed(2)} ${(bob - throwUp * 16).toFixed(2)}) rotate(${(swing * 0.5).toFixed(2)} 53 34)`);
|
||||||
|
arm.setAttribute("transform", `rotate(${(-throwUp * 22).toFixed(2)} 42 49)`);
|
||||||
|
|
||||||
|
smile.style.opacity = String(1 - blowing);
|
||||||
|
puckerOuter.style.opacity = String(blowing);
|
||||||
|
puckerInner.style.opacity = String(blowing);
|
||||||
|
|
||||||
|
const hornOpacity = pidIntroClamp(blow * 2.2, 0, 1) * (1 - recoil);
|
||||||
|
hornGroup.style.opacity = String(hornOpacity);
|
||||||
|
const hornD = `M25 57 q${(hornLen * 0.42).toFixed(2)} ${(-3 - horn * 2).toFixed(2)} ${hornLen.toFixed(2)} ${(-6 - horn * 5).toFixed(2)}`;
|
||||||
|
hornTube.setAttribute("d", hornD);
|
||||||
|
hornDash.setAttribute("d", hornD);
|
||||||
|
hornTube.style.strokeWidth = String(4 - horn * 1.6);
|
||||||
|
puff1.setAttribute("cx", String(25 + hornLen + 3));
|
||||||
|
puff1.setAttribute("cy", String(51 - horn * 5));
|
||||||
|
puff1.setAttribute("r", String(2.2 + puff * 2.4));
|
||||||
|
puff1.style.opacity = String(puff * 0.85);
|
||||||
|
puff2.setAttribute("cx", String(25 + hornLen + 8));
|
||||||
|
puff2.setAttribute("cy", String(48 - horn * 5));
|
||||||
|
puff2.setAttribute("r", String(1.4 + puff * 1.8));
|
||||||
|
puff2.style.opacity = String(puff * 0.6);
|
||||||
|
|
||||||
|
confettiPieces.forEach((p) => {
|
||||||
|
const e = T - confettiT0 - p.delay;
|
||||||
|
if (e <= 0) {
|
||||||
|
p.el.style.opacity = "0";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const x = (p.vx * e + Math.sin(e * 3.1 + p.i) * p.sway) * K;
|
||||||
|
const y = (p.vy * e + 520 * e * e) * K;
|
||||||
|
const fade = pidIntroClamp(1 - (e - 1.5) / 0.9, 0, 1);
|
||||||
|
p.el.style.opacity = String(fade);
|
||||||
|
p.el.style.transform = `translate(${x.toFixed(1)}px, ${y.toFixed(1)}px) rotate(${(p.spin * e).toFixed(1)}deg)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const burstWin = pidIntroClamp(1 - Math.abs(T - (CUES.Confete + 0.1)) / 0.3, 0, 1);
|
||||||
|
flash.style.opacity = String(burstWin * 0.1);
|
||||||
|
|
||||||
|
const shift = shiftFn(T);
|
||||||
|
const fly = flyFn(T);
|
||||||
|
const lockupX = SHIFT_X * shift;
|
||||||
|
const lockupScale = 1 - 0.26 * shift;
|
||||||
|
const target = pidIntroCornerTarget(ICON_SIZE, FINAL_ICON_SIZE, CORNER_MARGIN);
|
||||||
|
const flyX = lockupX + (target.x - lockupX) * fly;
|
||||||
|
const flyY = target.y * fly;
|
||||||
|
const flyScale = lockupScale + (FINAL_SCALE - lockupScale) * fly;
|
||||||
|
iconWrap.style.transform = `translate(${flyX.toFixed(1)}px, ${flyY.toFixed(1)}px) scale(${flyScale.toFixed(3)})`;
|
||||||
|
|
||||||
|
message.style.opacity = String(pidIntroClamp(shift * 1.5, 0, 1) * wordmarkOutFn(T));
|
||||||
|
|
||||||
|
const parabensY = parabensYFn(T);
|
||||||
|
parabens.style.opacity = String(parabensOFn(T));
|
||||||
|
parabens.style.transform = `translateY(${parabensY.toFixed(1)}px)`;
|
||||||
|
|
||||||
|
const logoIn = logoInFn(T);
|
||||||
|
wordmarkImg.style.opacity = String(logoIn);
|
||||||
|
wordmarkImg.style.transform = `translateY(${(-14 + (1 - logoIn) * 12).toFixed(1)}px) scale(${(0.9 + logoIn * 0.1).toFixed(3)})`;
|
||||||
|
|
||||||
|
rule.style.transform = `scaleX(${ruleFn(T).toFixed(3)})`;
|
||||||
|
|
||||||
|
const numVal = numFn(T);
|
||||||
|
num.style.opacity = String(pidIntroClamp(numVal, 0, 1));
|
||||||
|
num.style.transform = `scale(${(0.7 + numVal * 0.3).toFixed(3)})`;
|
||||||
|
numLabel.style.opacity = String(pidIntroClamp(numVal * 1.1 - 0.15, 0, 1));
|
||||||
|
|
||||||
|
const tag = tagFn(T);
|
||||||
|
tagline.style.opacity = String(tag);
|
||||||
|
tagline.style.transform = `translateY(${((1 - tag) * 12).toFixed(1)}px)`;
|
||||||
|
|
||||||
|
stage.style.opacity = String(stageFadeFn(T));
|
||||||
|
|
||||||
|
if (T < PID_INTRO_A_TOTAL) {
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
} else {
|
||||||
|
onDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
start = performance.now();
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
}, PID_INTRO_FADE_MS);
|
||||||
|
}
|
||||||
@ -1,24 +1,33 @@
|
|||||||
/*
|
/*
|
||||||
* Ícone "P.I.D." do card de login (`#login-logo-btn`). Fora do tema sazonal
|
* Ícone "P.I.D." do card de login (`#login-logo-btn`). Fora de qualquer tema
|
||||||
* Halloween: clicar faz os olhos do disquete piscarem
|
* sazonal: clicar faz os olhos do disquete piscarem
|
||||||
* (`.pid-icon-eye`/`pidIconBlink` em base.css, mesmo mecanismo da logo da
|
* (`.pid-icon-eye`/`pidIconBlink` em base.css, mesmo mecanismo da logo da
|
||||||
* sidebar em sidebar-brand.js). Durante o Halloween
|
* sidebar em sidebar-brand.js). Durante o Halloween
|
||||||
* (`pidIsHalloweenActive()`, seasonal-theme.js): o ícone vira o vampiro
|
* (`pidIsHalloweenActive()`, seasonal-theme.js): o ícone vira o vampiro
|
||||||
* (`pidApplyVampireIcon`) e o clique dispara a animação de "susto"
|
* (`pidApplyVampireIcon`) e o clique dispara a animação de "susto"
|
||||||
* (`pidTriggerVampireScare`, `.is-scaring` em base.css) em vez da piscada.
|
* (`pidTriggerVampireScare`, `.is-scaring` em base.css) em vez da piscada.
|
||||||
* Sem navegação nenhuma aqui — já estamos na tela de login, é só um toque de
|
* Durante o Aniversário (`pidIsAniversarioActive()`, só 15/10): o ícone
|
||||||
* graça ao clicar no mascote.
|
* ganha os balões (`pidApplyAniversarioIcon`) e o clique dispara o "clique
|
||||||
|
* de festa" (`pidTriggerAniversarioParty`, pula + joga confete + língua-de-
|
||||||
|
* sogra) ALÉM da piscada normal — a cara de repouso não muda nesse tema,
|
||||||
|
* então a piscada continua fazendo sentido (os dois efeitos convivem, ver
|
||||||
|
* base.css). Sem navegação nenhuma aqui — já estamos na tela de login, é só
|
||||||
|
* um toque de graça ao clicar no mascote.
|
||||||
*/
|
*/
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const btn = document.getElementById("login-logo-btn");
|
const btn = document.getElementById("login-logo-btn");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
|
|
||||||
const halloween = typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive();
|
const halloween = typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive();
|
||||||
|
const aniversario = !halloween && typeof pidIsAniversarioActive === "function" && pidIsAniversarioActive();
|
||||||
|
|
||||||
if (halloween) {
|
if (halloween) {
|
||||||
pidApplyVampireIcon(btn.querySelector(".login-card__logo"));
|
pidApplyVampireIcon(btn.querySelector(".login-card__logo"));
|
||||||
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon();
|
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon("pid-halloween-icone-escuro.svg");
|
||||||
if (typeof pidApplyHalloweenCobwebs === "function") pidApplyHalloweenCobwebs("pw-cobwebs-login");
|
if (typeof pidApplyHalloweenCobwebs === "function") pidApplyHalloweenCobwebs("pw-cobwebs-login");
|
||||||
|
} else if (aniversario) {
|
||||||
|
pidApplyAniversarioIcon(btn.querySelector(".login-card__logo"));
|
||||||
|
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon("pid-aniversario-icone-escuro.svg");
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
@ -27,6 +36,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aniversario) {
|
||||||
|
pidTriggerAniversarioParty(btn);
|
||||||
|
}
|
||||||
|
|
||||||
btn.querySelectorAll(".pid-icon-eye").forEach((eye) => {
|
btn.querySelectorAll(".pid-icon-eye").forEach((eye) => {
|
||||||
eye.classList.remove("is-blinking");
|
eye.classList.remove("is-blinking");
|
||||||
void eye.getBoundingClientRect();
|
void eye.getBoundingClientRect();
|
||||||
|
|||||||
@ -1,22 +1,33 @@
|
|||||||
/*
|
/*
|
||||||
* Tema sazonal "Halloween" do Portal — troca a marca P.I.D. (favicon, ícone
|
* Temas sazonais do Portal — trocam a marca P.I.D. (favicon, ícone do login,
|
||||||
* do login, ícone/assinatura da sidebar) pela variante "vampiro" durante uma
|
* ícone/assinatura da sidebar) por uma variante temática durante uma janela
|
||||||
* janela de datas (outubro) e troca a piscada de olhos ao clicar na logo por
|
* de datas. Dois hoje: "Halloween" (vampiro, 16 a 31/10) e "Aniversário"
|
||||||
* uma animação de "susto" (capa se abre, olhos ficam vermelhos e raivosos,
|
* (56 anos da De Paula Contadores, só 15/10 — balões + intro de festa) — as
|
||||||
* presas de fora, tremida + brilho vermelho) só nesse período — fora da
|
* duas janelas são mutuamente exclusivas por desenho (15 vs 16-31), nunca
|
||||||
* janela, tudo volta ao P.I.D. normal (`pid-icon-eye`/`is-blinking`). Ver
|
* ativas ao mesmo tempo. Fora de qualquer janela, tudo volta ao P.I.D. normal
|
||||||
* `docs/temas-sazonais/temas-sazonais.md`. Consumido por `sidebar-brand.js`,
|
* (`pid-icon-eye`/`is-blinking`). Ver `docs/temas-sazonais/temas-sazonais.md`.
|
||||||
* `login-logo-blink.js` e `auth.js` (troca de intro); incluído em toda
|
* Consumido por `sidebar-brand.js`, `login-logo-blink.js` e `auth.js` (troca
|
||||||
* página (mesmo padrão de `confirm-modal.js`) por isso mora aqui, não num
|
* de intro); incluído em toda página (mesmo padrão de `confirm-modal.js`)
|
||||||
* arquivo específico de página.
|
* por isso mora aqui, não num arquivo específico de página.
|
||||||
*
|
*
|
||||||
* PID_HALLOWEEN_PREVIEW_FORCE forçava o tema ligado o ano todo pra revisão
|
* PID_HALLOWEEN_PREVIEW_FORCE/PID_ANIVERSARIO_PREVIEW_FORCE forçam o tema
|
||||||
* visual; validado e desligado pelo usuário — a partir daqui só a checagem
|
* ligado o ano todo pra revisão visual; os dois já foram validados e
|
||||||
* de mês abaixo decide (outubro, todo ano).
|
* desligados pelo usuário — a partir daqui só a checagem de data abaixo
|
||||||
|
* decide (todo ano). As constantes ficam aqui de propósito, em vez de
|
||||||
|
* removidas, pra revisar o visual de novo sem ter que mexer no relógio.
|
||||||
|
*
|
||||||
|
* O Halloween só passou a começar em 16/10 (antes: outubro inteiro) porque
|
||||||
|
* 15/10 é o aniversário da empresa e ganhou tema próprio — os dois nunca
|
||||||
|
* disputavam o mesmo dia antes disso por acaso, agora é por definição.
|
||||||
*/
|
*/
|
||||||
const PID_HALLOWEEN_PREVIEW_FORCE = false;
|
const PID_HALLOWEEN_PREVIEW_FORCE = false;
|
||||||
|
const PID_ANIVERSARIO_PREVIEW_FORCE = false;
|
||||||
const PID_HALLOWEEN_MES = 9; // outubro, 0-indexed (Date#getMonth())
|
const PID_HALLOWEEN_MES = 9; // outubro, 0-indexed (Date#getMonth())
|
||||||
|
const PID_HALLOWEEN_DIA_INICIO = 16; // 16 a 31/10 — dia 15 é do tema de aniversário
|
||||||
|
const PID_ANIVERSARIO_MES = 9; // outubro, 0-indexed
|
||||||
|
const PID_ANIVERSARIO_DIA = 15; // aniversário da De Paula Contadores
|
||||||
const PID_HALLOWEEN_SCARE_MS = 900;
|
const PID_HALLOWEEN_SCARE_MS = 900;
|
||||||
|
const PID_ANIVERSARIO_PARTY_MS = 650;
|
||||||
|
|
||||||
/* Preferência do usuário (menu da conta → "Temas sazonais") pra desligar
|
/* Preferência do usuário (menu da conta → "Temas sazonais") pra desligar
|
||||||
qualquer tema sazonal (hoje só o Halloween, mas vale pra futuros também)
|
qualquer tema sazonal (hoje só o Halloween, mas vale pra futuros também)
|
||||||
@ -52,20 +63,28 @@ function pidIsHalloweenActive(data) {
|
|||||||
if (pidSeasonalThemesOptedOut()) return false;
|
if (pidSeasonalThemesOptedOut()) return false;
|
||||||
if (PID_HALLOWEEN_PREVIEW_FORCE) return true;
|
if (PID_HALLOWEEN_PREVIEW_FORCE) return true;
|
||||||
const hoje = data || new Date();
|
const hoje = data || new Date();
|
||||||
return hoje.getMonth() === PID_HALLOWEEN_MES;
|
return hoje.getMonth() === PID_HALLOWEEN_MES && hoje.getDate() >= PID_HALLOWEEN_DIA_INICIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Usa `pid-halloween-icone-escuro.svg` (o mesmo ícone/cores do vampiro
|
function pidIsAniversarioActive(data) {
|
||||||
usado na sidebar e no login), não `pid-halloween-favicon.svg` — esse
|
if (pidSeasonalThemesOptedOut()) return false;
|
||||||
último é uma variante simplificada (sem capa/gola, cores diferentes,
|
if (PID_ANIVERSARIO_PREVIEW_FORCE) return true;
|
||||||
pensada só pra tamanhos bem pequenos) e destoava do resto da marca sazonal
|
const hoje = data || new Date();
|
||||||
na aba do navegador. Mesmo padrão do P.I.D. normal, cujo favicon também
|
return hoje.getMonth() === PID_ANIVERSARIO_MES && hoje.getDate() === PID_ANIVERSARIO_DIA;
|
||||||
usa `pid-icone-escuro.svg` em vez do `pid-favicon.svg` dedicado — ver
|
}
|
||||||
"Logos em `static/img/`" no CLAUDE.md. */
|
|
||||||
function pidApplySeasonalFavicon() {
|
/* `iconFile` é o nome do arquivo em `static/img/` a usar como favicon
|
||||||
|
(`pid-halloween-icone-escuro.svg` ou `pid-aniversario-icone-escuro.svg`) —
|
||||||
|
sempre a variante "ícone/cores completas" de cada tema, nunca um favicon
|
||||||
|
dedicado simplificado: o favicon "sem balões"/"sem capa" pensado só pra
|
||||||
|
tamanhos bem pequenos destoava do resto da marca sazonal na aba do
|
||||||
|
navegador (mesmo motivo pelo qual o P.I.D. normal também usa
|
||||||
|
`pid-icone-escuro.svg` como favicon em vez do `pid-favicon.svg` dedicado —
|
||||||
|
ver "Logos em `static/img/`" no CLAUDE.md). */
|
||||||
|
function pidApplySeasonalFavicon(iconFile) {
|
||||||
const link = document.querySelector('link[rel="icon"]');
|
const link = document.querySelector('link[rel="icon"]');
|
||||||
if (link && link.href.indexOf("pid-icone-escuro.svg") !== -1) {
|
if (link && link.href.indexOf("pid-icone-escuro.svg") !== -1) {
|
||||||
link.href = link.href.replace("pid-icone-escuro.svg", "pid-halloween-icone-escuro.svg");
|
link.href = link.href.replace("pid-icone-escuro.svg", iconFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,6 +162,125 @@ function pidTriggerVampireScare(root) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Tema "Aniversário" (56 anos da De Paula Contadores, só 15/10) — porta da
|
||||||
|
marca comemorativa autorada em "P.I.D. Logo Design Aniversário/pid-marca-56"
|
||||||
|
(pasta de logos, fora do repositório; ver leiame.md lá: "o personagem
|
||||||
|
continua o mesmo, o acento é o número"). Ao contrário do Halloween, a cara
|
||||||
|
de repouso não muda (mesmos olhos/sorriso do P.I.D. normal, por isso o
|
||||||
|
markup abaixo preserva os dois `.pid-icon-eye` — a piscada ao clicar
|
||||||
|
continua funcionando fora do clique de festa) — só ganha três balões
|
||||||
|
(azul/âmbar/rosa) segurados por um braço saindo da lateral direita do
|
||||||
|
corpo, mais um bico-de-pato/confete escondidos que só aparecem no clique
|
||||||
|
de festa (ver `pidTriggerAniversarioParty()` abaixo). Cópias dos SVGs de
|
||||||
|
referência vivem em `static/img/pid-aniversario-*.svg` (mesmo papel de
|
||||||
|
`pid-halloween-*.svg`: não usadas diretamente pela UI, que injeta o markup
|
||||||
|
inline — só o favicon aponta pro arquivo de verdade). "56 ANOS" é
|
||||||
|
específico deste ano — ano que vem, atualizar o número aqui, no rótulo da
|
||||||
|
pílula abaixo e nos 3 arquivos `pid-aniversario-*.svg` a partir de uma
|
||||||
|
pasta `pid-marca-57` nova (mesmo espírito de "marca temporária" do
|
||||||
|
leiame.md original).
|
||||||
|
Estrutura em dois `<g>` aninhados: `.pid-aniv-hop` (sem nenhum `transform`
|
||||||
|
de atributo XML — recebe o "pulo" via CSS/`is-partying`, ver base.css) por
|
||||||
|
fora, `.pid-aniv-icon-rig` (o `transform="translate(...) scale(...)"`
|
||||||
|
estático que posiciona o desenho dentro da caixa 64×64/300×80) por dentro
|
||||||
|
— separar os dois evita o mesmo problema já documentado em `.pid-icon-eye`
|
||||||
|
(base.css): um `transform` de CSS aplicado ao MESMO elemento que já tem um
|
||||||
|
`transform` de atributo substitui o atributo inteiro, perdendo a posição. */
|
||||||
|
function pidAniversarioIconRigMarkup() {
|
||||||
|
return `
|
||||||
|
<g class="pid-aniv-balloons">
|
||||||
|
<path d="M43.5 17 Q47 23.5 53.5 29.5" stroke="#B9AECB" stroke-width="0.99" fill="none" stroke-linecap="round"></path>
|
||||||
|
<path d="M53 11.5 Q53.5 20 53.5 29.5" stroke="#B9AECB" stroke-width="0.99" fill="none" stroke-linecap="round"></path>
|
||||||
|
<path d="M59 20 Q57 24.5 53.5 29.5" stroke="#B9AECB" stroke-width="0.99" fill="none" stroke-linecap="round"></path>
|
||||||
|
<ellipse cx="43.5" cy="11" rx="4.6" ry="5.6" fill="#2E86DE"></ellipse>
|
||||||
|
<path d="M42.2 16.4 h2.6 l-1.3 1.9 z" fill="#2E86DE"></path>
|
||||||
|
<ellipse cx="53" cy="5.5" rx="4.6" ry="5.6" fill="#F2A93B"></ellipse>
|
||||||
|
<path d="M51.7 10.9 h2.6 l-1.3 1.9 z" fill="#F2A93B"></path>
|
||||||
|
<ellipse cx="59" cy="14" rx="4.6" ry="5.6" fill="#E8446E"></ellipse>
|
||||||
|
<path d="M57.7 19.4 h2.6 l-1.3 1.9 z" fill="#E8446E"></path>
|
||||||
|
</g>
|
||||||
|
<g class="pid-aniv-arm">
|
||||||
|
<path d="M42 45 Q50.5 43 53.5 32" stroke="#7B5BA8" stroke-width="3.5" fill="none" stroke-linecap="round"></path>
|
||||||
|
<circle cx="53.5" cy="30" r="3.4" fill="#7B5BA8"></circle>
|
||||||
|
</g>
|
||||||
|
<g class="pid-aniv-confetti">
|
||||||
|
<rect x="47" y="12" width="2.6" height="2.6" fill="#C6A24A"></rect>
|
||||||
|
<rect x="60" y="8" width="2.2" height="2.2" fill="#2E86DE"></rect>
|
||||||
|
<circle cx="52" cy="4" r="1.4" fill="#E8446E"></circle>
|
||||||
|
<rect x="63" y="19" width="2" height="2" fill="#F2A93B"></rect>
|
||||||
|
<circle cx="41" cy="7" r="1.2" fill="#F2EDE3"></circle>
|
||||||
|
<rect x="55" y="24" width="2.2" height="2.2" fill="#E8446E"></rect>
|
||||||
|
<circle cx="65" cy="13" r="1.3" fill="#C6A24A"></circle>
|
||||||
|
<rect x="37" y="13" width="1.8" height="1.8" fill="#2E86DE"></rect>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(2,18) scale(0.72)">
|
||||||
|
<path d="M10 4 h34 l14 14 v40 a2 2 0 0 1 -2 2 H8 a2 2 0 0 1 -2 -2 V6 a2 2 0 0 1 2 -2 z" fill="#7B5BA8"></path>
|
||||||
|
<rect x="22" y="4" width="20" height="16" fill="#C6A24A"></rect>
|
||||||
|
<rect x="34" y="7" width="5" height="10" fill="#7B5BA8"></rect>
|
||||||
|
<rect x="12" y="26" width="40" height="30" rx="2" fill="#241C33"></rect>
|
||||||
|
<g class="pid-icon-eye">
|
||||||
|
<circle cx="24" cy="38" r="5" fill="#F2EDE3"></circle>
|
||||||
|
<circle cx="26" cy="36.2" r="1.6" fill="#241C33"></circle>
|
||||||
|
</g>
|
||||||
|
<g class="pid-icon-eye">
|
||||||
|
<circle cx="40" cy="38" r="5" fill="#F2EDE3"></circle>
|
||||||
|
<circle cx="42" cy="36.2" r="1.6" fill="#241C33"></circle>
|
||||||
|
</g>
|
||||||
|
<path class="pid-aniv-smile" d="M25 47 q7 6 14 0" stroke="#C6A24A" stroke-width="3" fill="none" stroke-linecap="round"></path>
|
||||||
|
<ellipse class="pid-aniv-pucker-outer" cx="32" cy="48.5" rx="4" ry="3.4" fill="#C6A24A"></ellipse>
|
||||||
|
<ellipse class="pid-aniv-pucker-inner" cx="32" cy="48.5" rx="2" ry="1.7" fill="#241C33"></ellipse>
|
||||||
|
</g>
|
||||||
|
<g class="pid-aniv-horn">
|
||||||
|
<path class="pid-aniv-horn-tube" d="M25 53 q12.6 -5 30 -11" stroke="#E8446E" stroke-width="2.4" fill="none" stroke-linecap="round"></path>
|
||||||
|
<path class="pid-aniv-horn-dash" d="M25 53 q12.6 -5 30 -11" stroke="#F2EDE3" stroke-width="1" stroke-dasharray="1.6 2.4" fill="none" stroke-linecap="round"></path>
|
||||||
|
<circle class="pid-aniv-horn-puff" cx="58" cy="41" r="2.2" fill="#F2A93B"></circle>
|
||||||
|
<circle class="pid-aniv-horn-puff" cx="63" cy="38" r="1.4" fill="#C6A24A"></circle>
|
||||||
|
</g>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Substitui o conteúdo de um <svg> de ícone (viewBox 0 0 64 64, mesmo do
|
||||||
|
P.I.D. normal — ao contrário do vampiro, não precisa alargar a caixa) pelo
|
||||||
|
ícone com balões. `dataset.aniversarioApplied` evita reaplicar (mesmo
|
||||||
|
padrão de `dataset.vampApplied`). */
|
||||||
|
function pidApplyAniversarioIcon(svg) {
|
||||||
|
if (!svg || svg.dataset.aniversarioApplied) return;
|
||||||
|
svg.innerHTML = `<g class="pid-aniv-hop"><g class="pid-aniv-icon-rig" transform="translate(0.1,4.1) scale(0.913)">${pidAniversarioIconRigMarkup()}</g></g>`;
|
||||||
|
svg.dataset.aniversarioApplied = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Idem, pra assinatura horizontal da sidebar expandida — viewBox 0 0 300 80,
|
||||||
|
igual ao P.I.D. normal (os balões cabem na mesma caixa, sem alargar como
|
||||||
|
o vampiro precisou por causa da capa). Ganha uma pílula dourada "56 ANOS"
|
||||||
|
ao lado do wordmark, no lugar da tagline normal. */
|
||||||
|
function pidApplyAniversarioFull(svg) {
|
||||||
|
if (!svg || svg.dataset.aniversarioApplied) return;
|
||||||
|
svg.innerHTML = `
|
||||||
|
<g transform="translate(8,8)"><g class="pid-aniv-hop"><g class="pid-aniv-icon-rig" transform="translate(0.1,4.1) scale(0.913)">${pidAniversarioIconRigMarkup()}</g></g></g>
|
||||||
|
<text x="86" y="42" font-family="'Space Grotesk',Helvetica,Arial,sans-serif" font-size="34" font-weight="700" letter-spacing="1.4" fill="#F2EDE3">P.I.D.</text>
|
||||||
|
<rect x="192" y="21" width="86" height="23" rx="11.5" fill="#C6A24A"></rect>
|
||||||
|
<text x="235" y="36.5" text-anchor="middle" font-family="'DM Mono',ui-monospace,monospace" font-size="10.5" font-weight="500" letter-spacing="1.8" fill="#4B2E75">56 ANOS</text>
|
||||||
|
<text x="88" y="61" font-family="'DM Mono',ui-monospace,monospace" font-size="9.5" letter-spacing="2.15" fill="#C6A24A">PORTAL INTERNO DA DE PAULA</text>`;
|
||||||
|
svg.dataset.aniversarioApplied = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dispara o "clique de festa" (`.is-partying`, keyframes em base.css) em
|
||||||
|
todo `.pid-aniv-hop` dentro de `root` — o personagem pula, o braço joga
|
||||||
|
confete pro alto e a língua-de-sogra desenrola da boca, num ciclo só de
|
||||||
|
`PID_ANIVERSARIO_PARTY_MS`. Ao contrário do susto do vampiro, isso não
|
||||||
|
substitui a piscada de olhos — os dois convivem (a piscada continua
|
||||||
|
disparando `.pid-icon-eye`/`is-blinking` de dentro do próprio clique de
|
||||||
|
festa, ver `sidebar-brand.js`/`login-logo-blink.js`). Mesmo truque de
|
||||||
|
remover+reaplicar a classe (em vez de só adicionar) de
|
||||||
|
`pidTriggerVampireScare` pra permitir cliques repetidos re-tocarem do
|
||||||
|
zero. */
|
||||||
|
function pidTriggerAniversarioParty(root) {
|
||||||
|
root.querySelectorAll(".pid-aniv-hop").forEach((hop) => {
|
||||||
|
hop.classList.remove("is-partying");
|
||||||
|
void hop.getBoundingClientRect();
|
||||||
|
hop.classList.add("is-partying");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/* Teia de aranha decorativa de canto (`static/css/halloween-cobweb.css`) —
|
/* Teia de aranha decorativa de canto (`static/css/halloween-cobweb.css`) —
|
||||||
markup só existe em memória (nunca hardcoded no template) pra não sobrar
|
markup só existe em memória (nunca hardcoded no template) pra não sobrar
|
||||||
nada no HTML fora da janela do Halloween. `.pw-spider`/`.pw-legs` (mesmas
|
nada no HTML fora da janela do Halloween. `.pw-spider`/`.pw-legs` (mesmas
|
||||||
@ -276,14 +414,112 @@ function pidApplyHalloweenCobwebs(containerId) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Atalho de teclado Tab+B (segurar Tab e apertar B) — reseta todas as
|
/* Bolo de aniversário decorativo, tema sazonal Aniversário — só na tela
|
||||||
aranhas "mortas" salvas (`pidResetDeadSpiders()`) e recarrega a página,
|
Principal (`portal.html`), fixo no canto inferior-direito da JANELA
|
||||||
mesmo raciocínio do toggle de opt-out (`account.js`): o estado já foi
|
(`position:fixed`, não da página — sempre visível, mesmo com scroll).
|
||||||
aplicado no carregamento atual, não dá pra desfazer in-place. Não chama
|
Clicar apaga as velinhas (`.is-blowing`/`pidCakeFlameOut` +
|
||||||
|
`pidCakeSmokeRise` em `aniversario-decor.css`, 1x só), e elas ficam
|
||||||
|
apagadas até o próximo carregamento da página.
|
||||||
|
|
||||||
|
**O estado apagado NÃO é persistido**, ao contrário da aranha morta do
|
||||||
|
Halloween — e isso é deliberado, não esquecimento. A persistência chegou a
|
||||||
|
existir aqui (copiada de `pidMarkSpiderDead`) e foi removida: as velas
|
||||||
|
nasciam apagadas pro resto da vida depois do primeiro clique, sem nenhuma
|
||||||
|
pista disso na tela, e só voltavam com o atalho Tab+B. O usuário relatou
|
||||||
|
como bug ("não aparece o fogo das velinhas"), porque é um easter egg que
|
||||||
|
some sozinho e não volta. A aranha persiste porque o usuário PEDIU
|
||||||
|
explicitamente; aqui ele pediu só "ao clicar nele as velas se apagam". */
|
||||||
|
|
||||||
|
/* As chamas nascem exatamente no topo de cada vela (`y` do `<rect>`), não
|
||||||
|
alguns pontos acima — na primeira versão flutuavam soltas bem longe do
|
||||||
|
pavio. O gotejamento de cada camada é da cor da camada DE CIMA (creme
|
||||||
|
escorrendo sobre o dourado, dourado escorrendo sobre o roxo), senão fica
|
||||||
|
invisível contra a própria camada.
|
||||||
|
|
||||||
|
`.pid-cake-halo` é o halo de luz no ar em volta das velas (o brilho colado
|
||||||
|
em cada chama vem de um `drop-shadow` no CSS) — fica DEPOIS das camadas do
|
||||||
|
bolo e ANTES das velas, pra a luz lavar o topo do bolo sem desbotar as
|
||||||
|
chamas, que continuam nítidas por cima. O `<radialGradient>` mora num
|
||||||
|
`<defs>` com id fixo: o bolo é injetado uma vez só, em portal.html, então
|
||||||
|
não há risco de id duplicado (mesmo padrão do `clipPath` da intro do
|
||||||
|
Halloween em index.html). */
|
||||||
|
const PID_CAKE_SVG = `<svg viewBox="0 0 120 110" role="img" aria-hidden="true">
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="pidCakeHaloGrad">
|
||||||
|
<stop offset="0%" stop-color="#F2A93B" stop-opacity="0.42"></stop>
|
||||||
|
<stop offset="45%" stop-color="#F2A93B" stop-opacity="0.14"></stop>
|
||||||
|
<stop offset="100%" stop-color="#F2A93B" stop-opacity="0"></stop>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
<ellipse class="pid-cake-shadow" cx="60" cy="102" rx="42" ry="6"></ellipse>
|
||||||
|
<path class="pid-cake-tier2" d="M18 74 h84 v24 a2 2 0 0 1 -2 2 H20 a2 2 0 0 1 -2 -2 z"></path>
|
||||||
|
<path class="pid-cake-drip2" d="M18 74 q7 9 14 0 q7 9 14 0 q7 9 14 0 q7 9 14 0 q7 9 14 0 q7 9 14 0 Z"></path>
|
||||||
|
<circle class="pid-cake-dot pid-cake-dot--blue" cx="32" cy="88" r="2.4"></circle>
|
||||||
|
<circle class="pid-cake-dot pid-cake-dot--pink" cx="60" cy="91" r="2.4"></circle>
|
||||||
|
<circle class="pid-cake-dot pid-cake-dot--amber" cx="88" cy="88" r="2.4"></circle>
|
||||||
|
<path class="pid-cake-tier1" d="M32 50 h56 v24 H32 z"></path>
|
||||||
|
<path class="pid-cake-drip1" d="M32 50 q5.6 7 11.2 0 q5.6 7 11.2 0 q5.6 7 11.2 0 q5.6 7 11.2 0 q5.6 7 11.2 0 Z"></path>
|
||||||
|
<ellipse class="pid-cake-halo" cx="60" cy="30" rx="48" ry="30" fill="url(#pidCakeHaloGrad)"></ellipse>
|
||||||
|
<g class="pid-cake-candle">
|
||||||
|
<rect x="42" y="34" width="4" height="16" fill="#2E86DE"></rect>
|
||||||
|
<path class="pid-cake-smoke" d="M44 32 q4 -5 0 -10"></path>
|
||||||
|
<path class="pid-cake-flame" d="M44 34 q4 -5 0 -9 q-4 4 0 9 z"></path>
|
||||||
|
</g>
|
||||||
|
<g class="pid-cake-candle">
|
||||||
|
<rect x="58" y="30" width="4" height="20" fill="#C6A24A"></rect>
|
||||||
|
<path class="pid-cake-smoke" d="M60 28 q4 -5 0 -10"></path>
|
||||||
|
<path class="pid-cake-flame" d="M60 30 q4 -5 0 -9 q-4 4 0 9 z"></path>
|
||||||
|
</g>
|
||||||
|
<g class="pid-cake-candle">
|
||||||
|
<rect x="74" y="34" width="4" height="16" fill="#E8446E"></rect>
|
||||||
|
<path class="pid-cake-smoke" d="M76 32 q4 -5 0 -10"></path>
|
||||||
|
<path class="pid-cake-flame" d="M76 34 q4 -5 0 -9 q-4 4 0 9 z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>`;
|
||||||
|
|
||||||
|
/* Preenche o container (já presente e `hidden` no template,
|
||||||
|
`#pid-aniv-cake` em portal.html) e liga o clique — botão porque é
|
||||||
|
interativo (`aria-label`, foco por teclado funcionam de graça). O clique
|
||||||
|
adiciona `.is-blowing` (toca `pidCakeFlameOut`/`pidCakeSmokeRise` uma vez)
|
||||||
|
e, depois de `PID_CAKE_BLOW_MS`, troca pra `.is-blown` (estado final em
|
||||||
|
repouso, sem `animation` nenhuma rodando) — que dura só até o próximo
|
||||||
|
carregamento, ver o comentário acima. É um no-op seguro em qualquer outra
|
||||||
|
página (o id não existe ali). */
|
||||||
|
const PID_CAKE_BLOW_MS = 1400;
|
||||||
|
|
||||||
|
function pidApplyAniversarioCake(containerId) {
|
||||||
|
const el = document.getElementById(containerId);
|
||||||
|
if (!el || el.dataset.cakeApplied) return;
|
||||||
|
el.innerHTML = `<button type="button" class="pid-aniv-cake__btn" aria-label="Assoprar as velinhas">${PID_CAKE_SVG}</button>`;
|
||||||
|
el.hidden = false;
|
||||||
|
el.dataset.cakeApplied = "1";
|
||||||
|
|
||||||
|
const btn = el.querySelector(".pid-aniv-cake__btn");
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
btn.addEventListener(
|
||||||
|
"click",
|
||||||
|
() => {
|
||||||
|
el.classList.add("is-blowing");
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.remove("is-blowing");
|
||||||
|
el.classList.add("is-blown");
|
||||||
|
}, PID_CAKE_BLOW_MS);
|
||||||
|
},
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Atalho de teclado Tab+B (segurar Tab e apertar B) — reseta as aranhas
|
||||||
|
"mortas" (`pidResetDeadSpiders()`) salvas e recarrega a página, mesmo
|
||||||
|
raciocínio do toggle de opt-out (`account.js`): o estado já foi aplicado
|
||||||
|
no carregamento atual, não dá pra desfazer in-place. Não chama
|
||||||
`preventDefault()` no Tab (continuaria navegando o foco normalmente,
|
`preventDefault()` no Tab (continuaria navegando o foco normalmente,
|
||||||
acessibilidade intacta) — só rastreia se ele está pressionado no momento
|
acessibilidade intacta) — só rastreia se ele está pressionado no momento
|
||||||
em que o B chega. Incluído em toda página (mesmo padrão de
|
em que o B chega. Incluído em toda página (mesmo padrão de
|
||||||
`confirm-modal.js`), mas só tem efeito onde há aranha salva como morta. */
|
`confirm-modal.js`), mas só tem efeito onde há aranha salva como morta. O
|
||||||
|
bolo do tema de Aniversário não entra aqui: as velinhas reacendem sozinhas
|
||||||
|
a cada carregamento, então não há o que resetar. */
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
let tabHeld = false;
|
let tabHeld = false;
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Logo da sidebar (`#sidebar-brand-link`, presente em todo shell) — link de
|
* Logo da sidebar (`#sidebar-brand-link`, presente em todo shell) — link de
|
||||||
* verdade pra `portal.html` (tela Principal). Fora do tema sazonal
|
* verdade pra `portal.html` (tela Principal). Fora de qualquer tema sazonal:
|
||||||
* Halloween: os olhos do ícone piscam antes de navegar
|
* os olhos do ícone piscam antes de navegar (`.pid-icon-eye`/`pidIconBlink`
|
||||||
* (`.pid-icon-eye`/`pidIconBlink` em base.css, 200ms), navegação adiada só o
|
* em base.css, 200ms), navegação adiada só o suficiente pra a piscada
|
||||||
* suficiente pra a piscada terminar de tocar. Durante o Halloween
|
* terminar de tocar. Durante o Halloween (`pidIsHalloweenActive()`,
|
||||||
* (`pidIsHalloweenActive()`, seasonal-theme.js): o ícone/assinatura viram o
|
* seasonal-theme.js): o ícone/assinatura viram o vampiro
|
||||||
* vampiro (`pidApplyVampireIcon`/`pidApplyVampireFull`) e o clique dispara a
|
* (`pidApplyVampireIcon`/`pidApplyVampireFull`) e o clique dispara a
|
||||||
* animação de "susto" (`pidTriggerVampireScare`, `.is-scaring` em base.css)
|
* animação de "susto" (`pidTriggerVampireScare`, `.is-scaring` em base.css)
|
||||||
* em vez da piscada, com a navegação adiada o suficiente pra ela ser vista.
|
* em vez da piscada, com a navegação adiada o suficiente pra ela ser vista.
|
||||||
|
* Durante o Aniversário (`pidIsAniversarioActive()`, só 15/10): o
|
||||||
|
* ícone/assinatura ganham os balões (`pidApplyAniversarioIcon`/`Full`) e o
|
||||||
|
* clique dispara o "clique de festa" (`pidTriggerAniversarioParty`, pula +
|
||||||
|
* joga confete + língua-de-sogra) ALÉM da piscada normal — a cara de
|
||||||
|
* repouso não muda nesse tema, então a piscada continua fazendo sentido
|
||||||
|
* (os dois efeitos convivem, ver base.css). As duas janelas nunca coincidem
|
||||||
|
* (15 vs 16-31/10), por isso Halloween é checado primeiro e ganha
|
||||||
|
* prioridade só por clareza de código, não porque possa colidir de verdade.
|
||||||
* ctrl/cmd/shift/clique do meio continuam abrindo em nova aba normalmente
|
* ctrl/cmd/shift/clique do meio continuam abrindo em nova aba normalmente
|
||||||
* (não intercepta esses casos).
|
* (não intercepta esses casos).
|
||||||
*/
|
*/
|
||||||
@ -16,13 +24,20 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
if (!brand) return;
|
if (!brand) return;
|
||||||
|
|
||||||
const halloween = typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive();
|
const halloween = typeof pidIsHalloweenActive === "function" && pidIsHalloweenActive();
|
||||||
|
const aniversario = !halloween && typeof pidIsAniversarioActive === "function" && pidIsAniversarioActive();
|
||||||
|
|
||||||
if (halloween) {
|
if (halloween) {
|
||||||
pidApplyVampireIcon(brand.querySelector(".sidebar__logo--icon"));
|
pidApplyVampireIcon(brand.querySelector(".sidebar__logo--icon"));
|
||||||
pidApplyVampireFull(brand.querySelector(".sidebar__logo--full"));
|
pidApplyVampireFull(brand.querySelector(".sidebar__logo--full"));
|
||||||
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon();
|
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon("pid-halloween-icone-escuro.svg");
|
||||||
// Container só existe em portal.html (#pw-cobwebs-portal) — no-op nos demais shells.
|
// Containers só existem em portal.html — no-op nos demais shells.
|
||||||
if (typeof pidApplyHalloweenCobwebs === "function") pidApplyHalloweenCobwebs("pw-cobwebs-portal");
|
if (typeof pidApplyHalloweenCobwebs === "function") pidApplyHalloweenCobwebs("pw-cobwebs-portal");
|
||||||
|
} else if (aniversario) {
|
||||||
|
pidApplyAniversarioIcon(brand.querySelector(".sidebar__logo--icon"));
|
||||||
|
pidApplyAniversarioFull(brand.querySelector(".sidebar__logo--full"));
|
||||||
|
if (typeof pidApplySeasonalFavicon === "function") pidApplySeasonalFavicon("pid-aniversario-icone-escuro.svg");
|
||||||
|
// Container só existe em portal.html (#pid-aniv-cake) — no-op nos demais shells.
|
||||||
|
if (typeof pidApplyAniversarioCake === "function") pidApplyAniversarioCake("pid-aniv-cake");
|
||||||
}
|
}
|
||||||
|
|
||||||
brand.addEventListener("click", (event) => {
|
brand.addEventListener("click", (event) => {
|
||||||
@ -37,14 +52,21 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aniversario) {
|
||||||
|
pidTriggerAniversarioParty(brand);
|
||||||
|
}
|
||||||
|
|
||||||
brand.querySelectorAll(".pid-icon-eye").forEach((eye) => {
|
brand.querySelectorAll(".pid-icon-eye").forEach((eye) => {
|
||||||
eye.classList.remove("is-blinking");
|
eye.classList.remove("is-blinking");
|
||||||
void eye.getBoundingClientRect();
|
void eye.getBoundingClientRect();
|
||||||
eye.classList.add("is-blinking");
|
eye.classList.add("is-blinking");
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(
|
||||||
window.location.href = brand.getAttribute("href");
|
() => {
|
||||||
}, 260);
|
window.location.href = brand.getAttribute("href");
|
||||||
|
},
|
||||||
|
aniversario ? PID_ANIVERSARIO_PARTY_MS - 100 : 260
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/login.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/login.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'css/login-intro.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/login-intro.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'css/login-intro-halloween.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/login-intro-halloween.css' %}" />
|
||||||
|
<link rel="stylesheet" href="{% static 'css/login-intro-aniversario.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'css/halloween-cobweb.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/halloween-cobweb.css' %}" />
|
||||||
<script src="{% static 'js/theme.js' %}" defer></script>
|
<script src="{% static 'js/theme.js' %}" defer></script>
|
||||||
</head>
|
</head>
|
||||||
@ -196,12 +197,80 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Variante sazonal (Aniversário/56 anos) da intro acima — ver
|
||||||
|
login-intro-aniversario.css/.js e
|
||||||
|
`docs/temas-sazonais/temas-sazonais.md`. Só é exibida quando
|
||||||
|
`pidIsAniversarioActive()` (seasonal-theme.js, só 15/10); nos demais
|
||||||
|
dias, #login-intro (ou #login-intro-halloween, em outubro) continua
|
||||||
|
sendo o único usado. -->
|
||||||
|
<div class="login-intro-a" id="login-intro-aniversario" hidden aria-hidden="true">
|
||||||
|
<div class="login-intro-a__flash" id="login-intro-a-flash"></div>
|
||||||
|
<div class="login-intro-a__stage" id="login-intro-a-stage">
|
||||||
|
<div class="login-intro-a__icon-wrap" id="login-intro-a-iconwrap">
|
||||||
|
<svg class="login-intro-a__icon" id="login-intro-a-icon" viewBox="0 0 96 80">
|
||||||
|
<g class="login-intro-a__balloons">
|
||||||
|
<path d="M43.5 21.1 Q47 27.6 53.5 33.6"></path>
|
||||||
|
<path d="M53 15.6 Q53.5 24.1 53.5 33.6"></path>
|
||||||
|
<path d="M59 24.1 Q57 28.6 53.5 33.6"></path>
|
||||||
|
<ellipse class="login-intro-a__balloon login-intro-a__balloon--blue" cx="43.5" cy="15.1" rx="4.6" ry="5.6"></ellipse>
|
||||||
|
<path class="login-intro-a__balloon-tie--blue" d="M42.2 20.5 h2.6 l-1.3 1.9 z"></path>
|
||||||
|
<ellipse class="login-intro-a__balloon login-intro-a__balloon--amber" cx="53" cy="9.6" rx="4.6" ry="5.6"></ellipse>
|
||||||
|
<path class="login-intro-a__balloon-tie--amber" d="M51.7 15 h2.6 l-1.3 1.9 z"></path>
|
||||||
|
<ellipse class="login-intro-a__balloon login-intro-a__balloon--pink" cx="59" cy="18.1" rx="4.6" ry="5.6"></ellipse>
|
||||||
|
<path class="login-intro-a__balloon-tie--pink" d="M57.7 23.5 h2.6 l-1.3 1.9 z"></path>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g class="login-intro-a__arm">
|
||||||
|
<path d="M42 49 Q50.5 47 53.5 36"></path>
|
||||||
|
<circle cx="53.5" cy="34" r="3.4"></circle>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g transform="translate(2 22) scale(0.72)">
|
||||||
|
<path class="login-intro-a__body-path" d="M10 4 h34 l14 14 v40 a2 2 0 0 1 -2 2 H8 a2 2 0 0 1 -2 -2 V6 a2 2 0 0 1 2 -2 z"></path>
|
||||||
|
<rect class="login-intro-a__tab" x="22" y="4" width="20" height="16"></rect>
|
||||||
|
<rect class="login-intro-a__tab-shadow" x="34" y="7" width="5" height="10"></rect>
|
||||||
|
<rect class="login-intro-a__screen" x="12" y="26" width="40" height="30" rx="2"></rect>
|
||||||
|
<circle class="login-intro-a__eye" cx="24" cy="38" r="5"></circle>
|
||||||
|
<circle class="login-intro-a__eye" cx="40" cy="38" r="5"></circle>
|
||||||
|
<circle class="login-intro-a__pupil" cx="26" cy="36.2" r="1.6"></circle>
|
||||||
|
<circle class="login-intro-a__pupil" cx="42" cy="36.2" r="1.6"></circle>
|
||||||
|
<path class="login-intro-a__smile" id="login-intro-a-smile" d="M25 47 q7 6 14 0"></path>
|
||||||
|
<ellipse class="login-intro-a__pucker-outer" id="login-intro-a-pucker-outer" cx="32" cy="48.5" rx="4" ry="3.4"></ellipse>
|
||||||
|
<ellipse class="login-intro-a__pucker-inner" id="login-intro-a-pucker-inner" cx="32" cy="48.5" rx="2" ry="1.7"></ellipse>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g id="login-intro-a-horn">
|
||||||
|
<path class="login-intro-a__horn-tube" id="login-intro-a-horn-tube" d="M25 57 q0 -3 0 -6"></path>
|
||||||
|
<path class="login-intro-a__horn-dash" id="login-intro-a-horn-dash" d="M25 57 q0 -3 0 -6"></path>
|
||||||
|
<circle class="login-intro-a__horn-puff--1" id="login-intro-a-puff1" cx="28" cy="51" r="2.2"></circle>
|
||||||
|
<circle class="login-intro-a__horn-puff--2" id="login-intro-a-puff2" cx="33" cy="48" r="1.4"></circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
<div class="login-intro-a__confetti" id="login-intro-a-confetti"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="login-intro-a__message" id="login-intro-a-message">
|
||||||
|
<div class="login-intro-a__parabens-row">
|
||||||
|
<span class="login-intro-a__parabens" id="login-intro-a-parabens">Parabéns</span>
|
||||||
|
<img class="login-intro-a__wordmark-img" id="login-intro-a-wordmark-img" src="{% static 'img/pid-aniversario-depaula-wordmark.png' %}" alt="De Paula Contadores" />
|
||||||
|
</div>
|
||||||
|
<div class="login-intro-a__rule" id="login-intro-a-rule"></div>
|
||||||
|
<div class="login-intro-a__numrow">
|
||||||
|
<span class="login-intro-a__num" id="login-intro-a-num">56</span>
|
||||||
|
<span class="login-intro-a__num-label" id="login-intro-a-num-label">Anos</span>
|
||||||
|
</div>
|
||||||
|
<div class="login-intro-a__tagline" id="login-intro-a-tagline">Portal Interno da De Paula</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="{% static 'js/api.js' %}"></script>
|
<script src="{% static 'js/api.js' %}"></script>
|
||||||
<script src="{% static 'js/confirm-modal.js' %}"></script>
|
<script src="{% static 'js/confirm-modal.js' %}"></script>
|
||||||
<script src="{% static 'js/seasonal-theme.js' %}"></script>
|
<script src="{% static 'js/seasonal-theme.js' %}"></script>
|
||||||
<script src="{% static 'js/login-logo-blink.js' %}"></script>
|
<script src="{% static 'js/login-logo-blink.js' %}"></script>
|
||||||
<script src="{% static 'js/login-intro.js' %}"></script>
|
<script src="{% static 'js/login-intro.js' %}"></script>
|
||||||
<script src="{% static 'js/login-intro-halloween.js' %}"></script>
|
<script src="{% static 'js/login-intro-halloween.js' %}"></script>
|
||||||
|
<script src="{% static 'js/login-intro-aniversario.js' %}"></script>
|
||||||
<script src="{% static 'js/auth.js' %}"></script>
|
<script src="{% static 'js/auth.js' %}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/widgets.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/widgets.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'css/ramais-lookup.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/ramais-lookup.css' %}" />
|
||||||
<link rel="stylesheet" href="{% static 'css/halloween-cobweb.css' %}" />
|
<link rel="stylesheet" href="{% static 'css/halloween-cobweb.css' %}" />
|
||||||
|
<link rel="stylesheet" href="{% static 'css/aniversario-decor.css' %}" />
|
||||||
<script src="{% static 'js/theme.js' %}" defer></script>
|
<script src="{% static 'js/theme.js' %}" defer></script>
|
||||||
<script src="{% static 'js/portal-reveal.js' %}" defer></script>
|
<script src="{% static 'js/portal-reveal.js' %}" defer></script>
|
||||||
</head>
|
</head>
|
||||||
@ -266,6 +267,7 @@
|
|||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="pw-corners pw-corners--portal" id="pw-cobwebs-portal" hidden aria-hidden="true"></div>
|
<div class="pw-corners pw-corners--portal" id="pw-cobwebs-portal" hidden aria-hidden="true"></div>
|
||||||
|
<div class="pid-aniv-cake" id="pid-aniv-cake" hidden aria-hidden="true"></div>
|
||||||
<header class="topbar">
|
<header class="topbar">
|
||||||
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Alternar menu">
|
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Alternar menu">
|
||||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16" stroke-linecap="round"/></svg>
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 6h16M4 12h16M4 18h16" stroke-linecap="round"/></svg>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user