/* ==========================================================================
   CX Analytics — sistema visual
   ==========================================================================
   Alinhado ao padrão do time de Produtos (produtos.eqi.life), a partir das telas
   de referência: Início, Follow Up, Operacional, Oportunidades, Solicitações,
   Internacional e Calculadoras.

   O QUE DEFINE O PADRÃO, em ordem de importância:

   1. NEUTROS MORNOS, não cinza-frio. O fundo é um off-white bege (#F7F6F1) e o
      texto secundário tem viés cáqui (#6F6A5B), não cinza-azulado. Trocar só o
      verde e manter cinza neutro é o que faz um site "parecer que recebeu umas
      cores novas" em vez de pertencer ao ecossistema.
   2. VERDE ESCURO É TINTA DE TEXTO, não de fundo. Título, ícone e rótulo ativo
      são verde-escuro sobre claro. Fundo verde aparece em três lugares só:
      botão primário, barra de ação contextual e o item selecionado do segmented
      control. NÃO em cabeçalho de card nem em cabeçalho de tabela.
   3. SAGE (#DCE5DB) É O "SELECIONADO". Item ativo da sidebar, aba ativa dentro
      de card, faixa de hero. Sempre com texto verde-escuro.
   4. BORDA EM VEZ DE SOMBRA. Card é branco com 1px morno. Sombra só no que
      flutua (dropdown, modal).
   5. HIERARQUIA POR PESO E COR, não por caixa colorida. Título 600 verde-escuro,
      subtítulo 400 mudo, logo abaixo. Não há faixa colorida separando um do outro.

   Toda classe que os templates já usavam continua existindo — a migração é de
   ESTILO, não de marcação, justamente porque editar
   por `replace` de string em template com texto duplicado já quebrou três
   layouts sem `check` nem HTTP 200 acusarem.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Fonte — DM Sans, servida localmente
   --------------------------------------------------------------------------
   Arquivo local e não CDN de propósito: o container não tem saída garantida
   para fonts.gstatic.com, e fonte que falha em produção derruba a identidade
   inteira para o stack do sistema.

   Variável de 400 a 700 num arquivo só (`font-weight: 400 700`), então os
   pesos 500/600 que este CSS usa são interpolados sem baixar mais nada.
   `latin` cobre todo o português (ç, ã, õ, á, é, í, ó, ú vivem em U+00C0-00FF);
   `latin-ext` entra para nome próprio estrangeiro.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dmsans-latin.6c1e961057d2.woff2") format('woff2');
  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: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/dmsans-latin-ext.b9209b52c467.woff2") format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------------------
   Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Verde da marca. A escala antiga ia até um mint saturado (#00C796) que não
     existe no padrão de Produtos; aqui ela termina num verde-médio sóbrio, e o
     peso vem do tom escuro, não da saturação. */
  --brand-50: #F2F5F1;
  --brand-100: #E4EDE5;
  --brand-200: #C9DBCC;
  --brand-300: #A3C1AA;
  --brand-400: #5C9478;
  --brand-500: #3E7A5E;
  --brand-600: #1E5540;
  --brand-700: #123A2C;
  --brand-800: #0F3328;
  --brand-900: #0B2620;
  --brand-950: #071A15;

  /* Sage — a cor do "selecionado". Item ativo da sidebar, aba ativa, faixa de
     hero. Nunca carrega texto branco: o contraste com verde-escuro é o efeito. */
  --sage-100: #E7EEE6;
  --sage-200: #DCE5DB;
  --sage-300: #CBD9C9;

  /* Neutros MORNOS. O viés bege é o que casa com o padrão de Produtos — cinza
     neutro no mesmo layout lê como "outro produto". */
  --warm-50: #FAF9F5;
  --warm-100: #F7F6F1;
  --warm-150: #F4F3ED;
  --warm-200: #EFEDE5;
  --warm-300: #E6E3DA;
  --warm-400: #D8D4C8;

  --bg-app: var(--warm-100);
  --bg-sidebar: var(--warm-50);
  --bg-surface: #FFFFFF;
  --bg-inset: var(--warm-150);
  --bg-hover: var(--warm-100);

  --border-subtle: var(--warm-300);
  --border-strong: var(--warm-400);

  /* Título em verde-escuro e corpo em quase-preto esverdeado: os dois papéis são
     distintos no padrão de referência, e usar a mesma cor nos dois achata a
     hierarquia que o material constrói só com peso e tom. */
  --text-title: #10352A;
  --text-primary: #23302A;
  --text-secondary: #6F6A5B;
  --text-tertiary: #948E7C;
  --text-on-dark: #FFFFFF;
  --text-on-dark-muted: rgba(255, 255, 255, 0.68);

  --accent: var(--brand-500);
  /* Mantido por compatibilidade: `--panel-header-bg` era o verde da faixa de
     cabeçalho, que deixou de existir. Aponta para a superfície do card para que
     qualquer regra remanescente não pinte uma faixa escura de volta. */
  --panel-header-bg: var(--bg-surface);

  --good: #1E7A52;
  --good-bg: #E7F1E9;
  --good-border: #C8DFD0;
  --bad: #B4423A;
  --bad-bg: #F8EBE8;
  --bad-border: #EBCFC9;
  --warn: #B07B22;
  --warn-text: #7A5A22;
  --warn-bg: #FBEEDA;
  --warn-border: #F0DCBB;
  --info: #2C5F8A;
  --info-bg: #E8EFF6;
  --info-border: #CFDEEC;

  --radius-sm: 8px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-pill: 999px;

  /* Card é borda, não sombra. Sombra existe só para o que FLUTUA — e aí é
     generosa, para o painel descolar do fundo morno. */
  --shadow-card: 0 1px 2px rgba(16, 53, 42, 0.03);
  --shadow-pop: 0 4px 6px -2px rgba(16, 53, 42, 0.06), 0 12px 28px -8px rgba(16, 53, 42, 0.16);
  --shadow-modal: 0 24px 64px -12px rgba(11, 38, 32, 0.28);

  /* 252px e não 236: a sidebar virou a ÚNICA navegação por página, então rótulo
     truncado ali não tem mais uma faixa de abas no topo para compensar — e
     "Estratégia de campan…" é um item de menu que não diz para onde vai. */
  --sidebar-w: 252px;
  --sidebar-w-collapsed: 68px;

  /* MEDIDAS DE LAYOUT FLUIDAS, separadas por eixo. Largura e altura variam
     independentemente em desktop: 1280×720 tem largura para quatro colunas e pouca
     altura para cabeçalho + filtros + conteúdo; 2560×1080 tem largura sobrando.

     `--content-max` acompanha a janela entre 1440px e 1920px: preso em 1440px, um
     monitor de 2560px mostrava o conteúdo numa faixa central com ~430px vazios de
     cada lado. Texto corrido tem limite próprio em `ch` e não estica junto.

     `--ritmo-y` é o espaçamento vertical do cabeçalho e entre seções, em função da
     ALTURA da janela: ~14px numa área útil de 590px (notebook 1280×720 com a barra do
     navegador), 22px a partir de ~920px. */
  --content-max: clamp(1440px, 82vw, 1920px);
  --gutter: clamp(20px, 1.25vw + 8px, 32px);
  --ritmo-y: clamp(12px, 2.4vh, 22px);

  /* Compat: nomes que o CSS antigo expunha. Mantidos para nenhuma regra órfã
     cair em `unset` e pintar preto. */
  --sidebar-bg: var(--bg-sidebar);
  --sidebar-text: var(--text-title);
  --sidebar-text-muted: var(--text-secondary);
  --sidebar-hover-bg: var(--warm-200);
  --sidebar-active-bg: var(--sage-200);
  --sidebar-active-text: var(--text-title);
  --sidebar-border: var(--border-subtle);

  /* ------------------------------------------------------------------------
     Cores dos GRÁFICOS
     ------------------------------------------------------------------------
     Existem como variável, e não como hex dentro do JS, porque o Chart.js
     desenha em canvas: o canvas não herda nada de CSS, então uma cor fixa no JS
     ficaria clara no tema escuro sem nenhum erro em lugar nenhum — gráfico com
     grade branca sobre fundo escuro não derruba `check` nem muda o HTTP 200.

     Cada arquivo de gráfico lê estas variáveis com um helper LOCAL, de três
     linhas, e não por um módulo compartilhado:
     compartilhar helper de JS entre telas quebrou os gráficos em 05/08/2026 com
     causa nunca identificada. Todo `cor()` no JS leva um fallback em hex, então
     variável ausente degrada para a cor do tema claro em vez de para preto.
     ------------------------------------------------------------------------ */
  --chart-grid: #EBE9E1;
  --chart-tick: #6F6A5B;
  --chart-label: #23302A;
  --chart-muted: #948E7C;

  /* Série 1 é a mais escura e 6 a mais clara: a ordem é a rampa, e trocar a
     ordem inverte a leitura de qualquer gráfico empilhado. */
  --chart-1: #123A2C;
  --chart-2: #2E6B50;
  --chart-3: #3E7A5E;
  --chart-4: #5C9478;
  --chart-5: #AFC7B5;
  --chart-6: #8AA396;

  --chart-good: #1E7A52;
  --chart-bad: #B4423A;
  --chart-bad-claro: #DDAFA9;
  --chart-warn: #B07B22;
  --chart-amber: #C1852A;

  --chart-fill: rgba(18, 58, 44, 0.12);
  --chart-fill-forte: rgba(62, 122, 94, 0.55);
  --chart-fill-bad: rgba(180, 66, 58, 0.12);
  --chart-fill-suave: rgba(18, 58, 44, 0.08);

  --focus-ring: rgba(62, 122, 94, 0.16);

  /* ------------------------------------------------------------------------
     Ícones em `data:` URI — o `url()` INTEIRO na variável
     ------------------------------------------------------------------------
     Um data URI é uma string opaca: não existe forma de pôr `var(--cor)` dentro
     do SVG, então a cor do traço fica escrita ali (`%23` é o `#` escapado).

     Guardar o `url()` completo num token é o que mantém a regra "no tema escuro
     só os tokens mudam". A alternativa era repetir cada `background-image` nos
     dois blocos escuros — seis regras de componente duplicadas duas vezes, que
     é exatamente a segunda folha de estilo em paralelo que este arquivo evita.
     ------------------------------------------------------------------------ */
  --icone-ok: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231E7A52' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.5 12.5l2.5 2.5 4.5-5'/%3E%3C/svg%3E");
  --icone-atencao: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237A5A22' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.8 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.8a2 2 0 0 0-3.4 0Z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
  --icone-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232C5F8A' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 16v-5M12 8h.01'/%3E%3C/svg%3E");
  --icone-vazio: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23948E7C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
  --icone-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%236F6A5B' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
  --icone-chevron-forte: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%2310352A' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
}

:root {
  color-scheme: light;
}

/* ==========================================================================
   TEMA ESCURO
   ==========================================================================
   Só os TOKENS são redefinidos — nenhuma regra de componente aparece aqui. Foi
   possível porque o tema claro já não tem cor solta: cada card, borda e texto
   sai de uma variável. Componente redefinido no bloco escuro seria uma segunda
   folha de estilo a manter em paralelo, e as duas sairiam de sincronia na
   primeira mudança de layout.

   Escuro MORNO e esverdeado, não preto azulado: o claro é bege, e um escuro
   neutro-frio ao lado dele lê como outro produto. Mesma razão pela qual os
   neutros claros têm viés cáqui.

   Duas portas de entrada, nesta ordem de precedência:
     1. `@media (prefers-color-scheme: dark)` sob `:root:not([data-tema="claro"])`
        — segue o sistema quando o usuário não escolheu nada;
     2. `:root[data-tema="escuro"]` — a escolha explícita no botão vence sempre,
        nos dois sentidos.
   O `:not([data-tema="claro"])` da primeira é o que permite forçar CLARO dentro
   de um sistema em escuro: sem ele, a media query venceria e o botão só
   funcionaria em uma direção.
   ========================================================================== */
@media (prefers-color-scheme: dark) {
  :root:not([data-tema="claro"]) {
    color-scheme: dark;

    --warm-50: #1B201C;
    --warm-100: #171B18;
    --warm-150: #202520;
    --warm-200: #262C27;
    --warm-300: #2E352F;
    --warm-400: #414A42;

    --bg-app: #131715;
    --bg-sidebar: #171B18;
    --bg-surface: #1C211D;
    --bg-inset: #232823;
    --bg-hover: #262C27;

    --border-subtle: #2C332D;
    --border-strong: #3C443D;

    --text-title: #E9F0EA;
    --text-primary: #D3DBD5;
    --text-secondary: #9BA49C;
    --text-tertiary: #79827A;
    --text-on-dark: #0E1410;
    --text-on-dark-muted: rgba(14, 20, 16, 0.66);

    /* A escala inverte: no claro o peso vem do tom ESCURO; no escuro, do CLARO.
       Manter #123A2C como "brand-700" aqui pintaria botão primário quase
       invisível sobre o fundo. */
    --brand-100: #1E2A22;
    --brand-200: #2A3A2F;
    --brand-300: #3E5A48;
    --brand-400: #5C9478;
    --brand-500: #6FB894;
    --brand-600: #57A883;
    --brand-700: #4C9B74;
    --brand-800: #3E8A66;
    --brand-900: #2E6B50;

    --sage-100: #202B24;
    --sage-200: #2A3A2F;
    --sage-300: #38493D;

    --good: #58BF8C;
    --good-bg: #1A2A21;
    --good-border: #2C4535;
    --bad: #E3837A;
    --bad-bg: #2C1E1C;
    --bad-border: #4A2E2A;
    --warn: #D9A34A;
    --warn-text: #E4C083;
    --warn-bg: #2A2317;
    --warn-border: #453A22;
    --info: #7FB3DF;
    --info-bg: #1A2430;
    --info-border: #2A3A4A;

    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.28);
    --shadow-pop: 0 4px 6px -2px rgba(0, 0, 0, 0.32), 0 12px 28px -8px rgba(0, 0, 0, 0.5);
    --shadow-modal: 0 24px 64px -12px rgba(0, 0, 0, 0.62);

    /* Gráficos: a rampa também inverte — série clara sobre fundo escuro. */
    --chart-grid: #2C332D;
    --chart-tick: #9BA49C;
    --chart-label: #E9F0EA;
    --chart-muted: #79827A;

    --chart-1: #7FC7A3;
    --chart-2: #5CAE86;
    --chart-3: #4C9B74;
    --chart-4: #3E8A66;
    --chart-5: #2C6249;
    --chart-6: #6E9A85;

    --chart-good: #58BF8C;
    --chart-bad: #E3837A;
    --chart-bad-claro: #7A4640;
    --chart-warn: #D9A34A;
    --chart-amber: #D9A34A;

    --chart-fill: rgba(127, 199, 163, 0.14);
    --chart-fill-forte: rgba(92, 174, 134, 0.5);
    --chart-fill-bad: rgba(227, 131, 122, 0.14);
    --chart-fill-suave: rgba(127, 199, 163, 0.08);
    --focus-ring: rgba(111, 184, 148, 0.20);

    /* Icones de traco claro. O SVG e string opaca, entao o token guarda o
       url() inteiro — ver o comentario na definicao do tema claro. */
    --icone-ok: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2358BF8C' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.5 12.5l2.5 2.5 4.5-5'/%3E%3C/svg%3E");
    --icone-atencao: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E4C083' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.8 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.8a2 2 0 0 0-3.4 0Z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
    --icone-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237FB3DF' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 16v-5M12 8h.01'/%3E%3C/svg%3E");
    --icone-vazio: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2379827A' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
    --icone-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%239BA49C' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
    --icone-chevron-forte: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23E9F0EA' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");

  }
}

/* Escolha explícita no botão. Bloco IDÊNTICO ao de cima, e a duplicação é
   intencional: `@media` e seletor de atributo não se combinam numa regra só, e
   as duas portas precisam produzir exatamente a mesma paleta. Alterar uma sem a
   outra deixa o tema do sistema diferente do tema do botão — o tipo de
   divergência que só aparece na máquina de quem tem o sistema em escuro. */
:root[data-tema="escuro"] {
  color-scheme: dark;

  --warm-50: #1B201C;
  --warm-100: #171B18;
  --warm-150: #202520;
  --warm-200: #262C27;
  --warm-300: #2E352F;
  --warm-400: #414A42;

  --bg-app: #131715;
  --bg-sidebar: #171B18;
  --bg-surface: #1C211D;
  --bg-inset: #232823;
  --bg-hover: #262C27;

  --border-subtle: #2C332D;
  --border-strong: #3C443D;

  --text-title: #E9F0EA;
  --text-primary: #D3DBD5;
  --text-secondary: #9BA49C;
  --text-tertiary: #79827A;
  --text-on-dark: #0E1410;
  --text-on-dark-muted: rgba(14, 20, 16, 0.66);

  --brand-100: #1E2A22;
  --brand-200: #2A3A2F;
  --brand-300: #3E5A48;
  --brand-400: #5C9478;
  --brand-500: #6FB894;
  --brand-600: #57A883;
  --brand-700: #4C9B74;
  --brand-800: #3E8A66;
  --brand-900: #2E6B50;

  --sage-100: #202B24;
  --sage-200: #2A3A2F;
  --sage-300: #38493D;

  --good: #58BF8C;
  --good-bg: #1A2A21;
  --good-border: #2C4535;
  --bad: #E3837A;
  --bad-bg: #2C1E1C;
  --bad-border: #4A2E2A;
  --warn: #D9A34A;
  --warn-text: #E4C083;
  --warn-bg: #2A2317;
  --warn-border: #453A22;
  --info: #7FB3DF;
  --info-bg: #1A2430;
  --info-border: #2A3A4A;

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.28);
  --shadow-pop: 0 4px 6px -2px rgba(0, 0, 0, 0.32), 0 12px 28px -8px rgba(0, 0, 0, 0.5);
  --shadow-modal: 0 24px 64px -12px rgba(0, 0, 0, 0.62);

  --chart-grid: #2C332D;
  --chart-tick: #9BA49C;
  --chart-label: #E9F0EA;
  --chart-muted: #79827A;

  --chart-1: #7FC7A3;
  --chart-2: #5CAE86;
  --chart-3: #4C9B74;
  --chart-4: #3E8A66;
  --chart-5: #2C6249;
  --chart-6: #6E9A85;

  --chart-good: #58BF8C;
  --chart-bad: #E3837A;
  --chart-bad-claro: #7A4640;
  --chart-warn: #D9A34A;
  --chart-amber: #D9A34A;

  --chart-fill: rgba(127, 199, 163, 0.14);
  --chart-fill-forte: rgba(92, 174, 134, 0.5);
  --chart-fill-bad: rgba(227, 131, 122, 0.14);
  --chart-fill-suave: rgba(127, 199, 163, 0.08);
  --focus-ring: rgba(111, 184, 148, 0.20);

  /* Icones de traco claro. O SVG e string opaca, entao o token guarda o
     url() inteiro — ver o comentario na definicao do tema claro. */
  --icone-ok: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2358BF8C' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M8.5 12.5l2.5 2.5 4.5-5'/%3E%3C/svg%3E");
  --icone-atencao: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23E4C083' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.3 3.8 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.8a2 2 0 0 0-3.4 0Z'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
  --icone-info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%237FB3DF' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 16v-5M12 8h.01'/%3E%3C/svg%3E");
  --icone-vazio: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2379827A' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 12h-6l-2 3h-4l-2-3H2'/%3E%3Cpath d='M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z'/%3E%3C/svg%3E");
  --icone-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%239BA49C' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");
  --icone-chevron-forte: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='none' stroke='%23E9F0EA' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 8l4 4 4-4'/%3E%3C/svg%3E");

}

* {
  box-sizing: border-box;
}

/* --------------------------------------------------------------------------
   Rodinha de carregamento
   --------------------------------------------------------------------------
   Aparece no item clicado enquanto a página nova não pinta. Existe porque
   clicar numa aba não dava retorno nenhum até a resposta chegar, e em tela com
   consulta pesada isso é meio segundo parecendo que o clique não pegou — a
   reação natural é clicar de novo.

   Anel de borda em vez de SVG animado: não precisa de marcação nova em lugar
   nenhum, e um `::before`/`::after` posicionado não empurra o texto do item.
   -------------------------------------------------------------------------- */
@keyframes cx-girar {
  to { transform: rotate(360deg); }
}

/* Na sidebar a rodinha entra na faixa de indentação que já existe (os subitens
   têm 40px de padding à esquerda), então ela NÃO desloca o rótulo — item que
   pula de lugar no clique parece defeito, não carregamento. */
.sidebar-subitem.is-carregando {
  background: var(--warm-200);
}

/* `top: 15px` em vez de `50%`: o subitem pode ter duas linhas agora, e centrado
   na altura total a rodinha desceria para o meio do bloco em vez de ficar ao lado
   da PRIMEIRA linha do rótulo. */
.sidebar-subitem.is-carregando::before {
  content: '';
  position: absolute;
  left: 16px;
  top: 14px;
  width: 13px;
  height: 13px;
  border: 1.6px solid var(--warm-400);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: cx-girar 0.6s linear infinite;
}

/* Na aba e na pill de período a rodinha entra depois do texto: ali há espaço
   sobrando à direita e a caixa é inline, então não há faixa livre à esquerda
   para ocupar sem empurrar nada. */
.subabas-item.is-carregando::after,
.periodo-pill.is-carregando::after {
  content: '';
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: 8px;
  vertical-align: -2px;
  border: 1.6px solid var(--warm-400);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: cx-girar 0.6s linear infinite;
}

/* Botão de formulário enquanto o envio está em curso (ligado por `layout.js`). Reusa o
   mesmo anel das abas em vez de um indicador próprio: dois jeitos de dizer "ocupado" na
   mesma tela fazem o segundo parecer outra coisa.

   `opacity` além da rodinha porque o botão também fica `disabled`, e um botão desabilitado
   que continua com a aparência de clicável convida ao segundo clique — que é exatamente o
   que este estado existe para evitar. */
.btn.is-enviando {
  opacity: 0.72;
  cursor: progress;
}

.btn.is-enviando::after {
  content: '';
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: 8px;
  border: 1.6px solid var(--warm-400);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: cx-girar 0.6s linear infinite;
}

/* No botão primário (fundo verde escuro chapado) o anel morno desaparece — mesma correção
   que a aba ativa precisa, logo abaixo. */
.btn--primario.is-enviando::after,
.filtros-aplicar.is-enviando::after {
  border-color: var(--text-on-dark-muted);
  border-top-color: var(--text-on-dark);
}

/* Na aba ativa (fundo verde escuro) o anel morno desaparece. */
.subabas-item.active.is-carregando::after {
  border-color: rgba(255, 255, 255, 0.32);
  border-top-color: #FFFFFF;
}

/* Botão "Gerar PowerPoint" do Relatório N3 enquanto o arquivo é montado no servidor
   (ligado pelo script inline em `relatorio_n3.html` — sem arquivo próprio em `core/js/`
   porque esta tela não tem gráfico e o contrato de remontagem de Chart.js não se
   aplica). `.btn-print` não carrega a classe `.btn`, por isso é uma regra própria e não
   um seletor a mais em `.btn.is-enviando` — mesmo tratamento visual, para "ocupado"
   continuar querendo dizer a mesma coisa em toda a tela.

   `pointer-events: none` é a trava contra clique duplo: com o botão desabilitado
   visualmente mas ainda clicável, o reflexo natural é clicar de novo. */
.btn-print.is-enviando {
  opacity: 0.72;
  cursor: progress;
  pointer-events: none;
}

.btn-print.is-enviando::after {
  content: '';
  display: inline-block;
  width: 12px;
  height: 12px;
  margin-left: 8px;
  border: 1.6px solid var(--warm-400);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  animation: cx-girar 0.6s linear infinite;
}

/* Respeita quem desligou animação no sistema: a rodinha para de girar e vira um
   anel estático, que ainda comunica "ocupado" sem movimento. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .sidebar-subitem.is-carregando::before,
  .subabas-item.is-carregando::after,
  .periodo-pill.is-carregando::after {
    animation: none !important;
    border-top-color: var(--brand-600);
  }
}

body {
  margin: 0;
  font-family: 'DM Sans', -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  background: var(--bg-app);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: inherit;
  text-decoration: none;
}

code {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.92em;
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: 5px;
  padding: 1px 5px;
}

/* Foco visível e na cor da marca em tudo que é focável. O padrão do navegador é
   um anel azul, que é a única cor fora da paleta que aparecia no site. */
:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

.app-shell {
  display: flex;
  min-height: 100vh;
}

