/* =========================================================================
   Zelaria — assets/css/zelaria.css
   Sistema visual da plataforma. Carregado em toda tela, clara ou escura.

   Direção estética: "zeladoria moderna". Campo azul-gelo com textura de planta
   baixa, superfícies brancas de borda nítida, títulos em serifa quente
   (referência ao documento impresso de condomínio: convenção, ata, balancete)
   e interface em sans humanista. O roxo da marca é acento afiado de ação —
   nunca gradiente lavado sobre branco.

   Fontes auto-hospedadas de propósito: CDN do Google entregaria o IP de cada
   morador ao Google a cada carregamento, o que briga com a regra 14 (LGPD).

   Contraste verificado: texto principal e secundário acima de 4.5:1 nos dois
   temas; alvo de toque mínimo de 44x44px; foco sempre visível.
   ========================================================================= */

/* -------------------------------------------------------------------------
   1. Fontes
   ---------------------------------------------------------------------- */
@font-face {
    font-family: 'Fraunces';
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
    src: url('../fonts/fraunces-latin-wght-normal.woff2') format('woff2-variations');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Manrope';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url('../fonts/manrope-latin-wght-normal.woff2') format('woff2-variations');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* -------------------------------------------------------------------------
   2. Tokens — tema claro (padrão)
   ---------------------------------------------------------------------- */
:root {
    /* Superfícies */
    --fundo:            #f2f9fc;
    --superficie:       #ffffff;
    --superficie-2:     #eaf4f8;
    --superficie-3:     #dceef4;

    /* Traços */
    --borda:            #cde2ea;
    --borda-forte:      #a4c8d7;

    /* Tinta */
    --tinta:            #12212e;
    --tinta-2:          #4a6577;
    --tinta-3:          #5e7889;

    /* Marca e ação */
    --gelo:             #d7ebf1;
    --marca:            #c77dff;
    --acao:             #7b2cbf;
    --acao-forte:       #63229b;
    --acao-texto:       #ffffff;
    --acao-suave:       #f4e6ff;
    --acao-borda:       #dcb8f7;

    /* Estados */
    --sucesso:          #0d6f56;
    --sucesso-suave:    #dcf3ec;
    --alerta:           #8a5100;
    --alerta-suave:     #ffefd8;
    --erro:             #ad2018;
    --erro-suave:       #fde8e6;

    /* Estrela de avaliação: cor própria, não semântica — "vazia" precisa
       continuar visível sobre um cartão claro (branco puro sumiria), e
       "preenchida" é sempre o mesmo dourado nos dois temas, porque estrela
       de nota é convenção universal, não decisão de marca. */
    --estrela-vazia:    #c7d3d9;
    --estrela:          #ffc107;

    /* Elevação */
    --sombra-1:  0 1px 2px rgba(18, 33, 46, .06);
    --sombra-2:  0 4px 12px -4px rgba(18, 33, 46, .14);
    --sombra-3:  0 18px 40px -22px rgba(18, 33, 46, .40);

    /* Métrica */
    --r-1: 8px;
    --r-2: 12px;
    --r-3: 18px;
    --r-max: 999px;

    --fonte-titulo: 'Fraunces', Georgia, 'Times New Roman', serif;
    --fonte-corpo:  'Manrope', ui-sans-serif, system-ui, -apple-system, sans-serif;

    /* Textura de planta baixa das telas públicas */
    --grade: rgba(18, 33, 46, .045);

    color-scheme: light;
}

/* -------------------------------------------------------------------------
   3. Tokens — tema escuro (inversão coerente da mesma identidade)
   ---------------------------------------------------------------------- */
[data-tema='escuro'] {
    --fundo:            #0b161f;
    --superficie:       #13222d;
    --superficie-2:     #1a2f3d;
    --superficie-3:     #23404f;

    --borda:            #26404f;
    --borda-forte:      #3a6076;

    --tinta:            #e4f1f6;
    --tinta-2:          #a6c0cd;
    --tinta-3:          #90aab8;

    --gelo:             #2b4a5b;
    --marca:            #c77dff;
    --acao:             #c77dff;
    --acao-forte:       #d9a3ff;
    --acao-texto:       #1a0f26;
    --acao-suave:       #2c1c3d;
    --acao-borda:       #5b3a7a;

    --sucesso:          #5fd8b4;
    --sucesso-suave:    #10332b;
    --alerta:           #f0b45f;
    --alerta-suave:     #3a2a10;
    --erro:             #ff938c;
    --erro-suave:       #3d1a17;

    /* No escuro a estrela vazia pode ser branca de verdade — o cartão por
       trás é escuro, não some como sumiria no claro. */
    --estrela-vazia:    #e4f1f6;

    --sombra-1:  0 1px 2px rgba(0, 0, 0, .40);
    --sombra-2:  0 4px 12px -4px rgba(0, 0, 0, .55);
    --sombra-3:  0 18px 40px -22px rgba(0, 0, 0, .85);

    --grade: rgba(215, 235, 241, .05);

    color-scheme: dark;
}

/* -------------------------------------------------------------------------
   4. Base
   ---------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    min-height: 100dvh;
    background: var(--fundo);
    color: var(--tinta);
    font-family: var(--fonte-corpo);
    font-size: 1rem;
    font-weight: 450;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3 {
    font-family: var(--fonte-titulo);
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -.015em;
    margin: 0 0 .5rem;
}

h1 { font-size: 1.6rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; font-family: var(--fonte-corpo); font-weight: 700; letter-spacing: 0; }

p { margin: 0 0 1rem; }
p:last-child { margin-bottom: 0; }

a {
    color: var(--acao);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}
a:hover { color: var(--acao-forte); }

small, .sutil {
    font-size: .8125rem;
    color: var(--tinta-2);
    line-height: 1.5;
}

.icone { flex: none; vertical-align: -.15em; }

/* Foco visível em tudo que é interativo — nunca removido. */
:focus-visible {
    outline: 3px solid var(--acao);
    outline-offset: 2px;
    border-radius: 4px;
}
[data-tema='escuro'] :focus-visible { outline-color: var(--marca); }

.so-leitor-tela {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Pular para o conteúdo (navegação por teclado) */
.pular-para-conteudo {
    position: absolute;
    left: .5rem; top: -4rem;
    z-index: 100;
    padding: .75rem 1rem;
    background: var(--superficie);
    color: var(--tinta);
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-1);
    transition: top .15s ease;
}
.pular-para-conteudo:focus { top: .5rem; }

/* -------------------------------------------------------------------------
   5. Camada pública (login, cadastro, convite)
   Textura de planta baixa: grade fina + duas manchas de luz frias.
   ---------------------------------------------------------------------- */
.tela-publica {
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 1.5rem 1rem calc(1.5rem + env(safe-area-inset-bottom));
    background-color: var(--fundo);
    background-image:
        radial-gradient(90% 60% at 15% -10%, var(--superficie) 0%, transparent 60%),
        radial-gradient(70% 50% at 95% 0%, var(--gelo) 0%, transparent 55%),
        linear-gradient(var(--grade) 1px, transparent 1px),
        linear-gradient(90deg, var(--grade) 1px, transparent 1px);
    background-size: auto, auto, 28px 28px, 28px 28px;
}

.cartao-publico {
    width: 100%;
    max-width: 25rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-3);
    box-shadow: var(--sombra-3);
    padding: 1.75rem 1.5rem;
}

.cartao-publico--largo { max-width: 34rem; }

.marca {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--fonte-titulo);
    font-size: 1.3rem;
    font-weight: 600;
    letter-spacing: -.02em;
    color: var(--tinta);
    text-decoration: none;
}
.marca__simbolo {
    display: grid;
    place-items: center;
    width: 2.125rem; height: 2.125rem;
    border-radius: 10px;
    background: var(--acao-suave);
    border: 1px solid var(--acao-borda);
    color: var(--acao);
}
[data-tema='escuro'] .marca__simbolo { color: var(--marca); }

.cabecalho-publico { margin-bottom: 1.5rem; }
.cabecalho-publico h1 { margin: 1.25rem 0 .25rem; font-size: 1.45rem; }
.cabecalho-publico p { color: var(--tinta-2); font-size: .9375rem; margin: 0; }

.rodape-publico {
    margin-top: 1.25rem;
    text-align: center;
    font-size: .8125rem;
    color: var(--tinta-2);
}

/* -------------------------------------------------------------------------
   6. Camada autenticada — casca do app

   Um HTML só, duas formas:

   celular (< 64rem)   topo fixo com marca e ações; logo abaixo a navegação
                       como faixa que rola na horizontal, com os grupos
                       separados por um traço e os títulos escondidos — numa
                       faixa horizontal, título de grupo rouba espaço e não
                       orienta ninguém.

   desktop (>= 64rem)  barra lateral fixa colada na borda esquerda da tela,
                       com os títulos de grupo à mostra, o seletor de
                       condomínio no alto e o bloco "Você" empurrado para o
                       rodapé. O topo perde a marca (que sobe para a lateral)
                       e sobra com sino e tema à direita, alinhados com a
                       borda direita do conteúdo.

   Por que a lateral é `fixed` colada em left:0 e não uma coluna de grade:
   coluna de grade acompanha a altura do conteúdo e para no meio da tela em
   página curta. E por que colada na borda, e não centralizada junto com o
   conteúdo: centralizar a casca inteira joga o menu para dentro da tela em
   monitor largo, e aí o olho tem de atravessar um vão a cada leitura. Colada
   na borda, a lateral É a borda; o conteúdo é que se centraliza no que sobra.
   ---------------------------------------------------------------------- */

/* Métricas da casca. Ficam aqui, e não no bloco de tokens do topo do arquivo,
   porque só fazem sentido junto das regras que as consomem. --recuo é o mesmo
   recuo lateral do topo e do conteúdo: é ele que faz o sino cair exatamente na
   vertical da borda direita do primeiro cartão. */
:root {
    --lateral: 15.5rem;
    --coluna:  82rem;
    --recuo:   1rem;
}

/* A escada do recuo vive AQUI, inteira e em ordem crescente. Espalhada por
   vários pontos do arquivo ela quebra sozinha: duas media queries que casam na
   mesma largura são resolvidas por ordem de escrita, não pela largura — um
   `min-width: 48rem` escrito depois de um `min-width: 64rem` ganha dele numa
   tela de 1600px, e ninguém entende por que o recuo encolheu. */
@media (min-width: 48rem) { :root { --recuo: 1.5rem; } }
@media (min-width: 64rem) { :root { --recuo: 2rem; } }
@media (min-width: 90rem)  { :root { --recuo: 2.5rem; --lateral: 17rem; } }
@media (min-width: 120rem) { :root { --coluna:  92rem; } }
@media (min-width: 160rem) { :root { --coluna: 106rem; } }

.app {
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
}

/* ---- Topo ------------------------------------------------------------- */
.topo {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--superficie);
    border-bottom: 1px solid var(--borda);
    padding: .5rem var(--recuo);
    padding-top: calc(.5rem + env(safe-area-inset-top));
}

