/* ============================================================================
   COMMON.CSS — o DESIGN SYSTEM do webapp
   Carregado por todas as paginas: login, registro, painel, agenda, admin.
   ----------------------------------------------------------------------------
   REGRA (dono, 2026-08-16): "consistencia e coerencia nao sao negociaveis".
   Componente visual compartilhado mora AQUI, uma definicao so. Classe com o
   mesmo nome definida em duas folhas de pagina e divida, mesmo quando as duas
   concordam hoje.

   O QUE JA E COMPONENTE
   ---------------------
     tokens          cores, tipografia, espacamento, sombras, raios, transicoes
     .btn            + -primary / -text / -outlined / -error; 44px, e 40px em
                     contexto denso (.modal-footer, .filters-grid)
     .btn-nav-link   navegacao no cabecalho
     cabecalho       recolhimento do lado direito em 3 etapas, decidido por
                     ESPACO MEDIDO (js/navegacao.js), nunca por breakpoint
                     fixo — a logo nunca muda. + .menu-novo (o botao "+" que
                     cria documento) e .user-menu-who/-nav (identidade e, na
                     etapa 3, a navegacao dentro do menu do usuario)
     .form-input     campos e selects, + .compacto para barra de filtros
     .form-select
     .form-secao     grupo de campos com titulo, recolhivel (+ .form-secao-campos,
                     .largo, .form-nota, .form-secao-cabeca-fixa para o que
                     nao recolhe)
     .section-note   texto explicativo curto sob um TITULO DE SECAO (nao um
                     campo — isso e `.form-nota`). Sem italico e sem
                     `--text-disabled`: esse par e a assinatura de "vazio"
                     (`.interp-empty`), e a nota de secao costuma dizer o
                     contrario
     datepicker      tema do flatpickr + .cal-btn / .cal-ico / .cal-ancora;
                     fiacao em js/datepicker.js (ligarBotaoCalendario,
                     ligarCampoData)
     .kpi            cartao de um numero, + .compacto
     .kpi-grupo      cartao de titulo + N valores + rodape
     .kpi-row        a linha dos dois
     .aviso          faixa de atencao (apelido: .ed-aviso/.pend-aviso)
     .tabs-nav       barra de abas, + .tab-btn (apelido: .painel-tabs/.painel-tab)
     .spinner        o disco de carregando, + @keyframes girar
     .badge          selo de rotulo curto, 12px/500/raio total, + as TRES
                     variantes de modalidade (-clinica / -escolar / -domiciliar,
                     uma por valor do dominio). A geometria canonica foi DECIDIDA
                     por hierarquia medida, nao por gosto: ver o bloco do componente
     .seg/.seg-b     segmentado: N opcoes exclusivas, todas visiveis. Serve como
                     FILTRO (botoes, `.on` no ativo — Agenda) e como CAMPO de
                     formulario (radios dentro do rotulo — registro e edicao de
                     sessao). Os dois no mesmo componente porque e o mesmo
                     desenho: mudar so o desenho do filtro deixaria duas barras
                     diferentes na mesma tela
     modalidade      --mod-*  (agenda)
     desfecho        --desf-* + forma, nunca so cor (deuteranopia)
     .busca-com-limpar / .busca-limpar   campo de busca por nome com "x" para
                     limpar (Admin: Pacientes/Profissionais/Programas/Especialidades)

   ARMADILHAS DE LAYOUT QUE JA REINCIDIRAM
   ---------------------------------------
     ITEM DE FLEX QUE HOSPEDA TEXTO LEVA `min-width: 0` JUNTO.

     `flex: 1` nao autoriza o item a encolher abaixo da largura do CONTEUDO: o
     minimo automatico e `auto`. Com texto dentro, o item cresce, a linha
     estoura e a PAGINA ROLA DE LADO — e o sintoma e sempre enganoso, porque
     nada aparece cortado e nenhum texto fica ilegivel. Procura-se o defeito na
     tela errada.

     Quatro ocorrencias ate 2026-09-08: `.header-content`, `.tabs-nav` do admin,
     `.sessao-info`, e `.segment-btn` — esta ultima aguentou tres botoes de
     status e estourou no quarto ("Pendentes", #112), levando a aba Profissionais
     de 390px para 417px numa tela de 390.

     A nota esta AQUI, e nao no item do ledger, porque as tres primeiras vezes
     foram descobertas por medicao depois do estrago. Quem edita flex neste
     projeto olha para este arquivo; o ledger, nao.

   O QUE AINDA E POR PAGINA (divida conhecida)
   -------------------------------------------
     .modal*      quatro telas usam os nomes, e NAO e o mesmo componente: no
                  admin `.modal` e o veu, no registro e a caixa, na agenda e no
                  painel e o envoltorio. Unificar exige mexer na marcacao das
                  quatro, e nao so no CSS.
     .filters-*   tres consumidores (agenda, admin, painel) com `.filters-grid`
                  em flex numa e em grid nas outras, e um `.filter-label` de
                  14px acima do campo contra outro de 12px. Promover hoje
                  obrigaria o painel a anular o componente — e componente que
                  todo consumidor anula nao e componente. Precisa de decisao de
                  design, nao de refatoracao.
     .empty-state / .loading-state   tres telas, tres marcacoes diferentes.
     .action-row  PROMOVIDA em 2026-09-02: o segundo consumidor apareceu (o
                  link do modal de desfecho para o prontuario, que nasce em
                  `js/minha-agenda.js` e vive em tres paginas). Fica na secao de
                  LISTAS E LINHAS, abaixo.
     .escopo-b, .cfg-*, .dchip, .jt, .btn-nav
                  um consumidor so (a Agenda). `.btn-nav` PARECIA ter dois — o
                  painel tinha `.ma-nav .btn-nav` escrito por inteiro —, mas o
                  bloco `.ma-nav` do painel era CSS morto: nenhuma marcacao o
                  usa desde que a regua de dias substituiu o navegador de
                  semana. Duas definicoes nao sao dois consumidores. Viram
                  componente quando o segundo aparecer de verdade — nao antes.

   COMO PROMOVER, SEM QUEBRAR
   --------------------------
   A folha da pagina carrega DEPOIS desta e vence empate de especificidade. Se o
   nome ja existe numa pagina, o rename entra NO MESMO COMMIT — senao o
   componente novo e sobrescrito em silencio, so naquela tela.

   Tres vezes o mesmo enredo, sempre revelado pela TERCEIRA tela: `.btn` (a
   agenda recebeu botao cru do navegador), o datepicker (tema numa folha de
   pagina e seletores aqui: cabecalho branco no branco) e `.form-input` (a classe
   nao casava com nada na agenda). O que parecia componente era copia.

   O vendor (flatpickr.min.css) tem de vir ANTES desta folha em todo HTML, senao
   vence os overrides de tema.
   ============================================================================ */

/* --- CSS Reset --- */
*, *::before, *::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* `hidden` VALE, custe o que custar (LEDGER #65). A regra do navegador
   ([hidden] { display: none }) perde para qualquer `display` explicito do
   componente — foi assim que botoes com `hidden` continuaram na tela CINCO
   vezes, uma por componente (.segment-btn foi a ultima, oferecendo acoes que
   respondiam 403). O !important e necessario: as folhas de pagina carregam
   DEPOIS desta e empatariam em especificidade. Remover elemento do DOM segue
   sendo o padrao preferido para permissao; isto e a rede para o que sobra. */
[hidden] { display: none !important; }

/* --- Design Tokens --- */
:root {
  /* Primaria: Azul Royal — Identidade Calmare */
  --primary: #0a3d91;
  --primary-dark: #082e6e;
  --primary-light: #00aeef;
  --primary-50: #eef5ff;
  --primary-100: #d9e8ff;
  --primary-alpha-10: rgba(10, 61, 145, 0.10);
  --primary-alpha-20: rgba(10, 61, 145, 0.20);

  /* Secundaria: Slate */
  --secondary: #475569;
  --secondary-light: #64748b;

  /* Acentos Calmare */
  --accent-pink: #e91e76;
  --accent-green: #a0c800;
  --accent-orange: #ff5500;

  /* Feedback */
  --success: #16a34a;
  --success-bg: #f0fdf4;
  --warning: #d97706;
  --warning-bg: #fffbeb;
  --error: #dc2626;
  --error-bg: #fef2f2;
  --info: #00aeef;
  --info-bg: #eef8ff;

  /* Modalidade de atendimento (Agenda) — derivada dos tokens acima, sem cor nova.
     O texto da celula NUNCA usa estas cores: fica em --text-primary (14:1 ou mais
     sobre os tres fundos). A cor vive no fundo e na faixa lateral, que precisam
     apenas dos 3:1 de componente grafico. Os tres fundos tem luminancia quase
     identica entre si, entao a modalidade tambem e sinalizada por marcador
     textual (E / D) — sem ele, viram a mesma celula em escala de cinza. */
  --mod-clinica-bg: var(--primary-100);
  --mod-clinica-ac: var(--primary);
  --mod-escolar-bg: var(--success-bg);
  --mod-escolar-ac: var(--success);
  --mod-domiciliar-bg: var(--warning-bg);
  --mod-domiciliar-ac: var(--warning);

  /* Desfecho do atendimento (Agenda). Canal diferente da modalidade: esta vive
     no fundo da celula, o desfecho num ponto sobreposto — nao competem.
     Verde e vermelho tem luminancia parecida e sao justamente o par que a
     deuteranopia confunde, entao o ponto TAMBEM difere em forma: atendido e
     disco cheio, faltou e anel grosso, desmarcou e anel fino. Cor sozinha nunca
     carrega a informacao. */
  --desf-atendido: var(--success);
  --desf-faltou: var(--error);
  --desf-desmarcou: var(--text-disabled);
  /* "Ninguem disse, e ja passou" (#152). Laranja porque e cobranca e nao
     desfecho — verde/vermelho/cinza sao os tres que ALGUEM informou. */
  --desf-pendente: var(--accent-orange);

  /* Os EVENTOS COM PARTICIPANTES da agenda (migration 059), em tres cores de
     TEXTO escuras (>= 7:1 sobre branco): a forma — borda, icone e rotulo
     escrito — carrega a informacao, e a cor so reforca. Camada DATADA
     (encaixe, orientacao, avaliacao, supervisao externa) e borda tracejada; a
     orientacao recorrente (ate a migration 060, "supervisao fixa") e camada da
     ESCALA, e por isso borda dupla, nunca tracejada. Tons do mockup aprovado
     (`docs/agenda/mockup-eventos-com-participantes.html`). */
  --ev-encaixe: var(--primary);
  --ev-orientacao: #a3104a;      /* rosa Calmare escurecido para texto */
  --ev-orientacao-bg: #fdeef3;
  --ev-avaliacao: #075985;       /* azul-ceu escurecido para texto */
  --ev-avaliacao-bg: var(--info-bg);
  --ev-recorrente: #334155;      /* ardosia: a camada da escala */
  --ev-recorrente-bg: var(--background-alt);
  /* Supervisao EXTERNA (#166): verde-lima Calmare escurecido para texto
     (#3f6212 sobre branco ~7,6:1). */
  --ev-supervisao: #3f6212;
  --ev-supervisao-bg: #f3f8e6;

  /* Neutros */
  --text-primary: #0f172a;
  --text-secondary: #64748b;
  --text-disabled: #94a3b8;
  --text-white: #ffffff;

  --surface: #ffffff;
  --background: #f8fafc;
  --background-alt: #f1f5f9;
  --border-color: #e2e8f0;
  --border-dark: #cbd5e1;

  /* Tipografia */
  --font-primary: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /* Espacamento (base 4px) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;

  /* Sombras */
  --shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
  --shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04);
  --shadow-md: 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.05);
  --shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, 0.08), 0 4px 6px -4px rgba(15, 23, 42, 0.04);
  --shadow-xl: 0 20px 25px -5px rgba(15, 23, 42, 0.1), 0 8px 10px -6px rgba(15, 23, 42, 0.05);

  /* Border Radius */
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-full: 9999px;

  /* Transicoes */
  --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Componentes */
  --switch-bg: #b0bec5;
  --switch-bg-active: var(--primary);

  /* Gradientes */
  --gradient-login: linear-gradient(135deg, #0a3d91 0%, #00aeef 100%);
}