/* ==========================================================================
   Sidebar
   ==========================================================================
   Invertida em relação à versão anterior: era um bloco verde-escuro sólido com
   texto branco; agora é off-white com tinta verde-escura, como no material.

   As páginas da área moram AQUI (grupo colapsável), não numa subnav horizontal.
   É a mudança estrutural que mais aproxima do ecossistema: em Produtos, "onde
   estou" é sempre a sidebar, e aba dentro da tela fica reservada para sub-visão
   do mesmo conteúdo.
   ========================================================================== */
.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  background: var(--bg-sidebar);
  border-right: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  padding: 14px 14px 12px;
  transition: width 0.16s ease;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  /* Barra de rolagem fina e morna — a padrão do Windows é um cinza-azulado que
     corta o tom bege da coluna inteira. */
  scrollbar-width: thin;
  scrollbar-color: var(--warm-400) transparent;
}

.sidebar::-webkit-scrollbar { width: 8px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb {
  background: var(--warm-400);
  border-radius: var(--radius-pill);
  border: 2px solid var(--bg-sidebar);
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px 16px;
}

.sidebar-brand-mark {
  color: var(--text-title);
  display: flex;
  align-items: center;
}

/* Logo real da EQI (core/_logo_eqi.html). O SVG usa fill="currentColor", então
   herda a cor do contexto e não precisa de duas versões do arquivo. Altura fixa
   e largura automática: o viewBox é 80x42 e travar a largura distorceria. */
.sidebar-brand-mark .eqi-logo {
  height: 20px;
  width: auto;
  display: block;
}

.topbar-logo .eqi-logo {
  height: 22px;
  width: auto;
  display: block;
}

.sidebar-brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}

.sidebar-brand-name {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
  letter-spacing: -0.005em;
}

.sidebar-brand-sub {
  font-size: 0.72rem;
  color: var(--text-secondary);
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

/* Rótulo de grupo: no material é caixa normal e discreto ("Planejamento"), não
   versalete espaçado. Uppercase somava um segundo nível de ênfase a um elemento
   cuja função é justamente não competir com os itens. */
.sidebar-group-label {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-secondary);
  padding: 12px 10px 6px;
  white-space: nowrap;
}

.sidebar-group-label--muted {
  color: var(--text-tertiary);
}

.sidebar-divider {
  height: 1px;
  background: var(--border-subtle);
  margin: 10px 2px;
  flex-shrink: 0;
}

/* Item simples (Administração, Reportar problema, área em breve) */
.sidebar-item,
.sidebar-link,
.sidebar-area-item {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  color: var(--text-title);
  transition: background-color 0.12s ease;
  width: 100%;
  text-align: left;
}

.sidebar-item:hover,
.sidebar-link:hover,
.sidebar-area-item:hover {
  background: var(--warm-200);
}

.sidebar-icon {
  width: 17px;
  height: 17px;
  flex-shrink: 0;
  color: var(--text-title);
  opacity: 0.85;
}

/* Rótulo QUEBRA LINHA em vez de truncar. "Customer Marketi…" e "Estratégia de
   campan…" não dizem para onde o item leva, e no menu da navegação isso é o
   defeito mais caro possível: quem não reconhece o item não clica. Duas linhas
   com entrelinha apertada custam 18px e resolvem. */
.sidebar-label {
  min-width: 0;
  line-height: 1.3;
  overflow-wrap: break-word;
}

/* Recolhida, a coluna tem 68px e o rótulo está escondido — mas o `white-space`
   precisa voltar a `nowrap` para o item não tentar quebrar durante a transição
   de largura e pular de altura. */
.app-shell.sidebar-collapsed .sidebar-label {
  white-space: nowrap;
}

/* Grupo de área — <details>/<summary>, sem JS.
   Escolha deliberada: compartilhar helper de JS entre
   telas quebrou os gráficos em 05/08 com causa nunca identificada, e o menu de
   navegação é o último lugar onde vale gastar risco de JS. `open` vem do
   servidor quando a área é a da URL, então a árvore já chega aberta na página
   certa, sem flash. */
.sidebar-grupo {
  border-radius: var(--radius-sm);
}

.sidebar-grupo-summary {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-title);
  cursor: pointer;
  list-style: none;
  transition: background-color 0.12s ease;
}

.sidebar-grupo-summary::-webkit-details-marker {
  display: none;
}

.sidebar-grupo-summary:hover {
  background: var(--warm-200);
}

/* Grupo que contém a página atual: o próprio cabeçalho fica marcado, senão a
   área ativa só se distingue rolando até o subitem — e com a árvore de outra
   área também aberta, não se distingue de jeito nenhum. */
.sidebar-grupo--ativo > .sidebar-grupo-summary {
  color: var(--text-title);
  font-weight: 600;
}

.sidebar-chevron {
  width: 14px;
  height: 14px;
  margin-left: auto;
  flex-shrink: 0;
  color: var(--text-secondary);
  transition: transform 0.16s ease;
}

.sidebar-grupo[open] > .sidebar-grupo-summary .sidebar-chevron {
  transform: rotate(90deg);
}

/* Sublista: os subitens do material não têm ícone e ficam indentados na altura
   do rótulo do pai, alinhados entre si. */
.sidebar-sublista {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px 0 6px;
}

/* Subitem também quebra em vez de truncar, pelo mesmo motivo do rótulo do grupo.
   `position: relative` fica aqui (e não numa regra solta) porque a rodinha de
   carregamento é um `::before` absoluto ancorado neste elemento. */
.sidebar-subitem {
  display: block;
  position: relative;
  padding: 8px 10px 8px 40px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  line-height: 1.35;
  color: var(--text-primary);
  overflow-wrap: break-word;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.sidebar-subitem:hover {
  background: var(--warm-200);
  color: var(--text-title);
}

.sidebar-subitem.active {
  background: var(--sage-200);
  color: var(--text-title);
  font-weight: 600;
}

/* Página "em construção" (23/09/2026): `<span>`, nunca `<a>` — sem `href` não tem para
   onde ir. Mesma paleta de `.sidebar-item.disabled` (texto terciário, sem hover), mas
   com `display: flex` para o cadeado ficar ao lado do rótulo, porque `.sidebar-subitem`
   base é `display: block` (só texto, sem ícone). */
.sidebar-subitem--bloqueada {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-tertiary);
  cursor: default;
}

.sidebar-subitem--bloqueada:hover {
  background: transparent;
  color: var(--text-tertiary);
}

.sidebar-subitem-cadeado {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* Área travada ("Em breve") */
.sidebar-item.disabled,
.sidebar-area-item.disabled {
  color: var(--text-tertiary);
  cursor: default;
}

.sidebar-item.disabled:hover,
.sidebar-area-item.disabled:hover {
  background: transparent;
}

.sidebar-item.disabled .sidebar-icon,
.sidebar-area-item.disabled .sidebar-icon {
  color: var(--text-tertiary);
  opacity: 0.7;
}

.sidebar-footer {
  padding-top: 8px;
  margin-top: 8px;
  border-top: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex-shrink: 0;
}

/* Logout é POST porque a LogoutView do Django não aceita GET desde a 5.0 — e
   porque link de logout em GET é disparável por imagem forjada de terceiro. O
   <form> precisa não interferir no layout da coluna. */
.sidebar-form {
  display: contents;
}

.sidebar-item--botao {
  border: none;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* Cartão do usuário: caixa morna com o identificador em duas linhas e o botão
   de tema à direita, como no rodapé da sidebar de referência. */
.sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 8px;
  padding: 9px 10px;
  background: var(--warm-200);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}

.sidebar-user-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  line-height: 1.3;
}

.sidebar-user-name {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--text-title);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-user-email {
  font-size: 0.72rem;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Avatar: some no padrão novo (o material não tem), mas a classe fica definida
   para não virar marcação sem estilo se alguém reintroduzir. */
.sidebar-user-avatar {
  display: none;
}

/* --------------------------------------------------------------------------
   Switch de tema — sol / trilha / lua
   --------------------------------------------------------------------------
   `role="switch"` com `aria-checked` no <button>, e não um checkbox escondido:
   o estado é lido por leitor de tela sem precisar de <label> pareado, e o CSS
   se pendura no próprio atributo — então a posição do botão nunca sai de
   sincronia com o estado anunciado, porque os dois leem a mesma fonte.

   Os dois ícones ficam FORA da trilha, como no material: o lado ativo acende e
   o outro fica mudo, então dá para saber qual tema está valendo sem interpretar
   para que lado a bolinha foi.
   -------------------------------------------------------------------------- */
.tema-switch {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 2px;
  border: none;
  background: none;
  cursor: pointer;
  border-radius: var(--radius-sm);
}

.tema-switch svg {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--text-tertiary);
  transition: color 0.18s ease;
}

/* O ícone do tema VIGENTE acende. */
.tema-switch[aria-checked="false"] .tema-switch-sol,
.tema-switch[aria-checked="true"] .tema-switch-lua {
  color: var(--text-title);
}

.tema-switch-trilha {
  position: relative;
  width: 32px;
  height: 18px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  background: var(--warm-300);
  border: 1px solid var(--border-subtle);
  transition: background-color 0.18s ease, border-color 0.18s ease;
}

.tema-switch[aria-checked="true"] .tema-switch-trilha {
  background: var(--brand-900);
  border-color: var(--brand-800);
}

.tema-switch-botao {
  position: absolute;
  top: 1px;
  left: 1px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--bg-surface);
  box-shadow: 0 1px 2px rgba(16, 53, 42, 0.28);
  transition: transform 0.18s ease, background-color 0.18s ease;
}

/* 14px de deslocamento: 32 (trilha) − 2 (bordas) − 14 (bolinha) − 2 (folga) */
.tema-switch[aria-checked="true"] .tema-switch-botao {
  transform: translateX(14px);
  background: var(--brand-500);
}

.tema-switch:hover .tema-switch-trilha {
  border-color: var(--border-strong);
}

/* Pouca altura (notebook com a barra do navegador): a coluna rola por conta própria, e
   com a árvore de uma área aberta o "Sair" e o cartão do usuário ficavam ~300px abaixo
   da dobra. Só o espaçamento vertical encolhe — largura, fonte e alvo de clique (≥30px)
   continuam os mesmos. */
@media (max-height: 820px) {
  .sidebar { padding-top: 10px; }
  .sidebar-brand { padding-bottom: 10px; }
  .sidebar-item,
  .sidebar-link,
  .sidebar-area-item,
  .sidebar-grupo-summary { padding-top: 7px; padding-bottom: 7px; }
  .sidebar-subitem { padding-top: 6px; padding-bottom: 6px; }
  .sidebar-subitem.is-carregando::before { top: 12px; }
  .sidebar-group-label { padding-top: 8px; padding-bottom: 4px; }
  .sidebar-divider { margin-top: 6px; margin-bottom: 6px; }
}

/* Estado recolhido
   --------------------------------------------------------------------------
   Todo o bloco vive dentro de `min-width: 721px` de propósito. No mobile a
   sidebar já vira uma faixa empilhada de largura cheia, e "recolher" ali não
   significa nada — a primeira versão desfazia isso lá embaixo com
   `display: revert`, que é ARMADILHA: `revert` volta ao valor do NAVEGADOR, não
   ao valor anterior da cascata. `.sidebar-brand-text` é `display: flex` aqui,
   e o revert dele daria `inline`, empilhando o nome e o subtítulo na mesma
   linha. Escopar por largura não tem esse problema porque a regra nem existe
   fora da faixa. */
@media (min-width: 721px) {
  .app-shell.sidebar-collapsed .sidebar {
    width: var(--sidebar-w-collapsed);
    padding-left: 10px;
    padding-right: 10px;
  }

  .app-shell.sidebar-collapsed .sidebar-label,
  .app-shell.sidebar-collapsed .sidebar-brand-text,
  .app-shell.sidebar-collapsed .sidebar-group-label,
  .app-shell.sidebar-collapsed .sidebar-area-rotulo,
  .app-shell.sidebar-collapsed .sidebar-area-check,
  .app-shell.sidebar-collapsed .sidebar-chevron,
  .app-shell.sidebar-collapsed .sidebar-sublista,
  .app-shell.sidebar-collapsed .sidebar-divider,
  .app-shell.sidebar-collapsed .sidebar-user-info,
  .app-shell.sidebar-collapsed .tema-switch {
    display: none;
  }

  /* Recolhida, o cartão do usuário viraria uma caixa vazia de 40px. */
  .app-shell.sidebar-collapsed .sidebar-user {
    display: none;
  }

  .app-shell.sidebar-collapsed .sidebar-item,
  .app-shell.sidebar-collapsed .sidebar-link,
  .app-shell.sidebar-collapsed .sidebar-grupo-summary,
  .app-shell.sidebar-collapsed .sidebar-area-item {
    justify-content: center;
    padding-left: 8px;
    padding-right: 8px;
  }
}

/* ==========================================================================
   Área principal, topbar e conteúdo
   ==========================================================================
   A topbar do material não é uma barra: é o cabeçalho da PÁGINA, sem fundo
   próprio e sem borda inferior — botão de recolher à esquerda, título grande
   com subtítulo mudo embaixo, logo da EQI à direita.
   ========================================================================== */
.app-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Texto só para leitor de tela: fora da tela mas dentro da árvore de
   acessibilidade. `display: none` e `visibility: hidden` o removeriam dela
   também, que é o oposto do objetivo. */
.sr-apenas {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   Cabeçalho fixo ao rolar
   --------------------------------------------------------------------------
   O invólucro é que é `sticky`, não a `.topbar`: a topbar tem largura máxima e
   margem automática (para alinhar com a coluna de conteúdo), e um elemento com
   largura limitada grudado no topo deixaria o conteúdo passando POR TRÁS dele
   nas laterais. O invólucro ocupa a largura toda e carrega o fundo; a topbar
   segue centralizada dentro dele.

   Nas telas longas — o Relatório N3 tem 560 linhas de marcação e a Estratégia
   585 — depois de duas rolagens não havia nada em tela dizendo qual página era.
   A borda inferior só aparece quando há conteúdo passando por baixo
   (`.is-rolado`, do layout.js), senão a linha fica sobrando no topo da página
   parada. */
.topbar-wrap {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg-app);
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.topbar-wrap.is-rolado {
  border-bottom-color: var(--border-subtle);
  box-shadow: 0 4px 12px -8px rgba(16, 53, 42, 0.35);
}


/* TRÊS COLUNAS, e as laterais têm largura IGUAL por construção (`1fr` cada). É isso que
   coloca a pílula de sub-abas no meio EXATO da faixa, e não no meio do vão entre o título e
   as ações — com flex + spacer o centro se deslocava conforme o título mudava de tamanho de
   uma tela para a outra.

   `minmax(0, 1fr)` e não `1fr`: `1fr` sozinho tem mínimo `auto`, então um título longo faria
   a coluna da esquerda crescer além da metade e a simetria iria embora justamente na tela em
   que ela mais aparece.

   SÃO 3 FILHOS DIRETOS PARA 3 COLUNAS. Um quarto filho cai numa segunda linha da grade
      sem erro nenhum — é o tipo de quebra que `check` e HTTP 200 não acusam, e por isso a
      auditoria de HTML conta os filhos contra as colunas.

   `align-items: start` preserva o alinhamento de hoje (botão e ações no topo, junto da
   primeira linha do título); só a pílula se centra, pelo `align-self` dela. */
.topbar {
  flex-shrink: 0;
  display: grid;
  /* `minmax(0, auto)` no meio, não `auto`: `auto` cresce sem limite e esmaga a
     coluna do título — com a 4ª sub-aba, "Estratégia de campanha" passou a quebrar
     em duas linhas. Assim as abas rolam (ver `.topbar-subabas`) e o título fica. */
  grid-template-columns: minmax(0, max-content) minmax(0, max-content) minmax(max-content, 1fr);
  align-items: start;
  gap: 14px;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: var(--ritmo-y) var(--gutter) calc(var(--ritmo-y) * 0.8);
  background: transparent;
}

/* QUEM ESCOLHE O MODO DAS SUB-ABAS É O `layout.js` (`ligarSubabasNaTopbar`), medindo
   título, abas e ações contra a largura da faixa — e não uma media query. O ponto de
   quebra depende do comprimento do título e da quantidade de abas, que mudam de tela
   para tela, e da sidebar aberta ou recolhida, que a viewport não enxerga. O corte fixo
   em 1560px mandava as abas para uma segunda linha em todo notebook (+63px de cabeçalho
   grudado), inclusive com "Projetos" e duas abas, que cabiam com folga.

     sem classe           abas logo depois do título (padrão, e o que vale sem JS)
     --abas-centro        três colunas simétricas, abas no meio exato da faixa
     --abas-abaixo        abas numa segunda linha, centralizadas */
.topbar.topbar--abas-centro {
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
}

.topbar.topbar--abas-abaixo {
  grid-template-columns: minmax(0, 1fr) auto;
}

/* COLUNA EXPLÍCITA EM CADA GRUPO, e isto não é redundância. Nas 18 telas sem sub-aba o
   filho do meio é `display: none` pelo `:empty` — e um item escondido SAI da grade, então o
   posicionamento automático jogava o grupo da direita na coluna 2 (a `auto`, estreita) e ele
   parava no MEIO da tela, com o vão sobrando à direita. Aconteceu de verdade.

   Com a coluna declarada, a posição de cada grupo deixa de depender de quantos filhos
   existem — que é a única forma de as telas com e sem sub-aba terem o mesmo cabeçalho. */
.topbar-left {
  grid-column: 1;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  min-width: 0;
}

.topbar-right {
  grid-column: 3;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 14px;
  min-width: 0;
}

/* O BLOCO DAS SUB-ABAS FICA DEPOIS DE `.topbar-left`/`.topbar-right`, e a ORDEM É A
   REGRA, não arrumação. A media query dele reatribui `.topbar-right` para a coluna 2 em
   tela estreita; com a mesma especificidade, quem vence a cascata é a regra MAIS ABAIXO
   no arquivo. Com este bloco acima, o `grid-column: 3` incondicional anulava a
   reatribuição e a grade criava uma terceira coluna implícita, empurrando o logo para
   fora da faixa — só numa janela de largura específica, e sem `check`, HTTP 200 ou a
   auditoria de classes acusarem nada. */
/* Sub-abas NA LINHA DO TÍTULO, dentro da `.topbar` que gruda.

   `:empty` é o que impede um `gap` sobrando nas 18 telas sem sub-aba. A `.topbar` é flex
      com `gap: 14px`, e um filho vazio ainda conta como filho — o vão apareceria entre o
      título e as ações sem nada dentro. Depende de o template não deixar espaço em branco
      dentro do span; está anotado lá também, porque quem reindentar o `base.html` quebra
      isto sem nenhum sintoma além do vão. */
.topbar-subabas {
  /* Rola em vez de empurrar o título. `min-width: 0` é o que permite o
     encolhimento dentro do grid — sem ele o item nunca fica menor que o
     conteúdo e o `minmax(0, auto)` acima não tem efeito. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
  grid-column: 2;
  display: flex;
  min-width: 0;
  /* Centraliza contra o bloco do título, em vez de encostar no topo como o
     `align-items: start` da grade faria. */
  align-self: center;
}

/* Segunda linha: as três colunas não cabem sem esconder aba atrás da rolagem da pílula
   (a 4ª aba escondida é um item de navegação que ninguém descobre). A coluna 3 DEIXA DE
   EXISTIR: sem reatribuir, o `grid-column: 3` do grupo da direita criaria uma terceira
   coluna implícita e empurraria o logo para fora da faixa. */
.topbar--abas-abaixo .topbar-right {
  grid-column: 2;
}

.topbar--abas-abaixo .topbar-subabas {
  grid-column: 1 / -1;
  grid-row: 2;
  justify-self: center;
  margin-top: 2px;
}

.topbar-subabas:empty {
  display: none;
}

/* A margem que separava as sub-abas do conteúdo não existe mais: aqui elas estão DENTRO da
   linha do cabeçalho, e margem inferior aumentaria a altura do bloco grudado. */
.topbar-subabas .subabas {
  margin-bottom: 0;
}

/* Versão compacta: na linha do título a pílula divide espaço com o próprio título e com as
   ações, então ela encolhe. Fora do cabeçalho (se algum dia voltar a ter faixa própria) a
   medida original continua valendo. */
.topbar-subabas .subabas-item {
  padding: 6px 12px;
  font-size: 0.8rem;
}

.sidebar-toggle {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text-secondary);
  cursor: pointer;
  /* Alinha o botão com a primeira linha do título, não com o bloco inteiro:
     com subtítulo presente o bloco tem duas linhas e o botão centralizado
     ficava na altura do subtítulo. */
  margin-top: 4px;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.sidebar-toggle svg {
  width: 16px;
  height: 16px;
}

.sidebar-toggle:hover {
  background: var(--bg-inset);
  border-color: var(--border-strong);
  color: var(--text-title);
}

/* A seta vira de lado quando a coluna está recolhida: o botão passa a apontar para
   onde a ação leva. Sem isso o mesmo ícone servia para "recolher" e "expandir", e
   nada na tela dizia qual dos dois o clique ia fazer. O rótulo e o `aria-label`
   trocam junto, no layout.js — ícone girado sem rótulo novo só ajuda quem vê. */
.sidebar-toggle svg {
  transition: transform 0.18s ease;
}

.app-shell.sidebar-collapsed .sidebar-toggle svg {
  transform: rotate(180deg);
}

/* Retorno tátil de clique. 1px é o suficiente para o botão parecer que afundou;
   mais que isso vira animação. */
.btn:active,
.btn-print:active,
.login-btn:active,
.login-sso:active,
.sidebar-toggle:active,
.subabas-item:active,
.toggle-btn:active {
  transform: translateY(1px);
}

.topbar-heading {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 1px;
}

/* Acompanha a ALTURA da janela, não a largura: o título gruda no topo, e num notebook
   de 590px úteis cada linha do cabeçalho sai do conteúdo o tempo todo. */
.topbar-title {
  font-weight: 600;
  font-size: clamp(1.3rem, 0.9rem + 1.2vh, 1.6rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--text-title);
}

.topbar-subtitle {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 4px;
  flex-shrink: 0;
}

.topbar-link {
  font-size: 0.82rem;
  color: var(--text-secondary);
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

.topbar-link:hover {
  background: var(--warm-200);
  color: var(--text-title);
}

.topbar-logo {
  color: var(--text-title);
  display: flex;
  align-items: center;
  margin-top: 2px;
  flex-shrink: 0;
}

.content {
  flex: 1;
  width: 100%;
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--gutter) 40px;
  counter-reset: secao;
}

/* O aviso operacional do `base.html` é uma linha só na maioria das vezes. Com o padding
   de caixa de conteúdo e sem margem embaixo ele somava ~60px colado no texto da barra de
   filtros. */
.content > .aviso {
  margin: 0 0 clamp(8px, 1.6vh, 14px);
  padding-top: 9px;
  padding-bottom: 9px;
  background-position: 16px 11px;
}

/* --------------------------------------------------------------------------
   Barra de ferramentas da página (descrição + filtros)
   --------------------------------------------------------------------------
   O que era `.content-header` com sobrescrita, h1 e subtítulo virou só a linha
   de contexto e ação: o título subiu para a topbar, onde o material o coloca.
   Manter os dois lugares imprimia o nome da página duas vezes na mesma tela.
   -------------------------------------------------------------------------- */
.content-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: clamp(12px, 2.2vh, 20px);
  min-height: 36px;
}

.content-header h1 {
  margin: 0 0 2px;
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-title);
}

.content-eyebrow {
  display: block;
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--text-tertiary);
  margin-bottom: 2px;
}

.content-header .subtitle {
  margin: 0;
  color: var(--text-secondary);
  font-size: 0.875rem;
  max-width: 78ch;
}

.content-header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  position: relative;
}

/* SEMPRE À DIREITA, inclusive quando o bloco desce para a própria linha (24/08/2026).

   O `.content-header` é `space-between` e distribui POR LINHA. Enquanto legenda e ações
   cabem juntas, o `space-between` já joga as ações para a direita; quando a legenda ocupa
   a linha inteira, as ações descem sozinhas e o `space-between` as deixa no COMEÇO. O
   resultado é uma barra que troca de lado conforme o comprimento da legenda e a largura da
   janela — medido: à esquerda em 1440px, à direita em 1180px, na mesma tela.

   `margin-left: auto` resolve porque margem automática consome o espaço livre ANTES de o
   `justify-content` entrar, então vale igual nos dois casos. É modificador e não mudança
   na classe base: as outras telas que usam `.content-header-actions` têm barras curtas que
   nunca descem de linha, e mexer na base para consertar esta arriscaria o layout delas sem
   sintoma nenhum aqui. */
.content-header-actions--fim {
  margin-left: auto;
}