.topo__linha {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-height: 44px;
    max-width: calc(var(--coluna) - var(--recuo) * 2);
    margin: 0 auto;
}

.topo__acoes {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: .25rem;
}

/* Busca fixa no topo — mesmo par ícone+campo de .busca-convencao, só que
   compacta, porque mora dentro de um cabeçalho que fica sempre visível
   (o .topo já é `position: sticky`, então a busca sobe junto). */
.topo__busca { margin-top: .5rem; }
.busca-topo {
    position: relative;
    display: flex;
    align-items: center;
    max-width: calc(var(--coluna) - var(--recuo) * 2);
    margin: 0 auto;
}
.busca-topo > .icone {
    position: absolute;
    left: .875rem;
    color: var(--tinta-3);
    pointer-events: none;
}
.busca-topo .entrada {
    padding-left: 2.5rem;
    min-height: 40px;
    border-radius: var(--r-max);
    background: var(--fundo);
    font-size: .9375rem;
}
.busca-topo .entrada:focus ~ .icone,
.busca-topo:focus-within > .icone { color: var(--acao); }

/* Resultados de navegação em modules/busca/index.php — a linha inteira é o
   link (diferente do uso comum de .linha, que é só o contêiner de uma ação
   ao lado). Por isso o reset de cor/sublinhado fica preso a este contêiner,
   sem mexer no .linha usado em todo o resto do site. */
.busca-destinos .linha {
    color: inherit;
    text-decoration: none;
    border-radius: var(--r-1);
    padding-inline: .5rem;
    transition: background .15s ease;
}
.busca-destinos .linha:hover,
.busca-destinos .linha:focus-visible { background: var(--superficie-2); }
.busca-destinos .linha__acoes { color: var(--tinta-3); }

/* O sino aponta para uma tela como qualquer item de menu — quando é a tela
   atual, ele recebe o mesmo destaque que um item ativo receberia. */
.sino[aria-current='page'] {
    color: var(--acao);
    background: var(--acao-suave);
}
[data-tema='escuro'] .sino[aria-current='page'] { color: var(--marca); }

/* ---- Seletor de portfólio --------------------------------------------- */
/* Assinatura visual do produto. Cada condomínio ganha uma etiqueta de cor,
   como chave num chaveiro. Rótulo fixo em cima do nome porque "Aurora" sozinho
   não diz o que é — "CONDOMÍNIO / Aurora" diz, e ainda dá alvo de toque maior. */
.portfolio { position: relative; }

.portfolio__botao {
    display: flex;
    align-items: center;
    gap: .625rem;
    width: 100%;
    min-height: 52px;
    padding: .4375rem .625rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    color: var(--tinta);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.portfolio__botao:hover { border-color: var(--borda-forte); }
.portfolio__botao[aria-expanded='true'] {
    border-color: var(--acao);
    background: var(--acao-suave);
}
.portfolio__botao > .icone { margin-left: auto; color: var(--tinta-2); }

.portfolio__etiqueta {
    width: .375rem;
    align-self: stretch;
    min-height: 1.75rem;
    border-radius: var(--r-max);
    background: var(--marca);
    flex: none;
}

.portfolio__texto { display: grid; min-width: 0; }

.portfolio__rotulo {
    font-size: .625rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--tinta-2);
    line-height: 1.5;
}

.portfolio__nome {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 650;
    font-size: .9375rem;
    line-height: 1.3;
}

.portfolio__lista {
    position: absolute;
    top: calc(100% + .5rem);
    left: 0;
    z-index: 30;
    min-width: 100%;
    width: max-content;
    max-width: min(22rem, calc(100vw - 2rem));
    max-height: min(60vh, 26rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: .375rem;
    margin: 0;
    list-style: none;
    background: var(--superficie);
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-2);
    box-shadow: var(--sombra-3);
}
.portfolio__lista[hidden] { display: none; }

