/* 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 `
` 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); } }