/* Filtros e Período ANDAM JUNTOS quando a barra quebra.

   São o par mais próximo dela — os dois respondem "qual recorte desta tela" — e eram o
   único conteúdo da barra antes de os recortes globais entrarem. Sem este agrupamento a
   quebra caía ENTRE os dois e o Período ficava sozinho numa linha, que foi o que a captura
   em 1180px mostrou. Agrupados, a barra quebra no único lugar que faz sentido: recortes
   numa linha, filtros e período na outra. */
.toolbar-periodo {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* --------------------------------------------------------------------------
   Título de seção
   --------------------------------------------------------------------------
   No material ("Pendências", "Seguindo", "Oportunidades Abertas") é texto
   verde-escuro semibold, sem régua e sem versalete. A linha horizontal que a
   versão anterior desenhava até o fim da tela era o elemento mais pesado da
   página, competindo com os próprios cards.
   -------------------------------------------------------------------------- */
.section-divider {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: calc(var(--ritmo-y) * 1.4) 0 calc(var(--ritmo-y) * 0.65);
}

.section-divider:first-child {
  margin-top: 0;
}

.section-divider span {
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-title);
  white-space: nowrap;
}

/* Numeração da seção, como o "01 HIGHLIGHTS" do N3. Contador em vez de número
   escrito à mão: inserir uma seção no meio renumera sozinho, e número fora de
   ordem num relatório é pior que não ter numeração. */
.section-divider--numerada {
  counter-increment: secao;
}

.section-divider--numerada::before {
  content: counter(secao, decimal-leading-zero);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-tertiary);
  letter-spacing: 0.04em;
}

/* ==========================================================================
   Botões
   ==========================================================================
   Sistema explícito, com as classes antigas mapeadas nele. Antes cada botão
   tinha estilo próprio no lugar onde nasceu: `.filtros-aplicar` era verde
   chapado, `.btn-print` era outlined com raio diferente, `.login-btn` tinha um
   terceiro tom de verde. Três botões primários com três verdes na mesma
   identidade é o sintoma de "recebeu umas cores" que este trabalho corrige.
   ========================================================================== */
.btn,
.filtros-aplicar,
.btn-print,
.login-btn,
.login-sso {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.btn svg,
.btn-print svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
}

/* Primário — verde escuro chapado ("Gerar Ordem", "+ Nova Oportunidade",
   "Entendi, vamos começar" no material). */
.btn--primario,
.filtros-aplicar,
.login-btn {
  background: var(--brand-700);
  border-color: var(--brand-700);
  color: var(--text-on-dark);
  font-weight: 500;
}

.btn--primario:hover,
.filtros-aplicar:hover,
.login-btn:hover {
  background: var(--brand-800);
  border-color: var(--brand-800);
}

/* Secundário — branco com borda ("Bloqueios", "Dashboard de Assembléias"). */
.btn--secundario,
.btn-print,
.login-sso {
  background: var(--bg-surface);
  border-color: var(--border-subtle);
  color: var(--text-title);
}

.btn--secundario:hover,
.btn-print:hover,
.login-sso:hover {
  background: var(--bg-inset);
  border-color: var(--border-strong);
  color: var(--text-title);
}

/* As três variantes abaixo (`--suave`, `--icone`, `--link`), mais `.campo` e
   `.input`, AINDA NÃO SÃO USADAS por nenhum template. Estão aqui porque são os
   objetos que o material de referência usa e que este painel vai precisar quando
   ganhar tela de operação (formulário, ação em linha de tabela) — e derivar o
   estilo de novo na hora produziria um quarto verde de botão, que é o problema
   que este arquivo acabou de consertar.
   Não são load-bearing: apagar não muda nenhuma tela de hoje. */

/* Suave — sage chapado ("Conferir"). Ação afirmativa que não é a principal
   da tela. */
.btn--suave {
  background: var(--sage-200);
  border-color: var(--sage-200);
  color: var(--text-title);
}

.btn--suave:hover {
  background: var(--sage-300);
  border-color: var(--sage-300);
}

/* Ícone só — quadrado, como os botões de recarregar e limpar do material. */
.btn--icone {
  width: 34px;
  height: 34px;
  padding: 0;
  background: var(--bg-surface);
  border-color: var(--border-subtle);
  color: var(--text-secondary);
}

.btn--icone:hover {
  background: var(--bg-inset);
  border-color: var(--border-strong);
  color: var(--text-title);
}

/* Link — texto verde com seta ("Ver ativos →"). */
.btn--link {
  padding: 4px 2px;
  background: none;
  border-color: transparent;
  color: var(--brand-600);
  font-weight: 500;
}

.btn--link:hover {
  color: var(--brand-800);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.btn:disabled,
.btn-print:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn--full {
  width: 100%;
}

/* ==========================================================================
   Campos de formulário
   ==========================================================================
   Altura 38px, raio 8px, borda morna, foco em verde com halo — o mesmo em
   input, select e textarea, para os filtros das telas de dashboard parecerem os
   mesmos controles das telas de operação do material.
   ========================================================================== */
.campo {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.campo-label,
.filtro-campo-label {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-secondary);
}

.campo input,
.campo select,
.campo textarea,
.input,
.filtro-campo select,
.login-field input {
  width: 100%;
  padding: 9px 12px;
  font-family: inherit;
  font-size: 0.875rem;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.campo input::placeholder,
.input::placeholder,
.campo textarea::placeholder {
  color: var(--text-tertiary);
}

.campo input:focus,
.campo select:focus,
.campo textarea:focus,
.input:focus,
.filtro-campo select:focus,
.login-field input:focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* Chevron próprio no select: o nativo do Windows é um triângulo cinza-azulado
   com fundo próprio, o único controle da tela que não seguia a paleta. */
.campo select,
.filtro-campo select,
select.input,
.combo-campanha-input {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 32px;
  background-image: var(--icone-chevron);
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 15px 15px;
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   Pills de filtro (período, filtros, chips)
   --------------------------------------------------------------------------
   No material são botões brancos com borda e chevron, à esquerda da barra, com
   a ação primária à direita. É o mesmo objeto do "Fechamento ⌄" da tela de
   Oportunidades.
   -------------------------------------------------------------------------- */
.periodo-pill,
.filtros-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 12px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  color: var(--text-secondary);
  white-space: nowrap;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}

.periodo-pill svg,
.filtros-pill svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--text-secondary);
}

.periodo-pill strong {
  color: var(--text-title);
  font-weight: 600;
}

.periodo-pill--form {
  cursor: pointer;
  gap: 6px;
}

.periodo-pill--form:hover,
.filtros-pill:hover {
  border-color: var(--border-strong);
}

.periodo-pill--form label {
  cursor: pointer;
}

/* O select dentro da pill é o conteúdo dela, não um campo: sem borda, sem
   fundo, herdando a caixa. O chevron vem do próprio select (regra acima), então
   a pill não desenha um segundo. */
.periodo-pill--form select {
  appearance: none;
  -webkit-appearance: none;
  border: none;
  background: transparent;
  font: inherit;
  font-weight: 600;
  color: var(--text-title);
  cursor: pointer;
  padding: 0 20px 0 2px;
  outline: none;
  background-image: var(--icone-chevron-forte);
  background-repeat: no-repeat;
  background-position: right 0 center;
  background-size: 14px 14px;
}

.estrategia-toolbar {
  display: flex;
  align-items: center;
  /* `wrap` porque os recortes globais entraram nesta barra e ela deixou de caber numa
     linha em notebook. Sem ele os botoes seriam espremidos ate ficarem ilegiveis. */
  flex-wrap: wrap;
  /* `flex-END` E NAO `flex-start`, e a diferenca so aparece quando a barra QUEBRA: o
     `.content-header` e `space-between`, entao enquanto tudo cabe numa linha o bloco
     inteiro ja vai para a direita e este valor nao muda nada. Quando quebra, a barra passa
     a ocupar a largura toda — e sem o `flex-end` os controles ficariam colados a esquerda
     enquanto a linha de cima continua alinhada a direita, que foi exatamente o
     desalinhamento que o `width: 100%` do `.toolbar-recortes` produzia. */
  justify-content: flex-end;
  gap: 10px;
}

/* Recortes GLOBAIS na barra de topo (24/08/2026).

   Eles vivem ao lado de Período e Filtros porque são a mesma categoria de decisão: "o que
   alimenta esta tela". Antes ficavam no cabeçalho de cinco painéis, ao lado do seletor
   LOCAL de visão e com peso visual idêntico — e clicar num deles mexia na tela inteira sem
   nada avisando.

   SEM `width: 100%`, e a ausência é a correção: com ele o bloco reivindicava a linha
   inteira e descia sozinho, deixando Filtros e Período na linha de cima. Os três são a
   mesma decisão e ficam na mesma linha; quem cuida do notebook estreito é o `flex-wrap`
   do `.estrategia-toolbar`, que só quebra quando de fato não cabe.

   O rótulo "Recorte" à esquerda diz o que são os botões sem depender de o leitor inferir —
   é o que distingue este grupo do seletor local de visão, que não tem rótulo. */
.toolbar-recortes {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ALTURA EXPLÍCITA, E SÓ DENTRO DA BARRA. As pilhas de Período e Filtros declaram
   `height: 36px`; o `.seg-group` não declara nada e sai por soma de padding com
   line-height — dá ~35px, e o valor MUDA com a fonte que o navegador conseguiu carregar.
   Três objetos vizinhos com um pixel de diferença é o que se lê como desalinhado sem dar
   para nomear o motivo.

   Escopado à barra de propósito: o mesmo `.seg-group` serve o recorte por produto, que
   vive num cabeçalho de painel ao lado de controles menores e ficaria alto demais lá. */
.toolbar-recortes .seg-group {
  height: 36px;
}

/* A `line-height` EXPLÍCITA é o que faz os 36px acima serem alcançáveis em vez de
   apertados. A conta fecha exata:

       36px = 1px de borda x2  +  3px de padding do grupo x2  +  28px do link
       28px = 5px de padding do link x2  +  18px de line-height

   Sem ela o link herda a `line-height` do corpo (~1,5 de 0,78rem = 18,7px) e a soma dá
   36,7px dentro de uma caixa de 36 — o texto encosta na borda, e o quanto encosta MUDA com
   a fonte que o navegador conseguiu carregar. A DM Sans é servida por nós, mas o fallback
   existe justamente para o caso de ela não chegar. */
.toolbar-recortes .seg-link {
  line-height: 18px;
}

.toolbar-recortes-label {
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

.filtros-toggle {
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.filtros-toggle:hover,
.filtros-toggle.is-open {
  border-color: var(--brand-500);
  color: var(--text-title);
}

.filtros-badge {
  background: var(--sage-200);
  color: var(--text-title);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  font-size: 0.72rem;
  font-weight: 600;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  font-size: 0.78rem;
  color: var(--text-secondary);
}

.filter-chip strong {
  color: var(--text-title);
  font-weight: 600;
}

/* Painel flutuante de filtros */
.filtros-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 50;
  width: min(700px, 92vw);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  padding: 18px;
  text-align: left;
}

.filtros-panel-header {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-title);
  margin-bottom: 14px;
}

.filtros-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}

@media (max-width: 720px) {
  .filtros-grid { grid-template-columns: 1fr 1fr; }
}

/* Relatório de acessos: três filtros curtos. Em meia coluna o nome do mês saía cortado no
   celular, então lá vira uma coluna só. */
.filtros-grid.filtros-grid--acessos { grid-template-columns: repeat(3, minmax(0, 280px)); }

@media (max-width: 720px) {
  .filtros-grid.filtros-grid--acessos { grid-template-columns: 1fr; }
}

.filtro-campo {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.filtros-panel-acoes {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 14px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border-subtle);
}

