/* ═══════════════════════════════════════════════════════════════
   sondar-motion.css — movimento e acabamento do Sondar

   Arquivo ADITIVO, no mesmo padrão do sondar-promo.css: não toca em
   styles.css nem no styles.src.css. Carregado depois deles no
   index.html, então sobrescreve pela ordem da cascata.

   Princípio: amplitude baixa e duração longa. Movimento que a pessoa
   sente e não percebe. Nada pisca, nada salta, nada compete com o
   conteúdo. Quem pediu menos movimento no sistema não recebe nenhum.
   ═══════════════════════════════════════════════════════════════ */

:root {
    /* O CSS atual não tem nenhum token de tempo: as durações estão
       espalhadas como 0.15s, 0.2s, 0.3s, 0.8s, 1.2s, 1.5s, e o easing
       quase nunca é declarado. Daqui pra frente sai tudo daqui. */
    --mv-rapida: 120ms;
    --mv-base: 220ms;
    --mv-hover: 300ms;
    --mv-lenta: 380ms;
    --mv-saida: cubic-bezier(.2, .8, .2, 1);
    --mv-suave: cubic-bezier(.4, 0, .2, 1);

    /* O --primary-dark original (#562ddd) é MAIS CLARO que o
       --primary-color (#522FCB), então todo hover que promete escurecer
       na verdade clareia. Este é o tom escuro de verdade. */
    --mv-primary-escuro: #3D1F9E;
}

/* ═══ 1. Banner "Chama a galera." ═══════════════════════════════ */

/* O gradiente respira. Área de 300% e um passeio mais largo e mais
   rápido que o original: agora dá pra ver a cor caminhar. */
.hero-banner-inner {
    background-size: 300% 300%;
    background-position: 0% 50%;
    animation: mvGradiente 14s ease-in-out infinite;
    isolation: isolate;
}

@keyframes mvGradiente {
    0% {
        background-position: 0% 50%;
    }

    33% {
        background-position: 100% 30%;
    }

    66% {
        background-position: 60% 100%;
    }

    100% {
        background-position: 0% 50%;
    }
}

/* Os dois glows já existiam no HTML, parados. Agora têm curso próprio:
   percorrem o banner inteiro em vez de só respirar no canto. */
.hero-glow {
    will-change: transform, opacity;
}

.hero-glow-1 {
    animation: mvGlow1 17s ease-in-out infinite;
}

.hero-glow-2 {
    animation: mvGlow2 21s ease-in-out infinite;
}

@keyframes mvGlow1 {
    0% {
        transform: translate3d(var(--mv-px, 0px), var(--mv-py, 0px), 0) scale(1);
        opacity: .34;
    }

    30% {
        transform: translate3d(calc(-280px + var(--mv-px, 0px)), calc(60px + var(--mv-py, 0px)), 0) scale(1.3);
        opacity: .5;
    }

    62% {
        transform: translate3d(calc(150px + var(--mv-px, 0px)), calc(110px + var(--mv-py, 0px)), 0) scale(.92);
        opacity: .3;
    }

    100% {
        transform: translate3d(var(--mv-px, 0px), var(--mv-py, 0px), 0) scale(1);
        opacity: .34;
    }
}

@keyframes mvGlow2 {
    0% {
        transform: translate3d(calc(var(--mv-px, 0px) * -1), calc(var(--mv-py, 0px) * -1), 0) scale(1);
        opacity: .3;
    }

    35% {
        transform: translate3d(calc(330px - var(--mv-px, 0px)), calc(-70px - var(--mv-py, 0px)), 0) scale(1.35);
        opacity: .46;
    }

    70% {
        transform: translate3d(calc(-170px - var(--mv-px, 0px)), calc(45px - var(--mv-py, 0px)), 0) scale(.9);
        opacity: .26;
    }

    100% {
        transform: translate3d(calc(var(--mv-px, 0px) * -1), calc(var(--mv-py, 0px) * -1), 0) scale(1);
        opacity: .3;
    }
}