/* --- Base Styles --- */
body {
  font-family: var(--font-primary);
  font-size: var(--text-base);
  color: var(--text-primary);
  background: var(--background);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.container {
  width: 100%;
  max-width: 800px;
  margin: 0 auto;
  padding: 0 var(--space-4);
}

.container-wide {
  max-width: 1200px;
}

/* --- Utilidades --- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ============================================================================
   BOTOES — base compartilhada

   Estavam definidos em admin/styles.css e painel/styles.css, e em lugar nenhum
   comum: a agenda usou `.btn` no modal de edicao e recebeu o botao cru do
   navegador. Mesma classe do "Carregar mais" do painel, que usava
   `.btn-outlined` sem que a classe existisse naquela pagina.
   ============================================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  /* 44px e o alvo de toque recomendado, e este app e mobile-first. O admin ja
     usava esta medida; painel e agenda usavam 36px — o menor estava justamente
     nas telas que mais se usam no celular. */
  min-height: 44px;
  padding: var(--space-3) var(--space-5);
  border: none;
  border-radius: var(--radius-md);
  font-family: var(--font-primary);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: all var(--transition-base);
  text-decoration: none;
}
.btn .material-icons { font-size: 18px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* Contextos densos: numa barra de filtros ou num rodape de modal o botao divide
   a linha com selects e campos, e 44px quebra o alinhamento. Regra do sistema
   sobre o CONTEXTO, nao classe a se lembrar de por em cada botao. */
.modal-footer .btn,
.filters-grid .btn {
  min-height: 40px;
  padding: var(--space-2) var(--space-4);
}

.btn-primary { background: var(--primary); color: var(--text-white); }
.btn-primary:hover:not(:disabled) { background: var(--primary-dark); box-shadow: var(--shadow-md); }

.btn-text { background: transparent; color: var(--text-secondary); }
.btn-text:hover { background: var(--background-alt); }

.btn-outlined { background: transparent; color: var(--primary); border: 1px solid var(--primary); }
.btn-outlined:hover { background: var(--primary); color: var(--text-white); }

.btn-error { background: var(--error); color: var(--text-white); }
.btn-error:hover:not(:disabled) { background: #b91c1c; }

/* Navigation links (Admin, Meu Painel) — estilo outline consistente.
   Altura 44px (nao 36): e alvo de toque do cabecalho, igual a qualquer outro
   (#169). O rotulo NAO some por breakpoint fixo — quem decide se ele cabe e o
   componente de cabecalho (`js/navegacao.js`, `recalcularCabecalho`), medindo
   o espaco de verdade; ver a secao CABECALHO abaixo. */
.btn-nav-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  font-family: var(--font-primary);
  cursor: pointer;
  text-decoration: none;
  /* NUNCA `transition: all` aqui (#169): a largura muda entre as etapas do
     cabecalho, e `all` faz o NAVEGADOR ANIMAR essa mudanca — o que quer dizer
     que `getBoundingClientRect()`, chamado logo depois de trocar a etapa,
     mede o quadro DO MEIO da transicao (mais estreito), nao o final. O
     algoritmo de `recalcularCabecalho()` concluia "cabe" com uma largura que
     so existiu por 150ms, e a largura de VERDADE — maior — so aparecia
     depois, sobrepondo a logo sem nenhum resize novo para corrigir. Medido:
     768px, folga lida ~67px no instante da troca, ~-44px 700ms depois. */
  transition: color var(--transition-fast), background-color var(--transition-fast),
    border-color var(--transition-fast);
}
.btn-nav-link .material-icons { font-size: 18px; }
.btn-nav-link:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-50);
}

/* ============================================================================
   USER MENU — Componente global (header de todas as paginas)

   `.user-menu` e o toggle GENERICO (aberto/fechado, Esc, clique fora, fecha
   ao abrir outro) — `js/api.js` liga qualquer elemento com esta classe, nao
   so o do usuario. `.menu-novo` (abaixo) e OUTRA instancia da mesma classe,
   com o proprio visual: o toggle e um so, o resto e por cima.
   ============================================================================ */
.user-menu {
  position: relative;
}
/* Altura 44px (nao 36): alvo de toque do cabecalho (#169). O rotulo/e-mail
   NAO somem por breakpoint fixo — quem decide e `recalcularCabecalho`,
   medindo o espaco de verdade; ver a secao CABECALHO abaixo. */
.user-menu-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-3);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-secondary);
  font-size: var(--text-sm);
  font-family: var(--font-primary);
  cursor: pointer;
  /* NUNCA `transition: all` (#169) — mesma razao de `.btn-nav-link`: a
     largura deste botao muda entre as etapas do cabecalho, e `all` faria o
     navegador ANIMAR a mudanca, entao `getBoundingClientRect()` mediria o
     quadro do meio da transicao. Ver o comentario em `.btn-nav-link`. */
  transition: color var(--transition-fast), background-color var(--transition-fast),
    border-color var(--transition-fast);
}
.user-menu-trigger:hover {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-50);
}
.user-menu-trigger .material-icons:first-child {
  font-size: 20px;
}
.user-menu-trigger #user-email {
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-menu-arrow {
  font-size: 18px !important;
  transition: transform 0.2s;
}
.user-menu.open .user-menu-arrow {
  transform: rotate(180deg);
}
/* `:not(.menu-novo)` (#169, revisao de code-review): sem ele, esta regra —
   DUAS classes de especificidade — vencia `.menu-novo-trigger` (uma classe
   so) mesmo vindo ANTES dela na folha, e o "+" aberto clareava para o
   contorno cinza do menu do usuario, parecendo outro controle. O "+" tem a
   propria regra de aberto logo abaixo, preenchido como sempre foi. */
.user-menu.open:not(.menu-novo) .user-menu-trigger {
  border-color: var(--primary);
  color: var(--primary);
  background: var(--primary-50);
}
.menu-novo.open .menu-novo-trigger {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
  color: var(--text-white);
}

/* Dropdown */
.user-menu-dropdown {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 180px;
  background: #ffffff;
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  box-shadow: 0 10px 15px -3px rgba(15,23,42,0.08), 0 4px 6px -4px rgba(15,23,42,0.04);
  padding: 8px 0;
  z-index: 200;
  animation: userMenuFadeIn 0.15s ease;
}
.user-menu.open .user-menu-dropdown {
  display: block;
}
@keyframes userMenuFadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}
.user-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: none;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--text-sm);
  font-family: var(--font-primary);
  cursor: pointer;
  transition: background var(--transition-fast);
  text-align: left;
}
.user-menu-item .material-icons {
  font-size: 18px;
  color: var(--text-secondary);
}
.user-menu-item:hover {
  background: var(--background);
}
.user-menu-item:hover .material-icons {
  color: var(--error);
}
.user-menu-item:hover {
  color: var(--error);
}
.user-menu-divider {
  height: 1px;
  background: var(--border-color);
  margin: var(--space-2) 0;
}

/* ============================================================================
   IDENTIDADE E NAVEGACAO DENTRO DO MENU (#169, C-3/C-4)

   Plantados uma vez por `garantirIdentidadeUsuario()` (js/navegacao.js), no
   TOPO do dropdown — antes de "Notificacoes" e "Sair", que sao plantados por
   outro codigo e podem chegar antes ou depois no tempo. `.user-menu-nav`
   comeca vazio e so recebe os `.btn-nav-link` (nos MOVIDOS, nao clonados)
   quando o cabecalho nao tem espaco nem para os icones (etapa 2).
   ============================================================================ */
.user-menu-who {
  padding: var(--space-2) var(--space-4) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.user-menu-who strong {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}
.user-menu-who span {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-menu-nav {
  display: flex;
  flex-direction: column;
}
.user-menu-nav:empty { display: none; }
/* O link, deslocado do cabecalho para dentro do menu, troca de roupa: era um
   botao com borda e 44px quadrados: aqui e uma linha do menu, como
   `.user-menu-item`, mas sem herdar o hover vermelho dela — sair nao e a
   mesma familia de acao de "ir para a Agenda". */
.user-menu-nav .btn-nav-link {
  height: auto;
  min-height: 44px;
  width: auto;
  border: none;
  border-radius: 0;
  background: transparent;
  justify-content: flex-start;
  padding: var(--space-3) var(--space-4);
  color: var(--text-primary);
}
.user-menu-nav .btn-nav-link span { display: inline !important; }
.user-menu-nav .btn-nav-link:hover {
  background: var(--background);
  border-color: transparent;
  color: var(--primary);
}

/* ============================================================================
   O BOTAO "+" — CRIAR DOCUMENTO (#169, C-1)

   Substitui "Nova Sessao" e "Nova Ficha" nas paginas onde existiam
   (painel e admin): um botao so, cujo menu oferece so o que a permissao da
   pessoa autoriza. Monta em `#menu-novo-mount` — ausente nas paginas que nao
   oferecem criacao pelo cabecalho (agenda, registro, prontuario).
   ============================================================================ */
.menu-novo-trigger {
  border-color: var(--primary);
  background: var(--primary);
  color: var(--text-white);
}
.menu-novo-trigger:hover {
  background: var(--primary-dark);
  border-color: var(--primary-dark);
  color: var(--text-white);
}
.menu-novo-dropdown {
  min-width: 280px;
  max-width: min(340px, calc(100vw - 32px));
  padding: var(--space-2) 0;
}
.menu-novo-titulo {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.menu-novo-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  color: var(--text-primary);
  text-decoration: none;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.menu-novo-item:hover { background: var(--primary-50); }
.menu-novo-item .material-icons { color: var(--primary); font-size: 20px; margin-top: 2px; }
.menu-novo-item strong { display: block; font-size: var(--text-sm); font-weight: var(--weight-semibold); }
.menu-novo-item small { display: block; font-size: var(--text-xs); color: var(--text-secondary); margin-top: 1px; }
.menu-novo-sep {
  height: 1px;
  background: var(--border-color);
  margin: var(--space-2) 0;
}
.menu-novo-nota {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4) var(--space-1);
  font-size: var(--text-xs);
  color: var(--text-secondary);
}
.menu-novo-nota .material-icons { font-size: 16px; }

/* ============================================================================
   CABECALHO — recolhimento do lado direito em ETAPAS (#169)

   A logo NUNCA muda (C-2) — ela mantem a altura e a proporcao de sempre em
   `.header-title .header-logo`, acima. Quem decide quanto do lado direito
   cabe e `recalcularCabecalho()` (js/navegacao.js), medindo a folga real
   entre a logo e o primeiro item — nunca um breakpoint fixo, porque o numero
   de itens muda com o PAPEL de quem entrou, nao so com a largura da tela.
   Tres etapas, marcadas em `.header-right[data-etapa]`:

     0 — tudo com rotulo (o padrao desta folha, acima)
     1 — nav links e o "+" perdem o rotulo, viram quadrados de 44px
     2 — a navegacao sai do cabecalho e entra no topo do menu do usuario
         (`.user-menu-nav`, acima); o gatilho do menu troca a seta por "☰"

   O ATRIBUTO AUSENTE NAO E MAIS A ETAPA 0 — e "ainda nao medido". Da
   primeira medicao em diante `js/navegacao.js` sempre grava um valor
   explicito ('0', '1' ou '2'), nunca remove o atributo. A ausencia so
   acontece ANTES da primeira medicao (enquanto `GET /api/auth/navegacao`
   ainda nao respondeu), e e para esse instante que existe o PALPITE DE CSS
   logo abaixo.
   ============================================================================ */
.header-right[data-etapa="1"] .btn-nav-link span,
.header-right[data-etapa="1"] .menu-novo-trigger span,
.header-right[data-etapa="2"] .menu-novo-trigger span,
.header-right[data-etapa="1"] .user-menu-trigger #user-email,
.header-right[data-etapa="2"] .user-menu-trigger #user-email,
.header-right[data-etapa="1"] .user-menu-trigger .user-menu-arrow {
  display: none;
}
.header-right[data-etapa="1"] .btn-nav-link,
.header-right[data-etapa="1"] .menu-novo-trigger,
.header-right[data-etapa="2"] .menu-novo-trigger,
.header-right[data-etapa="1"] .user-menu-trigger,
.header-right[data-etapa="2"] .user-menu-trigger {
  min-width: 44px;
  padding: 0;
  justify-content: center;
}
/* Largura fixa quando so ha UM icone dentro: o "+" (nas duas etapas — ele
   nunca ganha um segundo icone) e o link/usuario na etapa 1 (o nome ja saiu,
   e a seta some junto). Sem isto o quadrado respira mais que o vizinho por
   causa de um `padding` herdado. Na etapa 2 o usuario fica com DOIS icones
   (account_circle + a seta virada "☰") e por isso so leva `min-width` acima. */
.header-right[data-etapa="1"] .btn-nav-link,
.header-right[data-etapa="1"] .menu-novo-trigger,
.header-right[data-etapa="2"] .menu-novo-trigger,
.header-right[data-etapa="1"] .user-menu-trigger {
  width: 44px;
}
/* Sem rotulo, o respiro generoso do modo com texto (--space-4, 16px) so
   consome espaco que os quadrados precisam. */
.header-right[data-etapa="1"],
.header-right[data-etapa="2"] {
  gap: var(--space-2);
}

/* ============================================================================
   PALPITE INICIAL, so ate a primeira medicao (#169, ponto 4 da revisao)

   Entre o primeiro paint e `GET /api/auth/navegacao` responder ha tres
   esperas de rede em fila (token, navegacao, permissoes) antes de
   `recalcularCabecalho()` rodar pela primeira vez — e ate la o atributo
   `data-etapa` nao existe. Sem este palpite, o cabecalho ficava com rotulo
   inteiro (etapa 0) em QUALQUER largura por esse instante: real numa rede
   lenta, nao teorico — `#user-email` comeca com "Carregando..." em duas
   paginas (admin, registro), e isso sozinho ja e largo o bastante para
   sobrepor a logo a 320px.

   So vale enquanto o atributo estiver AUSENTE — `:not([data-etapa])`. No
   instante em que `recalcularCabecalho()` mede pela primeira vez, o
   atributo passa a ter um valor SEMPRE (nunca mais e removido, mesmo para a
   etapa 0 — ver o comentario da secao acima), e estas regras nunca mais se
   aplicam. Os dois limiares (~768, ~480) sao a MESMA aparencia das etapas 1
   e 2 acima — replicada aqui porque um seletor de atributo nao "adivinha"
   `:not`.

   A etapa 2 de verdade MOVE os links de navegacao para dentro do menu —
   coisa que CSS sozinho nao faz. O palpite aqui so os ESCONDE
   (`display:none`) abaixo de 480px: nenhuma sobreposicao, a navegacao fica
   momentaneamente inalcancavel ate o JS medir e move-los para o menu de
   verdade, o que e melhor que sobrepor a logo.
   ============================================================================ */
@media (max-width: 768px) {
  .header-right:not([data-etapa]) {
    gap: var(--space-2);
  }
  .header-right:not([data-etapa]) .btn-nav-link span,
  .header-right:not([data-etapa]) .menu-novo-trigger span,
  .header-right:not([data-etapa]) .user-menu-trigger #user-email,
  .header-right:not([data-etapa]) .user-menu-trigger .user-menu-arrow {
    display: none;
  }
  .header-right:not([data-etapa]) .btn-nav-link,
  .header-right:not([data-etapa]) .menu-novo-trigger,
  .header-right:not([data-etapa]) .user-menu-trigger {
    width: 44px;
    min-width: 44px;
    padding: 0;
    justify-content: center;
  }
}
@media (max-width: 480px) {
  .header-right:not([data-etapa]) .btn-nav-link {
    display: none;
  }
}

/* ============================================================================
   NOTIFICATION BADGE + MENU ITEM
   ============================================================================ */
.user-menu-trigger { position: relative; }
.notif-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--error);
  color: white;
  font-size: 11px;
  font-weight: var(--weight-bold);
  line-height: 18px;
  text-align: center;
}
.menu-badge {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--error);
  color: white;
  font-size: 11px;
  font-weight: var(--weight-bold);
  line-height: 20px;
  text-align: center;
}