.filtros-limpar {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.filtros-limpar:hover {
  color: var(--text-title);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Pré-visualização de quantidade do painel de Filtros (18/09/2026). `margin-right: auto`
   empurra o texto para a ESQUERDA dentro do mesmo `.filtros-panel-acoes` (que é
   `justify-content: flex-end`), sem precisar de um wrapper novo para separar "info" de
   "ação" — Limpar filtros e Filtrar continuam colados à direita. */
.filtros-contagem {
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin-right: auto;
}

.filtros-contagem--vazia {
  color: var(--bad);
}

/* Feedback do clique BLOQUEADO em "Filtrar" com contagem zerada (18/09/2026) — o texto
   já diz "Nenhum resultado encontrado", mas um clique que não faz nada visível parece
   botão quebrado. O pulso confirma que o clique foi ouvido e recusado de propósito. JS
   remove e reaplica a classe a cada tentativa, para repetir mesmo sem mudar o filtro. */
.filtros-contagem--pulso {
  animation: filtrosContagemPulso 0.5s ease;
}

@keyframes filtrosContagemPulso {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

.table-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}

.table-filters-label {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
  margin-right: auto;
}

/* ==========================================================================
   Segmented control (abas dentro de card e alternadores de visão)
   ==========================================================================
   Dois formatos no material, e os dois estão aqui:
   · `.subabas`     — recipiente branco com a aba ativa em verde escuro, no nível
                      da PÁGINA ("Início | Ativos | Relatórios" do Follow Up).
   · `.toggle-group`— linha sem recipiente, ativa em branco sobre fundo morno, no
                      nível do PAINEL ("Internas | Quebrando | Recebendo").
   A segunda existe porque vive DENTRO de um card branco, e um segundo recipiente
   branco ali cria caixa dentro de caixa.
   ========================================================================== */
.subabas {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

.subabas-item {
  padding: 8px 15px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-title);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.subabas-item:hover {
  background: var(--warm-150);
}

.subabas-item.active {
  background: var(--brand-800);
  color: var(--text-on-dark);
}

.subabas-item.active:hover {
  background: var(--brand-900);
}

/* Sub-abas — sub-visões da página atual.
   --------------------------------------------------------------------------
   Ocupa o espaço logo abaixo do título, que antes era a faixa com as 7 páginas
   da área. Aquela faixa saiu porque repetia exatamente a lista da sidebar: dois
   controles com as mesmas opções, e o espaço mais valioso da tela gasto
   repetindo o menu.

   Rolagem interna e não quebra de linha: se um dia uma página tiver muitas
   sub-visões, a pílula rola dentro dela mesma e o corpo da página nunca rola na
   horizontal. */
.subabas {
  display: flex;
  max-width: 100%;
  margin-bottom: 20px;
  overflow-x: auto;
  scrollbar-width: none;
}

.subabas::-webkit-scrollbar { display: none; }

.subabas-item {
  flex-shrink: 0;
  text-decoration: none;
}

.subabas-item.disabled {
  color: var(--text-tertiary);
  cursor: default;
  pointer-events: none;
}

.toggle-group {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}

.toggle-btn {
  border: none;
  background: transparent;
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  padding: 5px 12px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.toggle-btn:hover {
  color: var(--text-title);
}

/* Canal travado (Cobertura de base): visível, para a pessoa saber que existe, mas sem
   clique. */
.toggle-btn--bloqueado,
.toggle-btn--bloqueado:hover {
  color: var(--text-secondary);
  opacity: 0.45;
  cursor: not-allowed;
}

.toggle-btn-cadeado {
  width: 12px;
  height: 12px;
  margin-right: 3px;
  vertical-align: -1px;
}

.toggle-btn.active {
  background: var(--bg-surface);
  color: var(--text-title);
  font-weight: 600;
  box-shadow: var(--shadow-card);
}

/* Variante GRANDE E CHEIA do `.toggle-group` (23/09/2026, pedido do Gabriel: "os botões
   podem ser maiores para que consumam todo o box e tenham o mesmo tamanho" — Captação da
   EQI Digital, seletor "EQI Digital" / "EQI Digital+" / "Digital & Digital+").
   MODIFICADOR, não mudança na base: `.toggle-group` é compartilhado por outros
   seletores de recorte da aplicação (canal, jornada×campanha...), e content-hugging é o
   certo ali. Aqui os rótulos têm larguras muito diferentes ("EQI Digital" contra
   "Digital & Digital+"), então content-hugging deixava os 3 botões com tamanhos
   desiguais — o oposto do pedido.
   `flex-basis: 100%` joga o grupo para a PRÓPRIA linha, cheia, abaixo do título —
   mesmo mecanismo de `.panel-header > .panel-header-note` (linha de baixo do cabeçalho),
   só que aplicado ao controle em vez de ao texto. Sem isso o grupo ficaria espremido no
   canto direito da linha do título, sem largura para os botões crescerem. */
.toggle-group--full {
  display: flex;
  flex-basis: 100%;
  padding: 4px;
}

.toggle-group--full .toggle-btn {
  flex: 1;
  text-align: center;
  padding: 10px 16px;
  font-size: 0.85rem;
}

/* Seletor que troca o CONJUNTO DE DADOS, não a visão — hoje o recorte jornada ×
   campanha dos painéis de "Objetivos & Áreas". São links: a troca acontece no
   servidor, porque o recorte refaz o ranking inteiro.

   Forma idêntica à do `.toggle-group`, de propósito: os dois convivem no mesmo
   cabeçalho, e desenhar um deles diferente sugeriria que fazem coisas de naturezas
   distintas — quando, para quem usa, os dois são "escolha o recorte".

   CLASSE PRÓPRIA E NÃO `.toggle-btn`: o `initToggles` pega TODOS os `.toggle-btn`
      dentro de um `[data-toggle]`, então reaproveitar a classe faria os dois grupos
      disputarem o mesmo estado e clicar em "Jornadas" esconderia o bloco do ranking.
      Nenhum token novo aqui — a diferença para o toggle é só `text-decoration`. */
.seg-group {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}

.seg-link {
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 500;
  text-decoration: none;
  padding: 5px 12px;
  border-radius: 6px;
  white-space: nowrap;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.seg-link:hover {
  color: var(--text-title);
}

.seg-link.active {
  background: var(--bg-surface);
  color: var(--text-title);
  font-weight: 600;
  box-shadow: var(--shadow-card);
}

.seg-link--indisponivel,
.seg-link--indisponivel:hover {
  color: var(--text-secondary);
  opacity: 0.5;
  cursor: not-allowed;
}

/* ==========================================================================
   Cards (painéis)
   ==========================================================================
   Um card é: superfície branca, 1px morno, raio 12, sem faixa colorida. O
   cabeçalho é título verde-escuro semibold e, embaixo, nota muda — o par
   "Direcionamento / Nenhuma métrica disponível no momento" do material.

   `.panel-header--light` era a variante clara; agora TODO cabeçalho é claro e a
   classe sobrevive como apelido sem efeito, para os 48 usos nos templates não
   precisarem de edição.
   ========================================================================== */
.panel {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.panel-header {
  background: transparent;
  color: var(--text-title);
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 6px 12px;
  padding: 16px 18px 0;
  font-size: 0.875rem;
}

/* Apelido histórico — o estilo já é este. Mantido para os templates não
   mudarem; a regra em branco documenta que a diferença deixou de existir. */
.panel-header--light {
  background: transparent;
  color: var(--text-title);
  padding: 16px 18px 0;
}

.panel-header-title {
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.35;
  letter-spacing: -0.005em;
  color: var(--text-title);
}

/* Agrupa título + selo num único filho direto do cabeçalho. Necessário porque
   `.panel-header` é `space-between`: com três filhos, o do meio flutua no vão. */
.panel-header-title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}

/* A nota do cabeçalho fica EMBAIXO do título, não na outra ponta da linha.
   --------------------------------------------------------------------------
   `.panel-header` é `space-between`, então a nota — que é irmã do título no DOM —
   ia para a borda direita do card. Num painel largo isso jogava
   "Fonte: Marketing Cloud + Salesforce + Produtos + BTG" a 900px do "ROI de
   produtos" que ela explica, e o par título/legenda deixava de se ler como par.

   No material os dois são sempre uma coluna: título, e a legenda muda logo
   abaixo ("Direcionamento" / "Nenhuma métrica disponível no momento").

   `flex-basis: 100%` força a nota para a própria linha e `order: 3` a mantém
   depois de tudo — assim os controles do cabeçalho (toggle-group, selos)
   continuam na LINHA DO TÍTULO, à direita, que é onde eles devem ficar. */
.panel-header-note {
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.45;
}

/* `>` (filho DIRETO) e não descendente: há cabeçalho em que a nota vive DENTRO do
   `.panel-header-title-row`, ao lado do título (o painel de Highlights da Digital usa
   as duas formas no mesmo cabeçalho). Ali ela é um complemento na mesma linha e
   empurrá-la para baixo separaria o par que o agrupamento existe para juntar. */
.panel-header > .panel-header-note {
  flex: 1 0 100%;
  order: 3;
  margin-top: 1px;
}

/* Medida de linha das notas explicativas. Com o conteúdo acompanhando monitores largos,
   um painel de largura cheia em 1920px passava de 200 caracteres por linha; 130ch quase
   não muda nada em notebook e devolve a leitura em tela grande. A nota do cabeçalho fica de
   fora: lá ela é item flex com base 100% (é o que a põe na linha de baixo), e `max-width`
   limita a base — a nota voltava para a linha do título, solta no meio do painel. */
.panel-subcaption,
.panel-footer,
.adm-nota,
.n3-nota {
  max-width: 130ch;
}

.panel-header-chip {
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  font-size: 0.74rem;
}

.panel-body {
  padding: 14px 18px 18px;
}

.panel-footer {
  padding: 0 18px 16px;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Nota logo abaixo do título, ainda fora do corpo. Alinhada com o padding do
   cabeçalho para formar uma coluna só de texto com ele. */
.panel-subcaption {
  display: block;
  padding: 4px 18px 0;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

.panel-subcaption-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.panel-subcaption-row .panel-subcaption {
  padding: 0;
}

.panel--wide {
  margin-bottom: 18px;
}

/* Painel de abertura do Relatório N3 e dos Highlights da Digital. Era verde
   chapado com números brancos; virou sage, que é como o material marca faixa de
   destaque (o hero da tela de Operacional). O número segue grande e agora em
   verde-escuro sobre claro — mais legível que branco sobre verde. */
.panel--destaque {
  background: var(--sage-100);
  border-color: var(--sage-300);
}

.panel--destaque .panel-header,
.panel--destaque .panel-header--light {
  color: var(--text-title);
}

.panel--destaque .panel-body {
  background: transparent;
  color: var(--text-title);
}

.panel--destaque .stat-label {
  color: var(--text-secondary);
}

.panel--destaque .stat-value,
.panel--destaque .stat-value--lg {
  color: var(--text-title);
}

.panel--destaque .panel-header-note {
  color: var(--text-secondary);
}

/* Divisória entre os números. `:not(:first-child)` em vez de borda à direita
   para não sobrar um traço solto no fim da linha quando ela quebra. */
.panel--destaque .n3-kpi:not(:first-child) {
  border-left: 1px solid var(--sage-300);
  padding-left: 28px;
}

/* Chart.js com maintainAspectRatio:false precisa de um contêiner com altura
   explícita — sem isso o canvas cresce sem parar (canvas herda a altura do pai,
   pai herda a altura do canvas). Todo <canvas> deve ficar dentro disto. */
/* Altura que acompanha a altura da janela. O template declara a altura de referência
   como NÚMERO SEM UNIDADE (`style="--altura: 300"`), e o fator vai de 0,85 numa área útil
   de 590px a 1,25 a partir de ~950px — o mesmo gráfico tinha 300px num notebook em que
   mal cabia e num monitor de 1080 linhas em que sobrava meia tela. Sem unidade porque a
   conta multiplica por uma medida (`calc` não divide px por px). Sem `--altura` a
   declaração é inválida e vale `auto`; mini-gráfico de card (32–42px) continua com
   `height` fixo no `style`, que vence esta regra: ali a altura é parte do desenho. */
.chart-box {
  position: relative;
  width: 100%;
  height: clamp(calc(var(--altura) * 0.85px),
                calc(var(--altura) * (0.85px + (100vh - 590px) / 900)),
                calc(var(--altura) * 1.25px));
}

/* --------------------------------------------------------------------------
   Grades de painéis
   -------------------------------------------------------------------------- */
/* `auto-fit`, não `auto-fill`: com `auto-fill`, dois painéis numa faixa de 980px viravam
   três trilhas de 316px — cada tabela rolando dentro do painel e uma coluna vazia ao lado. */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 16px;
}

/* SEM CARD ÓRFÃO NA ÚLTIMA LINHA. `auto-fit` decide colunas pela largura e não pela
   quantidade, então 4 painéis viravam 3 + 1 e 5 KPIs viravam 4 + 1 em todo notebook — o
   último sozinho numa linha vazia. Quantidade contada com `:has()` (só o que renderizou).

   4 painéis: 2 × 2 até a faixa comportar os quatro lado a lado (~1600px de janela com a
   sidebar aberta), e então 4 numa linha.
   5 KPIs: uma linha de 5 desde 1200px — card de 180px ainda lê rótulo, número e
   mini-gráfico, e uma linha de KPIs custa metade da altura de duas. `min-width: 0` nos
   filhos porque o `<canvas>` do mini-gráfico tem 300px intrínsecos e seguraria a trilha.

   `:where()` deixa a regra dos 4 painéis com o peso de `.panel-grid` puro: ela vem antes
   das variantes (`--3col`, `--4col`, `mensal-grid`...), que continuam valendo por ordem. */
.panel-grid:where(:has(> :nth-child(4):last-child)) {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (min-width: 1600px) {
  .panel-grid:where(:has(> :nth-child(4):last-child)) {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

@media (min-width: 1200px) {
  .stat-row:has(> :nth-child(5):last-child),
  .engajamento-row:has(> :nth-child(5):last-child) {
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  .engajamento-row:has(> :nth-child(5):last-child) > .engajamento-card {
    padding-left: 12px;
    padding-right: 12px;
  }
}

/* 6 e 7 cards de tendência: 3 + 3 e 4 + 3 em notebook (eram 5 + 1 e 6 + 1), uma linha
   só quando cada card ainda tem ~200px. */
.lead-time-row:has(> :nth-child(6):last-child) {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.lead-time-row:has(> :nth-child(7):last-child) {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@media (min-width: 1700px) {
  .lead-time-row:has(> :nth-child(6):last-child) {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }

  .lead-time-row:has(> :nth-child(7):last-child) {
    grid-template-columns: repeat(7, minmax(0, 1fr));
  }
}

.stat-row > *,
.engajamento-row > *,
.lead-time-row > * {
  min-width: 0;
}

/* Grids de 3 e 4 cards de KPI das telas da EQI Digital. Colunas fixas (não
   `auto-fit`) para o número de cards ser o que o painel decidiu, e colapso
   explícito no mobile. */
.panel-grid--3col { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 18px; }
.panel-grid--4col { grid-template-columns: repeat(4, minmax(0, 1fr)); margin-bottom: 18px; }

@media (max-width: 1100px) {
  .panel-grid--4col { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 780px) {
  .panel-grid--3col,
  .panel-grid--4col { grid-template-columns: 1fr; }
}

.panel-grid--2col {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  margin-bottom: 18px;
}

/* Painel ímpar sozinho na última linha ocupa as duas colunas. Sem isto ele fica
   em MEIA largura com um vão vazio do lado. A regra depende da posição, não da
   quantidade, então continua correta quando um painel não renderiza. */
.panel-grid--2col > .panel:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

/* Painel de MEIA largura, com um filho só. Não serve usar `--2col`: ele é
   `auto-fit`, que colapsa a coluna vazia e devolve o filho único à largura
   cheia. Aqui as duas colunas são explícitas. `minmax(0, 1fr)` e não
   `minmax(320px, ...)`: com 320px fixo as duas exigiriam 640px e estourariam a
   tela estreita. */
.panel-grid--metade {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 18px;
}
@media (max-width: 900px) {
  .panel-grid--metade { grid-template-columns: 1fr; }
}

/* Gestão de fundos da EQI Digital: gráficos do indicador escolhido, 2 por linha; o 3º do
   "Em aberto" ocupa a linha inteira em vez de ficar órfão em meia largura. */
.fundos-graficos {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 18px;
}
.fundos-graficos > .panel:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}
@media (max-width: 760px) {
  .fundos-graficos { grid-template-columns: 1fr; }
}

.fundos-filtros { align-items: flex-end; gap: 12px; }
.fundos-filtros .filtro-campo { flex: 0 1 170px; }

/* Seletor de meses em caixa (slicer suspenso do BI): resumo com cara de `<select>` e, ao
   abrir, a árvore ano → meses por cima do conteúdo. */
.seletor-meses { position: relative; }
.seletor-meses-caixa > summary { list-style: none; }
.seletor-meses-caixa > summary::-webkit-details-marker { display: none; }
.seletor-meses-resumo {
  padding: 9px 32px 9px 12px;
  font-size: 0.875rem;
  line-height: normal;
  color: var(--text-primary);
  background: var(--bg-surface) var(--icone-chevron) no-repeat right 9px center / 15px 15px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.seletor-meses-caixa[open] > .seletor-meses-resumo,
.seletor-meses-resumo:focus-visible {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.seletor-meses-painel {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  width: max-content;
  /* Nome de fundo chega a 70 caracteres: sem teto, a caixa passaria da tela. */
  max-width: min(440px, calc(100vw - 32px));
  max-height: 320px;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 6px 4px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-pop);
}
.seletor-meses-arvore,
.seletor-meses-meses { list-style: none; margin: 0; padding: 0; }
.seletor-meses-meses { padding-left: 26px; }
.seletor-meses-linha { display: flex; align-items: center; gap: 2px; }
.seletor-meses-expandir {
  flex: 0 0 22px;
  height: 22px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
}
button.seletor-meses-expandir::before {
  content: '›';
  display: inline-block;
  font-size: 1.1rem;
  line-height: 1;
  transition: transform 0.12s ease;
}
button.seletor-meses-expandir[aria-expanded="true"]::before { transform: rotate(90deg); }
.seletor-meses-opcao {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 4px 4px;
  font-size: 0.875rem;
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
}
.seletor-meses-opcao:hover { background: var(--bg-hover); }
.seletor-meses-opcao { white-space: normal; }
.seletor-meses-busca { padding: 2px 4px 6px; }
.seletor-meses-busca input {
  width: 100%;
  padding: 6px 10px;
  font: inherit;
  font-size: 0.85rem;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
}
.seletor-meses-busca input:focus {
  outline: none;
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.seletor-meses-opcao input { accent-color: var(--brand-500); margin: 0; }
/* Até 22 meses de coluna: o nome do indicador quebrava em 3–4 linhas e triplicava a altura. */
.fundos-tabela-mensal td:first-child,
.fundos-tabela-mensal th:first-child { white-space: nowrap; }
/* Painel recolhível da Gestão de fundos: a faixa do título abre e fecha. */
.fundos-recolhivel > summary { cursor: pointer; list-style: none; }
.fundos-recolhivel > summary::-webkit-details-marker { display: none; }
.fundos-recolhivel > summary .panel-header-title::before {
  content: '▸';
  display: inline-block;
  width: 16px;
  color: var(--text-tertiary);
  transition: transform 0.12s ease;
}
.fundos-recolhivel[open] > summary .panel-header-title::before { transform: rotate(90deg); }
.fundos-recolhivel > summary > .panel-header { display: flex; flex-direction: column; }
.fundos-equipe-totais {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding-top: 0;
}
.fundos-equipe-totais > .lead-time-card { display: flex; flex-direction: column; gap: 4px; }
.fundos-recolhivel-convite {
  display: block;
  padding: 0 18px 14px;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--brand-600);
}
.fundos-recolhivel[open] .fundos-recolhivel-convite { display: none; }
/* Boletas por produto: o `display` das classes venceria o atributo `hidden`. */
[data-boletas-vistas][hidden],
[data-boletas-painel][hidden] { display: none; }
@media (max-width: 760px) {
  .fundos-equipe-totais { grid-template-columns: 1fr; }
}
/* Liquidez de cada fundo: caixa compacta em pílula. Sem `background`: o atalho apagaria
   o chevron de `select.input`. */
.fundos-liquidez-form { margin: 0; }
.fundos-liquidez-select,
.fundos-liquidez-tag {
  width: auto;
  min-width: 158px;
  padding: 4px 28px 4px 12px;
  font-size: 0.8rem;
  font-weight: 500;
  border: 1px solid var(--border-subtle);
  border-radius: 999px;
  background-position: right 8px center;
  background-size: 13px 13px;
}
.fundos-liquidez-tag { display: inline-block; min-width: 0; padding-right: 12px; }

/* Grade dos gráficos mês a mês: 2 por linha (1 no mobile), sem auto-fit —
   evita espremer os 5 numa linha só em telas largas. */
.mensal-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 18px;
}
/* Dentro do painel de "Resultado por campanha" os gráficos abrem colados nos cards. */
.roi-produtos-table-wrap .mensal-grid { margin-top: 14px; }
@media (max-width: 760px) {
  .mensal-grid { grid-template-columns: 1fr; }
}
/* Gráfico de série longa (Serviços e boletas: cross-sell desde jan/2024) na linha inteira. */
.mensal-grid > .mensal-grid-largo { grid-column: 1 / -1; }
.tabela-pesquisa { max-width: 320px; margin-bottom: 10px; }


/* Entrada suave do painel de mês a mês ao ser revelado (18/09/2026) — era um corte seco
   de `hidden` para visível. `[hidden]` vira `display:none`, que não tem o que animar; o
   truque é a ANIMAÇÃO rodar sozinha sempre que o elemento reentra no fluxo (o navegador
   trata a saída de `display:none` como entrada nova), sem precisar de classe nem JS —
   vale para as 17 telas que usam este painel, não só o Resumo Executivo. Curta, no mesmo
   tom das outras transições do material (0,12–0,2s). A regra global de
   `prefers-reduced-motion` (topo do arquivo) já zera a duração para quem desligou
   animação — não precisa de guarda própria aqui. */
@keyframes painel-mensal-entra {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

[data-mensal-panel]:not([hidden]) {
  animation: painel-mensal-entra 0.18s ease;
}

/* ==========================================================================
   Carrossel genérico (17/09/2026)
   ==========================================================================
   Nasceu em "Por onde começar" (tela inicial), pensado para qualquer lista de
   cards que hoje quebraria linha de forma desigual (o card ímpar sobrando
   sozinho, com metade da tela vazia do lado). GENÉRICO DE PROPÓSITO — zero
   referência a área, métrica ou tela — para outra lista de cards reusar sem
   copiar CSS.

   A ROLAGEM É NATIVA (`scroll-snap`), NÃO REIMPLEMENTADA EM JS: funciona por
   toque, trackpad e roda do mouse mesmo se o `layout.js` não rodar. Setas e
   bolinhas são REFORÇO, iniciadas por `data-carrossel` — sem depender de
   template novo, porque o conteúdo de cada card é diferente em cada uso.

   Contrato de marcação (ver `core/templates/core/inicio.html` para um caso):
     <div class="carrossel" data-carrossel>
       <button type="button" class="carrossel-seta carrossel-seta--prev"
               data-carrossel-anterior aria-label="Anterior">…svg…</button>
       <div class="carrossel-viewport">
         <div class="carrossel-trilha">
           <div class="carrossel-item">…card…</div>
         </div>
       </div>
       <button type="button" class="carrossel-seta carrossel-seta--next"
               data-carrossel-proximo aria-label="Próximo">…svg…</button>
     </div>
     <div class="carrossel-dots" data-carrossel-dots></div>

   As bolinhas são criadas pelo `layout.js`, não escritas no template — com 1
   item só não faria sentido nenhuma, e é o JS quem sabe contar.
   ========================================================================== */
.carrossel {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
}

.carrossel-viewport {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

.carrossel-viewport::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .carrossel-viewport { scroll-behavior: auto; }
}

.carrossel-trilha {
  display: flex;
  gap: 16px;
}

/* `display: flex` + `> .panel { flex: 1 }`: sem isso, o card mais curto (menos
   atalhos) ficaria com um vão transparente por baixo dentro do item, em vez de o
   PAINEL BRANCO ir até o fim — que é como a grade antiga já se comportava, porque
   ali o `.panel` era o próprio item esticado pelo grid. */
.carrossel-item {
  flex: 0 0 320px;
  display: flex;
  flex-direction: column;
  scroll-snap-align: start;
}

.carrossel-item > .panel {
  flex: 1;
}

@media (max-width: 560px) {
  /* 84vw e não 100%: uma lasca do próximo card à direita é o que avisa que dá
     para continuar rolando — card exato na largura da tela pareceria a lista
     inteira. */
  .carrossel-item { flex-basis: 84vw; }
}

.carrossel-seta {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface);
  color: var(--text-title);
  box-shadow: var(--shadow-card);
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}

.carrossel-seta svg {
  width: 16px;
  height: 16px;
}

.carrossel-seta:hover:not(:disabled) {
  border-color: var(--brand-400);
  color: var(--brand-600);
}

.carrossel-seta:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Quando os itens cabem inteiros na viewport, NENHUMA seta rola nada — as duas nascem
   `:disabled` pelo `layout.js`. Opacidade reduzida ainda deixa um círculo visível do
   lado de espaço vazio, o que lê como widget quebrado quando há poucos itens (achado em
   "Lead times do processo", Operação CMKT, 21/09/2026). Escondida de vez, a viewport
   ocupa o espaço que sobrava para as setas (`.carrossel` é `display: flex`, então filho
   ausente simplesmente não reserva lugar) e a lista de poucos itens lê como uma fileira
   comum, não como carrossel capenga. Quando os itens NÃO cabem, a classe nunca é
   aplicada (`layout.js`, `atualizar()`) e as setas continuam do jeito de sempre. */
.carrossel--coube-tudo .carrossel-seta {
  display: none;
}

/* Com poucos itens, empacotar à esquerda (o padrão do flex) deixa um vão vazio inteiro
   de um lado só — pareceu "faltando alguma coisa" mais do que "cabe tudo". Centralizado,
   o vão se reparte pros dois lados e a fileira lê como um conjunto pequeno DE PROPÓSITO,
   não como uma lista cortada. `.carrossel-trilha` já ocupa 100% da `.carrossel-viewport`
   por padrão (é `display: flex` em fluxo normal, não item flex de outro container) —
   `justify-content` não precisa de `min-width` extra pra ter espaço sobrando. Quando os
   itens NÃO cabem (`.carrossel--coube-tudo` ausente), não há espaço sobrando nenhum e
   `justify-content` fica sem efeito — os itens continuam left-to-right, roláveis, do
   jeito de sempre. */
.carrossel--coube-tudo .carrossel-trilha {
  justify-content: center;
}

@media (max-width: 720px) {
  /* Seta some, a rolagem por toque já resolve — num carrossel de celular a
     seta só rouba a largura de card que o polegar não precisa. O `layout.js`
     continua escrevendo em `.disabled` (elemento oculto, sem efeito nenhum);
     quem decide o esconder é este CSS, num lugar só. */
  .carrossel-seta { display: none; }
}

.carrossel-dots {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-top: 10px;
}

.carrossel-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease;
}

.carrossel-dot.is-ativo {
  background: var(--brand-600);
  transform: scale(1.3);
}

/* Mensal/Semanal em duas páginas — Cobertura de base por e-mail da Estratégia de campanha
   (18/09/2026, pedido do Gabriel). A 1ª VERSÃO REUSAVA O `.carrossel` (rolagem horizontal
   com `scroll-snap`) e QUEBROU: sem `min-width: 0` no item, o conteúdo (tabela larga, grade
   de cards) força o item a crescer mais que o `flex-basis`, e as duas páginas apareciam
   lado a lado, espremidas, em vez de uma por vez — captura de tela do Gabriel, 18/09/2026.
   Trocado por `hidden`/`display:none` simples: sem flexbox de largura variável, não tem
   como o item "vazar" para o lado do outro.

   A 2ª VERSÃO ainda tinha as duas setas como colunas de um `display:flex` de 3 filhos
   (seta, conteúdo, seta) — e ficou ESCROTA (captura de tela do Gabriel, 18/09/2026): a
   tabela é bem mais alta que os 34px do botão, então as duas setas ficavam no topo,
   distantes uma da outra e do conteúdo, sem ler como um par de navegação. Depois viraram
   dois círculos JUNTOS numa linha só, fora do fluxo do conteúdo — melhor, mas os dois
   círculos sem rótulo não diziam para ONDE cada um levava.

   HOJE CADA SETA CARREGA O NOME DO DESTINO (pedido do Gabriel, mesmo dia): "‹ Visão
   mensal" fixo à esquerda, "Visão semanal ›" fixo à direita — `justify-content:
   space-between` para cada um ficar na ponta que aponta. O rótulo NÃO troca com a página
   ativa: os dois nomeiam o DESTINO do clique (é para onde a seta leva), não o estado
   atual — e por isso a legenda "Visão mensal"/"Visão semanal" que ficava dentro de cada
   página (duplicada) saiu; o par de botões já é a legenda. `btn btn--link`, não
   `.carrossel-seta`: com texto, vira link com ícone — `.carrossel-seta` é círculo de
   ícone sozinho, feito para os 34px de um SVG, não para caber "Visão semanal" do lado. */
.cobertura-paginas-setas {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}

/* ==========================================================================
   Ícone de informação contextual (18/09/2026, corrigido no mesmo dia)
   ==========================================================================
   Primeiro caso de uso: as ressalvas de método da Estratégia de campanha
   (`estrategia_campanha.html`), que viviam como parágrafo permanente embaixo dos cards —
   texto correto, mas poluição visual. GENÉRICO DE PROPÓSITO, como o carrossel acima:
   zero referência a métrica ou tela, para qualquer indicador da aplicação reusar.

   POR QUE NÃO É SÓ O `title` NATIVO DO HTML: este projeto já tentou depender só de
   `title` na Saúde da base e teve de voltar atrás — "`title` só aparece depois de ~1s
   parado, não existe em toque, e informação que depende de descobrir um gesto não está
   publicada" (`_badge_fonte.html`). O ícone "i", visível e com cursor de clique, é o
   próprio convite; `title` dependia de a pessoa descobrir que havia algo ali.

   A 1ª VERSÃO USAVA `position: absolute` E REVELAVA NO HOVER — E QUEBROU NO PRIMEIRO
   USO REAL, dentro de um `.panel` (captura de tela do Gabriel, 18/09/2026):
     · `absolute` fica preso à CAIXA do ancestral posicionado mais próximo, e `.panel`
       tem `overflow: hidden` (é o que dá o canto arredondado a todo card do material).
       O card "cortava" na borda do PAINEL, não na borda da TELA — undefined
       exatamente o "não seja cortado pela viewport" que o pedido corrigido proíbe.
     · HOVER e CLIQUE eram dois mecanismos de exibição independentes — nada
       coordenava os dois. Passar o mouse num ícone enquanto outro estava aberto por
       clique deixava DOIS cards visíveis ao mesmo tempo, cada um sem saber do outro.

   A CORREÇÃO: `position: fixed`, com `top`/`left` calculados em JS
   (`ligarInfoTooltips`, `layout.js`) a partir do ícone (`getBoundingClientRect`).
   `fixed` ESCAPA de `overflow: hidden` de qualquer ancestral — é relativo à JANELA,
   não à caixa do painel —, e é isso que permite o card flutuar por CIMA de tudo,
   inclusive saindo da área do próprio painel, sem empurrar nem redimensionar nada ao
   redor (nenhuma das duas propriedades participa do fluxo do layout).

   SÓ CLIQUE ABRIA E FECHAVA depois do conserto acima — hover tinha sido RETIRADO da
   revelação do card. Era o hover em CSS puro (`:hover { display: block }` no card,
   sem passar pelo JS) que causava os dois cards simultâneos: ele não coordenava com o
   `open` do `<details>` nem com o `_tooltipAberto` do JS, então dava pra ter um aberto
   por clique e outro por hover ao mesmo tempo, cada um sem saber do outro.

   HOVER DE MOUSE VOLTOU no mesmo dia, mais tarde (pedido do Gabriel: "se eu passar o
   mouse já deve aparecer"), mas dessa vez ESCREVENDO no mesmo `open` que o clique
   controla, em vez de reveler o card por fora dele — ver `ligarInfoTooltips` no
   layout.js (`pointerenter`/`pointerleave`, filtrados por `pointerType === 'mouse'`
   para não competir com o toque). Como as duas entradas mexem no MESMO estado, o
   `toggle` que já fecha "um aberto por vez" vale para as duas igual — não tem como
   reabrir o bug de dois cards simultâneos, porque não existe mais um caminho de
   revelação que não passe por ali. `<details>`/`<summary>` continua dando teclado de
   graça (Enter/Espaço no summary abre e fecha) — o que o JS acrescenta é caber na
   tela, fechar ao clicar fora, abrir/fechar no hover do mouse e não deixar dois abertos.
   ========================================================================== */
.info-tooltip {
  position: relative;
  display: inline-flex;
  vertical-align: middle;
}

.info-tooltip-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  list-style: none;
  user-select: none;
  transition: border-color 0.12s ease, background-color 0.12s ease, color 0.12s ease;
}

.info-tooltip-icone::-webkit-details-marker { display: none; }

/* Só o ÍCONE reage ao hover (cor), nunca o card — ver o porquê acima. */
.info-tooltip-icone:hover,
.info-tooltip[open] .info-tooltip-icone {
  background: var(--sage-200);
  border-color: var(--brand-400);
  color: var(--brand-700);
}

/* `display` fica em `[open]`, mas QUEM ABRE/FECHA É O JS (`layout.js`), nunca só o
   clique nativo do `<summary>` — sem o JS coordenando, nada garante um card só aberto
   por vez nem a posição calculada. `top`/`left` são preenchidos por `posicionarInfoTooltip`
   a cada abertura; os valores no CSS são só o estado inicial antes do JS calcular. */
.info-tooltip-card {
  display: none;
  position: fixed;
  z-index: 70;
  top: 0;
  left: 0;
  width: min(320px, calc(100vw - 32px));
  max-height: min(360px, calc(100vh - 32px));
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  padding: 10px 12px;
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--text-secondary);
  /* Texto SEMPRE completo — nada de line-clamp/truncamento. Extenso demais rola
     dentro do card (`overflow-y` acima), sem crescer sem limite pela tela. */
  white-space: normal;
}

.info-tooltip[open] .info-tooltip-card {
  display: block;
}

/* Variante de aviso (`tom="aviso"`): mesmo comportamento, cor de atenção. */
.info-tooltip--aviso .info-tooltip-icone {
  background: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn-text);
}

.info-tooltip--aviso .info-tooltip-icone:hover,
.info-tooltip--aviso[open] .info-tooltip-icone {
  background: var(--warn-bg);
  border-color: var(--warn);
  color: var(--warn-text);
}

.info-tooltip--aviso .info-tooltip-card {
  border-left: 3px solid var(--warn);
}

/* Contagem curta com a ressalva no ícone de aviso ao lado. */
.nota-aviso {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 10px 0 0;
  font-size: 0.8rem;
  color: var(--warn-text);
}

/* ==========================================================================
   KPIs e blocos de número
   ========================================================================== */
.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
  gap: 14px;
  margin-bottom: 18px;
}

.stat-box,
.engajamento-card,
.lead-time-card {
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Cards dentro de um painel não repetem a sombra do painel — caixa com sombra
   dentro de caixa com sombra é o que faz uma tela parecer empilhada. */
.panel-body .stat-box,
.panel-body .engajamento-card,
.panel-body .lead-time-card {
  box-shadow: none;
}

/* Sparkline no rodapé: card com linha a mais (meta, delta) empurrava o gráfico dele para
   baixo dos vizinhos da mesma fileira. */
.lead-time-card > .chart-box:last-child {
  margin-top: auto;
}

.stat-label {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* No card de KPI o selo fica AO LADO do rótulo, e o rótulo longo é que quebra. Com o
   `wrap` do título de painel, "Captação atribuída por real de disparo" mandava o selo para
   uma linha própria, e o número desse card descia ~46px em relação aos vizinhos. */
.stat-box > .panel-header-title-row,
.lead-time-card > .panel-header-title-row {
  flex-wrap: nowrap;
  align-items: flex-start;
}

.stat-box > .panel-header-title-row > .stat-label,
.lead-time-card > .panel-header-title-row > .stat-label {
  min-width: 0;
}

.stat-box > .panel-header-title-row > .badge,
.lead-time-card > .panel-header-title-row > .badge {
  flex-shrink: 0;
}

.stat-value {
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
}

.stat-value--lg {
  font-size: 1.75rem;
  font-weight: 600;
  letter-spacing: -0.025em;
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
}

.stat-delta {
  font-size: 0.78rem;
  font-weight: 500;
}

.stat-delta.delta-bom { color: var(--good); }
.stat-delta.delta-mau { color: var(--bad); }

/* Janela do card acumulado ("acumulado jan-ago/26"), logo abaixo do valor.

   NAO E `.stat-delta` SEM COR: o delta e uma COMPARACAO e o periodo e uma legenda do
   proprio numero. Reusar a classe faria a proxima pessoa procurar delta-bom/delta-mau
   aqui e concluir que faltou pintar. Tom secundario porque ela qualifica o valor, nao
   compete com ele. */
.stat-periodo {
  font-size: 0.74rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.stat-delta-pill {
  display: inline-block;
  border-radius: var(--radius-pill);
  padding: 3px 10px;
  font-size: 0.76rem;
  font-weight: 500;
}

.stat-delta-pill.delta-bom { background: var(--good-bg); color: var(--good); }
.stat-delta-pill.delta-mau { background: var(--bad-bg); color: var(--bad); }

/* Variação contra o mês anterior (`core/_stat_delta.html`): seta e % na cor do resultado
   e o mês de referência ao lado. O card quebra em linhas para a seta sentar AO LADO do
   valor; todo filho que não é valor nem seta continua ocupando a linha inteira. */
.stat-box:has(> .stat-variacao),
.engajamento-card:has(> .stat-variacao) {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  /* Sem isto as linhas se espalham pela altura do card, e o rótulo desce em relação
     ao card vizinho da mesma fileira. */
  align-content: flex-start;
  column-gap: 10px;
}

.stat-box:has(> .stat-variacao) > *,
.engajamento-card:has(> .stat-variacao) > * {
  flex-basis: 100%;
}

.stat-box:has(> .stat-variacao) > .stat-value,
.stat-box:has(> .stat-variacao) > .stat-value--lg,
.engajamento-card:has(> .stat-variacao) > .engajamento-card-value,
.stat-box > .stat-variacao,
.engajamento-card > .stat-variacao {
  flex-basis: auto;
}

.stat-variacao {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  cursor: default;
}

.stat-delta-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.2;
}

.stat-delta-chip.delta-bom { color: var(--good); }
.stat-delta-chip.delta-mau { color: var(--bad); }

.stat-delta-seta {
  font-size: 0.66rem;
  line-height: 1;
}

.stat-delta-ref {
  font-size: 0.74rem;
  color: var(--text-tertiary);
}

.stat-pair {
  display: flex;
  gap: 28px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border-subtle);
}

/* ROI de produtos */
/* `.roi-produtos-body`, `.roi-produtos-stats` e `.roi-stat-block` SAÍRAM em 19/08/2026.
   Os KPIs de ROI passaram a usar o `engajamento-card`, que é o mesmo componente do painel
   de Engajamento e já traz caixa, hover, foco e o caret — três regras a menos para manter
   em sincronia com aquele. O que sobrou aqui é só o que é específico deste painel. */

.roi-custo-caption {
  font-size: 0.78rem;
  color: var(--text-secondary);
}

/* A tabela agora fica EMBAIXO dos KPIs, em largura cheia — o `flex: 1` de quando ela dividia
   a linha com a coluna de KPIs não tem mais função, e o `min-width` de 320px só serviria
   para forçar rolagem horizontal em tela estreita. Quem rola é o `.table-scroll` de dentro. */
.roi-produtos-table-wrap {
  min-width: 0;
}

/* KPIs de status da aba Projetos */
.projeto-kpis {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 14px;
  margin-bottom: 20px;
}

@media (max-width: 860px) { .projeto-kpis { grid-template-columns: repeat(2, 1fr); } }

.projeto-kpi {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  box-shadow: var(--shadow-card);
  padding: clamp(10px, 1.8vh, 18px) 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  text-align: center;
}

/* O total era o único bloco verde chapado da tela. Sage mantém a ênfase sem
   reintroduzir a faixa escura que saiu de todo o resto do site. */
.projeto-kpi--total {
  background: var(--sage-100);
  border-color: var(--sage-300);
}

.projeto-kpi-valor {
  font-size: 2rem;
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--text-title);
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.projeto-kpi--total .projeto-kpi-valor { color: var(--text-title); }

.projeto-kpi-label {
  font-size: 0.78rem;
  color: var(--text-secondary);
}

.projeto-kpi--total .projeto-kpi-label { color: var(--text-secondary); }

/* Engajamento de e-mail */
.engajamento-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
  gap: 14px;
}

.engajamento-card--alerta {
  background: var(--bad-bg);
  border-color: var(--bad-border);
}

.engajamento-card-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 8px;
}

.engajamento-card-title {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text-title);
}

.engajamento-card-value {
  font-size: 1.2rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
}

.engajamento-card-caption {
  font-size: 0.74rem;
  line-height: 1.45;
  color: var(--text-secondary);
}

.engajamento-card-caret {
  display: inline-block;
  font-size: 0.7rem;
  color: var(--text-secondary);
  transition: transform 0.16s ease;
}

.engajamento-card.is-open .engajamento-card-caret {
  transform: rotate(180deg);
}

/* Padrão reutilizável de CARD INTERATIVO (clicável que revela/alterna algo).
   Aplicar `card-interativo` no card; o JS marca `is-open` quando ativo. */
.card-interativo {
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease, box-shadow 0.12s ease;
}

.card-interativo:hover {
  border-color: var(--brand-300);
  background-color: var(--warm-50);
}

.card-interativo:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 1px;
}

/* Estado ATIVO: sage, a mesma cor de "selecionado" da sidebar e das abas. */
.card-interativo.is-open {
  border-color: var(--sage-300);
  background-color: var(--sage-100);
}

/* Lead times / linhas de KPI dentro de painel */
.lead-time-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
  gap: 12px;
}

/* Variante para linha com POUCOS cards (2 ou 3). `.lead-time-row` usa auto-fit
   com 1fr, então com 2 cards cada um estica para meia tela e o painel fica com
   dois retângulos gigantes e vazios. O teto de largura impede o esticamento e o
   justify-content alinha à esquerda em vez de distribuir. */
.lead-time-row--compacta {
  grid-template-columns: repeat(auto-fit, minmax(190px, 260px));
  justify-content: start;
}

.lead-time-card--mau {
  background: var(--bad-bg);
  border-color: var(--bad-border);
}

/* Barra de proporção da Composição da base (Relacionamento, EQI Digital): visualiza os
   MESMOS três números dos cards de cima, um ao lado do outro — não é métrica nova, é a
   mistura da base (promotor/neutro/detrator), nunca um índice único. */
.composicao-barra {
  display: flex;
  height: 10px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--warm-200);
  margin-top: 16px;
}

.composicao-barra-seg--promotores { background: var(--chart-good); }
.composicao-barra-seg--neutros { background: var(--chart-muted); }
.composicao-barra-seg--detratores { background: var(--chart-bad); }

.composicao-barra-legenda {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 8px;
  font-size: 0.78rem;
  color: var(--text-secondary);
}

.composicao-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 5px;
}

.composicao-dot--promotores { background: var(--chart-good); }
.composicao-dot--neutros { background: var(--chart-muted); }
.composicao-dot--detratores { background: var(--chart-bad); }

/* Cards de lead time da Operação CMKT, em CARROSSEL — não mais em `.lead-time-row`
   (21/09/2026). Histórico: a grade quebrava linha sozinha e a última linha ficava com 2
   cards perdidos numa faixa quase vazia; as quatro tentativas anteriores tentavam
   consertar isso SEPARANDO "principais" de "resto" (esticado = "quadradão"; estreito com
   seta apagada = "escroto"; estreito com seta escondida = ainda ruim) — todas rejeitadas
   nas capturas do Gabriel, porque o problema nunca foi o tratamento do "resto", foi a
   quebra de linha em si. Tirando TODOS os cards do container fixo (grid) e pondo num
   `.carrossel` único (mesmo componente da tela Início — ver o contrato dele mais abaixo
   neste arquivo), não existe mais "última linha": ou cabe tudo (aí `.carrossel--coube-tudo`
   esconde as setas e centraliza, ver a regra dela) ou rola, sem nunca sobrar item
   solto numa fileira vazia.

   `min-width: 0` É OBRIGATÓRIO. Sem `width`/`height` por atributo, o tamanho intrínseco
   padrão de um `<canvas>` é 300×150px, e o default `min-width: auto` de item flex usa
   esse intrínseco como piso, ignorando `flex-basis`/`flex-shrink: 0` (bug já medido e
   corrigido aqui uma vez; sem esta regra ele volta). */
.lead-time-carrossel-item {
  min-width: 0;
  flex: 0 0 190px;
}

/* Sem isso, o card com rótulo de 2 linhas ("Taxa de sucesso no envio de teste") fica
   mais alto que os vizinhos e o pé da fileira desalinha — mesma razão do
   `.carrossel-item > .panel { flex: 1 }` de baixo, mas `.lead-time-card` não é `.panel`. */
.lead-time-carrossel-item > .lead-time-card {
  flex: 1;
}

.combo-headline,
.ctor-headline {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 4px 0 10px;
}

/* ==========================================================================
   Tabelas
   ==========================================================================
   Cabeçalho claro sobre fundo morno, rótulo mudo, sem versalete e sem zebra —
   a zebra e o `<th>` verde-escuro somavam dois padrões de listra a uma tela que
   no material é limpa. A separação vem de hairline por linha e realce no hover.
   ========================================================================== */
.data-table,
.matriz-tabela {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.data-table th,
.matriz-tabela thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-inset);
  text-align: left;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-secondary);
  padding: 10px 12px;
  white-space: nowrap;
  border-bottom: 1px solid var(--border-subtle);
}

