*, *::before, *::after { box-sizing: border-box; } /* Garante que o atributo/propriedade "hidden" sempre vença, mesmo em elementos cujo próprio componente define display:flex/grid/etc. */ [hidden] { display: none !important; } html, body { height: 100%; } body { margin: 0; font-family: var(--font-sans); background: var(--bg-canvas); color: var(--text-primary); -webkit-font-smoothing: antialiased; transition: background var(--transition-base), color var(--transition-base); } h1, h2, h3, h4, p { margin: 0; } button, input, select, textarea { font-family: inherit; color: inherit; } button { cursor: pointer; border: none; background: none; } /* Toque: tira o atraso de duplo toque (zoom) nos controles e o realce cinza padrão do navegador móvel; o próprio componente já dá o retorno visual. */ button, a, [role="button"], input[type="checkbox"], label { touch-action: manipulation; -webkit-tap-highlight-color: transparent; } a { color: inherit; text-decoration: none; } ul { list-style: none; margin: 0; padding: 0; } svg { display: block; flex-shrink: 0; } .visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; } ::selection { background: var(--accent); color: var(--accent-contrast); } /* Animações globais — rápidas de propósito (a ideia é sensação de fluidez, não "efeito"), e sem nenhum @media (prefers-reduced-motion) desativando: decisão deliberada do usuário, essas animações devem rodar sempre, independente da preferência de acessibilidade do sistema operacional. */ @keyframes pidFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes pidFadeSlideUp { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } @keyframes pidScaleIn { from { opacity: 0; transform: scale(0.96); } to { opacity: 1; transform: scale(1); } } /* Olhos do ícone "P.I.D." (o disquete) piscando ao clicar — usado tanto na logo da sidebar (`sidebar-brand.js`, `layout.css`) quanto no ícone do card de login (`login-logo-blink.js`), por isso mora aqui em vez de num CSS específico de página. Cada olho é um sem nenhum transform de atributo XML (a posição já vem dos cx/cy dos círculos) — importante porque um transform de CSS aplicado a um elemento que já tem transform de atributo SUBSTITUI o atributo inteiro, perdendo a posição; `transform-box:fill-box` faz o scaleY girar em torno do próprio olho, não da origem do SVG. */ .pid-icon-eye { transform-box: fill-box; transform-origin: center; } .pid-icon-eye.is-blinking { animation: pidIconBlink 200ms ease-in-out; } @keyframes pidIconBlink { 0%, 100% { transform: scaleY(1); } 50% { transform: scaleY(0.05); } } /* Variante "susto" do clique na logo, só durante o tema sazonal Halloween (`seasonal-theme.js`/`pidIsHalloweenActive()`) — substitui a piscada acima enquanto o ícone P.I.D. normal é trocado pelo vampiro (`pidApplyVampireIcon`/`pidApplyVampireFull`). `.pid-vamp-icon-rig` é o grupo raiz injetado dentro do (sidebar colapsada/expandida e botão do login); `.pid-vamp-face--friendly`/`--scare` são dois grupos sempre presentes, cruzando opacidade entre si. Mesmo cuidado de `.pid-icon-eye` com `transform-box:fill-box` pra girar/escalar em torno do próprio ícone, não da origem do SVG. */ .pid-vamp-icon-rig, .pid-vamp-cape-group { transform-box: fill-box; transform-origin: center; } .pid-vamp-face--friendly { opacity: 1; } .pid-vamp-face--scare { opacity: 0; } .pid-vamp-icon-rig.is-scaring { animation: pidVampShake 900ms ease; } .pid-vamp-icon-rig.is-scaring .pid-vamp-cape-group { animation: pidVampCapeBurst 900ms ease; } .pid-vamp-icon-rig.is-scaring .pid-vamp-face--friendly { animation: pidVampFaceOut 900ms ease; } .pid-vamp-icon-rig.is-scaring .pid-vamp-face--scare { animation: pidVampFaceIn 900ms ease; } @keyframes pidVampFaceOut { 0% { opacity: 1; } 14% { opacity: 0.15; } 22%, 70% { opacity: 0; } 100% { opacity: 1; } } @keyframes pidVampFaceIn { 0% { opacity: 0; } 14% { opacity: 0.85; } 22%, 70% { opacity: 1; } 100% { opacity: 0; } } @keyframes pidVampCapeBurst { 0%, 100% { transform: scale(1) rotate(0deg); } 16% { transform: scale(1.12) rotate(-3deg); } 30% { transform: scale(1.05) rotate(1.5deg); } 55% { transform: scale(1.07) rotate(-1deg); } } @keyframes pidVampShake { 0% { transform: translateX(0) scale(1); filter: drop-shadow(0 0 0 rgba(158, 50, 65, 0)); } 10% { transform: translateX(-2.4px) scale(1.05); } 18% { transform: translateX(2.6px) scale(1.08); filter: drop-shadow(0 0 10px rgba(158, 50, 65, 0.6)); } 28% { transform: translateX(-1.8px) scale(1.04); } 38% { transform: translateX(1.4px) scale(1.02); filter: drop-shadow(0 0 12px rgba(158, 50, 65, 0.65)); } 55% { transform: translateX(0) scale(1.01); filter: drop-shadow(0 0 8px rgba(158, 50, 65, 0.4)); } 100% { transform: translateX(0) scale(1); filter: drop-shadow(0 0 0 rgba(158, 50, 65, 0)); } } /* 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-width: thin; scrollbar-color: var(--border-subtle) transparent; } *::-webkit-scrollbar { width: 8px; height: 8px; } *::-webkit-scrollbar-thumb { background: var(--border-subtle); border-radius: var(--radius-pill); }