322 lines
9.7 KiB
CSS
322 lines
9.7 KiB
CSS
/* Teias de aranha decorativas de canto, tema sazonal Halloween — só no
|
|
canto superior-esquerdo e no inferior-direito (pedido explícito do
|
|
usuário, mesmos 2 cantos marcados nos prints que ele mandou), só no login
|
|
(`index.html`) e na tela Principal (`portal.html`) — nenhuma outra
|
|
página. Puramente decorativa (`pointer-events:none`, `z-index:-1` pra
|
|
ficar atrás de todo o conteúdo real, exceto a própria aranha — ver
|
|
"Clicar mata a aranha" abaixo), escondida por padrão (`hidden` no HTML) e
|
|
só exibida via `pidApplyHalloweenCobwebs()` (seasonal-theme.js) quando
|
|
`pidIsHalloweenActive()` — ver `docs/temas-sazonais/temas-sazonais.md`.
|
|
Desenho/cores validados no protótipo (artifact "Cantos Assombrados")
|
|
antes de aplicar aqui; o desenho da teia em si (fios/anéis) foi refeito
|
|
numa rodada seguinte a partir de uma referência visual do usuário, pra
|
|
parecer uma teia real (espiral levemente arqueada, mais fios, um fio
|
|
"perdido" se estendendo bem mais longe que o resto) em vez de um desenho
|
|
geométrico cru.
|
|
|
|
No login, ancorada em `.login-page` (`position:absolute`, ela já cobre
|
|
`min-height:100vh` e ganhou `isolation:isolate` em `login.css` — sem
|
|
isolar, o próprio fundo de `.login-page` pintava por cima da teia,
|
|
escondendo-a por completo). Na tela Principal, ancorada em
|
|
`.main-content` (mesma ideia, `isolation:isolate` em `layout.css`) — não
|
|
no viewport, porque a sidebar já é sólida e cobriria a teia do canto
|
|
esquerdo de qualquer jeito; assim a teia nunca fica escondida atrás dela.
|
|
A teia da Principal também é um pouco menor que a do login (pedido
|
|
explícito do usuário, "evitando cobrir muito as informações que constam
|
|
na página" — a tela Principal tem cards/widgets perto das bordas, o login
|
|
só tem o card centralizado) — mas as duas foram aumentadas numa rodada
|
|
seguinte, "ficaram muito pequenas". */
|
|
|
|
.pw-corners {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
overflow: hidden;
|
|
z-index: -1;
|
|
}
|
|
|
|
.pw-corner {
|
|
position: absolute;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.pw-corner--tl {
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
|
|
.pw-corner--br {
|
|
bottom: 0;
|
|
right: 0;
|
|
}
|
|
|
|
/* O canto inferior-direito reaproveita o MESMO desenho/markup do
|
|
superior-esquerdo (a teia é autorada assumindo o canto no (0,0) do seu
|
|
próprio viewBox), só espelhando a TEIA em si (`.pw-strands`) em torno do
|
|
centro do viewBox — não a aranha nem a caveira. Espelhar o `<div
|
|
class="pw-corner--br">` inteiro (como era antes) virava a aranha de
|
|
cabeça pra baixo e fazia ela "cair" pra cima ao morrer (bug relatado pelo
|
|
usuário): `scale(-1,-1)` em qualquer ancestral inverte literalmente tudo
|
|
dentro, inclusive a direção "pra baixo" das animações de queda/subida.
|
|
Por isso a aranha e a caveira têm sua própria posição "espelhada" abaixo
|
|
(variáveis `--rx`/`--ry`/`--rr`/`--ox`/`--oy` e `--sx`/`--sy`,
|
|
recalculadas pra esse canto), sem nenhum `scale`/`rotate` adicional nelas
|
|
— assim ficam sempre "em pé" e caem/sobem na direção real da tela nos
|
|
dois cantos. `transform-box:view-box` garante que o centro (100,100) do
|
|
espelhamento é sempre o centro do viewBox autorado, não a caixa
|
|
delimitadora visual dos fios (que dispararia pro lado errado por causa
|
|
do fio "perdido", bem mais comprido que o resto). */
|
|
.pw-corner--br .pw-strands {
|
|
transform-box: view-box;
|
|
transform-origin: 100px 100px;
|
|
transform: scale(-1, -1);
|
|
}
|
|
|
|
/* Na Principal, a topbar (`position:sticky`, `z-index:20`, fundo opaco —
|
|
layout.css) pintava por cima da metade de cima da teia do canto
|
|
superior-esquerdo, escondendo a aranha; empurra a teia pra começar
|
|
logo abaixo dela (`var(--topbar-height)`, mesmo token que a topbar já
|
|
usa). Não se aplica ao canto inferior-direito (nada cobre o rodapé) nem
|
|
ao login (sem topbar). */
|
|
.pw-corners--portal .pw-corner--tl {
|
|
top: var(--topbar-height);
|
|
}
|
|
|
|
.pw-corners--login .pw-corner {
|
|
width: clamp(220px, 22vw, 320px);
|
|
}
|
|
|
|
.pw-corners--portal .pw-corner {
|
|
width: clamp(160px, 15vw, 230px);
|
|
}
|
|
|
|
.pw-corner svg {
|
|
display: block;
|
|
width: 100%;
|
|
height: auto;
|
|
overflow: visible;
|
|
}
|
|
|
|
.pw-strands {
|
|
fill: none;
|
|
stroke: rgba(242, 237, 227, 0.24);
|
|
stroke-width: 0.7;
|
|
stroke-linecap: round;
|
|
animation: pwShimmer 6.5s ease-in-out infinite;
|
|
}
|
|
|
|
.pw-strand-stray {
|
|
stroke: rgba(242, 237, 227, 0.16);
|
|
}
|
|
|
|
.pw-ring {
|
|
stroke: rgba(242, 237, 227, 0.16);
|
|
stroke-width: 0.6;
|
|
}
|
|
|
|
.pw-legs {
|
|
stroke: #17111d;
|
|
stroke-width: 1.6;
|
|
stroke-linecap: round;
|
|
fill: none;
|
|
transform-box: fill-box;
|
|
transform-origin: center;
|
|
animation: pwLegsWiggle 0.85s ease-in-out infinite;
|
|
}
|
|
|
|
.pw-abdomen,
|
|
.pw-head {
|
|
fill: #17111d;
|
|
}
|
|
|
|
.pw-eye {
|
|
fill: #9e3241;
|
|
}
|
|
|
|
.pw-spider {
|
|
/* Posição/ângulo de descanso (--r*) e do "passeio" pra fora (--o*) — o
|
|
canto inferior-direito redefine essas 4 variáveis abaixo com os valores
|
|
espelhados (200-x, 200-y, +180deg), em vez de espelhar a aranha via
|
|
`transform` (ver comentário grande em `.pw-corner--br .pw-strands`). */
|
|
--rx: 85px;
|
|
--ry: 113px;
|
|
--rr: 51deg;
|
|
--ox: 138px;
|
|
--oy: 178px;
|
|
transform: translate(var(--rx), var(--ry)) rotate(var(--rr));
|
|
animation: pwWalk 8s ease-in-out infinite;
|
|
/* Corpo/pernas são quase a mesma cor do fundo escuro padrão (#17111d vs
|
|
--bg-canvas #121017) — sem esse brilho claro a aranha praticamente some,
|
|
sobrando só o olho vermelho visível. Removido no tema claro abaixo
|
|
(lá o contraste já é natural: aranha escura sobre fundo claro). */
|
|
filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.6)) drop-shadow(0 0 5px rgba(255, 255, 255, 0.3));
|
|
/* Única parte "viva" com `pointer-events:auto` — o resto da teia
|
|
(`.pw-corners`/`.pw-corner`) é `pointer-events:none`, decorativo. */
|
|
pointer-events: auto;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.pw-corner--br .pw-spider {
|
|
--rx: 115px;
|
|
--ry: 87px;
|
|
--rr: 231deg;
|
|
--ox: 62px;
|
|
--oy: 22px;
|
|
}
|
|
|
|
/* Clicar mata a aranha (pedido explícito do usuário) — some caindo/girando,
|
|
pernas encolhem, e uma caveirinha sobe da teia (ver `.pw-skull` abaixo);
|
|
a teia fica vazia pelo resto desta visita à página (só volta com um F5
|
|
de verdade — `pidApplyHalloweenCobwebs()`, seasonal-theme.js, reconstrói
|
|
tudo do zero a cada carregamento). `forwards` mantém o estado final
|
|
(invisível) depois que a animação termina, sem precisar de mais JS. Os
|
|
deslocamentos em `calc()` são sempre "queda pra baixo" (`--ry` + valor
|
|
positivo) em relação ao próprio ponto de descanso do canto, então caem
|
|
do jeito certo nos dois cantos sem precisar de uma segunda animação. */
|
|
.pw-spider.is-dying {
|
|
animation: pwSpiderDie 950ms ease-in forwards;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* Aranha já morta numa visita anterior (persistido em `localStorage`, ver
|
|
`pidGetDeadSpiders()`/`pidApplyHalloweenCobwebs()` em seasonal-theme.js)
|
|
— nasce direto no estado final, sem tocar `pwSpiderDie`/`pwWalk` de novo;
|
|
a caveira nem chega a aparecer (já subiu e sumiu na visita em que a
|
|
aranha morreu, não faz sentido reprisar isso a cada carregamento). Só
|
|
volta com o atalho Tab+B. */
|
|
.pw-spider.is-dead {
|
|
animation: none;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.pw-spider.is-dying .pw-legs {
|
|
animation: pwLegsDie 450ms ease-out forwards;
|
|
}
|
|
|
|
.pw-skull {
|
|
--sx: 85px;
|
|
--sy: 105px;
|
|
opacity: 0;
|
|
transform: translate(var(--sx), var(--sy)) scale(0.4);
|
|
transform-box: fill-box;
|
|
transform-origin: center;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.pw-corner--br .pw-skull {
|
|
--sx: 115px;
|
|
--sy: 95px;
|
|
}
|
|
|
|
.pw-skull circle {
|
|
fill: #f2ede3;
|
|
}
|
|
|
|
.pw-skull-socket {
|
|
fill: #17111d;
|
|
}
|
|
|
|
.pw-skull path {
|
|
stroke: #17111d;
|
|
stroke-width: 0.9;
|
|
fill: none;
|
|
stroke-linecap: round;
|
|
stroke-linejoin: round;
|
|
}
|
|
|
|
.pw-skull.is-rising {
|
|
animation: pwSkullRise 1300ms ease-out forwards;
|
|
}
|
|
|
|
/* Tema claro: a teia (cor creme, pensada pra contrastar com fundo escuro)
|
|
ficaria invisível sobre o fundo branco do tema claro — vira escura. A
|
|
aranha já é escura por natureza (#17111d), então já contrasta sozinha
|
|
contra um fundo claro; o brilho branco de cima (pensado pro fundo escuro)
|
|
só seria ruído aqui, por isso desligado. */
|
|
:root[data-theme="light"] .pw-strands {
|
|
stroke: rgba(20, 16, 28, 0.32);
|
|
}
|
|
|
|
:root[data-theme="light"] .pw-strand-stray,
|
|
:root[data-theme="light"] .pw-ring {
|
|
stroke: rgba(20, 16, 28, 0.2);
|
|
}
|
|
|
|
:root[data-theme="light"] .pw-spider {
|
|
filter: none;
|
|
}
|
|
|
|
@keyframes pwShimmer {
|
|
0%, 100% {
|
|
opacity: 0.85;
|
|
}
|
|
50% {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes pwLegsWiggle {
|
|
0%, 100% {
|
|
transform: rotate(0deg) scaleY(1);
|
|
}
|
|
50% {
|
|
transform: rotate(1.4deg) scaleY(0.95);
|
|
}
|
|
}
|
|
|
|
@keyframes pwWalk {
|
|
0%, 14% {
|
|
transform: translate(var(--rx), var(--ry)) rotate(var(--rr));
|
|
}
|
|
32%, 55% {
|
|
transform: translate(var(--ox), var(--oy)) rotate(var(--rr));
|
|
}
|
|
74%, 100% {
|
|
transform: translate(var(--rx), var(--ry)) rotate(var(--rr));
|
|
}
|
|
}
|
|
|
|
@keyframes pwSpiderDie {
|
|
0% {
|
|
transform: translate(var(--rx), var(--ry)) rotate(var(--rr));
|
|
opacity: 1;
|
|
}
|
|
45% {
|
|
transform: translate(calc(var(--rx) + 8px), calc(var(--ry) + 52px)) rotate(calc(var(--rr) + 99deg));
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
transform: translate(calc(var(--rx) + 13px), calc(var(--ry) + 117px)) rotate(calc(var(--rr) + 209deg));
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes pwLegsDie {
|
|
to {
|
|
transform: rotate(22deg) scaleY(0.72);
|
|
}
|
|
}
|
|
|
|
@keyframes pwSkullRise {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translate(var(--sx), var(--sy)) scale(0.35);
|
|
}
|
|
20% {
|
|
opacity: 1;
|
|
transform: translate(var(--sx), calc(var(--sy) - 15px)) scale(1.15);
|
|
}
|
|
32% {
|
|
transform: translate(var(--sx), calc(var(--sy) - 11px)) scale(1);
|
|
}
|
|
78% {
|
|
opacity: 1;
|
|
transform: translate(var(--sx), calc(var(--sy) - 37px)) scale(1);
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
transform: translate(var(--sx), calc(var(--sy) - 47px)) scale(0.9);
|
|
}
|
|
}
|