/* Padding lateral fluido: em 1280px as matrizes de 10–12 colunas ganham ~50px antes de
   precisar rolar; em monitor largo volta aos 12px. */
.data-table th,
.data-table td,
.matriz-tabela thead th,
.matriz-tabela td {
  padding-left: clamp(8px, 0.5vw + 3px, 12px);
  padding-right: clamp(8px, 0.5vw + 3px, 12px);
}

.data-table td,
.matriz-tabela td {
  padding-top: 10px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--warm-200);
  color: var(--text-primary);
  vertical-align: middle;
}

.data-table tbody tr:last-child td,
.matriz-tabela tbody tr:last-child td {
  border-bottom: none;
}

.data-table tbody tr,
.matriz-tabela tbody tr {
  transition: background-color 0.1s ease;
}

.data-table tbody tr:hover td,
.matriz-tabela tbody tr:hover td,
.matriz-tabela tbody tr:hover th.matriz-indicador {
  background: var(--warm-50);
}

/* Cabeçalho ordenável de "Campanhas do mês" (21/09/2026) — um `<a>` dentro do `<th>`,
   não um botão com JS: é GET simples (`?ordenar=`), funciona sem JS e a troca local já
   intercepta clique em link do mesmo caminho. `color: inherit` porque o `<th>` já define
   a cor neutra do cabeçalho; sem isso o link saía azul-padrão do navegador, fora da
   paleta. Peso e cor mudam só na coluna ATIVA — sem isso, ficaria impossível dizer qual
   das seis colunas está ordenando a tabela sem clicar em cada uma. */
.th-ordenar {
  color: inherit;
  text-decoration: none;
}

.th-ordenar:hover {
  color: var(--brand-700);
}

.th-ordenar.is-ativo {
  color: var(--brand-700);
  font-weight: 700;
}

/* Variante com valores centralizados (1ª coluna fica à esquerda) */
.data-table--centro th:not(:first-child),
.data-table--centro td:not(:first-child) {
  text-align: center;
}

/* Coluna numérica. A classe já era usada em 10 lugares nos templates e NÃO
   existia no CSS — número ficava alinhado à esquerda como texto, então dígito
   de milhar não batia com dígito de milhar na coluna. */
.data-table th.num,
.data-table td.num,
.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* Rolagem horizontal por tabela. `.tablewrap` também era classe fantasma nos
   templates da EQI Digital, com o overflow escrito em `style=` inline. */
.matriz-scroll,
.tablewrap,
.n3-tabela-wrap {
  overflow-x: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--warm-400) transparent;
}

.matriz-scroll::-webkit-scrollbar,
.tablewrap::-webkit-scrollbar,
.n3-tabela-wrap::-webkit-scrollbar,
.table-scroll::-webkit-scrollbar {
  height: 9px;
  width: 9px;
}

.matriz-scroll::-webkit-scrollbar-track,
.tablewrap::-webkit-scrollbar-track,
.n3-tabela-wrap::-webkit-scrollbar-track,
.table-scroll::-webkit-scrollbar-track {
  background: var(--warm-200);
  border-radius: var(--radius-pill);
}

.matriz-scroll::-webkit-scrollbar-thumb,
.tablewrap::-webkit-scrollbar-thumb,
.n3-tabela-wrap::-webkit-scrollbar-thumb,
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--warm-400);
  border-radius: var(--radius-pill);
}

/* --------------------------------------------------------------------------
   Botão de copiar tabela
   --------------------------------------------------------------------------
   A caixa e o botão são criados pelo layout.js — são 31 tabelas em 12 arquivos,
   e escrever a marcação em cada uma deixaria a que fosse esquecida sem o botão,
   sem nenhum aviso.

   Só aparece no hover/foco da tabela: um botão permanente em 31 tabelas viraria
   ruído numa tela que é para ser lida, não operada.
   -------------------------------------------------------------------------- */
.tabela-acoes {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}

/* Na linha do título: `margin-left: auto` empurra para a borda direita mesmo quando o
   cabeçalho só tem o título; a nota (`order: 3`) continua na linha de baixo. */
.tabela-copiar--cabecalho {
  margin-left: auto;
  align-self: center;
}

.tabela-copiar {
  padding: 3px 9px;
  border: 1px solid var(--border-subtle);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text-secondary);
  font-family: inherit;
  font-size: 0.72rem;
  font-weight: 500;
  line-height: 1.5;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.14s ease, border-color 0.14s ease, color 0.14s ease;
}

/* `:focus-within` junto do hover: quem navega por teclado precisa alcançar o
   botão, e com `opacity: 0` ele existe mas não é visível ao receber o foco. */
.tabela-caixa:hover .tabela-copiar,
.tabela-caixa:focus-within .tabela-copiar,
.panel:hover .tabela-copiar--cabecalho,
.panel:focus-within .tabela-copiar--cabecalho {
  opacity: 1;
}

.tabela-copiar:hover {
  border-color: var(--border-strong);
  color: var(--text-title);
}

.tabela-copiar.is-feito {
  opacity: 1;
  border-color: var(--good-border);
  background: var(--good-bg);
  color: var(--good);
}

/* Legenda abaixo do valor nos cards de "Resultado por campanha" (`.stat-box`). */
.produtos-kpi-nota {
  font-size: 0.75rem;
  color: var(--text-secondary);
}

/* Na impressão o botão não existe — o Relatório N3 vai impresso para a reunião. */
@media print {
  .tabela-acoes, .tabela-copiar { display: none !important; }
}

/* 280px é o piso do notebook; em monitor alto a caixa mostra mais linhas antes de rolar. */
.table-scroll {
  max-height: clamp(280px, 44vh, 480px);
  overflow-y: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  scrollbar-width: thin;
  scrollbar-color: var(--warm-400) transparent;
}

/* Vínculo campanha × produto: até ~25 linhas de FORMULÁRIO, não de leitura — a altura
   padrão de 280px (~1-2 linhas desta tabela, que é mais alta que as outras por ter
   campo + caixa "Sem campanha" empilhados na célula) obrigava a rolar por dentro da
   caixa toda hora. Modificador em vez de mudar `.table-scroll` direto: a base é usada
   em 31 tabelas, e a maioria delas é exibição curta. 1100px mostra ~5 produtos por vez
   (pedido do Gabriel, 13/09/2026 — 640px mostrava só 3). */
.table-scroll--alta {
  max-height: 1100px;
}

/* Matriz Resultado × Objetivo */
.matriz-tabela {
  min-width: 620px;
}

.matriz-tabela thead th {
  text-align: center;
}

.matriz-tabela td {
  text-align: center;
  white-space: nowrap;
}

/* 1ª coluna (nome do indicador) fixa à esquerda */
.matriz-tabela th.matriz-indicador {
  position: sticky;
  left: 0;
  z-index: 2;
  text-align: left;
  font-weight: 500;
  font-size: 0.85rem;
  padding: 10px 14px;
  background: var(--bg-surface);
  color: var(--text-primary);
  border-bottom: 1px solid var(--warm-200);
}

.matriz-tabela thead th.matriz-indicador {
  z-index: 3;
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 0.76rem;
  font-weight: 600;
  border-bottom: 1px solid var(--border-subtle);
}

/* O ÍCONE "i" (ver `_info_tooltip.html`) DENTRO DE `.matriz-indicador` ficava com o card
   cortado pelas linhas seguintes — achado em 21/09/2026 renderizando de verdade
   ("Disparos por cliente" em "Resultado por" saía com o texto do tooltip picotado).
   O card é `position: fixed` (relativo à JANELA), mas continua PINTADO dentro do
   stacking context do `<th>` que o contém — `position: sticky` + `z-index` no ancestral
   cria um. Como toda `.matriz-indicador` tem o MESMO z-index (2), a `<th>` de baixo
   (depois no DOM) ganha o empate e o FUNDO OPACO dela (`background: var(--bg-surface)`
   acima) cobre o card da linha de cima, mesmo o card "aparecendo" mais abaixo na tela.

   `:has()` sobe o z-index só da CÉLULA QUE TEM O TOOLTIP ABERTO — as outras continuam
   no mesmo z-index de sempre, então isto não muda a pilha de nada que não tem tooltip
   aberto, e nenhuma outra tabela do projeto usa `.info-tooltip` dentro de célula sticky
   (as demais vivem em `<td>` comum, sem esse conflito). */
.matriz-tabela th.matriz-indicador:has(.info-tooltip[open]) {
  z-index: 10;
}

/* Linhas ainda sem fonte de dado: mudo + itálico */
.matriz-linha--pendente td,
.matriz-linha--pendente th.matriz-indicador {
  color: var(--text-tertiary);
  font-style: italic;
}

.text-mau { color: var(--bad); font-weight: 500; }
.text-bom { color: var(--good); font-weight: 500; }

/* ==========================================================================
   Selos e pills de estado
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  padding: 2px 9px;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.6;
  white-space: nowrap;
  border: 1px solid transparent;
}

.badge--ok,
.badge--real {
  background: var(--good-bg);
  color: var(--good);
  border-color: var(--good-border);
}

.badge--alerta {
  background: var(--bad-bg);
  color: var(--bad);
  border-color: var(--bad-border);
}

.badge--ficticio {
  background: var(--warn-bg);
  color: var(--warn-text);
  border-color: var(--warn-border);
}

/* Painel que mistura fonte real com placeholder. Cor própria (azul, fora da
   escala verde/âmbar) porque não é um meio-termo de severidade: é uma categoria
   diferente, e o leitor precisa notar que tem que olhar quais colunas são
   quais. */
.badge--parcial {
  background: var(--info-bg);
  color: var(--info);
  border-color: var(--info-border);
  cursor: help;
}

.status-pill,
.n3-status {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  font-size: 0.74rem;
  font-weight: 500;
  white-space: nowrap;
  border: 1px solid transparent;
}

.status-pill--concluido,
.n3-status--concluido { background: var(--good-bg); color: var(--good); border-color: var(--good-border); }
.status-pill--em_andamento { background: var(--sage-200); color: var(--text-title); border-color: var(--sage-300); }
.n3-status--em_andamento { background: var(--sage-200); color: var(--text-title); border-color: var(--sage-300); }
.status-pill--parado,
.n3-status--parado { background: var(--warn-bg); color: var(--warn-text); border-color: var(--warn-border); }
.status-pill--cancelado,
.n3-status--cancelado { background: var(--bad-bg); color: var(--bad); border-color: var(--bad-border); }

.empty-tag {
  background: var(--sage-200);
  color: var(--text-title);
  border-radius: var(--radius-pill);
  padding: 3px 12px;
  font-size: 0.74rem;
  font-weight: 500;
}

/* ==========================================================================
   Avisos e estados vazios
   ==========================================================================
   Este é o componente do modal de referência ("Escolha um lugar de cada vez"):
   caixa morna sem borda gritante, ícone à esquerda, título em negrito e corpo
   mudo abaixo. Um estado vazio é RESPOSTA, não falha — por isso ele tem a forma
   de uma afirmação, e não de um vão onde algo deveria estar.

   `background-color`, NUNCA o atalho `background`, nas regras que carregam
   ícone. O atalho reseta `background-repeat`, `-position` e `-size` para os
   valores iniciais, e como as variantes vêm depois na cascata, o atalho apagava
   os longhands: o ícone voltava a `repeat` em tamanho automático e virava uma
   FILEIRA de triângulos atravessando o painel, por cima do texto.
   ========================================================================== */
.aviso,
.panel-vazio {
  color: var(--text-primary);
  font-size: 0.85rem;
  line-height: 1.55;
  margin: 4px 0 0;
  padding: 14px 16px 14px 44px;
  border-radius: var(--radius-md);
  border: 1px solid var(--sage-300);
  background-color: var(--sage-100);
  background-image: var(--icone-ok);
  background-repeat: no-repeat;
  background-position: 16px 15px;
  background-size: 17px 17px;
}

/* Variante para quando o vazio É um problema (falta fonte, falta configuração)
   — mesma caixa, cor e ícone de atenção, para não dar sinal verde a algo que
   precisa de ação. */
.aviso--atencao,
.panel-vazio--atencao {
  background-color: var(--warn-bg);
  border-color: var(--warn-border);
  color: var(--warn-text);
  background-image: var(--icone-atencao);
}

/* Falha que exige ação (ETL agendado que não concluiu). */
.aviso--erro {
  background-color: var(--bad-bg);
  border-color: var(--bad-border);
  color: var(--text-primary);
  background-image: var(--icone-atencao);
}

/* Variante informativa (neutra): explica sem afirmar que está tudo bem nem que
   há problema. É a forma certa para os blocos de instrução do BI CMKT. */
.aviso--info {
  background-color: var(--info-bg);
  border-color: var(--info-border);
  color: var(--text-primary);
  background-image: var(--icone-info);
}

/* Dentro do aviso os parágrafos não ganham margem própria: quando há mais de um
   (título + explicação), as margens dobravam e a caixa ficava desproporcional. */
.aviso p,
.panel-vazio p { margin: 0 0 6px; }
.aviso p:last-child,
.panel-vazio p:last-child { margin-bottom: 0; }

.aviso strong,
.panel-vazio strong {
  color: inherit;
  font-weight: 600;
}

/* Etapa atual de uma rotina em segundo plano (ex.: "Resultado por campanha" enquanto
   `atualizar_influencia` roda) — mesma escala do `.produtos-kpi-nota`, para marcar que
   é status auxiliar, não a explicação principal do aviso. */
.aviso-etapa {
  display: inline-block;
  margin-top: 4px;
  font-size: 0.8rem;
  color: var(--text-secondary);
  font-style: italic;
}

.aviso ol,
.aviso ul,
.panel-vazio ol,
.panel-vazio ul {
  margin: 6px 0 0;
  padding-left: 20px;
}

.aviso li,
.panel-vazio li { margin-bottom: 4px; }

/* Estado vazio centralizado, com borda TRACEJADA — a forma que o material usa
   nas colunas do kanban ("Sem oportunidades") e nos blocos do Follow Up. O
   tracejado diz "aqui cabe conteúdo e não há", que é diferente do que uma caixa
   sólida diz. O ícone entra por `background-image` para não exigir marcação
   nova nos lugares que já usam a classe. */
.panel-body--empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 52px 20px 28px;
  margin: 0;
  color: var(--text-secondary);
  text-align: center;
  font-size: 0.875rem;
  background-color: var(--bg-inset);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
  background-image: var(--icone-vazio);
  background-repeat: no-repeat;
  background-position: center 20px;
  background-size: 26px 26px;
}

.panel-body--empty p { margin: 0; }

/* O painel vazio recebe o estado dentro do corpo, então o corpo não pode ter
   padding próprio nem borda de card duplicada. */
.panel--empty .panel-body--empty {
  margin: 0 18px 18px;
}

/* Alertas do mês — cartões de severidade. Mesma linguagem do aviso: caixa
   morna, título negrito, corpo mudo. */
.alerts-section {
  margin-top: 26px;
}

.alerts-title {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text-title);
  margin: 0 0 14px;
}

.alerts-periodo {
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--text-secondary);
}

.alerts-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
  gap: 14px;
}

.alert-card {
  border-radius: var(--radius-md);
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.82rem;
  line-height: 1.5;
  border: 1px solid transparent;
}

.alert-card strong {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
}

.alert-card span {
  color: var(--text-secondary);
}

.alert-card--critico,
.alert-card--alta {
  background: var(--bad-bg);
  border-color: var(--bad-border);
}

.alert-card--critico strong,
.alert-card--alta strong {
  color: var(--bad);
}

.alert-card--atencao {
  background: var(--warn-bg);
  border-color: var(--warn-border);
}

.alert-card--atencao strong {
  color: var(--warn-text);
}

/* ==========================================================================
   Indicador de frescor dos dados (topbar)
   ==========================================================================
   Estado vem de FonteDados.situacao no model; aqui é só a forma. O ponto de cor
   nunca aparece sozinho: sempre acompanha texto ("falhou há 2 horas", "sem
   integração"), para não depender de percepção de cor.
   ========================================================================== */
.frescor {
  position: relative;
}

.frescor-resumo {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 11px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  font-size: 0.8rem;
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
  white-space: nowrap;
}

.frescor-resumo::-webkit-details-marker {
  display: none;
}

.frescor-resumo:hover {
  background: var(--bg-inset);
  border-color: var(--border-strong);
  color: var(--text-title);
}