/* ============================================================================
   NOTIFICATION MODAL (leitura — global)
   ============================================================================ */
.notif-modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  z-index: 1000;
  align-items: center;
  justify-content: center;
}
.notif-modal.active { display: flex; }
.notif-modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0,0,0,0.5);
}
.notif-modal-container {
  position: relative;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  width: 90%;
  max-width: 560px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}
.notif-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-color);
}
.notif-modal-header h2 {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.notif-modal-header h2 .material-icons { color: var(--primary); }
.notif-modal-close {
  background: none; border: none;
  font-size: 24px; cursor: pointer;
  color: var(--text-secondary); padding: var(--space-1);
}
.notif-modal-body {
  padding: var(--space-4) var(--space-5);
  overflow-y: auto;
  flex: 1;
}
.notif-card {
  padding: var(--space-4);
  border-radius: var(--radius-md);
  border: 1px solid var(--border-color);
  margin-bottom: var(--space-3);
  transition: background 0.15s;
}
.notif-card.unread {
  background: var(--primary-50);
  border-color: var(--primary-light);
}
.notif-card-header {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.notif-card-title {
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
  flex: 1;
}
.notif-type-badge {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  padding: 2px 8px;
  border-radius: var(--radius-full);
}
.notif-type-informativo { background: var(--primary-50); color: var(--primary); }
.notif-type-importante { background: #fff7ed; color: #c2410c; }
.notif-type-urgente { background: var(--error-bg); color: var(--error); }

/* ------------------------------------------- ponto de desfecho (Agenda/Painel)
   Forma + cor, nunca so cor: ver o comentario dos tokens --desf-*. */
.desf-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
  box-sizing: border-box;
}
.desf-dot.atendido { background: var(--desf-atendido); }
.desf-dot.faltou { border: 3px solid var(--desf-faltou); background: var(--surface); }
.desf-dot.desmarcou { border: 1.5px solid var(--desf-desmarcou); background: transparent; }
/* O QUARTO ESTADO: o atendimento passou e ninguem disse o que foi (#152).
   QUADRADO, e nao um quarto circulo: os tres acima ja se distinguem por
   preenchimento e espessura, e uma quarta variacao da mesma forma seria a
   diferenca mais fraca das quatro — justo a que precisa ser notada. TRACEJADO
   porque e o idioma que o DS ja usa para "nao terminado" (`.badge-rascunho`),
   e porque a forma tem de dizer sozinha, sem a cor. */
.desf-dot.pendente {
  border-radius: 2px;
  border: 2px dashed var(--desf-pendente);
  background: transparent;
  width: 10px;
  height: 10px;
}
/* ------------------------------------------- marca do tipo de evento (Agenda)
   Quadradinho com o icone do tipo, na forma da camada: tracejado na datada,
   borda dupla na orientacao recorrente. Serve ao menu "Novo evento", a legenda da
   grade, ao modal de presenca e a lista de pendencias — as duas ultimas moram
   em `js/minha-agenda.js`, que desenha em tres paginas. */
.ev-marca {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 7px;
  flex-shrink: 0;
  background: var(--surface);
  border: 1.5px dashed currentColor;
  color: var(--ev-encaixe);
}
.ev-marca .material-icons { font-size: 16px; }
.ev-marca.orientacao { color: var(--ev-orientacao); }
.ev-marca.avaliacao { color: var(--ev-avaliacao); }
.ev-marca.reuniao { color: var(--text-secondary); }
.ev-marca.supervisao { color: var(--ev-supervisao); }
.ev-marca.orientacao_recorrente {
  color: var(--ev-recorrente);
  border: 3px double currentColor;
  background: var(--ev-recorrente-bg);
}
.ev-marca.peq { width: 20px; height: 20px; border-radius: 5px; }
.ev-marca.peq .material-icons { font-size: 13px; }

.notif-card-body {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.5;
  white-space: pre-line;
}
.notif-card-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-3);
  font-size: var(--text-xs);
  color: var(--text-disabled);
}
.notif-card-actions { display: flex; gap: var(--space-2); }
.notif-btn-read {
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--primary);
  cursor: pointer;
  font-family: var(--font-primary);
  transition: all 0.15s;
}
.notif-btn-read:hover {
  background: var(--primary);
  color: white;
}
.notif-empty {
  text-align: center;
  padding: var(--space-6);
  color: var(--text-secondary);
}
.notif-section-label {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--text-disabled);
  margin-bottom: var(--space-2);
  margin-top: var(--space-4);
}
.notif-section-label:first-child { margin-top: 0; }
.notif-btn-dismiss {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-disabled);
  padding: 2px;
  border-radius: 50%;
  transition: all 0.15s;
  margin-left: auto;
  flex-shrink: 0;
}
.notif-btn-dismiss:hover {
  color: var(--error);
  background: var(--error-bg);
}

/* ============================================================================
   OBLIGATORY MODAL (bloqueante ao login)
   ============================================================================ */
.oblig-modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  z-index: 2000;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,0.7);
  -webkit-overflow-scrolling: touch;
}
.oblig-modal.active { display: flex; }
.oblig-modal-container {
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  width: 90%;
  max-width: 500px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.oblig-modal-header {
  padding: var(--space-4) var(--space-5);
  background: var(--error);
  color: white;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.oblig-modal-header .material-icons { font-size: 24px; }
.oblig-modal-header h2 { font-size: var(--text-base); font-weight: var(--weight-semibold); }
.oblig-modal-body {
  padding: var(--space-5);
  overflow-y: auto;
  flex: 1;
}
.oblig-modal-title {
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  margin-bottom: var(--space-3);
}
.oblig-modal-content {
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.6;
  white-space: pre-line;
  margin-bottom: var(--space-4);
}
.oblig-modal-footer {
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--border-color);
}
.oblig-checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.oblig-checkbox-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--primary);
  cursor: pointer;
}
.oblig-checkbox-row label {
  font-size: var(--text-sm);
  color: var(--text-primary);
  cursor: pointer;
}
.oblig-btn-confirm {
  width: 100%;
  padding: var(--space-3);
  border: none;
  border-radius: var(--radius-md);
  background: var(--primary);
  color: white;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  font-family: var(--font-primary);
  cursor: pointer;
  transition: opacity 0.15s;
}
.oblig-btn-confirm:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.oblig-btn-confirm:not(:disabled):hover {
  background: var(--primary-dark);
}
.oblig-counter {
  text-align: center;
  font-size: var(--text-xs);
  color: var(--text-disabled);
  margin-top: var(--space-2);
}

/* ============================================================================
   SHELL DA PAGINA — header e area principal
   Estava duplicado em admin/, painel/ e registro/ (mesmas regras, tres copias).
   Fica aqui para paginas novas nao repetirem a quarta: como o CSS especifico de
   cada pagina carrega depois, as tres existentes continuam mandando no proprio
   visual e nada muda para elas.
   ============================================================================ */
.header {
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  position: sticky;
  top: 0;
  z-index: 100;
}

.header-content {
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-6);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
}

.header-left { display: flex; align-items: center; gap: var(--space-4); }

/*
 * O cabecalho estourava a largura da tela no celular — em TODAS as paginas
 * (admin, painel, agenda, registro), 418px de conteudo numa janela de 390.
 * O produto e mobile-first e a pagina inteira rolava de lado.
 *
 * Causa: `.header-content` e flex, e item de flex nao encolhe abaixo do proprio
 * conteudo por padrao (`min-width: auto`). O titulo empurrava o menu do usuario
 * para fora, e como o menu esta a direita, o estouro so aparecia como barra de
 * rolagem horizontal — nao como texto cortado, que alguem teria notado.
 *
 * `min-width: 0` devolve a permissao de encolher; o titulo trunca com
 * reticencias em vez de empurrar. O menu do usuario NAO encolhe: e o unico
 * caminho para sair do sistema.
 */
