    /* ---------- LOADING (escudo animado) ----------
       Usado por renderLoading() (js/ui/helpers.js) — toda view pública de
       topo escreve isso direto em #app antes do fetch ao Supabase, pra nunca
       existir uma tela sem conteúdo do editor (ver CLAUDE.md, "Loading/erro
       e AdSense"). flex-direction:column + gap centralizam o escudo e a
       mensagem (a mensagem continua existindo por baixo do escudo — é ela
       que garante texto real na tela, não só a imagem). min-height:100vh é o
       que de fato centraliza na tela — renderLoading() substitui #app
       inteiro (sem header/footer durante o loading), então sem uma altura
       pra ocupar, justify-content:center não tinha espaço nenhum pra
       trabalhar e o escudo ficava grudado no topo. */
    .loading-container {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      padding: 40px 0;
      gap: 18px
    }

    .loading-shield {
      width: 220px;
      height: auto;
      animation: vz-pulse 2.4s ease-in-out infinite
    }

    @keyframes vz-pulse {
      0%, 100% {
        transform: scale(0.92);
        opacity: 0.5
      }

      50% {
        transform: scale(1.05);
        opacity: 1
      }
    }

    .loading-msg {
      margin: 0;
      color: var(--muted);
      font-size: 15px;
      text-align: center
    }