.frescor[open] .frescor-resumo {
  background: var(--bg-inset);
  border-color: var(--brand-300);
  color: var(--text-title);
}

.frescor-caret {
  width: 12px;
  height: 12px;
  transition: transform 0.16s ease;
}

.frescor[open] .frescor-caret {
  transform: rotate(180deg);
}

.frescor-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  flex-shrink: 0;
  background: var(--text-tertiary);
}

.frescor-dot--ok { background: var(--good); }
.frescor-dot--atrasada { background: var(--warn); }
.frescor-dot--erro { background: var(--bad); }
.frescor-dot--rodando { background: var(--brand-400); }
.frescor-dot--nao_configurada { background: var(--warm-400); }

.frescor-painel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 40;
  width: 350px;
  /* O painel vive dentro da topbar, que GRUDA: o que passar da borda de baixo da janela
     não é alcançável rolando a página, porque o painel sobe junto. Com 10 fontes e
     590px úteis o rodapé do painel ficava fora da tela. */
  max-height: calc(100vh - 96px);
  overflow-y: auto;
  padding: 14px 16px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
}

.frescor-painel-titulo {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-title);
  margin-bottom: 10px;
}

.frescor-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.frescor-item {
  display: grid;
  grid-template-columns: 7px auto 1fr auto;
  grid-template-areas:
    "dot nome pontilhado valor"
    ".   nota nota        nota";
  align-items: center;
  gap: 0 8px;
  font-size: 0.82rem;
}

.frescor-item .frescor-dot { grid-area: dot; }

.frescor-nome {
  grid-area: nome;
  color: var(--text-title);
  font-weight: 500;
}

.frescor-pontilhado {
  grid-area: pontilhado;
  border-bottom: 1px dotted var(--border-strong);
  transform: translateY(-2px);
}

.frescor-valor {
  grid-area: valor;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.frescor-nota {
  grid-area: nota;
  font-size: 0.72rem;
  color: var(--text-secondary);
  margin-top: 1px;
}

.frescor-painel-rodape {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Mês em andamento: pílula de atenção ao lado do alerta do ETL, na altura dos botões
   da topbar. `display: contents` no invólucro para o `<span>` vazio não abrir um
   `gap` a mais entre os ícones. */
.topbar-mes {
  display: contents;
}

.mes-andamento {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 32px;
  padding: 0 11px;
  border: 1px solid var(--warn-border);
  border-radius: var(--radius-sm);
  background: var(--warn-bg);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--warn-text);
  white-space: nowrap;
}

.mes-andamento::before {
  content: '';
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  background-color: var(--warn);
  -webkit-mask: var(--icone-atencao) center / contain no-repeat;
  mask: var(--icone-atencao) center / contain no-repeat;
}

/* Abaixo de 1440px a frase inteira empurra as sub-abas para a segunda linha (o modo da
   topbar mede `.topbar-actions`) e, mais estreito, invade o título: fica só "Dia 19/31".
   `font-size: 0` (e não `display: none`) mantém a frase para leitor de tela. */
@media (max-width: 1440px) {
  .mes-andamento { font-size: 0; }
  .mes-andamento::after {
    content: attr(data-curto);
    font-size: 0.8rem;
  }
}

/* Alerta do ETL agendado que não concluiu: ícone ao lado do seletor de frescor,
   texto completo ao clicar. Máscara (e não background-image) para pintar o ícone
   de atenção com `--bad`, que muda no tema escuro. */
.alerta-etl {
  position: relative;
}

.alerta-etl-icone {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--bad-border);
  border-radius: var(--radius-sm);
  background: var(--bad-bg);
  cursor: pointer;
  list-style: none;
}

.alerta-etl-icone::-webkit-details-marker {
  display: none;
}

.alerta-etl-icone::before {
  content: '';
  width: 17px;
  height: 17px;
  background-color: var(--bad);
  -webkit-mask: var(--icone-atencao) center / contain no-repeat;
  mask: var(--icone-atencao) center / contain no-repeat;
}

.alerta-etl-icone:hover,
.alerta-etl[open] .alerta-etl-icone {
  border-color: var(--bad);
}

.alerta-etl-painel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 40;
  width: 350px;
  padding: 12px 14px;
  background: var(--bg-surface);
  border: 1px solid var(--bad-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-primary);
  white-space: normal;
}

.alerta-etl-painel strong {
  font-weight: 600;
  color: var(--text-title);
}

.alerta-etl-painel a {
  display: block;
  margin-top: 8px;
  font-weight: 500;
}

@media (max-width: 860px) {
  .frescor-resumo-texto { display: none; }
  .frescor-painel,
  .alerta-etl-painel { width: min(330px, calc(100vw - 32px)); }
}

/* ==========================================================================
   Funis
   ========================================================================== */
/* Funil comercial: barra horizontal, largura proporcional ao valor. NÃO usa
   forma de funil de propósito: afunilar carrega implicação de sequência estrita,
   e os três desfechos (ganhas / em andamento / perdidas) são paralelos e
   excludentes, não etapas. */
.funilc-secao {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Espaço + linha separam as duas seções: progressão é sequência, desfecho não. */
.funilc-secao--desfecho {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px dashed var(--border-strong);
}

.funilc-secao-titulo {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
  margin-bottom: 6px;
}

.funilc-secao-base {
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--text-secondary);
}

.funilc-linha {
  display: grid;
  grid-template-columns: 120px 1fr 64px 48px;
  align-items: center;
  gap: 10px;
}

.funilc-rotulo {
  font-size: 0.85rem;
  color: var(--text-primary);
}

.funilc-trilha {
  background: var(--warm-200);
  border-radius: var(--radius-sm);
  height: 22px;
  overflow: hidden;
}

.funilc-barra {
  height: 100%;
  border-radius: var(--radius-sm);
  transition: width 0.2s ease;
}

/* Progressão em tom de marca; desfecho em cor semântica (bom / neutro / ruim). */
.funilc-barra--geradas { background: var(--brand-700); }
.funilc-barra--qualificadas { background: var(--brand-500); }
.funilc-barra--ganhas { background: var(--good); }
.funilc-barra--andamento { background: var(--brand-300); }
.funilc-barra--perdidas { background: var(--bad); }

.funilc-valor {
  font-size: 0.875rem;
  font-weight: 600;
  text-align: right;
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
}

.funilc-pct {
  font-size: 0.8rem;
  color: var(--text-secondary);
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.funilc-nota {
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

@media (max-width: 640px) {
  .funilc-linha { grid-template-columns: 92px 1fr 56px 42px; gap: 6px; }
}

/* Funil de oportunidades da Captação da equipe (EQI Digital): mesmas linhas do comercial,
   com os motivos de perda recolhidos num `<details>` (abre só quem quiser). */
.funil-oport .funilc-secao--desfecho {
  margin-top: 18px;
  padding-top: 16px;
}

/* A aba do funil: fechada mostra só "Geradas no mês" e o convite para abrir; a faixa inteira
   é clicável. As linhas do resumo são `<span>` (conteúdo de `<summary>`), daí o `display`. */
.funil-oport-resumo {
  display: block;
  cursor: pointer;
  list-style: none;
}

.funil-oport-resumo::-webkit-details-marker { display: none; }

.funil-oport-resumo > .panel-header { display: flex; }

.funil-oport-geradas,
.funil-oport-resumo .funilc-trilha,
.funil-oport-resumo .funilc-barra { display: block; }

.funil-oport-resumo:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: -2px;
  border-radius: var(--radius-md);
}

.funil-oport-abrir {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--border-strong);
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--brand-700);
}

.funil-oport-resumo:hover .funil-oport-abrir { text-decoration: underline; }

.funil-oport-seta {
  width: 16px;
  height: 16px;
  transition: transform 0.15s ease;
}

.funil-oport-abrir-aberto { display: none; }
.funil-oport[open] .funil-oport-abrir-aberto { display: inline; }
.funil-oport[open] .funil-oport-abrir-fechado { display: none; }
.funil-oport[open] .funil-oport-seta { transform: rotate(180deg); }

.funil-motivos {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px dashed var(--border-strong);
}

.funil-motivos-resumo {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
  border-radius: var(--radius-sm);
}

.funil-motivos-resumo::-webkit-details-marker { display: none; }

.funil-motivos-resumo:hover { color: var(--brand-700); }

.funil-motivos-resumo:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 3px;
}

.funil-motivos-seta {
  width: 14px;
  height: 14px;
  align-self: center;
  transition: transform 0.15s ease;
}

.funil-motivos[open] .funil-motivos-seta { transform: rotate(90deg); }

.funil-motivos-corpo { margin-top: 12px; }

/* Motivo de perda é frase ("Não conseguimos portabilizar (Garantias/CC)"): rótulo largo, que
   quebra, e na MESMA largura em todas as seções do painel — com 120px em cima e mais nos
   motivos, as barras começavam em pontos diferentes. */
.funil-oport .funilc-linha {
  grid-template-columns: 240px 1fr 64px 48px;
}

.funil-oport .funilc-rotulo { line-height: 1.3; }

@media (max-width: 640px) {
  .funil-oport .funilc-linha { grid-template-columns: 120px 1fr 48px 42px; }
}

/* Funil de cross-sell: 4 etapas em sequência, tom clareando conforme afunila.
   Rampa de um verde só — a antiga terminava num mint saturado que não existe na
   paleta de Produtos. */
.funilc-barra--impactados { background: var(--chart-1); }
.funilc-barra--abriram    { background: var(--chart-2); }
.funilc-barra--clicaram   { background: var(--chart-4); }
.funilc-barra--adquiriram { background: var(--chart-5); }

/* As linhas ficam DIRETO neste bloco, sem `.funilc-secao` (aqui é uma sequência
   só, não duas seções como no comercial) — então o espaçamento entre linhas tem
   de vir daqui. */
.funilc--cross {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.funilc-linha--cross {
  /* Primeira coluna igual à do funil comercial para os dois alinharem na mesma
     tela; a barra fica em 1fr e ocupa a largura do painel. */
  grid-template-columns: 120px 1fr 64px 56px 62px;
}

/* Barra mais alta e em forma de pílula: com 4 etapas e espaçamento maior, barra
   fina fica frágil. O trilho ganha um tom mais suave para o retângulo de fundo
   pesar menos que a própria barra. */
.funilc--cross .funilc-trilha {
  height: 26px;
  border-radius: var(--radius-pill);
  background: var(--warm-200);
}

.funilc--cross .funilc-barra { border-radius: var(--radius-pill); }
.funilc--cross .funilc-rotulo { font-size: 0.85rem; font-weight: 500; }
.funilc--cross .funilc-valor { font-size: 0.92rem; }
.funilc--cross .funilc-nota { margin-top: 4px; }

.funilc-linha--cabecalho {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-secondary);
  padding-bottom: 2px;
}

/* Cabeçalho não precisa da altura da barra — sem isso ele herda os 26px. */
.funilc-linha--cabecalho .funilc-trilha { height: auto; background: none; }

.cross-sell-body {
  display: flex;
  gap: 28px;
  flex-wrap: wrap;
  align-items: flex-start;
  padding-top: 6px;
  padding-bottom: 10px;
}

/* O gráfico lateral (`cross-sell-chart`) não é renderizado hoje, então o funil é
   o único filho deste flex. `min-width: 0` deixa ele encolher no responsivo em
   vez de estourar. */
.cross-sell-body .funilc {
  flex: 1;
  min-width: 0;
}

.cross-sell-chart {
  flex: 1;
  min-width: 280px;
}

/* Funil vertical legado (EtapaFunil), ainda usado pelo cross-sell antigo */
.funil {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.funil-linha {
  display: flex;
  align-items: center;
  gap: 14px;
}

.funil-rotulo {
  flex-shrink: 0;
  width: 110px;
  text-align: right;
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--text-primary);
}

.funil-forma-col {
  flex: 1;
  min-width: 0;
}

.funil-segmento {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 46px;
  color: var(--text-on-dark);
  font-size: 0.82rem;
  border-radius: 4px;
}

.funil-segmento--escuro {
  color: var(--text-title);
}

.funil-valor { font-weight: 600; }
.funil-percentual { font-size: 0.76rem; opacity: 0.85; }

.funil--compacto .funil-rotulo { width: 90px; font-size: 0.78rem; }
.funil--compacto .funil-segmento { min-height: 40px; font-size: 0.78rem; }

/* Taxa de cliques (CTOR) */
.ctor-body {
  display: flex;
  gap: 28px;
  flex-wrap: wrap;
}

.ctor-ranking,
.ctor-mes {
  flex: 1;
  min-width: 280px;
}

/* Alcance & saúde da base (gauge) */
.saude-body {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}

.saude-gauge {
  position: relative;
  flex-shrink: 0;
  width: 180px;
  height: 140px;
}

.saude-linha-box {
  flex: 1;
  min-width: 220px;
  height: 140px;
}

.saude-valor {
  position: absolute;
  left: 50%;
  bottom: 6px;
  transform: translateX(-50%);
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--text-title);
}

/* ==========================================================================
   Tela inicial
   ========================================================================== */
/* "20 de 21": o "de 21" é contexto, não número — mesmo tamanho deixaria os dois
   competindo e a leitura viraria "vinte e vinte e um". */
.stat-value-de {
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--text-secondary);
  letter-spacing: 0;
}

/* Linha-espinha da narrativa do mês. Corpo maior que texto de apoio porque é
   uma frase para ser LIDA, não uma legenda. */
.inicio-narrativa {
  margin: 0 0 18px;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--text-primary);
  max-width: 92ch;
}

.inicio-narrativa strong {
  font-weight: 600;
}

/* Número à esquerda e curva à direita, na mesma linha: o card fica com metade da
   altura de um bloco empilhado, e são muitos cards nesta tela. */
.inicio-destaque {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.inicio-destaque-numero {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

/* `min-width` no gráfico: sem ele o flex encolhe o canvas até uns poucos pixels
   quando o valor formatado é longo, e a curva desaparece sem nenhum aviso. */
.inicio-destaque-spark {
  flex: 1;
  min-width: 130px;
}

.inicio-destaque-meta {
  font-size: 0.76rem;
  color: var(--text-secondary);
}

/* Situação da fonte: o ponto de cor nunca vem sozinho — sempre com o rótulo ao
   lado, para a leitura não depender de percepção de cor. */
.fonte-situacao {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  white-space: nowrap;
}

/* Atalhos por área. Linha inteira clicável, com a seta na borda — alvo grande é
   mais fácil de acertar que o texto do rótulo. */
.inicio-atalhos {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.inicio-atalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  color: var(--text-title);
  transition: background-color 0.12s ease;
}

.inicio-atalho svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--text-tertiary);
  transition: transform 0.14s ease, color 0.14s ease;
}

.inicio-atalho:hover {
  background: var(--bg-inset);
}

.inicio-atalho:hover svg {
  color: var(--brand-600);
  transform: translateX(2px);
}

/* Página "em construção" no card da tela inicial — mesmo princípio do cadeado da
   sidebar (`.sidebar-subitem--bloqueada`): `<span>` sem `href`, alinhado à esquerda
   (a base tem `justify-content: space-between` para empurrar a seta, mas aqui não há
   seta) e sem hover, porque não há para onde ir. */
.inicio-atalho--bloqueado {
  justify-content: flex-start;
  color: var(--text-tertiary);
  cursor: default;
}

.inicio-atalho--bloqueado:hover {
  background: transparent;
}

.inicio-atalho--bloqueado svg {
  width: 14px;
  height: 14px;
}

/* Ícone de cada card de área na tela inicial (17/09/2026). Chip neutro (não verde
   sólido — princípio 2 do padrão do time de Produtos: fundo verde só em botão
   primário, item selecionado e barra de ação), com o traço do ícone em brand-600. */
.area-card-icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  background: var(--bg-inset);
  color: var(--brand-600);
}

.area-card-icone svg {
  width: 17px;
  height: 17px;
}

/* Ressalvas e pendências: <dl> porque é literalmente uma lista de definições —
   título curto, explicação embaixo. Duas colunas em tela larga; empilha antes de
   a coluna ficar estreita o suficiente para o texto virar uma tira. */
.inicio-ressalvas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  gap: 16px 28px;
  margin: 0;
}

@media (max-width: 720px) {
  .inicio-ressalvas { grid-template-columns: 1fr; }
}

.inicio-ressalva dt {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-title);
  margin-bottom: 3px;
}

.inicio-ressalva dd {
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

.inicio-detalhe {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}

.inicio-detalhe summary {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-title);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 7px;
}

.inicio-detalhe summary::-webkit-details-marker { display: none; }

/* Triângulo próprio, girando ao abrir. O marcador nativo do navegador é a única
   forma da tela que não segue a paleta. */
.inicio-detalhe summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 5px solid currentColor;
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.16s ease;
}

.inicio-detalhe[open] summary::before {
  transform: rotate(90deg);
}