.header-left { min-width: 0; }
.header-right { flex-shrink: 0; }
.header-title { min-width: 0; }
.header-title > span,
.header-title > .header-titulo-texto {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.header-title {
  font-size: var(--text-2xl);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
}

.header-title .header-logo { height: 36px; width: auto; object-fit: contain; }

.header-right { display: flex; align-items: center; gap: var(--space-4); }

.main-content {
  max-width: 1400px;
  margin: 0 auto;
  padding: var(--space-6);
}

@media (max-width: 768px) {
  .header-content { padding: var(--space-3) var(--space-4); }
  .main-content { padding: var(--space-4); }
  .header-title .header-logo { height: 28px; }
}

/* ============================================================================
   DATEPICKER — contraste do seletor de mes

   O cabecalho do calendario e azul, entao o <select> de mes herda texto branco
   (`color: inherit` do flatpickr). Isso funciona no cabecalho, mas o dropdown
   nativo abre sobre o fundo claro do sistema: branco sobre branco, e a lista de
   meses fica ilegivel — so o item selecionado aparece.

   As <option> precisam de cor E fundo explicitos: varios navegadores ignoram um
   sem o outro. Fica em common.css porque todo datepicker do app tem o mesmo
   cabecalho — admin, painel e registro.
   ============================================================================ */
/* Quatro classes de proposito: o vendor declara `background-color: transparent`
   nesta option com tres (`.flatpickr-current-month .flatpickr-monthDropdown-months
   .flatpickr-monthDropdown-month`) e e carregado DEPOIS do common.css — no empate
   de especificidade, quem vem depois vence. */
.flatpickr-calendar .flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month {
  color: var(--text-primary);
  background-color: var(--surface);
  font-weight: var(--weight-regular);
}

/* O item selecionado NAO e estilizado de proposito.
   O sistema pinta o realce dele com a propria cor (que pode ser escura), e
   forcar so a cor do texto deixava texto escuro sobre realce escuro — o item
   atual virava o unico ilegivel da lista. Sem a regra, o navegador escolhe o
   par realce+texto, que ja vem com contraste garantido.
   `color-scheme: light` mantem esse par no tema claro, combinando com o resto
   do calendario mesmo quando o sistema esta em modo escuro. */
.flatpickr-calendar .flatpickr-current-month .flatpickr-monthDropdown-months {
  color-scheme: light;
}

/* A altura do dropdown de mes vinha de `painel/agenda.css`. Mesmo componente,
   mesmo lugar: o cabecalho do calendario e daqui. */
.flatpickr-current-month .flatpickr-monthDropdown-months { height: 32px; }

/* O ano e um input numerico, nao um select: as setas herdam a cor do cabecalho
   e somem sobre o azul se nao forem fixadas. */
.flatpickr-current-month input.cur-year { color: var(--text-white); }
.numInputWrapper span.arrowUp::after { border-bottom-color: var(--text-white); }
.numInputWrapper span.arrowDown::after { border-top-color: var(--text-white); }

/* ============================================================================
   DATEPICKER — seletores de mês e ano (js/datepicker.js)

   Substituem os <select> nativos. A lista suspensa nativa é desenhada pelo
   sistema: o item selecionado recebe o realce dele, e estilizar só parte do par
   deixava o item atual ilegível. Aqui a lista é HTML comum, com as cores do
   design system, igual em todo aparelho.
   ============================================================================ */
.fpx-sel { position: relative; display: inline-flex; }

.fpx-sel-btn {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  height: 32px;
  padding: 0 4px 0 8px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-white);
  font-family: var(--font-primary);
  font-size: var(--text-base);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.fpx-sel-btn:hover,
.fpx-sel.aberto .fpx-sel-btn { background: rgba(255, 255, 255, .18); }
.fpx-sel-btn .material-icons { font-size: 18px; transition: transform var(--transition-fast); }
.fpx-sel.aberto .fpx-sel-btn .material-icons { transform: rotate(180deg); }

/* Posicionada por JS quando aberta (presa ao body). O z-index precisa passar do
   calendario, que ja fica acima do resto da pagina. */
.fpx-sel-lista {
  position: fixed;
  z-index: 100000;
  min-width: 100%;
  max-height: 232px;
  overflow-y: auto;
  padding: 4px;
  background: var(--surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  scrollbar-width: thin;
}

.fpx-sel-op {
  display: block;
  width: 100%;
  padding: 8px 12px;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-primary);
  font-size: var(--text-sm);
  font-weight: var(--weight-regular);
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.fpx-sel-op:hover { background: var(--background-alt); }
/* O item atual é marcado por nós, com o par cor+fundo escolhido junto — que era
   exatamente o que o <select> nativo não permitia garantir. */
.fpx-sel-op.atual {
  background: var(--primary-50);
  color: var(--primary);
  font-weight: var(--weight-semibold);
}

/* ============================================================================
   CAMPOS DE FORMULARIO — base compartilhada

   Estavam definidos em admin/, painel/, registro/ e agenda/, quatro vezes, com
   metricas diferentes. A agenda usou `.form-input` na busca de crianca e recebeu
   o campo cru do navegador: a classe existia nas outras tres folhas e nao naquela.
   Terceira vez que o mesmo padrao apareceu (antes: `.btn` e o datepicker).

   O select desenha a propria seta de dois jeitos diferentes — `background-image`
   no painel, elemento `.select-icon` no admin e no registro. Isso fica na folha
   de cada pagina: e marcacao, nao metrica.
   ============================================================================ */
.form-input,
.form-select {
  width: 100%;
  min-height: 44px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  font-family: var(--font-primary);
  font-size: var(--text-base);
  /* Fixo, e nao `normal`: com line-height automatico a altura do controle sai
     das metricas da fonte, e o MESMO CSS rendia 46px numa tela e 47 noutra. Um
     componente que define a propria altura nao depende de qual fonte carregou. */
  line-height: 1.2;
  background: var(--surface);
  color: var(--text-primary);
  color-scheme: light;
  box-sizing: border-box;
  transition: all var(--transition-base);
}
.form-select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 40px;
  cursor: pointer;
}
.form-input:focus,
.form-select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--primary-alpha-10);
}

/* Data e hora NATIVOS, com a mesma caixa dos demais campos.
   -----------------------------------------------------------------------
   `<input type="date">` e `<input type="time">` nao respeitam a altura do
   `.form-input` sozinhos: o Chrome desenha DENTRO deles um editor proprio
   (`::-webkit-datetime-edit`), um indicador de calendario e, no de hora, um
   botao de incremento — cada um com metrica propria. O mesmo CSS que da 45px
   num `<input type="text">` rendia 47px na data e 49px na hora, na mesma tela.
   Nao e detalhe estetico: e a mesma classe declarando alturas diferentes, que
   e o defeito que esta folha existe para nao ter (a conferencia "input: mesma
   altura entre telas" da suite `design-system` reprova por isso).

   A correcao devolve o controle a esta folha: zera o que o navegador poe por
   dentro e reescreve a altura pela MESMA formula do `.form-input` acima
   (padding + linha + borda), em vez de um numero fixo — assim ela acompanha os
   tokens se eles mudarem.

   HOJE SO A HORA CHEGA AQUI. Depois do #134 nao resta nenhum `type="date"` vivo
   no app — os tres ultimos (vigencia da jornada, vigencia da malha e o campo de
   data da ficha) viraram o datepicker do design system, que e um `type="text"` e
   nao passa por estas regras. As linhas de `date` ficam como rede: elas nao
   custam nada e alinham, sozinhas, qualquer campo nativo que volte a aparecer.
   O `type="time"` continua nativo em toda parte — agenda, jornada e ficha. */
input[type="date"].form-input,
input[type="time"].form-input {
  padding-top: 0;
  padding-bottom: 0;
  line-height: 1.2;
  min-height: calc(var(--space-3) * 2 + 1.2em + 2px);
}
input[type="date"].form-input::-webkit-datetime-edit,
input[type="time"].form-input::-webkit-datetime-edit {
  padding: 0;
  line-height: 1.2;
}
/* O botao de incremento do campo de hora e o que mais estica: 4px so ele. E
   ninguem o usa — quem digita hora digita, e a roda do teclado tambem funciona. */
input[type="date"].form-input::-webkit-inner-spin-button,
input[type="time"].form-input::-webkit-inner-spin-button {
  display: none;
}
input[type="date"].form-input::-webkit-calendar-picker-indicator,
input[type="time"].form-input::-webkit-calendar-picker-indicator {
  margin: 0;
  padding: 0;
  height: 16px;
}

/* A area de texto cresce na vertical e nunca na horizontal: arrastada para o
   lado, ela estoura a largura do modal e leva a pagina inteira a rolar de lado.
   Estava como `style="resize:vertical"` na marcacao do modal de avisos — que e a
   forma de escrever isto uma vez por tela em vez de uma vez. */
textarea.form-input {
  resize: vertical;
  min-height: 88px;
  line-height: 1.5;
}

/* Densidade, para a barra de filtros da agenda: o campo divide a linha com
   segmentos de 40px, e o tamanho cheio desalinha a barra inteira. */
.form-input.compacto,
.form-select.compacto {
  min-height: 40px;
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
}

/* Campo de data do flatpickr: o texto centralizado e o cursor de clique valem em
   toda tela que use o datepicker padrao. */
.flatpickr-input[type="hidden"] + .form-input,
input.flatpickr-input.form-input {
  text-align: center;
  cursor: pointer;
}

/* ============================================================================
   SEGMENTADO (.seg / .seg-b) — N opcoes exclusivas, TODAS visiveis
   ----------------------------------------------------------------------------
   Promovido da Agenda quando o registro de sessao virou o segundo consumidor:
   a modalidade deixou de ser um interruptor "ABA Escolar: sim/nao" e passou a
   ter tres valores (migration 035), e um interruptor nao segura tres.

   POR QUE SEGMENTADO E NAO <select> NO FORMULARIO
   Um <select> esconde duas das tres opcoes atras de um toque. Quem nunca abre a
   lista nao descobre que "domiciliar" existe e continua gravando clinica por
   omissao — exatamente a ambiguidade que a migration removeu do banco, de volta
   pela tela. Tres rotulos curtos cabem lado a lado ate em 360px.

   DUAS FORMAS, UM DESENHO
   Como FILTRO, os itens sao <button> e o ativo leva `.on` (a Agenda: dia,
   turno, modalidade, desfecho). Como CAMPO, os itens sao <label> com um
   <input type="radio"> dentro — o valor entra no formulario, o teclado navega
   com as setas e o leitor de tela anuncia "opcao 2 de 3", coisas que um
   <button> com `aria-pressed` nao entrega de graca. O estado marcado e lido por
   `:has(:checked)`; `.on` continua valendo para a forma de botao.

   `fieldset.seg` precisa zerar borda e margem do agente do usuario — sem isso o
   segmentado ganha a moldura antiga do navegador ao redor.
   ============================================================================ */
.seg {
  display: flex;
  gap: 2px;
  background: var(--background-alt);
  padding: 3px;
  border-radius: var(--radius-md);
  flex-wrap: wrap;
  border: none;
  margin: 0;
  min-width: 0;
}
.seg-b {
  border: none;
  background: transparent;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-family: var(--font-primary);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: 5px;
}
.seg-b:hover { color: var(--primary); background: var(--surface); }
.seg-b.on,
.seg-b:has(input:checked) {
  background: var(--primary);
  color: var(--text-white);
  font-weight: var(--weight-semibold);
}
/* O radio some da vista mas NAO do foco: `display:none` o tiraria da ordem de
   tabulacao e o segmentado deixaria de ser alcancavel pelo teclado. */
.seg-b input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}
/* O anel de foco vai no ROTULO, que e o que se ve: o input tem 1px e o anel
   dele apareceria como um risco solto ao lado do texto. */
.seg-b:focus-within {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.seg-b.on:focus-within { outline-color: var(--primary-dark); }

/* O segmentado como CAMPO pede `fieldset` + `legend`: e assim que o leitor de
   tela anuncia "Modalidade, opcao 1 de 3". A moldura do agente do usuario tem
   de sair, e a `legend` fica FORA do `.seg` — dentro, ela viraria mais um item
   do flex e apareceria como um quarto segmento. */
/* `.filter-group` entra na mesma regra: o segmentado "Mostrar" da barra de
   filtros do prontuario e o mesmo componente no mesmo papel, so que numa barra
   em vez de num formulario. Uma segunda regra com as mesmas quatro linhas seria
   a copia de sempre. */
fieldset.form-group,
fieldset.filter-group {
  border: none;
  margin: 0;
  padding: 0;
  min-width: 0;
}
fieldset.form-group > legend,
fieldset.filter-group > legend { padding: 0; }

/* ============================================================================
   SECAO DE FORMULARIO — grupo de campos com titulo

   Nasce com a P1 da multiespecialidade: o cadastro do paciente saiu de 2 campos
   para 14, e 14 campos empilhados num modal corrido nao sao um formulario, sao
   uma lista. O grupo com titulo e o que faz o usuario achar "onde se poe o
   telefone da mae" sem ler os 14 rotulos.

   Mora AQUI, e nao em admin/styles.css, por dois motivos:

   1. Ja nasce com DOIS consumidores — o formulario de paciente e o de
      profissional, que e o gatilho combinado do arquivo ("componente e o que
      aparece na segunda tela").
   2. Nao ha nada equivalente para reaproveitar. `.cfg-card` (agenda) e cartao de
      LEITURA de configuracao, com `.k`/`.v`; `.detail-section` (admin) e bloco
      de leitura do modal de sessao. Nenhum dos dois agrupa CAMPOS.

   Recolhivel, e aberta por padrao. Recolher e escolha de quem preenche — comecar
   fechada esconderia campo que a pessoa nem sabe que existe, que e o oposto do
   problema que este componente resolve. Fechada, o cabecalho diz quantos campos
   daquele grupo estao preenchidos, senao recolher viraria esquecer.

   A grade e de UMA coluna e vira duas a partir de 640px: mobile-first e a
   premissa do produto, e campo de formulario lado a lado no toque produz alvo
   menor que o dedo. `.largo` atravessa as duas colunas (nome, email, textarea).
   ============================================================================ */
.form-secao {
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--surface);
  margin-bottom: var(--space-4);
  overflow: hidden;
}
.form-secao:last-child { margin-bottom: 0; }

.form-secao-cabeca {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--background);
  border: 0;
  border-bottom: 1px solid var(--border-color);
  font-family: var(--font-primary);
  text-align: left;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.form-secao-cabeca:hover { background: var(--background-alt); }
.form-secao-cabeca:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}
.form-secao-cabeca > .material-icons { color: var(--primary); font-size: 20px; }

/* Cabeca que NAO recolhe.
   Fica aqui, e nao na folha da pagina, porque e variante do componente e nao
   componente novo: separar as duas cabecas em arquivos diferentes e o comeco
   exato da divergencia que este arquivo existe para impedir. A diferenca e uma
   so — nao e botao, entao nao tem foco, nao tem hover e nao tem seta. */
.form-secao-cabeca-fixa {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--background);
  border-bottom: 1px solid var(--border-color);
}
.form-secao-cabeca-fixa > .material-icons { color: var(--primary); font-size: 20px; }