.portfolio__item {
    display: flex;
    align-items: center;
    gap: .625rem;
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding: .5rem .625rem;
    border: 0;
    border-radius: var(--r-1);
    background: transparent;
    color: var(--tinta);
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.portfolio__item:hover { background: var(--superficie-2); }
.portfolio__item[aria-current='true'] {
    background: var(--acao-suave);
    color: var(--acao);
    font-weight: 700;
}
[data-tema='escuro'] .portfolio__item[aria-current='true'] { color: var(--marca); }

/* ---- Navegação -------------------------------------------------------- */
.nav {
    background: var(--superficie);
    border-bottom: 1px solid var(--borda);
}

/* Marca e identidade só existem na barra lateral do desktop. No celular a
   marca vive no topo e o nome da pessoa está a um toque em "Meu perfil". */
.nav__marca,
.nav__eu { display: none; }

/* O seletor fica FORA da faixa que rola, de propósito: dentro de um container
   com overflow, a lista suspensa seria cortada na borda dele. */
.nav__portfolio {
    padding: .625rem var(--recuo);
    border-bottom: 1px solid var(--borda);
}

.nav__rolagem {
    display: flex;
    align-items: stretch;
    /* `relative` não é enfeite: sem ele, o <span class="so-leitor-tela"> que
       acompanha cada contador (é `position: absolute` sem deslocamento) tem
       como bloco de contenção o documento inteiro, não esta caixa. Resultado:
       o recorte do overflow não o alcança, ele fica pousado lá pelos 1500px
       de onde a faixa rolou, e a PÁGINA passa a rolar de lado no celular.
       Com `relative` a caixa vira o bloco de contenção e o recorte pega.
       O mesmo defeito já existia na navegação anterior — veio junto. */
    position: relative;
    overflow-x: auto;
    scrollbar-width: none;
    padding-inline: calc(var(--recuo) - .5rem);
}
.nav__rolagem::-webkit-scrollbar { display: none; }

.nav__grupo { display: flex; align-items: stretch; }
.nav__grupo + .nav__grupo {
    margin-left: .5rem;
    padding-left: .5rem;
    border-left: 1px solid var(--borda);
}
.nav__grupo-titulo { display: none; }

.nav__lista {
    display: flex;
    align-items: stretch;
    gap: .125rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav__link {
    display: flex;
    align-items: center;
    gap: .5rem;
    min-height: 46px;
    padding: 0 .625rem;
    white-space: nowrap;
    color: var(--tinta-2);
    font-size: .9375rem;
    font-weight: 600;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: color .15s ease, background .15s ease;
}
.nav__link:hover { color: var(--tinta); }
.nav__link[aria-current='page'] {
    color: var(--acao);
    border-bottom-color: var(--acao);
}
[data-tema='escuro'] .nav__link[aria-current='page'] {
    color: var(--marca);
    border-bottom-color: var(--marca);
}

/* Sair fica sóbrio em repouso e vermelho só quando a pessoa chega nele — cor
   de perigo permanente vira ruído e a pessoa para de enxergá-la. */
.nav__link--sair:hover,
.nav__link--sair:focus-visible {
    color: var(--erro);
    background: var(--erro-suave);
}

.conteudo {
    flex: 1;
    width: 100%;
    max-width: var(--coluna);
    margin: 0 auto;
    padding: 1.25rem var(--recuo) calc(2.5rem + env(safe-area-inset-bottom));
}

.pagina-cabecalho {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .75rem;
    margin-bottom: 1.25rem;
}
.pagina-cabecalho > div:first-child { flex: 1 1 16rem; }
.pagina-cabecalho h1 { margin-bottom: .125rem; }
.pagina-cabecalho p { color: var(--tinta-2); font-size: .9375rem; margin: 0; }

/* ---- Barra lateral (só desktop) --------------------------------------- */
@media (min-width: 64rem) {
    /* !important de propósito: .so-no-celular é utilitário — sua única função
       é sumir no desktop, então precisa vencer qualquer `display` que a
       própria classe do elemento (ex.: .botao, que define `display: inline-flex`
       mais abaixo neste arquivo) já tenha fixado. Sem isso, o hambúrguer e o
       botão de fechar do menu (que são `.botao.so-no-celular`) ficavam visíveis
       no desktop — a regra de escondê-los perdia por ordem de declaração. */
    .so-no-celular { display: none !important; }

    /* O recuo reserva o lugar da lateral, que saiu do fluxo ao virar `fixed`. */
    .app { padding-left: var(--lateral); }

    .nav {
        position: fixed;
        inset: 0 auto 0 0;
        width: var(--lateral);
        z-index: 30;
        display: flex;
        flex-direction: column;
        border-bottom: 0;
        border-right: 1px solid var(--borda);
    }

    /* A altura casa com a do topo para que as duas linhas de borda se
       encontrem no mesmo pixel, sem degrau no encontro das duas colunas. */
    .nav__marca {
        display: flex;
        align-items: center;
        min-height: 61px;
        padding-inline: .875rem;
        border-bottom: 1px solid var(--borda);
    }
    .nav__marca .marca { font-size: 1.2rem; }

    .nav__portfolio {
        padding: .75rem .75rem .375rem;
        border-bottom: 0;
    }

    .nav__rolagem {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        overflow-x: hidden;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: .5rem .75rem 0;
        scrollbar-width: thin;
        scrollbar-color: var(--borda-forte) transparent;
    }
    .nav__rolagem::-webkit-scrollbar { display: block; width: 8px; }
    .nav__rolagem::-webkit-scrollbar-thumb {
        background: var(--borda-forte);
        border-radius: var(--r-max);
        border: 2px solid var(--superficie);
    }

    .nav__grupo { display: block; }
    /* Linha entre uma categoria e a próxima (Visão geral, Financeiro,
       Condomínio, ...) — não entre cada item dentro delas, só entre os
       grupos mesmo. */
    .nav__grupo + .nav__grupo {
        margin: 1rem 0 0;
        padding: 1rem 0 0;
        border-left: 0;
        border-top: 1px solid var(--borda);
    }

    /* Rodapé fixo (Meu perfil/Avaliar/Sair): sibling de .nav__rolagem, fora
       da área que rola — não .nav__grupo, não sticky, sem margem automática,
       sem véu de gradiente, sem checar transbordo. Conta da própria pessoa
       não é navegação de trabalho; separado assim, "Sair" nunca fica no meio
       dos módulos convidando clique errado, e nunca fica escondido atrás de
       nada porque nunca divide espaço com quem rola — é sempre a mesma
       borda, no mesmo lugar, ponto final. */
    .nav__rodape {
        flex: none;
        /* Recuo POSITIVO, não negativo: .nav__rodape é filho direto de .nav
           (não mais de .nav__rolagem), então não há padding de pai nenhum
           "cancelando" aqui — sem esse recuo a borda de cima ia de ponta a
           ponta da lateral, mais larga que a linha entre as outras
           categorias (que fica dentro do padding de .nav__rolagem). */
        margin-inline: .75rem;
        padding: .875rem 0 calc(.75rem + env(safe-area-inset-bottom));
        border-top: 1px solid var(--borda);
    }

    .nav__grupo-titulo {
        display: block;
        margin: 0 0 .375rem;
        padding-inline: .625rem;
        font-size: .6875rem;
        font-weight: 700;
        letter-spacing: .09em;
        text-transform: uppercase;
        color: var(--tinta-2);
    }

    .nav__eu {
        display: flex;
        align-items: center;
        gap: .5rem;
        min-width: 0;
        margin-bottom: .5rem;
        padding-inline: .625rem;
    }
    .nav__eu-texto { display: grid; min-width: 0; }
    .nav__eu-nome {
        font-size: .875rem;
        font-weight: 700;
        line-height: 1.35;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .nav__eu-papel {
        font-size: .75rem;
        line-height: 1.3;
        color: var(--tinta-2);
    }

    .nav__lista { display: block; }
    .nav__lista li + li { margin-top: .125rem; }

    .nav__link {
        position: relative;
        min-height: 38px;
        gap: .625rem;
        border-bottom: 0;
        border-radius: var(--r-1);
    }
    .nav__link:hover { background: var(--superficie-2); }
    .nav__link[aria-current='page'] {
        background: var(--acao-suave);
        border-bottom: 0;
    }
    [data-tema='escuro'] .nav__link[aria-current='page'] { border-bottom: 0; }

    /* Marca de posição colada na borda da lateral: diz "você está aqui" pela
       forma, antes de a cor ser lida. Cor sozinha nunca é o único indicador. */
    .nav__link[aria-current='page']::before {
        content: '';
        position: absolute;
        left: -.75rem;
        top: 50%;
        width: 3px;
        height: 1.25rem;
        margin-top: -.625rem;
        border-radius: 0 var(--r-max) var(--r-max) 0;
        background: currentColor;
    }

    .nav__rotulo { overflow: hidden; text-overflow: ellipsis; }
    .nav__link .contador { margin-left: auto; }

    /* Topo sem a marca: fundo do campo, não superfície de cartão, para não
       competir com a lateral. */
    .topo {
        background: var(--fundo);
        padding-block: .5rem;
    }
}

/* Tela grande com toque (notebook conversível, tablet deitado): o alvo volta
   aos 44px, porque ali o ponteiro é o dedo. */
@media (min-width: 64rem) and (pointer: coarse) {
    .nav__link { min-height: 44px; }
}

/* ---- Menu lateral no celular (hambúrguer) -----------------------------
   Abaixo de 64rem a navegação deixa de ser a faixa horizontal rolável e
   passa a ser a MESMA barra lateral do desktop, só que fora da tela por
   padrão (`transform`) e chamada por um botão de hambúrguer no topo — ver
   o botão [data-acao="abrir-menu"] e montarMenuMobile() em zelaria.js.
   Isso reaproveita o HTML da lateral inteiro: nenhuma navegação paralela
   para manter sincronizada. */
@media (max-width: 63.9375rem) {
    .nav {
        position: fixed;
        inset: 0 auto 0 0;
        width: min(var(--lateral), 85vw);
        z-index: 40;
        display: flex;
        flex-direction: column;
        border-bottom: 0;
        border-right: 1px solid var(--borda);
        transform: translateX(-100%);
        transition: transform .25s ease;
        padding-top: env(safe-area-inset-top);
    }
    .nav[data-aberto] { transform: translateX(0); }

    .nav__marca {
        display: flex;
        align-items: center;
        gap: .5rem;
        min-height: 56px;
        padding-inline: .875rem;
        border-bottom: 1px solid var(--borda);
    }
    .nav__marca .marca { flex: 1; }
    .nav__fechar { margin-left: auto; }

    .nav__portfolio { padding: .625rem .75rem .375rem; }

    .nav__rolagem {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        align-items: stretch;
        overflow-x: hidden;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: .5rem .75rem 1rem;
    }

    .nav__grupo { display: block; }
    .nav__grupo + .nav__grupo {
        margin: 1rem 0 0;
        padding: 1rem 0 0;
        border-left: 0;
        border-top: 1px solid var(--borda);
    }
    /* Rodapé fixo (Meu perfil/Avaliar/Sair) — sibling de .nav__rolagem, fora
       da área que rola. Ver o bloco desktop, mais acima, para o porquê de
       não ser mais um .nav__grupo sticky. */
    .nav__rodape {
        flex: none;
        margin: 1.25rem .75rem 0;
        padding-top: .875rem;
        padding-bottom: env(safe-area-inset-bottom);
        border-top: 1px solid var(--borda);
    }

    .nav__grupo-titulo {
        display: block;
        margin: 0 0 .375rem;
        padding-inline: .625rem;
        font-size: .6875rem;
        font-weight: 700;
        letter-spacing: .09em;
        text-transform: uppercase;
        color: var(--tinta-2);
    }

    .nav__eu {
        display: flex;
        align-items: center;
        gap: .5rem;
        min-width: 0;
        margin-bottom: .5rem;
        padding-inline: .625rem;
    }
    .nav__eu-texto { display: grid; min-width: 0; }
    .nav__eu-nome {
        font-size: .875rem;
        font-weight: 700;
        line-height: 1.35;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .nav__eu-papel { font-size: .75rem; line-height: 1.3; color: var(--tinta-2); }

    .nav__lista { display: block; }
    .nav__lista li + li { margin-top: .125rem; }

    .nav__link {
        min-height: 44px;
        gap: .625rem;
        border-bottom: 0;
        border-radius: var(--r-1);
    }
    .nav__link[aria-current='page'] {
        color: var(--acao);
        background: var(--acao-suave);
        border-bottom: 0;
    }
    [data-tema='escuro'] .nav__link[aria-current='page'] { color: var(--marca); }
    .nav__rotulo { overflow: hidden; text-overflow: ellipsis; }
    .nav__link .contador { margin-left: auto; }
}

@media (prefers-reduced-motion: reduce) {
    .nav { transition: none; }
}

/* Véu por trás do menu aberto — some no desktop porque lá a lateral nunca
   se sobrepõe ao conteúdo, então nunca há nada atrás dela para escurecer. */
.veu-menu {
    position: fixed;
    inset: 0;
    z-index: 39;
    background: rgba(15, 15, 20, .5);
}
@media (min-width: 64rem) {
    .veu-menu { display: none !important; }
}

/* ---- Confirmação de ação (troca o confirm() feio do navegador) --------
   Um só, no fim de includes/layout_end.php, reaproveitado por todo
   data-confirmar da plataforma — ver montarConfirmacao() em zelaria.js. */
.modal-confirmar {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: grid;
    place-items: center;
    padding: var(--recuo);
}
/* `display: grid` acima venceria o `[hidden]` do navegador (mesma
   especificidade, mas regra de autor sempre supera a do user-agent) — sem
   isto o modal ficaria visível o tempo todo, escondido só quando JS remove o
   atributo, nunca antes disso. Com o atributo na seleção, o hidden volta a
   valer. */
.modal-confirmar[hidden] { display: none; }
.modal-confirmar__veu {
    position: absolute;
    inset: 0;
    background: rgba(15, 15, 20, .55);
    animation: modal-confirmar-veu .18s ease both;
}
.modal-confirmar__caixa {
    position: relative;
    width: min(24rem, 100%);
    padding: 1.5rem;
    border-radius: var(--r-3);
    background: var(--superficie);
    border: 1px solid var(--borda);
    box-shadow: var(--sombra-3);
    text-align: center;
    animation: modal-confirmar-caixa .2s cubic-bezier(.2, .8, .3, 1.1) both;
}
.modal-confirmar__icone {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    margin: 0 auto .875rem;
    border-radius: var(--r-max);
    background: var(--alerta-suave);
    color: var(--alerta);
}
.modal-confirmar__texto {
    margin: 0 0 1.25rem;
    font-size: 1.0625rem;
    font-weight: 650;
    line-height: 1.4;
}
.modal-confirmar__acoes {
    display: flex;
    gap: .625rem;
}
.modal-confirmar__acoes .botao { flex: 1; }

@keyframes modal-confirmar-veu {
    from { opacity: 0; }
}
@keyframes modal-confirmar-caixa {
    from { opacity: 0; transform: translateY(.75rem) scale(.96); }
}
@media (prefers-reduced-motion: reduce) {
    .modal-confirmar__veu, .modal-confirmar__caixa { animation: none; }
}

/* ---- Modal genérico (trocar senha, trocar e-mail, e qualquer outro
   formulário curto que faça mais sentido num diálogo do que numa tela
   inteira) — mesmo véu/caixa/animação do modal de confirmação acima, mas com
   conteúdo livre em vez de um texto centralizado fixo, e pode haver mais de
   um na mesma página (cada um com seu próprio id). ------------------------ */
.modal {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: grid;
    place-items: center;
    padding: var(--recuo);
}
.modal[hidden] { display: none; }
.modal__veu {
    position: absolute;
    inset: 0;
    background: rgba(15, 15, 20, .55);
    animation: modal-confirmar-veu .18s ease both;
}
.modal__caixa {
    position: relative;
    width: min(28rem, 100%);
    max-height: calc(100vh - 2 * var(--recuo));
    overflow-y: auto;
    padding: 1.5rem;
    border-radius: var(--r-3);
    background: var(--superficie);
    border: 1px solid var(--borda);
    box-shadow: var(--sombra-3);
    animation: modal-confirmar-caixa .2s cubic-bezier(.2, .8, .3, 1.1) both;
}
.modal__fechar {
    position: absolute;
    top: .75rem;
    right: .75rem;
}
.modal__titulo {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 1.5rem .875rem 0;
    font-size: 1.0625rem;
    font-weight: 700;
}
@media (prefers-reduced-motion: reduce) {
    .modal__veu, .modal__caixa { animation: none; }
}


/* -------------------------------------------------------------------------
   7. Cartões, grades e listas
   ---------------------------------------------------------------------- */
.cartao {
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    box-shadow: var(--sombra-1);
    padding: 1.125rem;
}
.cartao + .cartao { margin-top: 1rem; }

/* Cartão que não pode passar batido — enquete aberta no topo do painel, por
   exemplo. Borda de destaque na cor de ação, sem virar um .aviso (que é pra
   alerta/erro, não pra "algo esperando sua participação"). */
.cartao--destaque { border-left: 3px solid var(--acao); }
.cartao__titulo {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .875rem;
    font-size: 1rem;
    font-weight: 700;
}

/* auto-FIT, não auto-FILL. A diferença é uma palavra e é a razão de o painel
   parecer encostado à esquerda: `auto-fill` mantém as colunas que sobraram,
   mesmo vazias, então três cartões numa faixa que caberia quatro ficam
   espremidos com um vão morto à direita. `auto-fit` colapsa a coluna vazia e
   os cartões esticam para ocupar a linha inteira — a mesma largura do cartão
   solto logo abaixo, que era o que destoava. */
.grade {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.lista-limpa { list-style: none; margin: 0; padding: 0; }

.linha {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    padding: .875rem 0;
    border-bottom: 1px solid var(--borda);
}
.linha:last-child { border-bottom: 0; padding-bottom: 0; }
.linha:first-child { padding-top: 0; }
.linha__principal { flex: 1 1 12rem; min-width: 0; }
.linha__titulo { font-weight: 650; }
.linha__acoes { display: flex; gap: .5rem; flex-wrap: wrap; }

.vazio {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--tinta-2);
    border: 1px dashed var(--borda-forte);
    border-radius: var(--r-2);
}
.vazio .icone { color: var(--tinta-3); margin-bottom: .5rem; }

/* -------------------------------------------------------------------------
   8. Formulários
   ---------------------------------------------------------------------- */
.campo { margin-bottom: 1rem; }

/* Campo-armadilha (honeypot) de formulário público: fora da tela, não
   "display:none" — alguns robôs pulam campo escondido por display/visibility,
   mas nenhum sabe que a página tem viewport. Diferente de .so-leitor-tela: ali
   o conteúdo é pra leitor de tela ler; aqui é pra ninguém ler (tem
   aria-hidden no wrapper), só existir no HTML pra fisgar preenchimento automático. */
.campo--armadilha {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.campo__rotulo {
    display: block;
    margin-bottom: .375rem;
    font-size: .875rem;
    font-weight: 650;
    color: var(--tinta);
}

.campo__dica {
    display: block;
    margin-top: .375rem;
    font-size: .8125rem;
    color: var(--tinta-2);
}

.campo__erro {
    display: flex;
    align-items: flex-start;
    gap: .375rem;
    margin-top: .375rem;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--erro);
}

.entrada,
.selecao {
    width: 100%;
    min-height: 46px;
    padding: .625rem .75rem;
    background: var(--superficie);
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-1);
    color: var(--tinta);
    font: inherit;
    font-size: 1rem; /* 16px evita o zoom automático do iOS */
    transition: border-color .15s ease, box-shadow .15s ease;
}
.entrada::placeholder { color: var(--tinta-3); }
.entrada:hover, .selecao:hover { border-color: var(--acao-borda); }
.entrada:focus, .selecao:focus {
    outline: none;
    border-color: var(--acao);
    box-shadow: 0 0 0 3px var(--acao-suave);
}
.entrada[aria-invalid='true'] { border-color: var(--erro); }
.entrada:disabled, .selecao:disabled {
    background: var(--superficie-2);
    color: var(--tinta-3);
    cursor: not-allowed;
}

.selecao {
    appearance: none;
    padding-right: 2.25rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%235e7889' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .625rem center;
}
[data-tema='escuro'] .selecao {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%2390aab8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6l6 -6'/%3E%3C/svg%3E");
}

/* Campo de senha com botão de revelar */
.campo-senha { position: relative; }
.campo-senha .entrada { padding-right: 3rem; }
.campo-senha__olho {
    position: absolute;
    right: 1px; top: 1px; bottom: 1px;
    width: 44px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: 0 var(--r-1) var(--r-1) 0;
    background: transparent;
    color: var(--tinta-2);
    cursor: pointer;
}
.campo-senha__olho:hover { color: var(--acao); }

.opcao {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    min-height: 44px;
    padding: .75rem;
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-1);
    cursor: pointer;
}
.opcao + .opcao { margin-top: .5rem; }
.opcao:hover { border-color: var(--acao-borda); background: var(--superficie-2); }
.opcao:has(input:checked) {
    border-color: var(--acao);
    background: var(--acao-suave);
}
.opcao input { margin-top: .2rem; width: 1.125rem; height: 1.125rem; accent-color: var(--acao); }
.opcao__texto { font-weight: 600; font-size: .9375rem; }
.opcao__texto span { display: block; font-weight: 450; font-size: .8125rem; color: var(--tinta-2); }

fieldset { margin: 0 0 1rem; padding: 0; border: 0; }
legend {
    padding: 0;
    margin-bottom: .5rem;
    font-size: .875rem;
    font-weight: 650;
}

/* Campo que só faz sentido para morador. Sem :has() (navegador antigo) ele
   aparece sempre — o que é o comportamento correto de degradação: o servidor
   é quem decide se a unidade é obrigatória. */
form:has(#papel-funcionario:checked) .se-morador { display: none; }

/* Alternância dos dois modos de cadastro de unidade. Sem :has(), os dois
   aparecem — e o servidor decide pelo campo "modo", então nada quebra. */
form:has(#modo-lista:checked) .se-faixa { display: none; }
form:has(#modo-faixa:checked) .se-lista { display: none; }

textarea.entrada { min-height: 7rem; resize: vertical; line-height: 1.5; }


/* -------------------------------------------------------------------------
   9. Botões
   ---------------------------------------------------------------------- */
.botao {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    min-height: 46px;
    min-width: 44px;
    padding: .625rem 1.125rem;
    border: 1px solid transparent;
    border-radius: var(--r-1);
    font: inherit;
    font-size: .9375rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: background .15s ease, border-color .15s ease, transform .1s ease;
}
.botao:active { transform: translateY(1px); }
.botao--bloco { width: 100%; }

.botao--primario {
    background: var(--acao);
    color: var(--acao-texto);
}
.botao--primario:hover { background: var(--acao-forte); color: var(--acao-texto); }

.botao--secundario {
    background: var(--superficie);
    border-color: var(--borda-forte);
    color: var(--tinta);
}
.botao--secundario:hover { border-color: var(--acao); color: var(--acao); }
[data-tema='escuro'] .botao--secundario:hover { color: var(--marca); }

.botao--fantasma {
    background: transparent;
    color: var(--tinta-2);
    padding: .5rem .75rem;
}
.botao--fantasma:hover { background: var(--superficie-2); color: var(--tinta); }

.botao--perigo {
    background: var(--superficie);
    border-color: var(--erro);
    color: var(--erro);
}
.botao--perigo:hover { background: var(--erro-suave); }

.botao--pequeno { min-height: 40px; padding: .375rem .75rem; font-size: .875rem; }

.botao--icone {
    padding: 0;
    width: 44px; height: 44px;
    min-height: 44px;
    background: transparent;
    color: var(--tinta-2);
    border-radius: var(--r-1);
}
.botao--icone:hover { background: var(--superficie-2); color: var(--tinta); }

.botao:disabled, .botao[aria-disabled='true'] {
    opacity: .55;
    cursor: not-allowed;
    transform: none;
}

/* Botão "carregando": diferente de simplesmente desabilitado — a ação está
   em andamento, não proibida. Fica mais visível que o cinza-apagado de cima
   (a regra abaixo vem depois no arquivo de propósito, para vencer a de cima)
   e o cursor muda para "em progresso" em vez de "proibido". */
.botao[aria-busy='true'] {
    opacity: .85;
    cursor: progress;
}

/* -------------------------------------------------------------------------
   Spinner: indicador de carregamento genérico — qualquer ação que espera o
   servidor (enviar formulário, perguntar à IA, publicar, apagar) usa este
   mesmo giro, sozinho dentro do botão, para nunca dar impressão de tela
   travada. Usa currentColor de propósito: o mesmo spinner funciona em botão
   primário (texto claro), secundário (texto escuro), de perigo ou só ícone,
   sem precisar de cor fixa nem de classe extra por variante.
   O !important é deliberado: a seção 12 deste arquivo ("Preferências do
   sistema") zera a duração de TODA animação do site com !important quando a
   pessoa pede menos movimento no sistema — regra certa para animação
   decorativa. O spinner não é decoração, é a própria informação de que algo
   está acontecendo; sem girar, ele vira um "C" parado que parece bug. Por
   isso esta é a única animação do site com permissão de ignorar aquela
   regra — e, como ".spinner" é mais específico que o seletor universal "*"
   de lá, este trecho já vence sozinho, sem depender de ordem no arquivo.
   ---------------------------------------------------------------------- */
.spinner {
    display: inline-block;
    width: 1.15em;
    height: 1.15em;
    border: .16em solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: spinner-girar .6s linear infinite !important;
    flex: none;
}

@keyframes spinner-girar {
    to { transform: rotate(360deg); }
}

.acoes-formulario {
    display: flex;
    flex-wrap: wrap;
    gap: .625rem;
    margin-top: 1.25rem;
}
.acoes-formulario .botao { flex: 1 1 10rem; }

/* -------------------------------------------------------------------------
   10. Avisos, selos e mensagens
   ---------------------------------------------------------------------- */
.aviso {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
    padding: .875rem 1rem;
    margin-bottom: 1.25rem;
    border: 1px solid;
    border-radius: var(--r-1);
    font-size: .9375rem;
    line-height: 1.5;
}
.aviso .icone { margin-top: .1rem; }
.aviso strong { display: block; }

.aviso--sucesso { color: var(--sucesso); background: var(--sucesso-suave); border-color: currentColor; }
.aviso--erro    { color: var(--erro);    background: var(--erro-suave);    border-color: currentColor; }
.aviso--alerta  { color: var(--alerta);  background: var(--alerta-suave);  border-color: currentColor; }
.aviso--info    { color: var(--tinta-2); background: var(--superficie-2);  border-color: var(--borda-forte); }

.selo {
    display: inline-flex;
    align-items: center;
    gap: .3125rem;
    padding: .1875rem .5rem;
    border-radius: var(--r-max);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .01em;
    white-space: nowrap;
}
.selo--pendente { color: var(--alerta);  background: var(--alerta-suave); }
.selo--ativo    { color: var(--sucesso); background: var(--sucesso-suave); }
.selo--inativo  { color: var(--tinta-2); background: var(--superficie-2); }
.selo--recusado { color: var(--erro);    background: var(--erro-suave); }
.selo--papel    { color: var(--acao);    background: var(--acao-suave); }
[data-tema='escuro'] .selo--papel { color: var(--marca); }

.contador {
    display: inline-grid;
    place-items: center;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 .3125rem;
    border-radius: var(--r-max);
    background: var(--acao);
    color: var(--acao-texto);
    font-size: .75rem;
    font-weight: 800;
}

/* Caixa de link de convite */
.link-convite {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
    padding: .75rem;
    background: var(--superficie-2);
    border: 1px dashed var(--borda-forte);
    border-radius: var(--r-1);
}
.link-convite code {
    flex: 1 1 14rem;
    min-width: 0;
    overflow-x: auto;
    white-space: nowrap;
    font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace;
    font-size: .8125rem;
    color: var(--tinta);
}

/* -------------------------------------------------------------------------
   11. Telas grandes
   ---------------------------------------------------------------------- */
@media (min-width: 48rem) {
    h1 { font-size: 1.9rem; }
    .cartao { padding: 1.5rem; }
    .cartao-publico { padding: 2.25rem 2rem; }
    .conteudo { padding-block: 2rem 3rem; }
    .acoes-formulario .botao { flex: 0 0 auto; }
}

@media (min-width: 64rem) {
    .cabecalho-publico h1 { font-size: 1.65rem; }
}

/* -------------------------------------------------------------------------
   12. Preferências do sistema
   ---------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

@media print {
    .topo, .nav, .acoes-formulario, .pular-para-conteudo { display: none !important; }
    .app { padding-left: 0; }
    .conteudo { max-width: none; }
    body { background: #fff; color: #000; }
}

/* -------------------------------------------------------------------------
   13. Utilitários de composição
   Existem porque a CSP do projeto proíbe 'unsafe-inline' em style-src, e nonce
   não vale para atributo style="" — só para elemento <style>. Ou seja: nenhum
   estilo inline pode existir nos templates. Estas classes cobrem os arranjos
   pontuais que antes seriam feitos com style inline.
   ---------------------------------------------------------------------- */
.pilha         { display: flex; align-items: center; gap: .5rem; }
.pilha--topo   { align-items: flex-start; }
.pilha--quebra { flex-wrap: wrap; }
.pilha--fina   { gap: .375rem; }
.empurra       { margin-left: auto; }
.cresce        { flex: 1 1 12rem; min-width: 0; }
.centro        { text-align: center; }

/* Ações que precisam vir uma embaixo da outra, cada uma ocupando a largura
   inteira — diferente de .acoes-formulario, que é lado a lado. Ex.: "Trocar
   senha" acima de "Salvar dados" no perfil. */
.acoes-empilhadas { display: flex; flex-direction: column; gap: .75rem; }

/* Campo com botão de ação ao lado, alinhado com o INPUT — não com o .campo
   inteiro (que inclui rótulo e dica). Usar .campos-lado aqui bagunçava o
   alinhamento: o botão descia até a base do bloco mais alto (o campo com
   dica), ficando torto em vez de ao lado da caixa de texto. */
.campo-com-botao { display: flex; gap: .5rem; align-items: center; }
.campo-com-botao .entrada { flex: 1 1 auto; min-width: 0; }
/* O botão não pode encolher: sem isto ele disputava espaço com o input e
   perdia — texto quebrando linha e cortando dentro da própria caixa. */
.campo-com-botao .botao { flex: none; white-space: nowrap; }

.mt-1 { margin-top: .375rem; }
.mt-2 { margin-top: .75rem; }
.mt-3 { margin-top: 1.25rem; }
.mb-3 { margin-bottom: 1.25rem; }
.sem-margem { margin: 0; }

.numero-grande {
    margin: 0;
    font-family: var(--fonte-titulo);
    font-size: 2rem;
    font-weight: 600;
    line-height: 1.1;
}

.nome-cartao { margin: 0; font-size: 1.15rem; }
.rotulo-secao { margin: 1.25rem 0 .5rem; font-size: .875rem; }
.estreito     { max-width: 12rem; }
.estreito--uf { max-width: 6rem; }

/* align-items: flex-end — quando um campo ao lado do outro tem rótulo visível
   e o vizinho não (ex.: filtro + botão "Filtrar", cujo rótulo é só para
   leitor de tela), alinhar pelo topo deixava os dois desencontrados: o campo
   com rótulo visível empurrava o próprio controle mais para baixo, e o botão
   sem rótulo visível ficava "flutuando" mais alto. Pelo fim, os controles
   (select, input, botão) sempre terminam na mesma linha, com ou sem rótulo
   visível acima. */
.campos-lado { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; }
.campos-lado > .campo { flex: 1 1 7rem; }

/* Paleta de cor do condomínio */
.paleta { display: flex; flex-wrap: wrap; gap: .5rem; }
.paleta label { cursor: pointer; }
.paleta__amostra {
    display: block;
    width: 44px; height: 44px;
    border-radius: 12px;
    border: 3px solid transparent;
    box-shadow: 0 0 0 1px var(--borda);
}
.paleta input:checked + .paleta__amostra { border-color: var(--tinta); }
.paleta input:focus-visible + .paleta__amostra {
    outline: 3px solid var(--acao);
    outline-offset: 2px;
}

/* Cores de etiqueta de condomínio. São classes fixas, e não style inline com o
   hex vindo do banco, justamente por causa da CSP. O mapa hex -> classe está
   em includes/ui.php (classe_cor). */
.cor-1 { background: #c77dff; }
.cor-2 { background: #5aa9c9; }
.cor-3 { background: #3fa87e; }
.cor-4 { background: #d98a3f; }
.cor-5 { background: #c95f7a; }
.cor-6 { background: #7b8ed8; }
.cor-7 { background: #63229b; }
.cor-8 { background: #4a6577; }

.portfolio__etiqueta--alta { min-height: 2.5rem; }

/* Valores monetários. Cor NUNCA é o único indicador: receita leva "+" e
   despesa leva "−" no texto, para quem não distingue as duas cores. */
.valor--receita { color: var(--sucesso); }
.valor--despesa { color: var(--erro); }
strong.valor--receita, strong.valor--despesa { white-space: nowrap; font-variant-numeric: tabular-nums; }
.numero-grande.valor--receita, .numero-grande.valor--despesa { font-variant-numeric: tabular-nums; }

/* Campo de arquivo: o seletor nativo é feio e desalinhado em todo navegador. */
input[type='file'].entrada {
    padding: .5rem;
    line-height: 1.4;
    cursor: pointer;
}
input[type='file'].entrada::file-selector-button {
    margin-right: .75rem;
    padding: .5rem .875rem;
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-1);
    background: var(--superficie-2);
    color: var(--tinta);
    font: inherit;
    font-size: .875rem;
    font-weight: 650;
    cursor: pointer;
}
input[type='file'].entrada::file-selector-button:hover {
    border-color: var(--acao);
    color: var(--acao);
}

/* -------------------------------------------------------------------------
   Abas (controle segmentado)
   Feito com radio + :has() em vez de JavaScript: a CSP proíbe script inline e
   uma troca de aba não merece um arquivo de script. Sem :has() os dois painéis
   aparecem empilhados — degradação aceitável, nada some da tela.
   ---------------------------------------------------------------------- */
.abas__controle {
    display: inline-flex;
    gap: .25rem;
    padding: .25rem;
    margin-bottom: 1rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
}
.abas__controle label { cursor: pointer; }

/* `label > span` e não `span` solto: o controle nasceu para o par
   radio + rótulo do balancete, mas a tela de conversas usa <a class="botao">
   aqui dentro, com um <span class="contador"> do lado do texto. O seletor
   largo pegava esse contador e o inflava para 42x40 com canto quadrado, no
   lugar da pastilha de 20px. Amarrado ao label, alcança só o que devia. */
.abas__controle label > span {
    display: flex;
    align-items: center;
    gap: .375rem;
    min-height: 40px;
    padding: 0 .875rem;
    border-radius: var(--r-1);
    color: var(--tinta-2);
    font-size: .875rem;
    font-weight: 650;
    white-space: nowrap;
}
.abas__controle label > input:checked + span {
    background: var(--superficie);
    color: var(--acao);
    box-shadow: var(--sombra-1);
}
[data-tema='escuro'] .abas__controle label > input:checked + span { color: var(--marca); }
.abas__controle label > input:focus-visible + span {
    outline: 3px solid var(--acao);
    outline-offset: 2px;
}

.abas:has(#aba-geral:checked)    .aba-painel--unidades { display: none; }
.abas:has(#aba-unidades:checked) .aba-painel--geral    { display: none; }

/* Tabela por unidade: rola na horizontal no celular sem quebrar o layout */
.rolagem { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tabela {
    width: 100%;
    border-collapse: collapse;
    font-size: .875rem;
}
.tabela th, .tabela td {
    padding: .625rem .5rem;
    text-align: left;
    border-bottom: 1px solid var(--borda);
    white-space: nowrap;
}
.tabela th {
    font-size: .75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--tinta-2);
}
.tabela td.numero, .tabela th.numero { text-align: right; font-variant-numeric: tabular-nums; }
.tabela tbody tr:hover { background: var(--superficie-2); }
.tabela td.envolve { white-space: normal; min-width: 14rem; }

/* Agenda: esconde as horas quando o evento é de dia inteiro. Sem :has() os
   campos aparecem e o servidor ignora — nada quebra. */
form:has(#dia-inteiro:checked) .se-com-hora { display: none; }

/* -------------------------------------------------------------------------
   Conversa (bolhas)
   ---------------------------------------------------------------------- */
.conversa {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

.bolha {
    max-width: min(46rem, 88%);
    padding: .75rem .875rem;
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    background: var(--superficie-2);
}
.bolha--minha {
    align-self: flex-end;
    background: var(--acao-suave);
    border-color: var(--acao-borda);
}
/* A resposta automática fica do MESMO lado E DA MESMA COR da síndica — para
   quem está perguntando, ela é a voz do condomínio, não uma terceira pessoa
   na conversa. .bolha--ia por isso não tem regra própria aqui: herda a
   aparência de .bolha (ou .bolha--minha, quando quem olha é a própria
   síndica) sem alteração nenhuma. O que distingue a resposta automática é só
   o texto acima do balão ("Resposta automática", ver Chat::rotuloAutor) —
   cor de mais só voltava a confundir, como confundia antes de tirar daqui a
   borda roxa que só a IA tinha. */

/* Selo de cargo ao lado do nome. Discreto de propósito: é orientação, não
   destaque — quem tem de saltar aos olhos é a mensagem. */
.bolha__cargo {
    padding: .0625rem .375rem;
    border-radius: var(--r-max);
    background: var(--superficie-3, var(--superficie));
    border: 1px solid var(--borda);
    font-size: .6875rem;
    font-weight: 700;
    color: var(--tinta-2);
    white-space: nowrap;
}
.bolha--sistema {
    align-self: center;
    max-width: 34rem;
    background: var(--alerta-suave);
    border-color: var(--alerta);
    color: var(--alerta);
    text-align: center;
    font-size: .875rem;
}

.bolha__topo {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem;
    margin-bottom: .25rem;
    font-size: .8125rem;
}
.bolha__topo strong { font-size: .8125rem; }
.bolha__texto { line-height: 1.55; overflow-wrap: anywhere; }
.bolha__marca { margin-top: .5rem; }

/* -------------------------------------------------------------------------
   Mural (avisos gerais, boletim mensal) — cartão por mensagem, e não a lista
   plana de .linha usada em cadastro: aqui o conteúdo É a tela inteira, então
   cada item ganha respiro, ícone e hierarquia própria, como um post e não
   como uma linha de tabela.
   ---------------------------------------------------------------------- */
.mural { display: flex; flex-direction: column; gap: .875rem; }
.mural__item {
    display: flex;
    gap: .75rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    box-shadow: var(--sombra-1);
    padding: 1rem 1.125rem;
}
.mural__marca {
    flex: none;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--r-max);
    background: var(--acao-suave);
    color: var(--acao);
    display: grid;
    place-items: center;
}
.mural__corpo { flex: 1 1 auto; min-width: 0; }
.mural__topo {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .5rem;
    margin-bottom: .375rem;
}
.mural__autor { font-weight: 700; font-size: .9375rem; }
.mural__data { font-size: .8125rem; color: var(--tinta-2); }
.mural__texto { line-height: 1.6; overflow-wrap: anywhere; }

/* -------------------------------------------------------------------------
   Notificações
   ---------------------------------------------------------------------- */
.sino { position: relative; }
.sino__contador {
    position: absolute;
    top: 2px; right: 2px;
    min-width: 1.125rem;
    height: 1.125rem;
    padding: 0 .25rem;
    display: grid;
    place-items: center;
    border-radius: var(--r-max);
    background: var(--erro);
    color: #fff;
    font-size: .6875rem;
    font-weight: 800;
    line-height: 1;
}
[data-tema='escuro'] .sino__contador { color: #2a0d0b; }

.linha--nova { position: relative; }
.linha--nova::before {
    content: '';
    position: absolute;
    left: -.75rem; top: 1.1rem;
    width: .375rem; height: .375rem;
    border-radius: var(--r-max);
    background: var(--acao);
}
[data-tema='escuro'] .linha--nova::before { background: var(--marca); }

/* -------------------------------------------------------------------------
   Avatar e avaliação
   ---------------------------------------------------------------------- */
.avatar {
    display: grid;
    place-items: center;
    width: 2.5rem; height: 2.5rem;
    flex: none;
    border-radius: var(--r-max);
    object-fit: cover;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
}
.avatar--pequeno { width: 2rem; height: 2rem; font-size: .8125rem; }
.avatar--grande { width: 6rem; height: 6rem; }
.avatar--iniciais {
    background: var(--acao-suave);
    border-color: var(--acao-borda);
    color: var(--acao);
    font-family: var(--fonte-titulo);
    font-weight: 600;
    font-size: 1rem;
}
.avatar--grande.avatar--iniciais { font-size: 2rem; }
[data-tema='escuro'] .avatar--iniciais { color: var(--marca); }

.notas { display: flex; gap: .5rem; flex-wrap: wrap; }
.notas label { cursor: pointer; }
.notas__opcao {
    display: grid;
    place-items: center;
    width: 48px; height: 48px;
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-1);
    font-weight: 700;
    color: var(--tinta-2);
}
.notas input:checked + .notas__opcao {
    background: var(--acao);
    border-color: var(--acao);
    color: var(--acao-texto);
}
.notas input:focus-visible + .notas__opcao {
    outline: 3px solid var(--acao);
    outline-offset: 2px;
}

.estrelas { color: var(--estrela); letter-spacing: .05em; }

/* Tela "o que você deseja avaliar?" — cartões de assunto antes do formulário.
   Flex centralizado, não grid esticado: com grid + auto-fill os cartões
   esticavam até preencher a largura inteira do cartão, ficando compridos e
   desalinhados. Em flex com largura fixa e `justify-content: center`, os
   cartões mantêm um tamanho compacto e a fileira fica centralizada, com
   ou sem quebra de linha. */
.avaliacao-categorias {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .75rem;
}
.avaliacao-categoria {
    display: flex;
    flex: 0 1 140px;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 1.25rem .75rem;
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-2);
    color: var(--tinta);
    text-align: center;
    text-decoration: none;
    font-weight: 600;
    background: var(--superficie);
    transition: border-color .15s ease, transform .15s ease, background .15s ease;
}
.avaliacao-categoria:hover {
    border-color: var(--acao);
    background: var(--acao-suave);
    transform: translateY(-2px);
}
.avaliacao-categoria .icone { color: var(--acao); }

/* Nota em estrelas — cinco pares radio+label em ordem invertida no HTML,
   com row-reverse pra virar 1..5 da esquerda pra direita. Assim, marcar (ou
   passar o mouse em) a estrela N acende ela e todas as anteriores via `~`. */
.avaliacao-estrelas {
    display: flex;
    flex-direction: row-reverse;
    justify-content: flex-end;
    gap: .2rem;
    width: fit-content;
}
.avaliacao-estrelas label {
    display: flex;
    padding: .2rem;
    border-radius: var(--r-1);
    color: var(--estrela-vazia);
    cursor: pointer;
}
.avaliacao-estrelas label .icone {
    transition: color .18s ease, fill .18s ease, transform .18s ease;
}
.avaliacao-estrelas input:checked ~ label,
.avaliacao-estrelas label:hover,
.avaliacao-estrelas label:hover ~ label {
    color: var(--estrela);
}
.avaliacao-estrelas input:checked ~ label .icone,
.avaliacao-estrelas label:hover .icone,
.avaliacao-estrelas label:hover ~ label .icone {
    fill: currentColor;
    transform: scale(1.12);
}
.avaliacao-estrelas input:focus-visible + label {
    outline: 3px solid var(--acao);
    outline-offset: 2px;
}

/* Aceso "varre" da esquerda pra direita, como pedido — o DOM vem invertido
   (5,4,3,2,1; ver comentário acima), então a label da estrela 1, a mais à
   esquerda na tela, é a ÚLTIMA <label> no DOM. O atraso cresce na direção
   contrária ao nth-of-type pra a onda de cor nascer sempre da esquerda. */
.avaliacao-estrelas label:nth-of-type(5) .icone { transition-delay: 0ms;   }
.avaliacao-estrelas label:nth-of-type(4) .icone { transition-delay: 40ms;  }
.avaliacao-estrelas label:nth-of-type(3) .icone { transition-delay: 80ms;  }
.avaliacao-estrelas label:nth-of-type(2) .icone { transition-delay: 120ms; }
.avaliacao-estrelas label:nth-of-type(1) .icone { transition-delay: 160ms; }

/* Clicar precisa SEMPRE mostrar a onda de cor, mesmo quando o mouse já
   deixou a estrela colorida pelo hover — sem isso, clicar numa estrela que
   já estava colorida (porque o mouse já tinha passado por cima) não mudava
   nada visualmente, e a animação "sumia". montarEstrelasAvaliacao() (ver
   zelaria.js) aplica esta classe por um instante no "change" do rádio: força
   tudo de volta pra cor vazia sem transição, tira a classe no quadro
   seguinte, e a transição normal (com o mesmo atraso em onda de cima) reage
   de novo — o clique sempre acende do zero. */
.avaliacao-estrelas--pulso input:checked ~ label,
.avaliacao-estrelas--pulso input:checked ~ label .icone {
    transition: none !important;
    color: var(--estrela-vazia) !important;
}

@media (prefers-reduced-motion: reduce) {
    .avaliacao-estrelas label .icone { transition: none; }
}

/* -------------------------------------------------------------------------
   14. Assembleia — gráfico, slide, editor de blocos e palco (etapa 9)

   O MESMO SLIDE EM TRÊS TAMANHOS
   O bloco `.slide` aparece em três lugares com tamanhos muito diferentes: na
   prévia da geração, dentro do editor, e projetado num telão. Ele não tem três
   folhas de estilo — tem uma, escrita em `em`. Quem muda o tamanho é só a
   `font-size` do recipiente. Assim título, número e espaçamento crescem juntos
   e na mesma proporção, em vez de o texto dobrar e a margem ficar igual.
   ---------------------------------------------------------------------- */

/* ---- Gráfico em SVG --------------------------------------------------- */
/* Cor vem de variável, nunca gravada no desenho: é o que faz o mesmo SVG
   servir o tema claro e o escuro sem ser gerado duas vezes. */
/* `relative` pelo mesmo motivo do .nav__rolagem lá em cima: a tabela oculta
   que acompanha cada gráfico (a que leitor de tela lê no lugar do desenho) é
   `position: absolute` sem deslocamento e tem `white-space: nowrap`. Sem um
   bloco de contenção aqui, ela vai parar no documento inteiro, com a largura
   toda dos números escritos por extenso — e a página passa a rolar de lado.
   Foi assim que o editor estourou 16px numa tela de 320. */
.gr__caixa { margin: 0; position: relative; }
.gr { width: 100%; height: auto; display: block; overflow: visible; }

.gr__grade  { stroke: var(--borda); stroke-width: 1; }
.gr__trilho { fill: var(--superficie-2); }

.gr__eixo,
.gr__rotulo,
.gr__valor {
    font-family: inherit;
    font-size: 11px;
    fill: var(--tinta-2);
}
.gr__rotulo { font-size: 12px; fill: var(--tinta); }
.gr__valor  { font-size: 12px; font-weight: 700; fill: var(--tinta); }

.gr__barra--receita { fill: var(--sucesso); }
.gr__barra--despesa { fill: var(--erro); }

.gr__legenda {
    display: flex;
    align-items: center;
    gap: .375rem;
    flex-wrap: wrap;
    margin: .5rem 0 0;
    font-size: .8125rem;
    color: var(--tinta-2);
}
.gr__chave {
    width: .75rem;
    height: .75rem;
    border-radius: 3px;
    flex: none;
}
.gr__chave + * { margin-right: .75rem; }
.gr__chave--receita { background: var(--sucesso); }
.gr__chave--despesa { background: var(--erro); }

/* ---- O bloco de slide ------------------------------------------------- */
.slide {
    display: flex;
    flex-direction: column;
    gap: .875em;
    container-type: inline-size;
}

.slide__titulo {
    margin: 0;
    font-family: var(--fonte-titulo);
    font-size: 1.5em;
    line-height: 1.2;
}

.slide__texto {
    margin: 0;
    font-size: 1.0625em;
    line-height: 1.65;
    color: var(--tinta);
    /* Medida de leitura: linha muito longa faz o olho perder a volta, e num
       telão a linha seria a largura da parede inteira. */
    max-width: 46ch;
}

.slide__nota,
.slide__vazio {
    margin: 0;
    font-size: .8125em;
    color: var(--tinta-2);
}
.slide__vazio { font-style: italic; }

/* Capa */
.slide__capa {
    display: flex;
    flex-direction: column;
    gap: .5em;
    padding: 1.5em 0;
}
.slide__titulo-capa {
    margin: 0;
    font-family: var(--fonte-titulo);
    font-size: 2.25em;
    line-height: 1.1;
}
.slide__subtitulo { margin: 0; font-size: 1.25em; color: var(--tinta-2); }
.slide__rodape    { margin: 0; font-size: .9375em; color: var(--tinta-3); }

/* Lista */
.slide__lista {
    margin: 0;
    padding-left: 1.25em;
    display: flex;
    flex-direction: column;
    gap: .625em;
    font-size: 1.0625em;
    line-height: 1.5;
    max-width: 56ch;
}
.slide__lista li::marker { color: var(--marca); }

/* Números */
.slide__numeros {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12.5em), 1fr));
    gap: .75em;
}
.slide__numero {
    display: flex;
    flex-direction: column;
    gap: .125em;
    padding: .875em 1em;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
}
.slide__numero-rotulo {
    font-size: .75em;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--tinta-2);
}
.slide__numero-valor {
    font-family: var(--fonte-titulo);
    font-size: 1.4em;
    line-height: 1.15;
    /* "R$ 3.920,00" quebrando entre o "R$" e o número é o tipo de detalhe que
       ninguém nota na tela do editor e todo mundo enxerga projetado na parede.
       O valor não quebra; se faltar largura, a coluna do grid é que cede. */
    white-space: nowrap;
    /* Tabular: numeral de largura fixa impede a coluna de números "dançar"
       quando o valor muda de 9 para 10. */
    font-variant-numeric: tabular-nums;
}
.slide__numero-nota { font-size: .75em; color: var(--tinta-2); line-height: 1.4; }

.slide__tabela { font-size: .9375em; }

/* ---- Prévia da geração ------------------------------------------------ */
.previa-slides {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-bottom: 1.5rem;
}
.previa-slides__item {
    position: relative;
    padding: 1.25rem 1.25rem 1.25rem 3rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-3);
}
.previa-slides__numero {
    position: absolute;
    top: 1.25rem;
    left: 1rem;
    display: inline-grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--r-max);
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    font-size: .75rem;
    font-weight: 800;
    color: var(--tinta-2);
}

/* A barra de confirmação gruda no rodapé: numa prévia de oito blocos, o botão
   de salvar ficaria a três rolagens de distância do que a pessoa acabou de
   ler. */
.acoes-formulario--fixa {
    position: sticky;
    bottom: 0;
    z-index: 15;
    margin: 0 calc(var(--recuo) * -1);
    padding: .875rem var(--recuo) calc(.875rem + env(safe-area-inset-bottom));
    background: var(--fundo);
    border-top: 1px solid var(--borda);
}

/* ---- Editor de blocos -------------------------------------------------- */
.editor-topo {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
}
.editor-topo__campo { margin: 0; }

.blocos {
    list-style: none;
    margin: 1.25rem 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .875rem;
}

.bloco {
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-3);
    overflow: hidden;
}
.bloco--arrastando {
    opacity: .6;
    border-color: var(--acao);
    box-shadow: var(--sombra-3);
}
/* Durante o arraste o texto não pode ser selecionado, senão o gesto vira
   seleção de parágrafo no meio do caminho. */
.blocos--arrastando { user-select: none; }

.bloco__barra {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .75rem;
    background: var(--superficie-2);
    border-bottom: 1px solid var(--borda);
}

.bloco__pega {
    display: inline-grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--r-1);
    color: var(--tinta-3);
    cursor: grab;
    touch-action: none; /* sem isto, o toque rola a página em vez de arrastar */
}
.bloco__pega:hover { color: var(--tinta); background: var(--superficie); }
.bloco__pega:active { cursor: grabbing; }

.bloco__tipo {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    font-size: .8125rem;
    font-weight: 700;
    color: var(--tinta-2);
}
.bloco__posicao {
    margin-left: auto;
    font-size: .75rem;
    color: var(--tinta-3);
}
.bloco__mover { display: flex; align-items: center; gap: .125rem; }
.bloco__mover .botao--icone { width: 32px; height: 32px; min-height: 32px; }
.bloco__remover:hover { color: var(--erro); background: var(--erro-suave); }

.bloco__corpo {
    display: grid;
    gap: 1.25rem;
    padding: 1.25rem;
}
.bloco__previa {
    padding: 1rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    font-size: .875rem;
}
.bloco__forma { margin: 0; }

@media (min-width: 64rem) {
    /* Prévia à esquerda, formulário à direita: a síndica vê o efeito da
       edição sem rolar entre uma coisa e outra.
       `align-items: start` porque, esticada, a prévia de um bloco de duas
       linhas ficava com a altura do formulário ao lado — meio palmo de caixa
       vazia embaixo do texto, parecendo que faltava conteúdo. */
    .bloco__corpo {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        align-items: start;
    }
}

/* ---- Palco (apresentação em tela cheia) -------------------------------- */
.palco-corpo {
    margin: 0;
    min-height: 100dvh;
    background: var(--fundo);
    overflow: hidden;
}

.palco {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 100dvh;
    padding: clamp(1.5rem, 5vw, 4rem);
    /* Sem contorno ao receber foco por script: o foco aqui é para o teclado
       funcionar, não para desenhar caixa na parede. */
    outline: none;
    cursor: pointer;
}

.palco__slide {
    display: none;
    width: 100%;
    /* 76rem e não 68rem: com 68 os "três números" viravam dois em cima e um
       embaixo, sobrando um buraco no canto do telão. Alargar a caixa foi
       melhor que encolher o tipo — número de prestação de contas projetado
       precisa ser lido do fundo da sala. Texto e lista têm limite próprio em
       `ch`, então não acompanham essa largura. */
    max-width: 76rem;
    /* É esta linha que faz o slide crescer: tudo dentro dele está em `em`. */
    font-size: clamp(1rem, 1.6vw + .45rem, 1.9rem);
}
.palco__slide--ativo { display: block; }

.palco__controles {
    position: fixed;
    bottom: calc(1rem + env(safe-area-inset-bottom));
    left: 50%;
    transform: translateX(-50%);
    z-index: 40;
    display: flex;
    align-items: center;
    gap: .25rem;
    padding: .25rem .5rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-max);
    box-shadow: var(--sombra-3);
    /* Encosta discreto e acorda ao chegar perto: numa assembleia a barra fica
       projetada na parede o tempo todo se não sumir. */
    opacity: .35;
    transition: opacity .2s ease;
}
.palco__controles:hover,
.palco__controles:focus-within { opacity: 1; }

.palco__contador {
    padding: 0 .5rem;
    font-size: .8125rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--tinta-2);
}

.palco__dica {
    position: fixed;
    top: calc(1rem + env(safe-area-inset-top));
    left: 50%;
    transform: translateX(-50%);
    z-index: 40;
    margin: 0;
    padding: .375rem .875rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-max);
    font-size: .8125rem;
    color: var(--tinta-2);
    transition: opacity .4s ease;
}
.palco__dica--oculta { opacity: 0; pointer-events: none; }

/* Impressão: sai a apresentação inteira, um bloco por página, sem o cromo. */
@media print {
    .palco__controles,
    .palco__dica { display: none !important; }

    .palco-corpo { overflow: visible; }
    .palco { display: block; min-height: 0; padding: 0; }

    .palco__slide {
        display: block !important;
        max-width: none;
        font-size: 12pt;
        break-after: page;
        padding: 1.5cm;
    }
    .palco__slide:last-child { break-after: auto; }
}

/* -------------------------------------------------------------------------
   15. Assembleia virtual e assistente (etapa 10)
   ---------------------------------------------------------------------- */

/* ---- Assistente -------------------------------------------------------- */
.sugestoes {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .75rem;
}

.sugestoes__item {
    min-height: 44px;
    padding: .5rem .875rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-max);
    color: var(--tinta);
    font: inherit;
    font-size: .875rem;
    text-align: left;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.sugestoes__item:hover {
    background: var(--acao-suave);
    border-color: var(--acao);
}

.conversa-ia {
    display: flex;
    flex-direction: column;
    gap: .875rem;
    margin-bottom: 1.25rem;
}

.fala {
    display: flex;
    flex-direction: column;
    gap: .375rem;
    max-width: 54ch;
    padding: .875rem 1rem;
    border: 1px solid var(--borda);
    border-radius: var(--r-3);
}

/* A pergunta encosta à direita e a resposta à esquerda. Numa conversa longa é
   o que deixa reler sem precisar ler quem falou. */
.fala--sindica {
    align-self: flex-end;
    background: var(--acao-suave);
    border-color: var(--acao-borda);
    border-bottom-right-radius: var(--r-1);
}
.fala--assistente {
    align-self: flex-start;
    background: var(--superficie);
    border-bottom-left-radius: var(--r-1);
}

.fala__quem {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--tinta-2);
}

.fala__texto { line-height: 1.6; }

/* De onde a resposta saiu. Existe porque a síndica repassa isto a morador: sem
   a procedência, uma frase gerada por IA e um dado conferido chegam com o mesmo
   peso na tela — e não têm o mesmo peso numa assembleia. */
.fala__base {
    display: flex;
    align-items: center;
    gap: .25rem;
    margin: .25rem 0 0;
    padding-top: .5rem;
    border-top: 1px solid var(--borda);
    font-size: .75rem;
    color: var(--tinta-2);
}

@media (min-width: 64rem) {
    .fala { max-width: 46rem; }
}

/* -------------------------------------------------------------------------
   16. Chat: faixa de filtro, compositor e balões (R2)
   ---------------------------------------------------------------------- */

/* ---- Faixa de condomínios ---------------------------------------------- */
/* Rola na horizontal em vez de quebrar linha: com onze condomínios, quebrar
   empurraria a lista de conversas para fora da tela antes de a pessoa ler a
   primeira. Barra escondida porque a ficha cortada na borda já avisa que tem
   mais para o lado. */
.faixa-filtro {
    display: flex;
    gap: .5rem;
    overflow-x: auto;
    scrollbar-width: none;
    margin-bottom: 1rem;
    padding-bottom: .125rem;
    /* Encosta as fichas na borda do conteúdo ao rolar, em vez de parar num
       recuo que parece corte. */
    margin-inline: calc(var(--recuo) * -1);
    padding-inline: var(--recuo);
    scroll-padding-inline: var(--recuo);
}
.faixa-filtro::-webkit-scrollbar { display: none; }

.ficha {
    display: inline-flex;
    align-items: center;
    gap: .4375rem;
    flex: none;
    min-height: 40px;
    padding: .375rem .875rem;
    background: var(--superficie);
    border: 1px solid var(--borda);
    border-radius: var(--r-max);
    color: var(--tinta-2);
    font-size: .875rem;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.ficha:hover { border-color: var(--borda-forte); color: var(--tinta); }

.ficha--ativa {
    background: var(--acao);
    border-color: var(--acao);
    color: var(--acao-texto);
}
[data-tema='escuro'] .ficha--ativa { color: var(--acao-texto); }

.ficha__etiqueta {
    width: .5rem;
    height: .5rem;
    border-radius: var(--r-max);
    background: var(--marca);
    flex: none;
}
/* Na ficha ativa a etiqueta some no fundo cheio: clareia para continuar visível. */
.ficha--ativa .ficha__etiqueta { box-shadow: 0 0 0 2px var(--acao-texto); }

/* ---- Compositor -------------------------------------------------------- */
/* Grudado no rodapé do cartão: numa conversa longa o campo de escrever ficava a
   uma rolagem inteira de distância da última mensagem — a pessoa lia embaixo e
   tinha de descer mais para responder. */
.compositor {
    position: sticky;
    bottom: 0;
    z-index: 10;
    margin: .75rem -1rem -1rem;
    padding: .75rem 1rem calc(.75rem + env(safe-area-inset-bottom));
    background: var(--superficie);
    border-top: 1px solid var(--borda);
}

.compositor__linha {
    display: flex;
    align-items: flex-end;
    gap: .5rem;
}

.compositor__campo {
    flex: 1;
    min-width: 0;
    /* Cresce até seis linhas e então rola por dentro: sem teto, uma mensagem
       longa empurraria a conversa inteira para fora da tela enquanto se digita. */
    max-height: 9rem;
    resize: none;
    overflow-y: auto;
    line-height: 1.5;
}

.compositor__enviar {
    flex: none;
    width: 44px;
    height: 44px;
    min-height: 44px;
    padding: 0;
    border-radius: var(--r-max);
}

/* ---- Balões ------------------------------------------------------------ */
.conversa__vazio {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    padding: 2rem 1rem;
    text-align: center;
    color: var(--tinta-2);
}
.conversa__vazio p { margin: 0; }

/* Mensagem que chegou pela sondagem entra suave. Sem isso ela aparece de
   estalo, e num chat aberto a pessoa não percebe que algo mudou. */
@keyframes bolha-entra {
    from { opacity: 0; transform: translateY(.5rem); }
    to   { opacity: 1; transform: none; }
}
.bolha--nova { animation: bolha-entra .22s ease-out; }

@media (prefers-reduced-motion: reduce) {
    .bolha--nova { animation: none; }
}

@media (max-width: 47.999rem) {
    /* Cara de aplicativo de mensagem no celular: balão mais largo, cantos
       arredondados com o "rabinho" do lado de quem falou, e o cabeçalho de
       nome/hora encolhido para o texto dominar. */
    .bolha { max-width: 92%; padding: .5rem .625rem; }
    .bolha--minha { border-bottom-right-radius: var(--r-1); }
    .bolha:not(.bolha--minha):not(.bolha--sistema) { border-bottom-left-radius: var(--r-1); }

    /* Cabeçalho em UMA linha. Deixado quebrar, nome + cargo + hora ocupavam
       duas linhas em cada balão — mais altura de etiqueta do que de mensagem
       numa tela de 390px. A hora vai para a direita e o nome é quem cede. */
    .bolha__topo {
        flex-wrap: nowrap;
        align-items: center;
        gap: .375rem;
        margin-bottom: .125rem;
        font-size: .6875rem;
    }
    .bolha__topo strong {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .bolha__topo .sutil { margin-left: auto; flex: none; }
}

/* Abaixo de 26rem o carimbo inteiro não cabe ao lado do nome e do cargo. Fica
   a hora, que é o que se procura dentro de uma conversa — a data já está no
   fio do assunto. */
@media (max-width: 25.999rem) {
    .bolha__data { display: none; }
    .bolha__cargo { max-width: 8rem; overflow: hidden; text-overflow: ellipsis; }
    .bolha__topo strong { font-size: .6875rem; }
    .bolha__cargo { font-size: .625rem; }
    .bolha__texto { font-size: .9375rem; }
}

/* -------------------------------------------------------------------------
   17. Encomendas (R3)
   ---------------------------------------------------------------------- */

/* Campo de código + botão de câmera lado a lado. */
.leitor {
    display: flex;
    align-items: stretch;
    gap: .5rem;
}
.leitor .entrada { flex: 1; min-width: 0; }
.leitor .botao { flex: none; }

.leitor__palco {
    margin-top: .75rem;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .5rem;
}

.leitor__video {
    width: 100%;
    max-width: 26rem;
    aspect-ratio: 4 / 3;
    background: #000;
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
    /* `cover` porque a proporção da câmera do celular não bate com a da caixa:
       sem isso a imagem sai achatada e o código de barras não é lido. */
    object-fit: cover;
}

/* Baixa da encomenda: painel que abre no lugar, sem tirar o porteiro da lista.
   Ele está com a fila na frente dele — mandar para outra tela e voltar a cada
   pacote seria três toques a mais, vezes vinte entregas por dia. */
.baixa { position: relative; }
.baixa > summary {
    list-style: none;
    cursor: pointer;
}
.baixa > summary::-webkit-details-marker { display: none; }

.baixa__caixa {
    margin-top: .75rem;
    padding: 1rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-2);
}

@media (min-width: 48rem) {
    /* No desktop o painel flutua sobre a lista, ancorado ao botão. No celular
       fica em fluxo mesmo: painel flutuante em tela estreita cobre a própria
       linha que a pessoa está tentando conferir. */
    .baixa__caixa {
        position: absolute;
        right: 0;
        z-index: 20;
        width: min(22rem, 80vw);
        background: var(--superficie);
        box-shadow: var(--sombra-3);
    }
}

/* Leitura de etiqueta por foto (R3). */
.etiqueta__linha {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-top: .75rem;
}
.etiqueta__linha .entrada { flex: 1 1 14rem; min-width: 0; }
.etiqueta__linha .botao { flex: none; }

/* Barra de progresso da transcrição (R4). */
/* <progress> precisa dos três: appearance para tirar o visual do sistema, e um
   pseudo-elemento por motor — não há seletor que cubra os dois, e agrupá-los
   numa regra só faria o navegador descartar a regra inteira por não reconhecer
   o seletor do outro. */
.barra {
    appearance: none;
    -webkit-appearance: none;
    display: block;
    width: 100%;
    height: .625rem;
    margin: 1rem 0 .5rem;
    background: var(--superficie-2);
    border: 1px solid var(--borda);
    border-radius: var(--r-max);
    overflow: hidden;
    color: var(--acao);
}
.barra::-webkit-progress-bar { background: transparent; }
.barra::-webkit-progress-value {
    background: var(--acao);
    border-radius: var(--r-max);
    transition: width .4s ease;
}
.barra::-moz-progress-bar {
    background: var(--acao);
    border-radius: var(--r-max);
}
[data-tema='escuro'] .barra { color: var(--marca); }
[data-tema='escuro'] .barra::-webkit-progress-value { background: var(--marca); }
[data-tema='escuro'] .barra::-moz-progress-bar { background: var(--marca); }

.extracao__linha {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin: 0 0 1rem;
    font-size: .9375rem;
    color: var(--tinta-2);
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .barra::-webkit-progress-value { transition: none; }
}

/* Texto formatado vindo da IA (R4g). */
.texto-rico > p { margin: 0 0 .625rem; }
.texto-rico > p:last-child { margin-bottom: 0; }
.texto-rico strong { font-weight: 800; }

.texto-rico__lista {
    margin: 0 0 .625rem;
    padding-left: 1.125rem;
    display: flex;
    flex-direction: column;
    gap: .3125rem;
}
.texto-rico__lista:last-child { margin-bottom: 0; }
.texto-rico__lista li::marker { color: var(--marca); }

/* -------------------------------------------------------------------------
   Índice temático da convenção/RI — a tela trata o conteúdo como sumário de
   livro de regras, não como painel de cards: numeração em serifa, trecho
   citado com barra lateral, sem grade de ícone colorido. Ver
   modules/documentos/convencao-ri.php.
   ---------------------------------------------------------------------- */

.busca-convencao {
    display: flex;
    flex-wrap: wrap;
    gap: .625rem;
    margin-bottom: .25rem;
}
.busca-convencao__campo {
    position: relative;
    display: flex;
    align-items: center;
    flex: 1 1 18rem;
}
.busca-convencao__campo > .icone {
    position: absolute;
    left: .9375rem;
    color: var(--tinta-3);
    pointer-events: none;
}
.busca-convencao__campo .entrada {
    padding-left: 2.75rem;
    min-height: 52px;
    border-radius: var(--r-max);
    font-size: 1.0625rem;
}
.busca-convencao__campo .entrada:focus ~ .icone,
.busca-convencao__campo:focus-within > .icone { color: var(--acao); }
.busca-convencao .botao { border-radius: var(--r-max); padding-left: 1.5rem; padding-right: 1.5rem; }

@keyframes entrada-indice {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

.indice-temas {
    list-style: none;
    margin: 0;
    padding: 0;
    counter-reset: none;
}
.indice-tema {
    opacity: 0;
    animation: entrada-indice .45s ease both;
    border-bottom: 1px solid var(--borda);
}
.indice-tema:last-child { border-bottom: 0; }

.indice-tema__link {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem .25rem;
    color: var(--tinta);
    text-decoration: none;
    border-radius: var(--r-1);
    transition: background-color .15s ease, padding-left .15s ease;
}
a.indice-tema__link:hover {
    background: var(--superficie-2);
    padding-left: .625rem;
}
a.indice-tema__link:hover .indice-tema__numero { color: var(--acao); }

.indice-tema__numero {
    font-family: var(--fonte-titulo);
    font-size: 1.125rem;
    font-variant-numeric: tabular-nums;
    color: var(--tinta-3);
    min-width: 2ch;
}
.indice-tema__rotulo {
    flex: 1 1 auto;
    font-family: var(--fonte-titulo);
    font-size: 1.0625rem;
    font-weight: 600;
}
.indice-tema__contador {
    font-size: .8125rem;
    color: var(--tinta-2);
    white-space: nowrap;
}
.indice-tema__link--desabilitado { cursor: default; }
.indice-tema--vazio .indice-tema__numero,
.indice-tema--vazio .indice-tema__rotulo { color: var(--tinta-3); }

.titulo-trechos { margin-bottom: 1rem; }

.trecho {
    margin: 0 0 .875rem;
    padding: 1rem 1.125rem;
    background: var(--superficie);
    border-left: 3px solid var(--acao);
    border-radius: 0 var(--r-2) var(--r-2) 0;
    box-shadow: var(--sombra-1);
    opacity: 0;
    animation: entrada-indice .4s ease both;
}
.trecho__origem {
    display: block;
    font-family: var(--fonte-titulo);
    font-style: italic;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--acao);
    margin-bottom: .5rem;
}
.trecho__texto {
    font-size: .9375rem;
    line-height: 1.65;
    color: var(--tinta);
}
mark.destaque {
    background: var(--acao-suave);
    color: var(--acao-forte);
    padding: .05em .2em;
    border-radius: .25em;
    font-weight: 700;
}

/* -------------------------------------------------------------------------
   Aviso de cookies (LGPD) — informativo, nunca bloqueia a tela.
   ---------------------------------------------------------------------- */
.aviso-cookies {
    position: fixed;
    left: 1rem;
    right: 1rem;
    bottom: 1rem;
    z-index: 80;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .875rem;
    max-width: 40rem;
    margin: 0 auto;
    padding: 1rem 1.125rem;
    background: var(--superficie);
    border: 1px solid var(--borda-forte);
    border-radius: var(--r-2);
    box-shadow: var(--sombra-3);
}
.aviso-cookies p {
    flex: 1 1 16rem;
    margin: 0;
    font-size: .8125rem;
    color: var(--tinta-2);
}
.aviso-cookies a { font-weight: 650; }

/* -------------------------------------------------------------------------
   Zona de perigo colapsável (excluir conta, e qualquer outra ação
   destrutiva escondida atrás de um clique de propósito).
   ---------------------------------------------------------------------- */
.excluir-conta summary {
    cursor: pointer;
    list-style: none;
}
.excluir-conta summary::-webkit-details-marker { display: none; }
.excluir-conta summary .linha__titulo { color: var(--erro); }
.excluir-conta[open] summary { border-bottom: 1px solid var(--borda); padding-bottom: .875rem; }

/* Qualquer outro <details> colapsável que não seja zona de perigo — mesmo
   tratamento de ponteiro e marcador, sem a cor de alerta do excluir-conta. */
.expansivel summary {
    cursor: pointer;
    list-style: none;
}
.expansivel summary::-webkit-details-marker { display: none; }

.trecho__completo-botao {
    display: inline-flex;
    align-items: center;
    gap: .3125rem;
    margin-top: .625rem;
    padding: 0;
    background: none;
    border: 0;
    font: inherit;
    font-size: .8125rem;
    font-weight: 650;
    color: var(--acao);
    cursor: pointer;
}
.trecho__completo-botao:hover { color: var(--acao-forte); }
.trecho__completo-botao .icone { transition: transform .2s ease; }
.trecho__completo-botao[aria-expanded='true'] .icone { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
    .indice-tema, .trecho { animation: none; opacity: 1; }
}