.inicio-detalhe-corpo {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.inicio-detalhe-corpo p {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin: 0;
  font-size: 0.84rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

.inicio-detalhe-corpo .badge {
  flex-shrink: 0;
}

/* ==========================================================================
   Relatório N3
   ==========================================================================
   A tela é uma sequência de tabelas densas (é um relatório para reunião, não um
   dashboard de navegação), então o que precisa de estilo aqui é: linha de KPI,
   scroll horizontal por tabela e a marcação visual de coluna fictícia.
   ========================================================================== */
.n3-kpis {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}

/* `flex: 1 1 160px` (23/09/2026, pedido do Gabriel: "Highlights do mês... aproveita
   melhor esse box") -- content-width sozinho deixava os 4 números clareados à ESQUERDA
   de um painel `.panel--wide`, com metade do card em branco à direita (achado no Resumo
   da EQI Digital, mas o painel é o MESMO do Relatório N3 do CMKT — 4 números aqui
   também, então o ganho é dos dois). Crescem até dividir a largura em 4 colunas iguais;
   `160px` de piso é só o gatilho de quebra em telas estreitas, `flex-wrap` já cobre o
   resto. */
.n3-kpi {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1 1 160px;
}

.n3-tabela-wrap {
  margin-top: 14px;
}

.n3-tabela-wrap--topo {
  margin-top: 0;
}

.n3-nota {
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

.n3-nota--solta {
  margin: 12px 0 0;
}

/* Marca de "mês em andamento" no cabeçalho de coluna da Cobertura de base.
   Usa os tokens de aviso que já existem — nenhum token novo, nenhuma cor literal, então
   os dois blocos de tema escuro continuam valendo sem nada duplicado.
   `text-transform` e não texto em maiúscula no template: o rótulo aparece em duas telas
   e escrevê-lo duas vezes deixaria as duas livres para divergir. */
/* Cards de mês da Cobertura de base — clicar expande o mês em semanas.
   Tokens existentes só; o "selecionado" é sage com texto verde-escuro, que é a regra 3 do
   padrão de Produtos. Sem cor literal, então os dois blocos de tema escuro seguem valendo. */
.mes-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

.mes-card {
  flex: 1 1 96px;
  min-width: 96px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 9px 11px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.12s ease, background 0.12s ease;
}

a.mes-card:hover,
a.mes-card:focus-visible {
  border-color: var(--brand-500);
  background: var(--bg-hover);
}

.mes-card.is-ativo {
  background: var(--sage-200);
  border-color: var(--sage-300);
}

/* Mês sem grão semanal: mesmo formato, sem afordância de clique. Esconder o mês faria a
   faixa ter menos meses que o gráfico logo acima, o que lê como dado faltando. */
.mes-card--inerte {
  opacity: 0.62;
  cursor: default;
}

.mes-card-rotulo {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.mes-card.is-ativo .mes-card-rotulo,
.mes-card.is-ativo .mes-card-valor,
.mes-card.is-ativo .mes-card-sub,
.mes-card.is-ativo .mes-card-acao {
  color: var(--text-title);
}

.mes-card-valor {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-title);
  line-height: 1.2;
}

.mes-card-sub {
  font-size: 0.68rem;
  color: var(--text-tertiary);
}

.mes-card-acao {
  margin-top: 3px;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--brand-500);
}

.mes-card--inerte .mes-card-acao {
  color: var(--text-tertiary);
}

/* Bloco semanal expandido. Recuo à esquerda com uma faixa sage para ele ler como
   DETALHE do mês selecionado, e não como um painel irmão do de cima. */
.semanal-bloco {
  margin-top: 16px;
  padding: 16px 0 4px 16px;
  border-left: 3px solid var(--sage-300);
}

.semanal-cabecalho {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 12px;
}

.semanal-titulo {
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--text-title);
}

.semanal-base {
  font-size: 0.78rem;
  color: var(--text-secondary);
}

/* Tabela longa cortada em 10 linhas, com "+ ver as outras N". CSS PURO — checkbox + label.
   Numa tela que desenha gráfico, `display: none` alternado por `:checked` não tem o risco
   que um handler de JS tem. Reutilizável: basta o trio `.tabela-limite` /
   `.tabela-limite-check` / `.tabela-limite-alvo` na tabela. */
.tabela-limite {
  display: flex;
  flex-direction: column;
}

/* NÃO usar `hidden` nem `display: none` aqui: os dois tiram o input da ordem de foco e o
   controle some para quem navega por teclado. Escondido visualmente, continua focável. */
.tabela-limite-check {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* `nth-child(n+11)` conta as linhas do CORPO, então o cabeçalho (que vive no `thead`)
      nunca entra na conta e permanece visível. Se alguém mover o cabeçalho para dentro do
      `tbody`, o corte passa a esconder a linha errada. */
.tabela-limite-check:not(:checked) ~ * .tabela-limite-alvo tbody tr:nth-child(n+11),
.tabela-limite-check:not(:checked) ~ .tabela-limite-alvo tbody tr:nth-child(n+11) {
  display: none;
}

.tabela-limite-botao {
  align-self: flex-start;
  margin-top: 10px;
  padding: 6px 12px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--brand-500);
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.tabela-limite-botao::before {
  content: '+';
  margin-right: 6px;
  font-weight: 700;
}

.tabela-limite-check:checked ~ .tabela-limite-botao::before {
  content: '−';
}

.tabela-limite-botao:hover {
  border-color: var(--brand-500);
  background: var(--bg-hover);
}

/* O foco do teclado vai para o input escondido, então o contorno precisa aparecer no
   `label` — senão quem navega por Tab não vê onde está. */
.tabela-limite-check:focus-visible ~ .tabela-limite-botao {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

.tabela-limite-check:checked ~ .tabela-limite-botao .tabela-limite-mais,
.tabela-limite-check:not(:checked) ~ .tabela-limite-botao .tabela-limite-menos {
  display: none;
}

/* Variante de 15 linhas (21/09/2026, "Campanhas do mês" — pedido do Gabriel: "deixa 15
   campanhas visíveis"). Reaproveita `.tabela-limite`/`.tabela-limite-check`/
   `.tabela-limite-botao` inteiros — só o ALVO do corte muda de classe, porque o número
   de linhas está fixo no SELETOR (`:nth-child`, que não aceita variável em CSS puro).
   Repetir `.tabela-limite-alvo` aqui com outro número colidiria com a regra de cima (as
   duas bateriam na mesma classe); `--15` evita a colisão sem duplicar o resto. */
.tabela-limite-check:not(:checked) ~ * .tabela-limite-alvo--15 tbody tr:nth-child(n+16),
.tabela-limite-check:not(:checked) ~ .tabela-limite-alvo--15 tbody tr:nth-child(n+16) {
  display: none;
}

/* Variante por GRUPO: um `<tbody>` por linha principal (campanha + disparos dela), então
   o corte conta `<tbody>`. `thead` não entra no `nth-of-type` de `tbody`. */
.tabela-limite-check:not(:checked) ~ * .tabela-limite-alvo--15-grupos tbody:nth-of-type(n+16),
.tabela-limite-check:not(:checked) ~ .tabela-limite-alvo--15-grupos tbody:nth-of-type(n+16) {
  display: none;
}

.tabela-limite-check:not(:checked) ~ * .tabela-limite-alvo--5-grupos tbody:nth-of-type(n+6),
.tabela-limite-check:not(:checked) ~ .tabela-limite-alvo--5-grupos tbody:nth-of-type(n+6) {
  display: none;
}

/* `.data-table tbody tr:last-child` tira a borda da última linha de CADA `<tbody>`, o que
   apagaria a divisória entre campanhas. */
.tabela-limite-alvo--15-grupos tbody:not(:last-of-type) tr:last-child td,
.tabela-limite-alvo--5-grupos tbody:not(:last-of-type) tr:last-child td {
  border-bottom: 1px solid var(--warm-200);
}

/* Disparos de uma campanha em "Campanhas do mês": abrem pelo checkbox da linha da
   campanha, CSS puro. Mesmo motivo de `.tabela-limite-check` para não usar `hidden`. */
.disparos-check {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.campanha-grupo:not(:has(.disparos-check:checked)) .disparo-linha {
  display: none;
}

/* Caixinha ⊞/⊟ do BI que a área usava antes do painel. */
.disparos-botao {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  margin-right: 8px;
  border: 1px solid var(--text-secondary);
  border-radius: 2px;
  color: var(--text-secondary);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  user-select: none;
  vertical-align: middle;
}

/* Sinal por pseudo-elemento: não entra no `innerText` do "Copiar". */
.disparos-botao::before {
  content: '+';
}

.disparos-check:checked + .disparos-botao::before {
  content: '−';
}

.disparos-botao:hover {
  border-color: var(--brand-500);
  background: var(--bg-hover);
}

.disparos-check:focus-visible + .disparos-botao {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

/* "Oportunidades por campanha": campanha CMKT → campanha do Salesforce → estágio × equipe,
   cada nível aberto pelo checkbox do nível acima. O detalhe é UMA linha logo depois da
   campanha do Salesforce, então o irmão adjacente (`+`) basta. O recuo alinha cada nível
   depois da caixinha de 14px + 8px. */
.oport-sf,
.oport-detalhe {
  display: none;
}

.oport-grupo:has(.oport-check-cmkt:checked) .oport-sf {
  display: table-row;
}

.oport-grupo:has(.oport-check-cmkt:checked) .oport-sf:has(.oport-check-sf:checked) + .oport-detalhe {
  display: table-row;
}

/* "Por equipe": o 1º nível chega sob demanda (`.grupo-sob-demanda`, aberto por `.is-aberto`). */
.grupo-sob-demanda.is-aberto .oport-sf {
  display: table-row;
}

.grupo-sob-demanda.is-aberto .oport-sf:has(.oport-check-sf:checked) + .oport-detalhe {
  display: table-row;
}

.oport-sf td:first-child {
  padding-left: 34px;
}

.oport-detalhe td {
  padding-left: 56px;
}

.oport-detalhe-grade {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
  gap: 12px 32px;
}

.oport-detalhe-titulo {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 4px;
  font-weight: 600;
}

.oport-detalhe-estagio ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

.oport-detalhe-estagio li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  color: var(--text-secondary);
  font-size: 0.8125rem;
}

/* Tabela de grupos (um `<tbody>` por comunicação em "Testes e campanhas"): mesma
   divisória entre grupos de `.tabela-limite-alvo--15-grupos`. */
.tabela-grupos tbody:not(:last-of-type) tr:last-child td {
  border-bottom: 1px solid var(--warm-200);
}

/* Disparos carregados sob demanda ("Testes e campanhas"): o botão é um link, para a tela
   funcionar sem JavaScript (`?campanha=` abre só aquela campanha). */
.grupo-sob-demanda:not(.is-aberto) .disparo-linha {
  display: none;
}

a.disparos-botao {
  text-decoration: none;
}

.disparos-botao[aria-expanded="true"]::before {
  content: '−';
}

.grupo-sob-demanda.is-carregando .disparos-botao {
  cursor: progress;
  opacity: 0.5;
}

.motivo-teste {
  display: block;
  white-space: nowrap;
}

.disparo-linha td {
  padding-top: 6px;
  padding-bottom: 6px;
  background: var(--bg-inset);
  color: var(--text-secondary);
  font-size: 0.78rem;
}

.disparo-linha td:first-child {
  padding-left: 34px;
}

.disparo-data {
  font-weight: 600;
  white-space: nowrap;
}

/* Nome cru do disparo é um token só, sem espaço (`Camp_1_..._701...`): sem quebra
   forçada estica a tabela inteira na horizontal. */
.disparo-nome {
  overflow-wrap: anywhere;
}

.tag-parcial {
  display: inline-block;
  margin-left: 5px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--warn-bg);
  color: var(--warn-text);
  border: 1px solid var(--warn-border);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: lowercase;
  vertical-align: middle;
}

/* Canal de onde a linha vem. Tabelas do CMKT misturam linhas só de e-mail (objeto
   `Send`) com linhas que somam e-mail e WhatsApp, e a mesma palavra ("Volume", "Open")
   significa coisas diferentes em telas vizinhas. `--dois` destaca a soma dos canais. */
.tag-canal {
  display: inline-block;
  margin-left: 5px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--bg-inset);
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
  vertical-align: middle;
}

.tag-canal--dois {
  background: var(--sage-100);
  color: var(--brand-700);
  border-color: var(--sage-300);
}

.n3-destaque {
  margin: 0 0 12px;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--brand-600);
}

.n3-linha-total td {
  background: var(--sage-100);
  font-weight: 600;
  color: var(--text-title);
}

/* Linha de APOIO: as contagens que formam o percentual da linha de cima. Menor
   e em cor secundária de propósito — o número de capa continua sendo o
   percentual, e estas existem para o cálculo poder ser conferido na tela
   (137.430 ÷ 152.691 = 90%) sem competir com ele pela atenção. */
.n3-linha-apoio td {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.n3-linha-apoio:first-of-type td {
  border-top: 1px solid var(--border-subtle);
}

/* Coluna sem fonte real: mudo e em itálico. O selo do painel diz que ele é
   fictício, mas não distingue QUAL coluna é — numa tabela com envios reais ao
   lado de custo inventado, essa distinção é o que evita alguém citar o número
   errado.
   `.n3-col-ficticia` SAIU em 10/09/2026 junto com as colunas de dinheiro inventadas do
   Relatório N3: nenhum template a usa mais. Sobrou `.n3-col-vazia`, que marca a coluna
   (ou, desde 22/09/2026 em "Resultados por objetivo", a LINHA — a tabela inverteu, a
   classe não é específica de eixo) que existe no relatório da área e ainda não tem
   fonte. */
.n3-col-vazia {
  color: var(--text-tertiary);
  font-style: italic;
}

.n3-subtitulo {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 24px 0 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-title);
}

.n3-subtitulo-contagem {
  font-size: 0.72rem;
  font-weight: 500;
  color: var(--text-secondary);
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
}

.n3-subtitulo-nota {
  font-size: 0.74rem;
  font-weight: 400;
  color: var(--text-secondary);
}

.n3-motivo {
  font-size: 0.76rem;
  color: var(--text-secondary);
  white-space: nowrap;
}

/* Captação negativa: cliente que converteu e depois sacou. O sinal é
   informação, não erro — precisa saltar aos olhos numa tabela cheia de valores
   positivos. */
.n3-negativo {
  color: var(--bad);
  font-weight: 600;
}

/* ==========================================================================
   Responsivo
   ========================================================================== */
@media (max-width: 1024px) {
  .content { padding-bottom: 32px; }
}

@media (max-width: 860px) {
  .topbar-title { font-size: 1.35rem; }
}

@media (max-width: 720px) {
  .app-shell {
    flex-direction: column;
  }

  .sidebar {
    width: 100% !important;
    height: auto;
    position: static;
    max-height: none;
    border-right: none;
    border-bottom: 1px solid var(--border-subtle);
    padding-bottom: 10px;
  }

  /* Nada a desfazer aqui: o bloco de "recolhido" está escopado em
     `min-width: 721px`, então nesta faixa ele nem entra na cascata. O botão de
     recolher sai de cena porque não teria efeito. */
  .sidebar-toggle { display: none; }

  .content-header { align-items: flex-start; }
  .projeto-kpis { grid-template-columns: repeat(2, 1fr); }
  .n3-kpis { gap: 20px; }
  .panel--destaque .n3-kpi:not(:first-child) { border-left: none; padding-left: 0; }
}

/* ==========================================================================
   ÁREA DE GESTÃO (/gestao/) — administração da própria aplicação
   ==========================================================================

   NENHUMA COR LITERAL AQUI. Toda cor sai de token de `:root`, que é a regra que
   sustenta o tema escuro deste arquivo: o escuro é redefinição de token, sem uma única
   regra de componente duplicada. Um `#hex` nesta seção quebraria o tema escuro da área de
   gestão SEM ERRO NENHUM — `check` limpo, HTTP 200, e a tela pintada na paleta clara com o
   tema escuro ligado.

   Também não há token novo: a área é construída inteira sobre os 91 que já existiam. Token
   novo teria de ser declarado em três lugares (`:root`, a media query e o seletor de
   atributo), e esquecer um dos dois blocos escuros produz divergência que só aparece na
   máquina de quem tem o sistema em escuro.
   ========================================================================== */

/* --- Mensagens de retorno (framework de messages) ------------------------- */
/* Fica no base.html, então vale nas 20 telas. Sem isto, uma mensagem enfileirada por uma
   ação que redireciona para tela de indicador se perderia calada. */
.mensagens {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 18px;
}

.mensagem {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--info-border);
  background: var(--info-bg);
  color: var(--text-primary);
  font-size: 0.88rem;
  line-height: 1.55;
}

.mensagem-icone {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  background-image: var(--icone-info);
  background-repeat: no-repeat;
  background-size: contain;
}

.mensagem--success {
  border-color: var(--good-border);
  background: var(--good-bg);
}

.mensagem--success .mensagem-icone { background-image: var(--icone-ok); }

.mensagem--warning {
  border-color: var(--warn-border);
  background: var(--warn-bg);
}

.mensagem--warning .mensagem-icone { background-image: var(--icone-atencao); }

.mensagem--error {
  border-color: var(--bad-border);
  background: var(--bad-bg);
}

.mensagem--error .mensagem-icone { background-image: var(--icone-atencao); }

.mensagem-texto { flex: 1; }

/* --- Alertas da visão geral ---------------------------------------------- */
.adm-alertas {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}

.adm-alerta {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

/* A faixa lateral é o que dá severidade sem pintar o card inteiro. Card colorido inteiro
   competiria com os selos de estado que já existem na mesma tela. */
.adm-alerta-marca {
  flex-shrink: 0;
  width: 3px;
  align-self: stretch;
  min-height: 34px;
  border-radius: var(--radius-pill);
  background: var(--text-tertiary);
}

.adm-alerta--critico .adm-alerta-marca { background: var(--bad); }
.adm-alerta--atencao .adm-alerta-marca { background: var(--warn); }
.adm-alerta--informativo .adm-alerta-marca { background: var(--info); }

.adm-alerta-corpo { flex: 1; min-width: 0; }

.adm-alerta-titulo {
  margin: 0 0 4px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-title);
}

.adm-alerta-detalhe {
  margin: 0;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--text-secondary);
}

.adm-alerta .btn { flex-shrink: 0; }

/* --- Tabelas da gestão --------------------------------------------------- */
.adm-celula-nota {
  font-size: 0.8rem;
  color: var(--text-secondary);
  max-width: 420px;
}

.adm-col-acao {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

.adm-linha-inativa { opacity: 0.62; }

/* Linha de detalhe logo abaixo da linha principal: o erro completo da fonte e a cauda da
   saída da rotina. Fundo diferente para não parecer uma linha de dado. */
.adm-linha-erro > td,
.adm-linha-saida > td {
  background: var(--bg-inset);
  font-size: 0.8rem;
  color: var(--text-secondary);
}

.adm-linha-erro > td { color: var(--bad); }

.adm-pessoa {
  display: flex;
  flex-direction: column;
  gap: 1px;
  text-decoration: none;
}

.adm-pessoa-nome {
  font-weight: 600;
  color: var(--text-title);
}

.adm-pessoa:hover .adm-pessoa-nome { color: var(--brand-600); }

.adm-pessoa-email {
  font-size: 0.76rem;
  color: var(--text-tertiary);
}

.adm-nivel {
  font-size: 0.72rem;
  color: var(--text-tertiary);
  white-space: nowrap;
}

.adm-nota {
  margin: 12px 0 0;
  font-size: 0.8rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

.adm-nota-inline {
  font-size: 0.72rem;
  color: var(--text-tertiary);
}

.adm-disco-barra {
  height: 10px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--warm-200);
  margin: 4px 0 10px;
}

.adm-disco-uso { height: 100%; background: var(--chart-good); }
.adm-disco-uso--atencao { background: var(--chart-warn); }
.adm-disco-uso--critico { background: var(--chart-bad); }

.adm-subtitulo {
  margin: 16px 0 8px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.adm-areas {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  font-size: 0.84rem;
  color: var(--text-primary);
}

/* --- Menu de ações por linha (details/summary, zero JS) ------------------- */
/* Sem `position: relative`: o painel abre no fluxo (ver o comentário dele), então não há
   nada para posicionar em relação a.
   Coluna flex com `align-items: flex-end` para alinhar botão e painel à direita da célula de
   ação. `margin-left: auto` no `<summary>` não serviria: ele é `inline-flex` (por
   `.adm-menu-botao`), e margem automática não empurra caixa de nível inline. */
.adm-menu {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.adm-menu-botao {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  list-style: none;
}

.adm-menu-botao::-webkit-details-marker { display: none; }

.adm-menu-botao:hover {
  background: var(--bg-hover);
  color: var(--text-title);
}

.adm-menu-botao svg { width: 16px; height: 16px; }

/* O PAINEL ABRE NO FLUXO, NÃO FLUTUANDO, e a razão é uma regra de CSS que morde em
   silêncio: `position: absolute` é recortado pelo ancestral mais próximo com `overflow`
   diferente de `visible`, e toda tabela daqui está dentro de `.tablewrap`, que tem
   `overflow-x: auto`.

   O detalhe que fecha o caso: pela especificação, quando um eixo é diferente de `visible`, o
   `visible` do outro eixo passa a computar como `auto`. Ou seja, `.tablewrap` recorta os DOIS
   eixos — e recorta MESMO QUANDO A TABELA NÃO ROLA, porque `overflow: auto` sempre estabelece
   contexto de recorte. Um menu flutuante ali apareceria cortado pela borda do painel, e o
   sintoma não teria nada a ver com a causa aparente (a tabela cabe na tela, então ninguém
   suspeitaria de rolagem).

   As saídas seriam: tirar o `overflow-x` da tabela (e aí tabela larga estoura a página),
   `position: fixed` com cálculo de posição em JS (o projeto evita JS na navegação por
   decisão registrada), ou abrir no fluxo. A terceira é a única que não troca um problema por
   outro: a linha cresce enquanto o menu está aberto, e nada pode recortá-lo.

   Continua com sombra porque ele ainda LÊ como camada sobreposta — é o princípio "borda em
   vez de sombra; sombra só no que se destaca do plano". */
.adm-menu-painel {
  display: flex;
  flex-direction: column;
  width: max-content;
  min-width: 190px;
  /* O alinhamento à direita vem do `align-items: flex-end` do `.adm-menu`. */
  margin: 6px 0 2px;
  padding: 6px;
  text-align: left;
  /* A célula de ação é `white-space: nowrap` (para o botão não quebrar); herdado aqui, ele
     impediria a nota explicativa de quebrar linha. */
  white-space: normal;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pop);
}

.adm-menu-painel a,
.adm-menu-botao-linha {
  display: block;
  width: 100%;
  padding: 7px 10px;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  font-family: inherit;
  font-size: 0.83rem;
  text-align: left;
  text-decoration: none;
  color: var(--text-primary);
  cursor: pointer;
}

.adm-menu-painel a:hover,
.adm-menu-botao-linha:hover {
  background: var(--bg-hover);
  color: var(--text-title);
}

.adm-menu-perigo { color: var(--bad); }

.adm-menu-perigo:hover {
  background: var(--bad-bg);
  color: var(--bad);
}

.adm-menu-nota {
  display: block;
  padding: 7px 10px;
  font-size: 0.76rem;
  line-height: 1.55;
  color: var(--text-tertiary);
  white-space: normal;
}

.adm-menu-painel form { display: contents; }

/* --- Paginação ----------------------------------------------------------- */
.adm-paginacao {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--border-subtle);
}

.adm-paginacao-posicao {
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* Estado inativo dos botões de página e da rotina bloqueada. `pointer-events: none` porque
   eles são `<span>`, não `<a>`: sem isto o cursor viraria de mão sobre algo que não clica. */
.adm-paginacao-inativo {
  opacity: 0.45;
  pointer-events: none;
}

/* --- Ficha (lista de definição) ------------------------------------------ */
.adm-ficha {
  display: grid;
  grid-template-columns: minmax(140px, auto) 1fr;
  gap: 8px 16px;
  margin: 0;
  font-size: 0.85rem;
}

.adm-ficha dt {
  color: var(--text-secondary);
  font-weight: 500;
}

.adm-ficha dd {
  margin: 0;
  color: var(--text-primary);
}

/* --- Matriz de permissões (leitura) ------------------------------------- */
/* Lista e não tabela cruzada: a maioria dos módulos usa 1 ou 2 das 5 ações, então 60% das
   células de uma tabela ficariam vazias — e célula vazia numa matriz de permissão é ambígua
   entre "não concedido" e "não existe". */
.perm-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.perm-item {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.perm-item:last-child { border-bottom: none; }

.perm-item--vazio { opacity: 0.6; }

.perm-modulo {
  flex: 0 0 auto;
  min-width: 190px;
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--text-title);
}

.perm-acoes {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.perm-acao {
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  border: 1px dashed var(--border-strong);
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--text-tertiary);
}

/* Concedido = sage com texto verde escuro, o "selecionado" do padrão do time de Produtos.
   A borda passa de tracejada a sólida: a distinção não depende só de cor. */
.perm-acao--sim {
  border-style: solid;
  border-color: var(--sage-300);
  background: var(--sage-200);
  color: var(--text-title);
  font-weight: 600;
}

.perm-nada {
  font-size: 0.72rem;
  font-style: italic;
  color: var(--text-tertiary);
}

/* --- Matriz de permissões (edição) -------------------------------------- */
.adm-matriz-edicao {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: 14px;
}

.adm-matriz-modulo {
  margin: 0;
  padding: 14px 16px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}

.adm-matriz-legenda {
  padding: 0 4px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text-title);
}

.adm-matriz-descricao {
  margin: 4px 0 10px;
  font-size: 0.76rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

.adm-matriz-acoes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 14px;
}

/* --- Caixas de seleção -------------------------------------------------- */
.adm-caixas-lista {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
}

.adm-caixa {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 0.84rem;
  color: var(--text-primary);
  cursor: pointer;
}

.adm-caixa input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: var(--brand-600);
  cursor: pointer;
}

/* Vínculo campanha × produto: linha nunca revisada, destacada para não passar batido
   entre ~25 produtos. A caixa "Sem campanha" fica junto do `<select>` da própria
   célula, não solta — é a decisão daquela linha, não uma ação da tabela inteira. */
.linha-precisa-decisao td {
  background: var(--warn-bg);
}

.vinculo-sem-campanha {
  margin-top: 6px;
}

/* UM CAMPO POR CAMPANHA VINCULADA, mais um vazio para acrescentar (15/09/2026). Sem o
   respiro entre eles os selects de uma linha com duas campanhas encostam e parecem um
   componente so. Nenhum token novo: o espaco e o mesmo da caixa "Sem campanha" acima. */
/* Célula que carrega a FÓRMULA no `title` (Saúde da base, 15/09/2026). O sublinhado é o
   único sinal de que há algo a ler ali -- e este projeto já registrou o limite disso: o
   `title` nativo só aparece depois de ~1s parado e não existe em toque, então ele serve
   para DETALHE de quem já entende a linha, nunca para informação que precisa estar
   publicada. As cinco fórmulas também estão na nota do painel, que é o canal publicado. */
.tem-dica {
  cursor: help;
  border-bottom: 1px dotted var(--border-strong);
}

.vinculo-mais {
  margin-top: 6px;
  font-size: 12px;
  padding: 4px 10px;
}

.vinculo-campo + .vinculo-campo,
.combo-campanha + .vinculo-campo,
.vinculo-campo + .combo-campanha,
.combo-campanha + .combo-campanha {
  margin-top: 6px;
}

/* Combobox de campanha (`vinculos_campanha_produto.js`, 13/09/2026): o `<select>` real
   continua sendo o campo submetido — só fica `hidden` quando o JS roda, então sem JS a
   tela é o `<select>` puro de sempre. Por cima, um `<input>` com a MESMA cara do select
   (mesmo chevron) funciona como campo de busca; uma lista de opções aparece ao focar.

   A LISTA É UMA SÓ, COMPARTILHADA POR TODA A TABELA — não uma por linha. São 25 linhas e
   ~175 campanhas cada; 25 listas próprias pesariam à toa, já que o conteúdo é sempre o
   mesmo catálogo. O JS reposiciona a lista única sob o campo focado. */
.combo-campanha {
  position: relative;
}

.combo-campanha-input {
  cursor: pointer;
}

.combo-lista {
  position: fixed;
  z-index: 60;
  max-height: 260px;
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-card);
  scrollbar-width: thin;
  scrollbar-color: var(--warm-400) transparent;
}

.combo-opcao {
  padding: 7px 12px;
  font-size: 0.875rem;
  color: var(--text-primary);
  cursor: pointer;
}

.combo-opcao:hover,
.combo-opcao--destaque {
  background: var(--sage-200);
  color: var(--text-title);
}

.combo-opcao--vazia {
  color: var(--text-tertiary);
  font-style: italic;
}

/* --- Formulários -------------------------------------------------------- */
.adm-formulario { max-width: 720px; }

.adm-formulario--largo { max-width: 100%; }

.adm-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 16px;
  margin-bottom: 16px;
}

.adm-form-grid--largo { grid-template-columns: 1fr; }

.adm-form-caixas {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 16px;
}

.adm-form-acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}

/* Estados do campo. `.campo` e `.campo-label` já existiam; ajuda e erro não, e a ausência
   do erro era o problema: um formulário que recusa o envio sem dizer o motivo é
   indistinguível de botão quebrado. */
.campo-ajuda {
  font-size: 0.74rem;
  line-height: 1.55;
  color: var(--text-tertiary);
}

.campo-erro {
  font-size: 0.76rem;
  font-weight: 500;
  color: var(--bad);
}

.campo--erro input,
.campo--erro select,
.campo--erro textarea {
  border-color: var(--bad-border);
}

.campo-obrigatorio {
  color: var(--bad);
  margin-left: 2px;
}

.campo--caixa { flex-direction: row; }

.campo-caixa-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--text-primary);
  cursor: pointer;
}

.campo-caixa-label input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: var(--brand-600);
}

/* Filtro de status da sub-aba "Oportunidades" (21/09/2026, Produtos & Cross-sell).
   `<fieldset>` sem borda/padding nativos do navegador — o `.panel` já dá a caixa;
   duplicar a borda do fieldset por dentro da do painel é moldura dupla. As opções
   quebram linha em `flex-wrap` porque a lista de status é do tamanho que o Salesforce
   tiver (hoje ~30), imprevisível — grid de coluna fixa sobraria ou apertaria. */
.filtros-oportunidades-status {
  border: none;
  padding: 0;
  margin: 0 0 14px;
}

.filtros-oportunidades-status legend {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-title);
  padding: 0 0 8px;
}

.filtros-oportunidades-opcoes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
}

/* Cabeçalho do painel de filtro em LINHA (título + contagem). O `.filtros-panel-header`
   original é um bloco de texto simples e continua sendo — daí o modificador em vez de
   mudar a regra base, que é usada pela tela de Estratégia de campanha. */
.filtros-panel-header--linha {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* O `.filtros-panel` base é o popover flutuante da Estratégia de campanha (absoluto, ancorado
   no botão Filtros). Na Gestão o formulário fica no fluxo da página, acima da tabela. */
.filtros-panel--embutido {
  position: static;
  width: auto;
  box-shadow: none;
  margin-bottom: 16px;
}

.filtros-panel-titulo {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-title);
}

/* --- Perfis -------------------------------------------------------------- */
.adm-perfil-resumo {
  padding: 12px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.adm-perfil-resumo:last-child { border-bottom: none; }

.adm-perfil-cabecalho {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 4px;
}

.adm-perfil-nome {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-title);
}

.adm-perfil-descricao {
  margin: 0 0 8px;
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

/* --- Rotinas ------------------------------------------------------------ */
.adm-rotinas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
  gap: 14px;
}

.adm-rotina {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}

.adm-rotina-topo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.adm-rotina-nome {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text-title);
}

.adm-rotina-descricao {
  flex: 1;
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--text-secondary);
}

/* `align-self: flex-start` para o botão não esticar na largura do card — botão de largura
   total lê como ação primária da tela, e aqui há vários lado a lado. */
.adm-rotina .btn { align-self: flex-start; }

/* --- Integrações por área: bloco recolhível e etapas da rotina automática ------------- */
.adm-area > .adm-area-resumo {
  cursor: pointer;
  list-style: none;
  display: flex;
}
.adm-area > .adm-area-resumo::-webkit-details-marker { display: none; }
.adm-area > .adm-area-resumo:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: -2px;
}
.adm-area-seta {
  width: 14px;
  height: 14px;
  flex: none;
  color: var(--text-secondary);
  transition: transform 0.15s ease;
}
.adm-area[open] .adm-area-seta { transform: rotate(90deg); }
.adm-area .adm-rotinas { margin-top: 16px; }