.form-secao-titulo {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
  letter-spacing: .01em;
}
.form-secao-conta {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  white-space: nowrap;
}
.form-secao-seta {
  color: var(--text-disabled);
  font-size: 20px;
  transition: transform var(--transition-fast);
}
.form-secao.recolhida .form-secao-seta { transform: rotate(-90deg); }
.form-secao.recolhida .form-secao-cabeca { border-bottom: 0; }
.form-secao.recolhida .form-secao-corpo { display: none; }

.form-secao-corpo { padding: var(--space-4); }

/* Uma coluna no toque, duas a partir de 640px. A regra do `.form-group` vence a
   margem que as folhas de pagina dao a ele: dentro da grade quem espaca e o
   `gap`, e somar os dois abria o dobro do vao so entre linhas. */
.form-secao-campos {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
.form-secao-campos .form-group { margin-bottom: 0; }
@media (min-width: 640px) {
  .form-secao-campos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .form-secao-campos .largo { grid-column: 1 / -1; }
}

/* Nota sob um campo: o que o servidor vai fazer com aquilo, dito antes de
   recusar. Ex.: "deixe em branco para apagar". */
.form-nota {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin-top: var(--space-1);
}

/* Nota de secao (#58): texto explicativo curto sob um TITULO DE SECAO, e nao
   sob um campo — e por isso nao e `.form-nota`, mesmo com a receita visual
   parecida. Promovida do admin (`.interp-empty` reaproveitado com este
   sentido em `admin/reports.js`), que era a TERCEIRA tela a improvisar a
   mesma necessidade sem classe propria (a segunda foi `.cfg-head p`, na
   Agenda).
   NAO e italico e NAO usa `--text-disabled`: essas duas escolhas sao a
   assinatura visual de "aqui nao ha nada" (`.interp-empty`, `.empty-message`),
   e uma nota de secao normalmente diz o oposto — o dado explicado por ela
   pode ser o mais importante da tela (ex.: por que "3 de 268" nao e abandono
   de registro). Repetir o estilo de vazio na frase que muda a leitura de
   todas as outras seria o mesmo erro que motivou a promocao. */
.section-note {
  font-size: var(--text-xs);
  color: var(--text-secondary);
  margin: 2px 0 var(--space-3);
}

/* Toast (`showToast`, js/ui.js) — PROMOVIDO em 2026-09-23. A agenda chamava
   `showToast` sem ter o container nem o estilo, e os avisos sumiam em silencio;
   ao trocar os tres `window.alert` de erro por toast (regra do dono: nenhum
   dialogo nativo no webapp), o silencio passaria a engolir mensagem de ERRO.

   A REGRA ESCOLHE PELA MARCACAO, como o `.modal` do admin (#84): vale para o
   container SEM a classe `.toast-container`. Admin e registro a tem, e tem
   toast de outro desenho — posicao propria e fundo PINTADO com a cor do
   status. A primeira versao desta regra os quebrava: o id vencia a classe
   deles, e o icone colorido sumia no fundo da mesma cor (achado pelo passe de
   code-review da entrega). Especificidade zerada (`:where`) nao bastava, porque
   so protege a propriedade que a pagina TAMBEM declara — borda e cor de icone
   vazavam. O painel usa este mesmo desenho (a copia dele tem os mesmos
   valores). Unificar de vez e decidir UM desenho de toast e tirar as copias. */
:where(#toast-container:not(.toast-container)) {
  position: fixed;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
:where(#toast-container:not(.toast-container) .toast) {
  background: var(--surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-3) var(--space-4);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  animation: toastIn .3s ease;
}
:where(#toast-container:not(.toast-container) .toast.success) { border-left: 4px solid var(--success); }
:where(#toast-container:not(.toast-container) .toast.error) { border-left: 4px solid var(--error); }
:where(#toast-container:not(.toast-container) .toast.info) { border-left: 4px solid var(--info); }
:where(#toast-container:not(.toast-container) .toast.warning) { border-left: 4px solid var(--warning); }
:where(#toast-container:not(.toast-container) .toast .material-icons) { font-size: 20px; }
:where(#toast-container:not(.toast-container) .toast.success .material-icons) { color: var(--success); }
:where(#toast-container:not(.toast-container) .toast.error .material-icons) { color: var(--error); }
:where(#toast-container:not(.toast-container) .toast.info .material-icons) { color: var(--info); }
:where(#toast-container:not(.toast-container) .toast.warning .material-icons) { color: var(--warning); }
@keyframes toastIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }

/* ============================================================================
   FICHA — os controles que nascem de uma `definicao`
   ----------------------------------------------------------------------------
   Vieram da previa da oficina de fichas (`admin/styles.css`, prefixo
   `.modelo-*`), onde eram o unico desenho de campo a partir da `definicao` de
   um modelo. A tela de PREENCHER (`/prontuario/ficha/`) e a segunda
   consumidora — e a segunda tela e sempre quem revela a copia, como ja
   aconteceu com `.btn`, o datepicker, `.form-input` e `.aviso`.

   Promocao com RENOMEACAO, e o nome importa: `.modelo-*` diz "pertence a tela
   de modelos", e nao e mais verdade. Sem apelido para os nomes antigos, porque
   quem os emitia era uma funcao so — `renderCampoDaPrevia` — que virou
   `FichaCampos.renderCampo` e passou a emitir os novos. Apelido aqui guardaria
   um nome que nenhuma marcacao usa.

   O que NAO subiu: `.modelo-escala` (os dois campinhos de min/max no DESENHO da
   ficha) e o resto do prefixo `.modelo-*`. Aquilo e a oficina, e a oficina tem
   uma tela so.
   ============================================================================ */

/* O asterisco do campo obrigatorio. Cor E simbolo: so a cor nao passa a
   informacao para quem nao a distingue. */
.ficha-req { color: var(--error); font-weight: var(--weight-bold); }

/* A regua de 0 a 10 em botoes. Vira `<input type=number>` acima disso — onze
   alvos ja e o limite de uma linha no celular. */
.ficha-escala { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.ficha-escala-op {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  /* 44px de altura: alvo de toque, e nao estetica. */
  min-width: 40px;
  min-height: 44px;
  justify-content: center;
  padding: var(--space-1);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: var(--text-sm);
}
.ficha-escala-op input { accent-color: var(--primary); }
.ficha-escala-op:has(input:checked) {
  border-color: var(--primary);
  background: var(--primary-50);
}

/* Definicao que a tela nao consegue desenhar. So aparece na previa da oficina,
   onde a ficha ainda esta sendo escrita: a que chega ao preenchimento ja passou
   pelo `camposDaDefinicao` do servidor. */
.ficha-invalido {
  font-size: var(--text-xs);
  color: var(--error);
  padding: var(--space-2) var(--space-3);
  background: var(--error-bg);
  border-radius: var(--radius-sm);
}

/* Uma opcao com caixa de marcar. Estava em `admin/styles.css` e passou a ter
   dois donos no dia em que a ficha preenchida usou `booleano` e `multipla`.
   44px: alvo de toque — a lista tem dezenas de linhas e no celular o dedo cobre
   tres delas se ficarem menores. */
.opcao-linha {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 44px;
  cursor: pointer;
}
/* O radio entra junto porque a LINHA e a mesma coisa: uma opcao com um
   marcador a esquerda e um rotulo tocavel de 44px. A escolha de destinatario do
   export do prontuario e "uma de duas", nao "quantas quiser" — e a unica
   diferenca entre os dois casos e a forma do marcador, que o navegador ja
   desenha. Deixar o radio de fora daria a mesma linha em duas alturas, que foi
   exatamente o defeito que canonizou `.badge`. */
.opcao-linha input[type="checkbox"],
.opcao-linha input[type="radio"] {
  width: 18px;
  height: 18px;
  accent-color: var(--primary);
  flex-shrink: 0;
}

/* A ficha FECHADA: rotulo e resposta, sem controle nenhum.
   Documento finalizado nao vira formulario desabilitado — campo cinza e convite
   a tentar digitar, e o convite nao tem como ser aceito. Vira texto. */
.ficha-lida-linha {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border-color);
}
.ficha-lida-linha:last-child { border-bottom: 0; padding-bottom: 0; }
.ficha-lida-linha:first-child { padding-top: 0; }
.ficha-lida-k {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: .04em;
}
.ficha-lida-v {
  font-size: var(--text-base);
  color: var(--text-primary);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* ============================================================================
   MODAL — a caixa que interrompe
   ----------------------------------------------------------------------------
   Estava em TRES folhas quando a tela de preencher ficha seria a QUARTA: admin
   (`.modal-content`, aberto por `style="display:flex"`), painel e agenda
   (`.modal-container`, aberto por `.active`). As duas ultimas sao a mesma
   marcacao com as mesmas medidas — copia, nao coincidencia.

   O que sobe e a familia `.modal-container`, que ja tinha dois donos. O painel
   perde a copia dele; a tela de ficha nasce sem nenhuma.

   O que NAO sobe, e por que:

   - **admin** usa outra MARCACAO para a CAIXA (`.modal-content`, direto
     dentro de `.modal`, sem `.modal-overlay`). O `.modal`/`.modal.active`
     EXTERNO foi unificado em 2026-09-23 (#84) — a folha do admin tinha copia
     propria so por causa do fundo escurecido e do respiro que a marcacao dela
     precisa sem o `.modal-overlay`; isso agora mora aqui mesmo, por
     MARCACAO (`.modal:not(:has(.modal-overlay))`), e nao por pagina. O que
     continua de fora e a familia INTERNA — `.modal-content`, `.modal-header`,
     `.modal-title` (com `.warning-icon`/`.success-icon`/`.info-icon`, que o
     DS nao tem), `.modal-close`, `.modal-body`, `.modal-footer` — porque
     unificar essa parte e reescrever a marcacao de 22 modais num arquivo de
     76 KB, bloco proprio. Ate la a folha do admin continua vencendo nesses
     nomes, porque carrega depois desta, e nada muda de aparencia la.
   - **agenda** (`/agenda/styles.css`) tem deltas reais: modal mais estreito,
     animacao de entrada, e um `.modal-title` que empilha titulo e subtitulo.
     Deixar os deltas la e certo; o que sobrou de identico e divida registrada.

   `display: none` no repouso e `.active` para abrir: mostrar por classe e nao
   por `style` inline e o que permite a folha decidir COMO abre.
   ============================================================================ */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 1000;
  /* A centralizacao mora na regra BASE, e nao so em `.active`: o admin abre os
     modais dele com `style.display = 'flex'` inline, sem a classe. Quando a
     copia do admin saiu (#84), ela levou junto o `justify-content` e o
     `align-items` que estavam na base de la, e os dez modais passaram a abrir
     no canto superior esquerdo — 533 conferencias verdes, e so a captura de
     tela mostrou. Com `display: none` estas duas linhas nao tem efeito. */
  align-items: center;
  justify-content: center;
}
.modal.active { display: flex; }

/* O admin (#84) usa `.modal > .modal-content` DIRETO, sem o `.modal-overlay`
   filho que da o fundo escurecido nas outras paginas — era exatamente por
   isso que a regra dele sobrevivia numa folha propria depois da unificacao
   acima. `:has()` escolhe pela MARCACAO, e nao pela pagina: quem nao tem
   `.modal-overlay` ganha aqui o fundo e o respiro que a folha do admin dava.
   Os modais que `js/minha-agenda.js` planta no admin TEM `.modal-overlay` e
   ficam de fora desta regra, como em qualquer outra pagina. */
.modal:not(:has(.modal-overlay)) {
  background: rgba(15, 23, 42, .5);
  padding: var(--space-4);
}

.modal-overlay {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, .5);
}

.modal-container {
  position: relative;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  width: 90%;
  max-width: 640px;
  /* 85vh, e nao 100%: a caixa precisa mostrar que ha pagina atras dela, senao
     vira outra tela e o botao de fechar deixa de ser obvio. */
  max-height: 85vh;
  display: flex;
  flex-direction: column;
}
/* Confirmacao e formulario curto: 640px de largura para tres linhas de texto
   deixa a leitura arrastada. */
.modal-estreito { max-width: 460px; }
/* A lista da confirmacao do DS (`confirmarAcao({ itens })`): o que ela vai
   gravar, uma linha por item. */
.ui-confirmar-itens { margin: 8px 0 0; padding-left: 20px; }
.ui-confirmar-itens li { margin: 2px 0; overflow-wrap: anywhere; }

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-color);
}
.modal-title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}
.modal-title .material-icons { color: var(--primary); }
.modal-close {
  background: none;
  border: none;
  font-size: 24px;
  cursor: pointer;
  color: var(--text-secondary);
  padding: var(--space-1);
}
.modal-body {
  padding: var(--space-5);
  overflow-y: auto;
  flex: 1;
}
.modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border-color);
}
/* Rodape sem botao nao desenha borda: o modal de leitura ficava com um risco
   solto no pe. */
.modal-footer:empty { display: none; }

/* ============================================================================
   KPI — cartao de numero em destaque
   ----------------------------------------------------------------------------
   Havia tres implementacoes: `.pk` no painel, `.kpi` na agenda e `.stat-card` no
   dashboard do admin — esta ultima com gradiente no icone e barra colorida a
   esquerda, dois recursos que o dono ja rejeitara noutras telas.

   Dois FORMATOS, uma casca so. Ler a marcacao do dashboard mostrou que aqueles
   cartoes nao sao KPI: cada um tem titulo, dois ou tres valores e um rodape com
   total. Achatar isso num numero unico perderia informacao, entao o agrupado e
   um formato do mesmo componente — mesma superficie, mesmo bloco de icone solido,
   mesmos tokens de cor — e nao um componente a parte.

   A cor vem de `--kpi-cor`, definido pelos modificadores semanticos abaixo. Nunca
   gradiente: cor solida foi decisao do dono.
   ============================================================================ */

/* A linha: auto-fit resolve as tres telas — com quatro cartoes da quatro colunas
   no desktop e empilha sozinha no toque, sem media query por pagina. */
.kpi-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}
/* O agrupado carrega tres numeros lado a lado: 200px o espremeria. */
.kpi-row:has(.kpi-grupo) { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

.kpi,
.kpi-grupo {
  display: flex;
  align-items: stretch;
  background: var(--surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  min-width: 0;
  transition: box-shadow var(--transition-base), transform var(--transition-base);
}
.kpi:hover,
.kpi-grupo:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); }

.kpi-ico {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--space-4);
  background: var(--kpi-cor, var(--primary));
}
.kpi-ico .material-icons { font-size: 26px; color: var(--text-white); }

/* --- formato 1: um numero --- */
.kpi-txt { min-width: 0; padding: var(--space-3) var(--space-4); }
.kpi-v {
  display: block;
  font-size: 1.5rem;
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.kpi-k {
  display: block;
  font-size: var(--text-sm);
  color: var(--text-secondary);
  line-height: 1.25;
}

/* Variante de densidade, nao componente novo: numa barra de contexto o KPI fica
   logo acima de uma grade densa, e o tamanho cheio empurraria a grade para fora
   da dobra. */
.kpi.compacto .kpi-ico { padding: 0 var(--space-3); }
.kpi.compacto .kpi-ico .material-icons { font-size: 18px; }
.kpi.compacto .kpi-txt { padding: var(--space-2) var(--space-3); }
.kpi.compacto .kpi-v { font-size: 20px; }
.kpi.compacto .kpi-k { font-size: var(--text-xs); font-weight: var(--weight-semibold); }
.kpi.compacto .kpi-v small {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--text-secondary);
  margin-left: 1px;
}

/* --- formato 2: varios numeros sob um titulo ---
   EMPILHA: o icone e uma faixa no TOPO, nao uma tarja lateral. A versao lateral
   foi tentada e o dono preferiu esta: com tres numeros dentro, o cartao deitado
   fica alto demais e no celular cai um por linha — quatro cartoes viram quatro
   telas de rolagem. Empilhado eles cabem 2x2. As metricas abaixo sao as que ja
   existiam; o que mudou foi so a cor, que deixou de ser gradiente. */
.kpi-grupo { flex-direction: column; }
.kpi-grupo .kpi-ico { padding: var(--space-5); }
.kpi-grupo .kpi-ico .material-icons { font-size: 48px; }

.kpi-grupo-corpo {
  min-width: 0;
  padding: var(--space-5);
  flex: 1;
  display: flex;
  flex-direction: column;
}
.kpi-grupo-titulo {
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
  letter-spacing: .3px;
  margin: 0 0 var(--space-4);
}
.kpi-grupo-valores {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.kpi-grupo-item { text-align: center; }
.kpi-grupo-v {
  display: block;
  font-size: 32px;
  font-weight: var(--weight-bold);
  color: var(--text-primary);
  line-height: 1;
  margin-bottom: var(--space-2);
  font-variant-numeric: tabular-nums;
}
.kpi-grupo-k {
  display: block;
  font-size: var(--text-xs);
  color: var(--text-secondary);
  letter-spacing: .3px;
}
.kpi-grupo-rodape {
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-color);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  margin-top: auto;
}
.kpi-grupo-total { display: flex; justify-content: space-between; align-items: center; }
.kpi-grupo-total strong {
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

/* --- cor, por significado e nunca por posicao na tela --- */
.kpi-primary { --kpi-cor: var(--primary); }
.kpi-success { --kpi-cor: var(--success); }
.kpi-info    { --kpi-cor: var(--info); }
.kpi-warning { --kpi-cor: var(--warning); }
.kpi-error   { --kpi-cor: var(--error); }
/* Em zero, um KPI de alerta perde a cor de alerta: pintar de vermelho uma boa
   noticia inverte o sinal. */
.kpi-error .kpi-v { color: var(--error); }
.kpi-error.zerado { --kpi-cor: var(--text-disabled); }
.kpi-error.zerado .kpi-v { color: var(--text-primary); }

@media (max-width: 992px) {
  .kpi-grupo .kpi-ico { padding: var(--space-3) var(--space-4); }
  .kpi-grupo .kpi-ico .material-icons { font-size: 36px; }
  .kpi-grupo-corpo { padding: var(--space-4); }
  .kpi-grupo-v { font-size: 26px; }
}

@media (max-width: 640px) {
  /* Duas por linha, sempre: quatro cartoes empilhados viram quatro telas de
     rolagem antes de chegar no que vem depois deles. */
  .kpi-row, .kpi-row:has(.kpi-grupo) { grid-template-columns: repeat(2, 1fr); }
  .kpi-grupo .kpi-ico { padding: var(--space-2) var(--space-3); }
  .kpi-grupo .kpi-ico .material-icons { font-size: 28px; }
  .kpi-grupo-corpo { padding: var(--space-3); }
  .kpi-grupo-titulo { font-size: var(--text-xs); margin-bottom: var(--space-2); }
  .kpi-grupo-valores {
    display: flex;
    justify-content: space-evenly;
    gap: var(--space-2);
    margin-bottom: var(--space-2);
  }
  .kpi-grupo-v { font-size: 20px; margin-bottom: 2px; }
  .kpi-grupo-k { font-size: 10px; }
  .kpi-grupo-rodape { padding-top: var(--space-2); font-size: var(--text-xs); }
  .kpi-row { gap: var(--space-3); }
  .kpi-ico { padding: 0 var(--space-3); }
  .kpi-ico .material-icons { font-size: 22px; }
  .kpi-txt { padding: var(--space-2) var(--space-3); }
  .kpi-v, .kpi-grupo-v { font-size: 1.25rem; }
  .kpi-k { font-size: var(--text-xs); }
}

/* ============================================================================
   AVISO — a faixa de atencao, componente compartilhado
   ----------------------------------------------------------------------------
   Estava em DUAS folhas com dois nomes: `.ed-aviso` (Agenda, no modal de edicao
   da escala) e `.pend-aviso` (Painel, o "voce tem X atendimentos sem desfecho").
   Mesma caixa, mesmos tokens, mesma medida — copia, nao coincidencia. A oficina
   de fichas do Admin seria a TERCEIRA, e a terceira tela e sempre quem revela a
   copia (foi assim com `.btn`, com o datepicker e com `.form-input`).

   Aviso, e nao alarme: cor de atencao, nunca a de erro. O que ele diz e "leia
   antes de seguir", e nao "algo quebrou". Quem precisa de erro usa `--error`.

   Os dois nomes antigos entram como APELIDO, pela mesma razao de `.painel-tabs`
   na barra de abas: renomear mora na marcacao e no JS daquelas paginas. As duas
   folhas ainda trazem as suas copias — apagar aqueles dois blocos e um commit
   de uma linha para quem for dono deles, e o apelido garante que nada mude de
   aparencia no dia em que forem apagados.
   ============================================================================ */
.aviso,
.ed-aviso,
.pend-aviso {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--warning-bg);
  border: 1px solid var(--warning);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--text-primary);
  text-align: left;
}
.aviso > .material-icons,
.ed-aviso > .material-icons,
.pend-aviso > .material-icons { color: var(--warning); flex-shrink: 0; }
/* `display: flex` numa classe vence o `display: none` do atributo `hidden` —
   mesma armadilha que `.termo-revogacao` ja tinha pisado no admin. */
.aviso[hidden] { display: none; }

/* ============================================================================
   BARRA DE ABAS — componente compartilhado
   ----------------------------------------------------------------------------
   Estava em TRES folhas: `.tabs-nav`/`.tab-btn` no Admin e na Agenda, e a mesma
   coisa com outro nome (`.painel-tabs`/`.painel-tab`) no Painel. As copias ja
   tinham divergido em silencio, e da para medir: 47px de altura na Agenda e no
   Painel contra 55px no Admin, e o rotulo da aba ativa em semibold nas duas
   primeiras contra medium na terceira. Ninguem decidiu isso — foi copia
   envelhecendo em tres lugares.

   O Painel entra pelo NOME ANTIGO porque renomear a classe mora na marcacao e em
   `painel/agenda.js`. O apelido e transitorio; a definicao ja e uma so, que era o
   ponto. Quem for renomear troca as duas pontas no mesmo commit.

   O que a tela estreita faz com as abas fica em CADA pagina, e nao aqui: o Admin
   tem oito e as poe para rolar, a Agenda tem quatro e esconde o rotulo, o Painel
   tem duas e cabe com rotulo e tudo. Sao decisoes de layout daquela pagina, nao
   do componente.
   ============================================================================ */
.tabs-nav,
.painel-tabs {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
  border-bottom: 2px solid var(--border-color);
  flex-wrap: wrap;
}

.tab-btn,
.painel-tab {
  background: none;
  border: none;
  padding: var(--space-3) var(--space-5);
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-primary);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  color: var(--text-secondary);
  cursor: pointer;
  transition: all var(--transition-base);
  white-space: nowrap;
  border-bottom: 3px solid transparent;
  margin-bottom: -2px;
}
.tab-btn .material-icons,
.painel-tab .material-icons { font-size: 20px; }
.tab-btn:hover,
.painel-tab:hover { color: var(--primary); background: var(--background); }
/* A aba ativa e mais escura E mais pesada: sobre um fundo claro, so a cor
   distingue mal quando a barra tem oito abas. */
.tab-btn.active,
.painel-tab.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
  font-weight: var(--weight-semibold);
}