/* Mais dois orbes, pelos pseudo-elementos, para o fundo nunca ficar
   parado em nenhum canto. Ficam atrás do conteúdo (z-index 1). */
.hero-banner-inner::before,
.hero-banner-inner::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    filter: blur(70px);
    pointer-events: none;
    z-index: 0;
}

.hero-banner-inner::before {
    width: 260px;
    height: 260px;
    background: #7b5cf5;
    top: 40%;
    left: -60px;
    animation: mvOrbe1 24s ease-in-out infinite;
}

.hero-banner-inner::after {
    width: 220px;
    height: 220px;
    background: #4ac4ff;
    bottom: -70px;
    right: 18%;
    opacity: .22;
    animation: mvOrbe2 29s ease-in-out infinite;
}

@keyframes mvOrbe1 {
    0% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * 0.7), calc(0px + var(--mv-py, 0px) * 0.8), 0) scale(1);
        opacity: .26;
    }

    40% {
        transform: translate3d(calc(470px + var(--mv-px, 0px) * 0.7), calc(-90px + var(--mv-py, 0px) * 0.8), 0) scale(1.25);
        opacity: .38;
    }

    75% {
        transform: translate3d(calc(-130px + var(--mv-px, 0px) * 0.7), calc(80px + var(--mv-py, 0px) * 0.8), 0) scale(.95);
        opacity: .2;
    }

    100% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * 0.7), calc(0px + var(--mv-py, 0px) * 0.8), 0) scale(1);
        opacity: .26;
    }
}

@keyframes mvOrbe2 {
    0% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * -0.9), calc(0px + var(--mv-py, 0px) * -0.65), 0) scale(1);
        opacity: .2;
    }

    45% {
        transform: translate3d(calc(-420px + var(--mv-px, 0px) * -0.9), calc(-60px + var(--mv-py, 0px) * -0.65), 0) scale(1.3);
        opacity: .3;
    }

    100% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * -0.9), calc(0px + var(--mv-py, 0px) * -0.65), 0) scale(1);
        opacity: .2;
    }
}

/* Mais dois orbes, agora pendurados no .hero-content. Ele é relative com
   z-index 1, então os pseudo-elementos precisam de z-index -1 para ficar
   atrás do texto sem sair do banner (o inner tem overflow hidden). */
.hero-content::before,
.hero-content::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    z-index: -1;
    will-change: transform, opacity;
}

.hero-content::before {
    width: 200px;
    height: 200px;
    background: #9f7aea;
    filter: blur(65px);
    top: -30px;
    left: 25%;
    animation: mvOrbe3 26s ease-in-out infinite;
}

.hero-content::after {
    width: 170px;
    height: 170px;
    background: #38bdf8;
    filter: blur(60px);
    bottom: -40px;
    left: 8%;
    animation: mvOrbe4 33s ease-in-out infinite;
}

@keyframes mvOrbe3 {
    0% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * 1.25), calc(0px + var(--mv-py, 0px) * -0.75), 0) scale(1);
        opacity: .24;
    }

    38% {
        transform: translate3d(calc(-300px + var(--mv-px, 0px) * 1.25), calc(70px + var(--mv-py, 0px) * -0.75), 0) scale(1.28);
        opacity: .34;
    }

    71% {
        transform: translate3d(calc(240px + var(--mv-px, 0px) * 1.25), calc(30px + var(--mv-py, 0px) * -0.75), 0) scale(.94);
        opacity: .2;
    }

    100% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * 1.25), calc(0px + var(--mv-py, 0px) * -0.75), 0) scale(1);
        opacity: .24;
    }
}

@keyframes mvOrbe4 {
    0% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * -1.1), calc(0px + var(--mv-py, 0px) * 1), 0) scale(1);
        opacity: .18;
    }

    33% {
        transform: translate3d(calc(400px + var(--mv-px, 0px) * -1.1), calc(-50px + var(--mv-py, 0px) * 1), 0) scale(1.22);
        opacity: .3;
    }

    66% {
        transform: translate3d(calc(180px + var(--mv-px, 0px) * -1.1), calc(-110px + var(--mv-py, 0px) * 1), 0) scale(.9);
        opacity: .16;
    }

    100% {
        transform: translate3d(calc(0px + var(--mv-px, 0px) * -1.1), calc(0px + var(--mv-py, 0px) * 1), 0) scale(1);
        opacity: .18;
    }
}