.adm-auto-areas { margin-top: 12px; }
.adm-auto-area {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-surface);
}
.adm-auto-area-nome {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--text-title);
}
.adm-auto-etapas {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.adm-area-resumo + .panel-body .adm-saida,
.panel-body > .adm-saida { margin-top: 14px; }

.adm-saida > summary {
  cursor: pointer;
  font-size: 0.78rem;
  color: var(--brand-600);
}

.adm-saida-texto {
  max-height: 320px;
  margin: 8px 0 0;
  padding: 10px 12px;
  overflow: auto;
  background: var(--bg-inset);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  font-size: 0.74rem;
  line-height: 1.55;
  color: var(--text-primary);
  /* `pre-wrap` e não `pre`: a saída do ETL tem linhas longas, e `pre` faria o painel inteiro
     rolar na horizontal — o `overflow-x` do contêiner não alcança o corpo da página, mas a
     tabela em volta esticaria. */
  white-space: pre-wrap;
  word-break: break-word;
}

/* --- Áreas (configurações) ---------------------------------------------- */
.adm-area-bloco {
  padding: 14px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.adm-area-bloco:last-of-type { border-bottom: none; }

.adm-area-cabecalho {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.adm-area-nome {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-title);
}

.adm-area-cabecalho form { display: contents; }

/* --- Confirmação e erro ------------------------------------------------- */
.adm-confirmar,
.adm-erro { max-width: 620px; }

.adm-consequencias {
  margin: 0;
  padding-left: 20px;
  font-size: 0.86rem;
  line-height: 1.7;
  color: var(--text-primary);
}

.adm-consequencias li { margin-bottom: 6px; }

.adm-confirmar-acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--border-subtle);
}

.adm-confirmar-form { display: contents; }

.adm-acoes-linha {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
}

.adm-acoes-linha form { display: contents; }

/* Botão "remover" dentro do chip de pessoa (Quem edita o quê). */
.adm-form-inline { display: inline; margin-left: 4px; }

/* Pessoa que casa com a pesquisa de Quem edita o quê. */
.filter-chip--achada {
  background: var(--info-bg);
  border-color: var(--info-border);
  color: var(--info);
  font-weight: 600;
}

/* Pesquisa, lista de pessoas e botão de marcar na mesma linha. */
.adm-marcar .input { width: auto; flex: 1 1 240px; min-width: 0; }
.adm-marcar [data-filtra-pessoas] { flex: 0 1 220px; }

.adm-acao-perigo { color: var(--bad); }

/* Botão destrutivo. NÃO é o estilo primário: nas telas de confirmação o "Cancelar" é o
   botão sólido, para que quem chega por engano e clica no que parece o principal saia sem
   fazer nada. */
.btn--perigo {
  background: var(--bad-bg);
  border-color: var(--bad-border);
  color: var(--bad);
}

.btn--perigo:hover {
  background: var(--bad);
  border-color: var(--bad);
  color: var(--text-on-dark);
}

/* --- Trilha de auditoria ------------------------------------------------- */
.trilha {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.trilha-item {
  display: flex;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--border-subtle);
}

.trilha-item:last-child { border-bottom: none; }

.trilha-marca {
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  margin-top: 6px;
  border-radius: var(--radius-pill);
  background: var(--brand-400);
}

.trilha-item--falha .trilha-marca,
.trilha-item--login_falho .trilha-marca,
.trilha-item--negado .trilha-marca { background: var(--bad); }

.trilha-item--login .trilha-marca { background: var(--good); }
.trilha-item--logout .trilha-marca { background: var(--text-tertiary); }
.trilha-item--excluir .trilha-marca { background: var(--warn); }

.trilha-corpo { flex: 1; min-width: 0; }

.trilha-frase {
  margin: 0 0 3px;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--text-primary);
}

.trilha-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 10px;
  margin: 0;
  font-size: 0.74rem;
  color: var(--text-tertiary);
}

.trilha-ip { white-space: nowrap; }

/* Quebra em vez de `nowrap`: "EtapaExecucao · Execução 10 · etapa 7 · …" tem ~490px e, no
   painel de meia largura da Visão geral, era cortado pelo `overflow: hidden` do card. */
.trilha-recurso { overflow-wrap: anywhere; }

.trilha-ip { font-variant-numeric: tabular-nums; }

.trilha-diff {
  list-style: none;
  margin: 8px 0 0;
  padding: 8px 10px;
  background: var(--bg-inset);
  border-radius: var(--radius-sm);
  font-size: 0.76rem;
}

.trilha-diff li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  padding: 2px 0;
}

.trilha-diff-campo {
  min-width: 130px;
  font-weight: 600;
  color: var(--text-secondary);
}

.trilha-diff-de {
  color: var(--text-tertiary);
  text-decoration: line-through;
}

.trilha-diff-seta { color: var(--text-tertiary); }

.trilha-diff-para {
  font-weight: 500;
  color: var(--text-title);
}

/* --- Item externo da sidebar (Django admin) ----------------------------- */
.sidebar-subitem--externo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
}

.sidebar-subitem--externo svg {
  flex-shrink: 0;
  width: 13px;
  height: 13px;
  opacity: 0.7;
}

/* --- Relatório de acessos (Auditoria) ----------------------------------- */
/* `<details>` aninhado (pessoa → semana → dia). As colunas numéricas têm largura fixa e
   iguais em todos os níveis: é o que alinha o total do mês com o das semanas embaixo. */
.acessos {
  --acessos-colunas: minmax(0, 1fr) 92px 128px 120px;
  font-size: 0.85rem;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.acessos-linha {
  display: grid;
  grid-template-columns: var(--acessos-colunas);
  align-items: center;
  gap: 0 12px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--warm-200);
  color: var(--text-primary);
}

.acessos-cabecalho {
  background: var(--bg-inset);
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-secondary);
  border-bottom-color: var(--border-subtle);
}

.acessos-grupo > summary {
  cursor: pointer;
  list-style: none;
}

.acessos-grupo > summary::-webkit-details-marker { display: none; }

.acessos-grupo > summary:hover { background: var(--warm-50); }

.acessos-grupo > summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

.acessos-grupo--pessoa:last-child > summary,
.acessos-grupo--pessoa:last-child[open] > :last-child { border-bottom: none; }

.acessos-rotulo {
  display: flex;
  align-items: baseline;
  gap: 8px;
  min-width: 0;
}

.acessos-mais {
  flex: 0 0 auto;
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border: 1px solid var(--border-strong);
  border-radius: 4px;
  color: var(--brand-600);
  font-weight: 700;
  line-height: 1;
  align-self: center;
}

.acessos-mais::before { content: "+"; }

.acessos-grupo[open] > summary .acessos-mais::before { content: "\2212"; }

.acessos-sub {
  display: inline-block;
  margin-left: 6px;
  font-size: 0.76rem;
  color: var(--text-secondary);
}

.acessos-total { font-weight: 700; color: var(--text-title); }

/* Recuo por nível no rótulo, não na linha: a grade das colunas numéricas não se desloca. */
.acessos-grupo--semana > summary,
.acessos-linha--semana-vazia {
  background: var(--warm-50);
}

.acessos-grupo--semana > summary .acessos-rotulo,
.acessos-linha--semana-vazia .acessos-rotulo { padding-left: 26px; }

.acessos-linha--semana-vazia { color: var(--text-tertiary); }

.acessos-linha--dia {
  background: var(--bg-inset);
  font-size: 0.8rem;
}

.acessos-linha--dia .acessos-rotulo { padding-left: 52px; }

/* Painéis mais acessados: mesma grade, com a barra proporcional ao painel mais aberto. */
/* Ocupa a coluna: com a largura do texto, a trilha da barra mudava de linha para linha e a
   comparação entre painéis deixava de valer. */
.paineis-nome { display: block; flex: 1 1 auto; min-width: 0; }

.paineis-posicao {
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

.paineis-barra {
  display: block;
  width: 100%;
  max-width: 320px;
  height: 4px;
  margin-top: 6px;
  background: var(--warm-200);
  border-radius: 2px;
}

.paineis-barra rect { fill: var(--brand-500); }

.acessos-linha--pessoa-painel { background: var(--warm-50); }

.acessos-linha--pessoa-painel .acessos-rotulo { padding-left: 26px; }

@media (max-width: 720px) {
  /* Sem a 3ª coluna (dias com acesso / pessoas) no celular: são quatro colunas em ~340px. */
  .acessos { --acessos-colunas: minmax(0, 1fr) 64px 84px; }
  .acessos-linha > :nth-child(3) { display: none; }
  .acessos-sub { display: block; margin-left: 0; }
  .acessos-grupo--semana > summary .acessos-rotulo,
  .acessos-linha--semana-vazia .acessos-rotulo { padding-left: 14px; }
  .acessos-linha--dia .acessos-rotulo { padding-left: 28px; }
  .acessos-linha--pessoa-painel .acessos-rotulo { padding-left: 14px; }
}

/* --- Responsividade da gestão ------------------------------------------- */
/* O CORPO DA PÁGINA NUNCA ROLA NA HORIZONTAL. Toda tabela larga já está dentro de
   `.tablewrap`, que tem `overflow-x: auto` — o que este bloco resolve é o que NÃO é
   tabela: grid de duas colunas, ficha em duas colunas, e a linha de ações do alerta. */
@media (max-width: 900px) {
  /* O alerta empilha: com o botão ao lado, o texto ficava em coluna de ~120px. */
  .adm-alerta { flex-wrap: wrap; }
  .adm-alerta .btn { margin-left: 15px; }

  .adm-matriz-edicao,
  .adm-rotinas { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  /* Ficha em uma coluna: com `minmax(140px, auto)` o valor ficava sem espaço no celular. */
  .adm-ficha { grid-template-columns: 1fr; gap: 2px 0; }
  .adm-ficha dt { margin-top: 8px; }

  /* O rótulo do módulo tinha `min-width: 190px`, o que forçava a linha a estourar a largura
     da tela antes de quebrar. Aqui ele solta e as ações vão para a linha de baixo. */
  .perm-modulo { min-width: 0; flex: 1 0 100%; }

  .adm-form-grid { grid-template-columns: 1fr; }

  /* No celular a célula de ação é estreita, e `min-width: 190px` no painel esticaria a
     tabela inteira (forçando rolagem horizontal por causa de um menu). Em relação à
     viewport ele cabe. */
  .adm-menu-painel { min-width: min(190px, 62vw); }

  .adm-confirmar-acoes,
  .adm-form-acoes { flex-direction: column; align-items: stretch; }

  .adm-confirmar-acoes .btn,
  .adm-form-acoes .btn { width: 100%; }
}

/* Texto de apoio pequeno — o denominador ao lado do nome de uma linha ("sobre
 * enviados"), hoje em Bounce/Descadastro na tabela de Cobertura de base. Até
 * 18/09/2026 também era a fração embaixo de cada Big Number ("241.165 entregues ÷
 * 241.612 enviados"); essa foi para o ícone "i" (pedido do Gabriel: simplificar a
 * tela), então o texto deixou de ficar plantado embaixo do número o tempo todo — a
 * classe continua com o mesmo nome porque o USO (apoio pequeno, secundário) é o
 * mesmo, só o lugar mudou.
 *
 * SUBSTITUIU UM `title` NATIVO, que foi a primeira tentativa e não serviu: o tooltip do
 *    navegador só aparece depois de ~1s parado, não existe em toque, e o único sinal de que
 *    havia algo ali era um pontilhado. A informação existia e ninguém via — o que, num
 *    painel que é apresentado, é o mesmo que não existir.
 *
 * Menor e em texto secundário: ela é apoio, não concorrente do número grande. */
.saude-stat-fracao {
  font-size: 0.72rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

/* Revelação por botão da tabela de números absolutos, dentro de "Cobertura de base"
 * (18/09/2026, pedido do Gabriel). `<details>` nativo e não o mecanismo de
 * `data-toggle`/`.toggle-btn` (JS de `initToggles`) de propósito: aquele painel já usa
 * esse mecanismo para o seletor de canal, e o JS pega TODOS os `.toggle-btn` de dentro
 * do `[data-toggle]` — um segundo grupo ali disputaria o mesmo estado. `<details>`
 * resolve sem JS novo e sem esse conflito; o `<summary>` já tem a classe `.btn`, então
 * o marcador nativo do navegador (▸) só precisa ser escondido para não duplicar a
 * indicação de "abre/fecha" ao lado do texto do botão.
 */
.matriz-detalhes {
  margin-top: 14px;
}

.matriz-detalhes > summary::-webkit-details-marker {
  display: none;
}

/* ==========================================================================
   Troca local de conteúdo (layout.js)
   ========================================================================== */
/* O miolo esmaece enquanto a resposta do filtro não chega. Sem nenhum sinal, uma troca que
 * leva meio segundo parece que o clique não pegou e o reflexo é clicar de novo — é o mesmo
 * motivo da rodinha nos itens de navegação.
 *
 * ESMAECE, NÃO BLOQUEIA. `pointer-events: none` era o caminho óbvio para impedir dois
 *    cliques seguidos, e está errado: pedido que travasse deixaria a tela inerte sem saída.
 *    Quem cuida do clique repetido é o `AbortController` do layout.js — o pedido mais novo
 *    cancela o anterior.
 *
 * Nenhuma cor aqui, de propósito: opacidade atravessa os dois temas sem precisar de token,
 * e token novo teria de ser declarado nos três lugares (`:root`, a media query e o seletor
 * de atributo). */
.content.is-trocando {
  opacity: 0.55;
  transition: opacity 120ms ease-out;
}

/* Rodinha no meio da tela durante a troca. Mora no `body`, e não na `.content`, porque a
 * opacidade da região esmaeceria a própria rodinha. `:has()` mantém uma fonte só de
 * verdade (a classe que o layout.js já liga e desliga); navegador sem `:has()` fica só com
 * o esmaecimento. O atraso de surgir evita o pisca em troca rápida. */
body:has(.content.is-trocando)::after {
  content: '';
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 60;
  width: 36px;
  height: 36px;
  margin: -18px 0 0 -18px;
  border: 3px solid var(--warm-400);
  border-top-color: var(--brand-600);
  border-radius: 50%;
  box-shadow: 0 0 0 12px var(--bg-surface), var(--shadow-pop);
  pointer-events: none;
  animation: cx-girar 0.6s linear infinite, cx-surgir 160ms ease-out 150ms both;
}

@keyframes cx-surgir {
  from { opacity: 0; }
  to { opacity: 1; }
}

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

/* ==========================================================================
   Impressão (o Relatório N3 é levado impresso para a reunião de N3)
   ========================================================================== */
/* =============================================================================
   CASES DE EQI MOMENTS — galeria de cards com foto (18/08/2026)
   =============================================================================

   É O PRIMEIRO COMPONENTE NOVO DESDE 12/08, e as três áreas de protótipo anteriores
   foram construídas com ZERO CSS novo de propósito. Este entra porque um card com foto
   enquadrada no topo não existe no sistema: `panel` é caixa de conteúdo, `lead-time-card`
   é KPI com sparkline e `projeto-kpi` é número com rótulo. Reaproveitar qualquer um dos
   três exigiria sobrescrever padding, altura e overflow — ou seja, escrever o mesmo CSS
   com o nome errado.

   ZERO COR LITERAL e ZERO TOKEN NOVO: tudo sai dos tokens que já existem. Token novo
      teria de ser declarado em TRÊS lugares (`:root`, a media query de escuro e o seletor
      de atributo), e esquecer um dos dois blocos escuros produz divergência que só aparece
      na máquina de quem tem o sistema em escuro.

   FICA ANTES DO BLOCO DE IMPRESSÃO, como todo acréscimo a este arquivo.
   ========================================================================== */

/* Resumo da galeria: uma LINHA acima dos cards, não um painel.
   Um painel só para "2 cases" era uma barra quase vazia empurrando a galeria para baixo — e
      numa galeria o conteúdo é o card, não o contador dele. */
.galeria-resumo {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 6px;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.cases-grid {
  display: grid;
  /* `auto-fill` com minmão de 280px: 3 colunas em tela cheia, 2 no notebook, 1 no celular,
     sem media query. NÃO é coluna fixa como `.projeto-kpis` (que declara 5 e exige 5
     filhos) — a galeria tem N cards e N muda a cada cadastro. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
  gap: 16px;
  margin-bottom: 4px;
}

.case-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  /* Borda em vez de sombra — princípio 4 do padrão do time de Produtos. Sombra só no que
     flutua (dropdown, modal), e um card de galeria não flutua. */
  overflow: hidden;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.case-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px);
}

/* A moldura da foto. `aspect-ratio` E `overflow: hidden` NO CONTÊINER, com a imagem em
   `object-fit: cover`: sem isso, foto retrato e foto paisagem dariam cards de alturas
   diferentes e a grade viraria escada. O card é de conteúdo cadastrado por gente, então a
   proporção da imagem é imprevisível por construção. */
.case-card-foto {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--bg-inset);
  border-bottom: 1px solid var(--border-subtle);
  overflow: hidden;
}

.case-card-foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Estado sem foto: a INICIAL do título sobre o fundo interno.
   Não é um ícone genérico de "imagem faltando", e a diferença é de leitura: ícone de
      imagem quebrada diz "algo deu errado", e aqui não deu — o campo é opcional. A inicial
      dá âncora visual e ainda diferencia um card do outro na grade. */
.case-card-inicial {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.6rem;
  font-weight: 700;
  color: var(--brand-300);
  background: var(--bg-inset);
  user-select: none;
}

.case-card-corpo {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px 16px;
  flex: 1;
}

/* O eyebrow do slide: ESCRITÓRIO · ASSESSOR · AuC. Verde, caixa alta, pequeno — é a única
   coisa que copio literalmente do material da área, porque é como eles leem o case. */
.case-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}

.case-card-meta span:not(:last-child)::after {
  content: '·';
  margin-left: 8px;
  color: var(--text-tertiary);
}

.case-card-titulo {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--text-title);
  margin: 0;
}

/* O trecho da história. TRUNCA EM 4 LINHAS COM `-webkit-line-clamp`, e o card TEM de ter
   para onde levar (o botão Conferir): truncar sem destino publica meia história, que num
   relatório apresentado é pior que não publicar. */
.case-card-resumo {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-secondary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.case-card-rodape {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: auto;
  padding-top: 4px;
}

.case-card-presente {
  font-size: 0.78rem;
  color: var(--text-tertiary);
  margin: 0;
}

/* --- detalhe do case ------------------------------------------------------ */

.case-detalhe {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}

@media (max-width: 900px) {
  .case-detalhe { grid-template-columns: 1fr; }
}

.case-detalhe-foto {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-inset);
}

.case-detalhe-foto img {
  width: 100%;
  height: auto;
  display: block;
}

/* A história no detalhe NÃO trunca, e a medida de linha é limitada de propósito: texto
   corrido em coluna de 900px passa de 120 caracteres por linha e o olho perde a linha ao
   voltar. */
.case-historia {
  font-size: 0.95rem;
  line-height: 1.65;
  color: var(--text-primary);
  max-width: 62ch;
  white-space: pre-line;
}

.case-ficha {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 14px;
  margin: 0;
  font-size: 0.88rem;
}

.case-ficha dt {
  color: var(--text-secondary);
  font-weight: 500;
}

.case-ficha dd {
  margin: 0;
  color: var(--text-title);
  font-weight: 600;
}

.case-assinatura {
  font-size: 0.78rem;
  color: var(--text-tertiary);
  border-top: 1px solid var(--border-subtle);
  padding-top: 10px;
  margin-top: 4px;
}

/* Ressalvas dos KPIs do painel de ROI. Texto NA TELA e não tooltip do selo — o `title`
   nativo só aparece depois de ~1s parado e não existe em toque, e este projeto já registrou
   esse fracasso na Saúde da base. Zero token novo, como o resto do arquivo. */
.roi-ressalvas {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border-subtle);
}

.roi-ressalva {
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text-secondary);
  /* Limita a medida de leitura: parágrafo de ressalva atravessando um painel largo vira
     uma linha de 200 caracteres, que é onde o olho se perde e a pessoa desiste. */
  max-width: 92ch;
}

/* ===================================================================================
   CARGA DE PLANILHA — sub-aba "Produtos" da Estratégia de campanha (18/08/2026)

   Zero token novo e zero cor literal, como o resto do arquivo: tudo sai dos tokens que
   já existiam. Token novo teria de ser declarado em TRÊS lugares (`:root`, a media query
   de escuro e o seletor de atributo), e esquecer um dos dois blocos escuros produz
   divergência que só aparece na máquina de quem tem o sistema em escuro.
   =================================================================================== */

/* Resumo em pares rótulo/valor. `auto-fill` e não `auto-fit`: com `auto-fit` os três
   itens da tela de confirmação esticariam para um terço da largura cada, e o resumo
   viraria três colunas gigantes de texto curto. */
.carga-resumo {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 220px), 1fr));
  gap: 14px 24px;
  margin: 0 0 18px;
}

.carga-resumo-item dt {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-tertiary);
  margin-bottom: 3px;
}

.carga-resumo-item dd {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-title);
}

/* O detalhe vai numa LINHA PRÓPRIA e em peso normal: ele qualifica o número ("por COD
   BTG", "soma da planilha inteira") e, na mesma linha e no mesmo peso, competiria com o
   valor pela atenção — que é justamente o que o número não pode perder. */
.carga-resumo-detalhe {
  display: block;
  font-size: 0.76rem;
  font-weight: 400;
  color: var(--text-secondary);
  margin-top: 2px;
}

.dropzone {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 4px;
}

/* O INPUT É ESCONDIDO SEM SAIR DA ORDEM DE FOCO. `display: none` e
   `visibility: hidden` tirariam o campo do alcance do teclado, e a tela ficaria
   operável só no mouse — sem nenhum sintoma visual para quem testa com mouse. */
.dropzone-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.dropzone-alvo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 28px 20px;
  text-align: center;
  cursor: pointer;
  border: 2px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  background: var(--bg-inset);
  transition: background 0.15s ease, border-color 0.15s ease;
}

.dropzone-alvo:hover {
  border-color: var(--brand-500);
  background: var(--bg-hover);
}

/* O anel de foco fica no ALVO e não no input (que é invisível), senão navegar por teclado
   até o campo não mostraria nada na tela. */
.dropzone-input:focus-visible + .dropzone-alvo {
  outline: 2px solid var(--brand-600);
  outline-offset: 2px;
}

/* Estado de arrasto. Classe posta pelo JS — sem JS ela nunca aparece, e o resto continua
   funcionando pelo clique no label. */
.dropzone-alvo.is-arrastando {
  border-color: var(--brand-600);
  border-style: solid;
  background: var(--sage-100);
}

.dropzone-icone {
  width: 34px;
  height: 34px;
  background: var(--icone-vazio) center / contain no-repeat;
  opacity: 0.8;
}

.dropzone-titulo {
  font-size: 0.98rem;
  font-weight: 600;
  color: var(--text-title);
}

.dropzone-ajuda {
  font-size: 0.8rem;
  color: var(--text-secondary);
  max-width: 46ch;
  line-height: 1.45;
}

/* O nome do arquivo escolhido. Nasce `hidden` e o JS o revela — sem JS o navegador já
   mostra o nome no próprio controle, então não há informação perdida. */
.dropzone-arquivo {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--brand-700);
  background: var(--sage-100);
  border-radius: var(--radius-pill);
  padding: 3px 12px;
  margin-top: 4px;
}

.dropzone-acoes {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.dropzone-aviso {
  font-size: 0.8rem;
  color: var(--text-secondary);
  flex: 1 1 260px;
  line-height: 1.45;
}

@media print {
  .sidebar,
  .topbar,
  .subabas,
  .btn-print,
  .content-header-actions,
  .frescor,
  .alerta-etl {
    display: none !important;
  }

  body { background: #FFFFFF; }
  .app-main { margin: 0 !important; }
  .content { max-width: none; padding: 0; }
  .panel {
    break-inside: avoid;
    box-shadow: none;
    border-color: #CCC8BC;
  }
  .data-table th,
  .matriz-tabela thead th { background: #F2F1EB !important; }
}

/* ==========================================================================
   Meta ao lado da métrica (core/_meta_card.html, core/_metas_da_tela.html)
   ========================================================================== */
.meta-card {
  font-size: 0.75rem;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

.meta-card .delta-bom { color: var(--good); font-weight: 600; }
.meta-card .delta-mau { color: var(--bad); font-weight: 600; }

.metas-da-tela .delta-bom { color: var(--good); font-weight: 600; }
.metas-da-tela .delta-mau { color: var(--bad); font-weight: 600; }

.metas-da-tela-link {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 500;
}

.metas-da-tela-sem { margin: 12px 0 0; }