/* ============================================================================
   SPINNER — o disco que gira enquanto algo carrega
   ----------------------------------------------------------------------------
   Tres definicoes e DOIS nomes de animacao para o mesmo circulo: `girar` na
   Agenda, `spin` no Registro e no Admin. O que muda de tela para tela e so o
   diametro, a espessura que o acompanha e a velocidade da volta — e isso
   continua em cada folha, como override do que esta aqui.
   ============================================================================ */
.spinner {
  width: 28px;
  height: 28px;
  border: 3px solid var(--border-color);
  border-top-color: var(--primary);
  border-radius: 50%;
  animation: girar .8s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* ============================================================================
   TEMA DO CALENDARIO (flatpickr) — componente do design system
   ----------------------------------------------------------------------------
   Morava em `painel/styles.css`, enquanto os seletores de mes e ano (`.fpx-sel`,
   logo acima) moravam aqui e ASSUMIAM este tema: `.fpx-sel-btn` usa
   `color: var(--text-white)`, que so se le sobre o cabecalho pintado de
   `--primary`. A Agenda carregou o datepicker sem esta folha e o cabecalho saiu
   branco no branco — os seletores estavam la, do tamanho certo, invisiveis.
   Metade de um componente num arquivo de pagina e a outra metade no design
   system e o tipo de divisao que so aparece quando a terceira tela chega.
   ============================================================================ */
.flatpickr-calendar {
  font-family: var(--font-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--border-color);
  background: var(--surface);
  width: 320px;
  color-scheme: light;
}
.flatpickr-calendar.open { z-index: 9999; }
.flatpickr-months {
  background: var(--primary);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  align-items: center;
}
.flatpickr-months .flatpickr-month {
  background: transparent;
  color: var(--text-white);
  height: 48px;
}
.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
  fill: var(--text-white);
  color: var(--text-white);
  padding: 8px 12px;
  height: 48px;
  display: flex;
  align-items: center;
}
.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
  background: rgba(255,255,255,0.15);
  border-radius: var(--radius-sm);
}
.flatpickr-months .flatpickr-prev-month:hover svg,
.flatpickr-months .flatpickr-next-month:hover svg {
  fill: var(--text-white);
}
.flatpickr-current-month {
  padding-top: 0;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  color: var(--text-white);
  font-size: var(--text-base);
  font-weight: 600;
}
.flatpickr-current-month span.cur-month {
  color: var(--text-white);
  font-weight: 600;
}
/* O ano E interativo: o calendario deixou de ser so um filtro passivo e virou o
   navegador da agenda, onde saltar de ano precisa ser possivel. Antes tinha
   `pointer-events: none` e as setas escondidas, entao o unico jeito de mudar de
   ano era avancar mes a mes ate dezembro. */