/* Entrada do conteúdo do banner: sobe uma vez, escalonado.
   `both` segura o estado inicial até a vez de cada elemento.

   Estava tão rápido que terminava antes de a pessoa olhar — a página
   abria já pronta e ninguém via animação nenhuma. Agora cada linha
   demora quase um segundo e o intervalo entre elas é grande o bastante
   para a cascata se ler como cascata. A última fecha por volta de 1,5s.
   O percurso também subiu de 14px para 20px, que é o que dá para
   perceber movimento em vez de só um piscar de opacidade. */
.hero-content>* {
    animation: mvEntrarHero 900ms var(--mv-saida) both;
}

.hero-logo {
    animation-delay: 120ms;
}

.hero-title {
    animation-delay: 300ms;
}

.hero-subtitle {
    animation-delay: 480ms;
}

.hero-buttons {
    animation-delay: 660ms;
}

@keyframes mvEntrarHero {
    from {
        opacity: 0;
        transform: translate3d(0, 20px, 0);
    }
}

@keyframes mvEntrar {
    from {
        opacity: 0;
        transform: translate3d(0, 14px, 0);
    }
}

/* Os botões de loja ganham elevação em vez de só escalar. */
.hero-store-btn {
    transition: transform var(--mv-base) var(--mv-saida),
        filter var(--mv-base) var(--mv-saida);
    border-radius: 8px;
}

.hero-store-btn:hover {
    transform: translate3d(0, -3px, 0) scale(1.02);
    opacity: 1;
    filter: drop-shadow(0 8px 16px rgba(0, 0, 0, .28));
}

.hero-store-btn:active {
    transform: translate3d(0, -1px, 0) scale(.99);
    transition-duration: var(--mv-rapida);
}

/* ═══ 2. Card de evento ═════════════════════════════════════════ */

/* O hover do card era só uma troca de cor de borda — o elemento
   principal da página tinha o feedback mais fraco do site, mais fraco
   inclusive que o do card patrocinado ao lado dele. */
/* A porcentagem precisa ser registrada para poder ser animada: sem o
   @property o navegador trata a variavel como texto e o valor salta de
   0% para 100% sem passar pelo meio — que e justamente o percurso que
   se quer ver. */
