479 lines
9.8 KiB
CSS
479 lines
9.8 KiB
CSS
*,
|
|
*::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 <g class="pid-icon-eye"> 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 <svg> (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);
|
|
}
|