.flatpickr-current-month input.cur-year {
  color: var(--text-white);
  font-weight: 600;
  font-size: var(--text-base);
  background: transparent;
  border: none;
  padding: 0;
  width: 52px;
  cursor: text;
}
.flatpickr-current-month input.cur-year:focus {
  background: rgba(255, 255, 255, .15);
  border-radius: var(--radius-sm);
}
.flatpickr-current-month .numInputWrapper { width: 68px; }
.flatpickr-current-month .numInputWrapper:hover { background: rgba(255, 255, 255, .12); }

/* setas do ano: alvo generoso, porque a maioria acessa por iPhone */
.flatpickr-current-month .numInputWrapper span {
  display: block;
  border: none;
  width: 18px;
  padding: 0;
  right: 0;
  opacity: 1;
}
.flatpickr-current-month .numInputWrapper span:hover { background: rgba(255, 255, 255, .22); }
.flatpickr-current-month .numInputWrapper span.arrowUp::after { border-bottom-color: var(--text-white); }
.flatpickr-current-month .numInputWrapper span.arrowDown::after { border-top-color: var(--text-white); }
span.flatpickr-weekday {
  color: var(--text-secondary);
  font-weight: 600;
  font-size: var(--text-xs);
  text-align: center;
}
.flatpickr-weekdays { padding: 8px 0 4px; width: 100% !important; }
.flatpickr-weekdaycontainer { width: 100% !important; display: flex !important; justify-content: space-around; }
.flatpickr-innerContainer,
.flatpickr-rContainer,
.flatpickr-days,
.dayContainer { width: 100% !important; max-width: 100% !important; min-width: 0 !important; }
.dayContainer { justify-content: center; }
.flatpickr-day {
  color: var(--text-primary);
  border-radius: var(--radius-sm);
  max-width: none;
  flex-basis: 14.2857%;
  height: 40px;
  line-height: 40px;
  font-size: var(--text-sm);
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
}
.flatpickr-day:hover,
.flatpickr-day:focus {
  background: var(--primary-50);
  border-color: var(--primary-light);
}
.flatpickr-day.today { border-color: var(--primary); font-weight: 700; }
.flatpickr-day.today:hover { background: var(--primary-50); border-color: var(--primary); }
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.selected:focus {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--text-white);
  font-weight: 600;
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay { color: var(--text-disabled); }
.flatpickr-day.flatpickr-disabled { color: var(--text-disabled); }
@media (max-width: 480px) {
  .flatpickr-calendar { width: calc(100vw - 32px); left: 16px !important; }
}

/* -------------------------------------------------- datepicker padrão do app
   O gatilho É o rótulo da data, com um ícone discreto dizendo que dá para
   tocar. Nasceu em "Minha agenda" e vale para toda tela: um ícone solto ao lado
   do texto obriga a mirar num alvo pequeno, e o rótulo já está ali. */
