portal_publico/static/css/base.css

466 lines
9.5 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 {
font-family: inherit;
color: inherit;
}
button {
cursor: pointer;
border: none;
background: none;
}
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);
}