@property --mv-borda {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

/* Hover só com mouse de verdade. `hover: hover` sozinho deixa passar
   aparelho de toque que se declara capaz de hover, e ali o toque no card
   deixava a borda desenhada e o zoom presos até tocar em outro lugar.

   Entrada e saída têm tempos diferentes: a entrada é o efeito, a saída
   é o sistema respondendo e precisa ser curta. A duração de entrada
   fica na regra :hover, que é a que vale enquanto o mouse está em cima. */
@media (hover: hover) and (pointer: fine) {
    .evento-card {
        /* Precisa de um contexto de posicionamento para a borda desenhada.
           O translate3d em repouso nao e enfeite: sem ele o navegador so
           promove o card a camada propria no primeiro hover, e esse
           primeiro passe engasga. Com ele, a camada ja existe. */
        position: relative;
        transform: translate3d(0, 0, 0);
        transition: transform 200ms var(--mv-saida);
    }

    /* Sem sombra: o desenho da borda ja e o retorno do hover, e a sombra
       em volta so sujava a leitura. Sobra uma elevacao minima, so pra dar
       tato — sem sombra, mais que isso nao se enxerga mesmo. */
    .evento-card:hover {
        transform: translate3d(0, -2px, 0);
        transition-duration: var(--mv-hover);
        transition-timing-function: var(--mv-suave);
    }

    /* A borda do CSS base acendia inteira de uma vez. Aqui ela fica na
       cor de repouso e quem pinta de roxo e o tracado por cima. */
    .evento-card:hover {
        border-color: var(--neutral-40, #B7B7B7);
    }

    /* O tracado: um conic-gradient que preenche de 0% a 100% a partir do
       topo, recortado em mascara para sobrar so a moldura de 1,5px. O
       resultado e a linha correndo pelo perimetro do card. */
    .evento-card::after {
        content: "";
        position: absolute;
        inset: 0;
        z-index: 2;
        /* 7px, e nao os 8px do card: `inset: 0` encosta na caixa interna,
           que ja perdeu 1px para a borda do card e por isso curva com raio
           7. Com 8 aqui, as quinas passavam do espaco e o `overflow:
           hidden` do card raspava justamente elas — a linha fechava com
           canto reto. */
        border-radius: 7px;
        padding: 1.5px;
        pointer-events: none;
        background: conic-gradient(from 180deg,
                var(--primary-color, #522FCB) var(--mv-borda),
                transparent 0);
        -webkit-mask:
            linear-gradient(#000 0 0) content-box,
            linear-gradient(#000 0 0);
        -webkit-mask-composite: xor;
        mask:
            linear-gradient(#000 0 0) content-box,
            linear-gradient(#000 0 0);
        mask-composite: exclude;
        transition: --mv-borda 200ms var(--mv-saida);
    }

    .evento-card:hover::after {
        --mv-borda: 100%;
        transition: --mv-borda 560ms var(--mv-suave);
    }
}

/* Teclado: o card acende a borda quando algo dentro dele recebe foco.
   Sem o traçado animado — ele é resposta ao mouse; para quem navega
   por Tab vale a clareza imediata. Vem depois do bloco de hover para
   ganhar dele quando os dois acontecem juntos. */
.evento-card:has(:focus-visible) {
    border-color: var(--primary-color, #522FCB);
}

/* Entrada dos eventos. Antes o grid era zerado e reconstruído, então
   o conteúdo simplesmente trocava — ao rolar, ao filtrar, ao buscar. */
.evento-card,
.skeleton-card {
    animation: mvEntrar 420ms var(--mv-saida) both;
}

/* O renderEventos() do script.js apaga o grid inteiro e reconstrói a
   lista toda a cada carregamento. Com a animação de entrada, isso fazia
   os cards já lidos reanimarem junto com os novos, e a lista inteira
   piscava a cada rolagem. O sondar-motion.js marca quem já entrou pelo
   data-event-id, e só os realmente novos animam. */
.evento-card.mv-sem-entrada {
    animation: none;
}

.eventos-grid> :nth-child(2) {
    animation-delay: 40ms;
}

.eventos-grid> :nth-child(3) {
    animation-delay: 80ms;
}

.eventos-grid> :nth-child(4) {
    animation-delay: 120ms;
}

.eventos-grid> :nth-child(5) {
    animation-delay: 160ms;
}

.eventos-grid> :nth-child(n+6) {
    animation-delay: 200ms;
}

/* A foto entrava num frame, com o espaço branco antes. O `.mv-carregada`
   é posto no evento load, em sondar-motion.js. O espaço já era
   reservado pelo container, então não há deslocamento de layout. */
.evento-image {
    opacity: 0;
    transform: scale(1.03);
    transition: opacity var(--mv-lenta) var(--mv-saida),
        transform 600ms var(--mv-saida);
}

.evento-image.mv-carregada {
    opacity: 1;
    transform: scale(1);
}

/* Foto que ja apareceu antes entra pronta: sem opacidade zero e sem
   transicao de entrada. E o mesmo tratamento dado ao card em
   `.mv-sem-entrada` — novidade anima, repeticao nao.

   As transicoes de `scale` e `filter` ficam: elas nao tem nada a ver
   com a entrada, sao do hover, e zera-las aqui mataria o zoom. */
.evento-image.mv-sem-fade {
    opacity: 1;
    transform: none;
    transition: scale 200ms var(--mv-saida),
        filter 200ms var(--mv-saida);
}

/* Ao passar o mouse no card, a foto ganha um respiro de saturacao e um
   zoom curto. O zoom usa a propriedade `scale`, e nao `transform`: o
   transform ja e do fade de carregamento, e as duas brigariam — a
   ultima regra venceria e o fade ou o zoom se perderia. Como `scale` e
   propriedade propria, as duas convivem.

   O container tem overflow hidden, entao a foto cresce por dentro do
   recorte, sem empurrar o card. */
@media (hover: hover) and (pointer: fine) {
    .evento-image {
        transition: opacity var(--mv-lenta) var(--mv-saida),
            transform 600ms var(--mv-saida),
            scale 200ms var(--mv-saida),
            filter 200ms var(--mv-saida);
    }

    .evento-card:hover .evento-image {
        scale: 1.05;
        filter: saturate(1.18);
        transition: opacity var(--mv-lenta) var(--mv-saida),
            transform 600ms var(--mv-saida),
            scale var(--mv-hover) var(--mv-suave),
            filter var(--mv-hover) var(--mv-suave);
    }
}

/* ═══ 3. Os botões que não tinham reação nenhuma ════════════════ */

.compact-filtros-btn,
.link-more,
.evento-toggle-link,
.filter-clear,
.filter-close,
.chip-remove,
.evento-site-link,
.popup-close,
.calendar-nav-btn,
.cal-day,
.breadcrumbs a,
.internal-links-grid a {
    transition: background-color var(--mv-rapida) var(--mv-saida),
        border-color var(--mv-rapida) var(--mv-saida),
        color var(--mv-rapida) var(--mv-saida),
        opacity var(--mv-rapida) var(--mv-saida),
        transform var(--mv-rapida) var(--mv-saida);
}

.compact-filtros-btn:hover,
.filter-clear:hover {
    background-color: var(--neutral-10);
    border-color: var(--primary-color);
}

.link-more:hover,
.evento-toggle-link:hover {
    opacity: .7;
    text-decoration: underline;
}

.filter-close:hover,
.chip-remove:hover,
.popup-close:hover {
    opacity: .6;
    transform: scale(1.1);
}

.evento-site-link:hover {
    color: var(--mv-primary-escuro);
}

.logo-link,
.insta-logo {
    transition: opacity var(--mv-rapida) var(--mv-saida);
}

.logo-link:hover,
.insta-logo:hover {
    opacity: .75;
}

/* Todo botão afunda um pouco ao ser clicado. Não existia um único
   `:active` no CSS da página. */
button:active,
.btn-download:active,
.filter-button:active,
.filter-chip:active,
.filter-apply:active {
    transform: scale(.97);
    transition-duration: var(--mv-rapida);
}

/* ═══ 4. Foco visível ═══════════════════════════════════════════ */

/* Não havia nenhum `:focus-visible` na página, e o campo de busca ainda
   fazia `outline: none` sem colocar nada no lugar. Navegar por teclado
   era às cegas. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 3px;
    border-radius: 4px;
}

.hero-banner-inner :where(a, button):focus-visible,
.promo-slide :where(a, button):focus-visible {
    outline-color: #fff;
}

.search-container-header .search-input:focus-visible {
    outline-offset: 0;
}

/* ═══ 5. Correções de token ═════════════════════════════════════ */

/* O copyright do rodapé era branco a 30% no preto: 2,6:1, ilegível para
   muita gente. A 55% dá 5,8:1 e continua sendo a linha mais discreta. */
.footer-copy {
    color: rgba(255, 255, 255, .55);
}

/* `--neutral-50` nunca foi definido no :root, então a declaração é
   inválida e a cor herda do body: os breadcrumbs saíam em preto quase
   puro em vez de cinza. */
.breadcrumbs,
.breadcrumbs a {
    color: var(--neutral-60);
}

.breadcrumbs a:hover {
    color: var(--primary-color);
}

/* ═══ 5b. O H1 trocando de texto na cara da pessoa ══════════════ */

/* O H1 chega com um texto provisório e o script.js reescreve depois,
   com a contagem ("629 Eventos em São Paulo"). Dava para ver a troca
   acontecendo. Agora ele fica coberto por um esqueleto, no mesmo
   vocabulário dos cards, até o texto final assentar.

   A classe é posta pelo JS, então quem está sem JS — e o crawler —
   continua recebendo o título visível normalmente. O texto segue no
   DOM o tempo todo: só a cor fica transparente, o que preserva a
   altura da caixa e evita a página pular quando o esqueleto sai. */
.seo-h1.mv-h1-carregando {
    position: relative;
    color: transparent;
}

.seo-h1.mv-h1-carregando::after {
    content: "";
    position: absolute;
    inset: 2px 0;
    border-radius: 6px;
    background: linear-gradient(90deg,
            var(--neutral-20, #E6E6E6) 25%,
            var(--neutral-10, #F3F3F3) 37%,
            var(--neutral-20, #E6E6E6) 63%);
    background-size: 400% 100%;
    animation: mvEsqueleto 1.4s ease infinite;
}

@keyframes mvEsqueleto {
    from {
        background-position: 100% 50%;
    }

    to {
        background-position: 0 50%;
    }
}

/* `border-radius: 3232px` no badge é "32px" digitado duas vezes. O
   valor pretendido era pílula, mas 32px destoa dos 8px que o resto do
   site usa — e a /fds/ espelha este elemento, então os dois andam
   juntos. */
.evento-badge {
    border-radius: 8px;
}

/* ═══ 6. Quem pediu menos movimento não recebe nenhum ═══════════ */

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        /* O atraso precisa cair junto. Sem isso, uma animação com
           `both` segura o estado inicial durante todo o delay — o
           banner escalonado ficaria invisível por mais de meio segundo
           justamente para quem pediu menos movimento. */
        animation-delay: 0ms !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }

    /* O spinner precisa continuar girando: ele comunica estado. */
    .loading-spinner {
        animation: spin .8s linear infinite !important;
    }

    .evento-image {
        opacity: 1;
        transform: none;
    }

    .hero-banner-inner {
        background-position: 0% 50%;
    }
}

/* ═══ 7. Toast ══════════════════════════════════════════════════ */

/* Substitui o alert() nativo do compartilhar — um diálogo bloqueante do
   sistema operacional no meio do fluxo era o sinal mais óbvio de site
   cru na página. A troca é feita em sondar-motion.js. */
.mv-toast {
    position: fixed;
    left: 50%;
    bottom: 28px;
    z-index: 3000;
    max-width: calc(100vw - 32px);
    padding: 12px 20px;
    border-radius: 999px;
    background: var(--neutral-90, #0F0F0F);
    color: #fff;
    font-family: var(--font-family, 'Lato', sans-serif);
    font-size: 14px;
    font-weight: 700;
    line-height: 1.3;
    text-align: center;
    box-shadow: 0 8px 28px rgba(0, 0, 0, .28);
    opacity: 0;
    transform: translate3d(-50%, 12px, 0);
    transition: opacity var(--mv-base) var(--mv-saida),
        transform var(--mv-base) var(--mv-saida);
    pointer-events: none;
}

/* Onde a barra do app ocupa o pé da tela, o toast sobe acima dela em
   vez de nascer por baixo. Mesmo corte da barra, em appbar.css. */
@media (max-width: 900px) {
    body.tem-appbar .mv-toast {
        bottom: calc(var(--appbar-altura, 84px) + 12px + env(safe-area-inset-bottom, 0px));
    }
}

/* Quem anuncia o toast para o leitor de tela é esta região, que existe
   desde o carregamento. Uma região ao vivo criada junto com o texto
   costuma ser ignorada: o leitor só anuncia mudança em região que ele
   já conhecia. */
.mv-anuncio {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.mv-toast-visivel {
    opacity: 1;
    transform: translate3d(-50%, 0, 0);
}