.cal-btn {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  background: none;
  border: none;
  padding: var(--space-1) var(--space-2);
  margin: calc(var(--space-1) * -1) calc(var(--space-2) * -1);
  border-radius: var(--radius-md);
  font-family: var(--font-primary);
  color: inherit;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.cal-btn:hover { background: var(--primary-50); }
/* O rótulo acompanha o ícone no hover: ele é o gatilho, e só o ícone mudar de
   cor sugere que o clique é no ícone. */
.cal-btn:hover .cal-ico,
.cal-btn:hover :is(h4, span) { color: var(--primary); }
.cal-ico { font-size: 16px; color: var(--text-disabled); align-self: center; }

/* Âncora do flatpickr: fica fora do que se redesenha, invisível e fora do foco.
   O calendário se posiciona pelo botão, não por ela. */
.cal-ancora {
  position: absolute;
  width: 0; height: 0;
  padding: 0; border: 0;
  opacity: 0;
  pointer-events: none;
}

@media (pointer: coarse) {
  .fpx-sel-op { padding: 12px; font-size: var(--text-base); }
  .fpx-sel-lista { max-height: 280px; }

  /* Alvos de toque: a maioria acessa por iPhone, e o padrão do flatpickr fica
     abaixo dos 44px recomendados. Estava só no painel — a agenda herdava um
     calendário de alvos pequenos. */
  .flatpickr-calendar { width: 320px; }
  .flatpickr-day { height: 42px; line-height: 42px; max-width: 42px; }

  /* As setas de mes. O padrao do flatpickr da 38px de largura, abaixo do alvo
     recomendado. A regra existia — em `painel/agenda.css`. Medido num aparelho
     de toque de 390px: a seta tinha 46px no painel e 38px na agenda e no admin.
     O mesmo calendario, em dois tamanhos, porque metade do componente morava
     numa folha de pagina. */
  .flatpickr-months .flatpickr-prev-month,
  .flatpickr-months .flatpickr-next-month { padding: 12px 16px; }
  .flatpickr-current-month .numInputWrapper span { width: 24px; height: 22px; }
}

/* ============================================================================
   BOTAO DE ICONE (.btn-icon) — acao secundaria dentro de uma linha de lista

   Morava so em `admin/styles.css`, e virou compartilhado no dia em que o cartao
   de "Meus Pacientes" (`/painel/`) precisou de uma segunda acao ao lado do
   toque principal. A alternativa era um sosia local com as mesmas medidas —
   exatamente a copia que `.btn`, `.badge` e `.modal-container` ja custaram.

   40px de diametro: alvo de toque minimo, e o mesmo do `.btn-icon` do Admin,
   medido no renderizado. As variantes SEMANTICAS (`edit`, `delete`,
   `reactivate`) ficam no Admin, que e quem tem essas acoes — cor de destruicao
   nao e vocabulario que toda tela precise carregar.
   ============================================================================ */
.btn-icon {
  width: 40px;
  height: 40px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  /* Serve tambem para `<a>`: o atalho do prontuario no cartao do paciente e um
     LINK (tem endereco, abre em nova aba, o "voltar" funciona), e sem isto ele
     saia sublinhado no meio de dois botoes. */
  text-decoration: none;
  transition: all var(--transition-base);
}
.btn-icon:hover { background: var(--background); color: var(--primary); }
.btn-icon:disabled { opacity: .5; cursor: not-allowed; }

/* ============================================================================
   VAZIO E CARREGANDO — a resposta "nao ha nada" e a resposta "ainda nao sei"

   Terceira tela, e o enredo de sempre (antes: `.btn`, `.form-input`, `.badge`,
   `.modal-container`). `admin/styles.css` e `painel/styles.css` declaravam a
   mesma classe com medidas diferentes — 32px de respiro contra 24px, corpo
   base contra `text-sm` —, e a tela de preencher ficha escreve
   `class="loading-state"` no HTML contra uma base que NAO EXISTE na folha dela:
   o "Carregando…" da tela mais movel do sistema saia sem estilo nenhum. A
   linha do tempo do prontuario seria a quarta marcacao.

   GEOMETRIA CANONICA: a do Admin. Nao e antiguidade — e o unico dos dois
   blocos que suporta a marcacao completa (icone de 48px, `h3`, `p`), que e a
   forma que um vazio precisa ter quando ele tambem EXPLICA o vazio: "nenhum
   registro no recorte" acompanhado de "os filtros estao ativos". O bloco do
   Painel so servia a uma linha de texto solta, e uma linha de texto cabe na
   geometria maior; o contrario nao e verdade.
   ============================================================================ */
.empty-state,
.loading-state {
  text-align: center;
  padding: var(--space-8);
  color: var(--text-secondary);
}
.empty-state .material-icons {
  font-size: 48px;
  color: var(--text-disabled);
  margin-bottom: var(--space-4);
}
.empty-state h3 {
  font-size: var(--text-xl);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  margin-bottom: var(--space-2);
}
.empty-state p { margin-bottom: var(--space-6); }
/* O ultimo paragrafo nao empurra o fundo do bloco: o respiro de baixo ja e o
   `padding` do proprio `.empty-state`, e somar os dois deixava o texto
   visivelmente fora do centro vertical da caixa. */
.empty-state p:last-child { margin-bottom: 0; }

/* ============================================================================
   SELO (.badge) — rotulo curto grudado num objeto que ja tem titulo proprio

   Morava em `painel/styles.css`, e a marcacao do Admin e da Agenda escrevia
   `class="badge badge-x"` contra uma base que ali nao existia: cada variante
   reescrevia a geometria inteira, e as tres envelheceram diferente. Quarta vez
   o mesmo enredo (antes: `.btn`, o datepicker e `.form-input`).

   GEOMETRIA CANONICA: 12px / 500 / 4px 8px / raio total. Nao e gosto:

   1. HIERARQUIA, medida no renderizado. O selo e rotulo SECUNDARIO de um objeto
      que ja tem titulo. Painel: titulo 16/600, meta 12/400, selo 12/500.
      Agenda: 14/600, 12/400, 12/500. Admin: titulo 18/500, meta 14/400, selo
      14/600 — o selo era o texto mais PESADO do cartao, mais pesado que o nome
      da crianca ao lado. Rotulo secundario nao passa na frente do titulo.
   2. QUANTOS CONSUMIDORES, contados na MARCACAO e nao em blocos de CSS. 12/500
      renderiza em tres telas (Painel, Agenda e o `.badge-audit` do Admin);
      14/600 em uma, com duas variantes.
   3. O QUE O DS JA DIZ para a mesma familia: `.notif-type-badge`, algumas
      linhas acima, ja e 12px/500/raio total. Canonizar 14/600 poria duas
      escalas de pilula contraditorias no mesmo arquivo.

   `display` FAZ PARTE do componente. Painel e Agenda declaravam a MESMA
   geometria e desenhavam caixas DIFERENTES — 27.2px contra 23.0px — porque
   nenhuma das duas declarava `display`: no Painel o selo cai dentro de um flex
   (`.sessao-badges`) e e blocificado, ganhando a caixa de linha inteira; na
   Agenda e um span inline, cuja caixa de linha nao cresce com padding vertical.
   Componente cuja altura depende do pai nao e componente. Por isso tambem o
   `line-height` e fixado aqui, em vez de herdado do contexto.

   COR: o texto do selo precisa de 4.5:1 sobre o PROPRIO fundo. Os tokens de
   acento (`--success`, `--warning`, `--info`) foram escritos para elemento
   GRAFICO, que precisa de 3:1 — o comentario dos `--mod-*` diz isso com todas
   as letras. Usa-los como cor de TEXTO era o defeito, e ele estava em quase
   todas as variantes: escolar dava 3.15:1, "propria" 2.35:1, "expirado" 2.45:1.
   A regra e por variante, nao uniforme: onde o acento passa ele fica (clinica,
   8.11:1); onde nao passa, o texto cai para `--text-primary` e a informacao
   continua dita pela PALAVRA, que o selo sempre carrega — o mesmo criterio de
   "forma + texto, nunca so cor" que governa `--desf-*`.
   ============================================================================ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  line-height: 1.6;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  white-space: nowrap;
}
/* `line-height: 1` nao e enfeite: sem ele o glifo do Material Icons herda a
   entrelinha do corpo e cresce a caixa de linha do selo, e o mesmo componente
   fica 0.8px mais alto so por ter icone — no Admin, que tem, contra o Painel,
   que nao tem. */
.badge .material-icons { font-size: 14px; line-height: 1; }

/* Modalidade do atendimento. Sai dos `--mod-*`, os MESMOS tokens que pintam a
   celula da grade: um segundo azul para dizer "clinica" seriam duas verdades
   sobre a mesma palavra.

   O apelido `.badge-aba-escolar` FOI EMBORA na migration 035, junto com o
   booleano `sessoes.aba_escolar`: a modalidade tem tres valores, "ABA Escolar"
   nomeava so um deles e nem todo atendimento fora da clinica e ABA (fono e TO
   tambem vao a escola). As tres variantes agora se chamam pelo id da
   modalidade, o mesmo que a API devolve — `badge-` + `modalidade`, sem de-para
   na marcacao. */
.badge-clinica {
  background: var(--mod-clinica-bg);
  color: var(--mod-clinica-ac);
}
.badge-escolar {
  background: var(--mod-escolar-bg);
  color: var(--text-primary);
}
/* Estado do documento de prontuario. FORMA + TEXTO, nunca so cor — regra do DS
   e da proposta: rascunho e TRACEJADO, finalizado leva CADEADO, adendo leva
   icone proprio. Quem nao distingue as cores continua lendo a palavra e vendo a
   borda.

   Dois consumidores desde o primeiro dia: a tela de preencher ficha e a faixa
   de pendencias do painel, que lista as fichas em rascunho ao lado dos
   atendimentos sem desfecho. A linha do tempo da P3 sera o terceiro. */
.badge-rascunho {
  background: var(--background);
  color: var(--text-secondary);
  border: 1px dashed var(--border-dark);
}
/* Pessoa cadastrada sem papel (#112): nao alcanca tela nenhuma ate alguem
   conceder acesso. Mesma gramatica do `.badge-rascunho` — fundo neutro, borda
   TRACEJADA, forma + palavra — porque e o mesmo tipo de aviso: "ainda nao e o
   que devia ser", nao um erro. NAO usa `--warning`: a faixa de "N pessoas sem
   acesso" acima da lista ja e ambar, e um segundo ambar no mesmo campo de
   visao empataria os dois avisos entre si. */
.badge-pendente {
  background: var(--background);
  color: var(--text-primary);
  border: 1px dashed var(--border-dark);
  /* E `<button>`, nao `<span>` (§4.6): no celular nao existe `hover`, e a dica
     precisa ser alcancavel por toque e por teclado. O reset abaixo cobre o que
     a folha da UA aplica a botao e as regras acima ainda nao cobriam. */
  font-family: inherit;
  margin: 0;
  cursor: pointer;
}
.badge-pendente:hover,
.badge-pendente:focus-visible {
  background: var(--background-alt);
  outline: 2px solid var(--primary);
  outline-offset: 1px;
}
.badge-aguardando {
  background: var(--info-bg);
  color: var(--text-primary);
}
.badge-finalizado {
  background: var(--success-bg);
  color: var(--text-primary);
}
.badge-adendo {
  background: var(--primary-50);
  color: var(--primary);
}
/* O quinto da familia, e o que a linha do tempo da P3 trouxe: material de
   avaliacao psicologica e de acesso exclusivo do psicologo (Res. CFP 001/2009,
   art. 2o, V), e o documento restrito APARECE na linha do tempo sem o corpo.
   Cinza e nao vermelho de proposito: nada esta errado, e o selo nao e alerta —
   e a mesma leitura do `.badge-rascunho`, que tambem diz "ainda nao e o que
   voce esperava ver" sem gritar. Olho fechado + a palavra, e nao cadeado: o
   cadeado ja e o icone de `.badge-finalizado`, e documento restrito costuma ser
   finalizado tambem — dois cadeados na mesma linha, um dizendo "fechado" e o
   outro "reservado", nao distinguem coisa nenhuma. */
.badge-restrito {
  background: var(--background-alt);
  color: var(--text-primary);
  border: 1px solid var(--border-dark);
}

/* Domiciliar nasce agora: a tela de preencher ficha traz a modalidade do
   atendimento vinda da agenda, e a agenda tem os TRES valores — o bloco
   domiciliar apareceria sem selo nenhum, que e dizer "clinica" por omissao.
   O texto e `--text-primary` e nao `--mod-domiciliar-ac`: o acento (#d97706)
   sobre o proprio fundo (#fffbeb) da 3.4:1, e selo e TEXTO — mesmo criterio
   que ja rebaixou o de escolar. */
.badge-domiciliar {
  background: var(--mod-domiciliar-bg);
  color: var(--text-primary);
}

/* Sensibilidade de uma PERMISSAO (migration 038). Duas variantes so: `comum`
   nao ganha selo nenhum, porque selo que aparece em toda linha deixa de marcar
   qualquer coisa — o mesmo criterio que fez `.badge-clinica` existir e
   "nenhuma modalidade" nao existir.

   FORMA + PALAVRA, nunca so cor: cada uma traz icone proprio (`gpp_maybe` /
   `gpp_bad`) e o rotulo escrito. Ambar e vermelho sao exatamente o par que a
   tela de desfecho ja tratou como indistinguivel sem um segundo canal.

   O texto e `--text-primary` sobre os dois fundos, e nao o acento: `--warning`
   (#d97706) sobre `--warning-bg` (#fffbeb) da 3.4:1, e selo e TEXTO — o mesmo
   rebaixamento que `.badge-escolar` e `.badge-domiciliar` ja levaram. A borda
   carrega o acento, onde 3:1 basta. */
.badge-sensivel {
  background: var(--warning-bg);
  color: var(--text-primary);
  border: 1px solid var(--warning);
}
.badge-critica {
  background: var(--error-bg);
  color: var(--text-primary);
  border: 1px solid var(--error);
}

/* ============================================================================
   BUSCA COM LIMPAR — o "x" dentro do campo de busca por nome
   ----------------------------------------------------------------------------
   Achado do dono (2026-09-08), testando a aba Profissionais: sem um botao de
   limpar, o unico jeito de esvaziar a busca era apagar letra por letra. Mora
   aqui, e nao em `admin/styles.css` (onde `.filter-search` foi definida), por
   quatro chamadores dentro da mesma pagina — Pacientes, Profissionais,
   Programas e Especialidades — que hoje repetem o mesmo par de classes; e o
   mesmo raciocinio que ja trouxe `.badge-pendente` para ca antes da segunda
   PAGINA aparecer.
   ============================================================================ */
.busca-com-limpar { position: relative; }
.busca-com-limpar .form-input { padding-right: var(--space-8); }
.busca-limpar {
  position: absolute;
  right: var(--space-2);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: var(--space-1);
  margin: 0;
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  cursor: pointer;
}
.busca-limpar .material-icons { font-size: 18px; }
.busca-limpar:hover,
.busca-limpar:focus-visible { background: var(--background-alt); color: var(--text-primary); }
/* `hidden` no atributo, nao numa classe: mesma convencao do resto do app —
   o campo comeca vazio, entao o botao comeca escondido. */
.busca-limpar[hidden] { display: none; }

/* ============================================================================
   AS DUAS DATAS DE UM REGISTRO DE SESSAO (migration 037)

   `data_atendimento` diz QUANDO ACONTECEU; `timestamp` diz QUANDO SE LANCOU.
   Elas divergem legitimamente, e a divergencia e informacao de gestao — nao
   inconsistencia a corrigir. Tres telas mostram esse par (painel, historico do
   Admin, modal dos dois), entao o componente mora aqui e nao em cada uma.

   HIERARQUIA: o atendimento e a data que governa a leitura clinica, e por isso
   fica no corpo do texto do contexto (herda tamanho e cor do `.sessao-meta` que
   o abriga). O lancamento e um segundo plano deliberado — mesma familia, um
   passo atras.

   COR NAO CARREGA INFORMACAO NENHUMA AQUI. Cada estado se le pela PALAVRA
   ("Atendimento em", "data nao informada", "3 dias depois") e pelo ICONE
   (`event`, `event_busy`, `edit_calendar`, `history`). O acento de `--warning`
   pinta o icone do atraso, que e elemento GRAFICO (3:1 basta); o texto ao lado
   fica em `--text-secondary`, que e o mesmo criterio por variante que governa
   os `.badge-*` acima.

   O texto de "data nao informada" e `--text-secondary` e nao `--text-disabled`:
   a linha nao esta desabilitada, esta dizendo uma verdade sobre o registro, e
   3.515 sessoes a exibem — rebaixa-la para o cinza de desativado (3.6:1 sobre
   branco) reprovaria o contraste de corpo de texto na maioria da tela.
   ============================================================================ */
.data-atendimento,
.data-lancamento {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  white-space: nowrap;
}
/* Sozinha tambem: no modal o atraso e um trecho DENTRO do valor do campo
   "Lançamento", e nao a linha inteira do cartao. */
.data-atrasada {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.data-atendimento .material-icons,
.data-lancamento .material-icons,
.data-atrasada .material-icons {
  font-size: 14px;
  line-height: 1;
  flex-shrink: 0;
}
.data-desconhecida {
  color: var(--text-secondary);
  font-style: italic;
}
.data-lancamento {
  color: var(--text-secondary);
  font-size: var(--text-xs);
}
.data-atrasada .material-icons { color: var(--warning); }
.data-atrasada strong {
  font-weight: var(--weight-semibold);
  color: var(--text-primary);
}

/* Datas em coluna quando o cartao mostra as duas: lado a lado elas competem, e
   e justamente o par divergente (o caso que importa) que fica mais dificil de
   ler. Em tela estreita a regra ja vale por quebra natural; aqui e explicita
   para nao depender da largura. */
.datas-do-registro {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  /* Item `flex` que hospeda texto precisa poder ENCOLHER. E a mesma armadilha
     do LEDGER #87, aqui pela quinta vez. */
  min-width: 0;
}

/* O `nowrap` acima serve ao desktop, onde quebrar "Atendimento em 30/08/2026,
   14:00–14:40" no meio seria pior que a linha longa. Na tela estreita ele vira
   defeito: a linha empurra o cartao para 398px numa janela de 360, a pagina
   rola de lado, e NENHUM texto aparece cortado — o sintoma que nao denuncia a
   causa. Foi assim que o #86 nasceu, e a linha do tempo da F5, com mais coisa
   competindo na mesma linha, transformou os 2px daquele caso em 38px.
   Quebrar aqui e melhor que rolar a pagina inteira de lado. */
@media (max-width: 480px) {
  .data-atendimento,
  .data-lancamento,
  .data-atrasada {
    white-space: normal;
    flex-wrap: wrap;
  }
}

/* A NOTA DO FILTRO. Existe para uma frase so, e a frase e obrigatoria: uma tela
   que oferece "De" e "Ate" sem dizer QUAL das duas datas do registro esta
   filtrando responde a pergunta errada em silencio. Desde a F4 a resposta e "a
   data em que aconteceu, caindo no lancamento quando ela nao existe" — antes
   era o lancamento puro. */
.filtros-nota {
  grid-column: 1 / -1;
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--text-xs);
  line-height: 1.5;
  color: var(--text-secondary);
  background: var(--background-alt);
  border-left: 3px solid var(--border-dark);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-2);
}
.filtros-nota .material-icons { font-size: 16px; line-height: 1.3; flex-shrink: 0; }
.filtros-nota strong { color: var(--text-primary); font-weight: var(--weight-semibold); }

/* ============================================================================
   .action-row — a LINHA DE ACAO

   Uma linha inteira que se toca: icone, texto (com um `small` opcional de
   apoio) e a acao. Serve tanto a `<button>` quanto a `<a href>` — o `width` e o
   `text-align` existem para que os dois desenhem igual, porque elemento
   interativo nativo traz alinhamento proprio do navegador.

   Veio de `agenda/styles.css`, onde nasceu com um consumidor so. O criterio de
   promocao estava escrito no indice acima e foi cumprido: o segundo consumidor
   e o link do modal de desfecho para o prontuario da crianca, que nasce em
   `js/minha-agenda.js` e portanto aparece na Agenda, no painel e no admin —
   tres paginas, das quais duas nao carregam a folha da Agenda.
   ============================================================================ */
.action-row {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; text-align: left;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  background: var(--surface);
  cursor: pointer;
  font-family: var(--font-primary);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--text-primary);
  transition: all var(--transition-fast);
}
.action-row:hover { border-color: var(--primary-light); background: var(--primary-50); }
.action-row .ico { font-size: 20px; color: var(--primary); flex-shrink: 0; }
.action-row small {
  display: block;
  font-weight: var(--weight-regular);
  color: var(--text-secondary);
  font-size: var(--text-xs);
}
.action-row.danger .ico { color: var(--error); }
.action-row.danger:hover { border-color: var(--error); background: var(--error-bg); color: var(--error); }
.action-row.danger:hover small { color: var(--error); }

/* ============================================================================
   PULSO — "algo esta correndo agora"
   ============================================================================

   Anel que se expande a partir de um icone. Nasce com os episodios de
   comportamento interferente do `/registro/` (pedido 108), e mora aqui porque
   a pergunta que ele responde nao e daquela tela: "isto esta em andamento e
   depende de voce encerrar" e a mesma frase de um atendimento sem desfecho ou
   de um documento em rascunho.

   FORMA JUNTO COM COR, como o ponto de desfecho da agenda: o anel pulsa, o
   icone e um cronometro, e os digitos correm ao lado. Quem nao distingue a cor
   ve o movimento; quem tem o movimento desligado ve o anel parado, maior que o
   de repouso — e por isso a regra de `prefers-reduced-motion` existe em vez de
   simplesmente sumir com a animacao.
*/
.pulso-anel {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--surface);
  color: var(--warning);
  flex-shrink: 0;
}
.pulso-anel .material-icons { font-size: 22px; }
.pulso-anel::before,
.pulso-anel::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid currentColor;
}
.pulso-anel::after { animation: pulsoAnel 1.6s ease-out infinite; }
@keyframes pulsoAnel {
  0%   { transform: scale(1);   opacity: .9; }
  100% { transform: scale(1.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .pulso-anel::after { animation: none; transform: scale(1.35); opacity: .5; }
}
